# Example: Sample Companion Pack

**Example plugin** · 1 look with motions · 2 characters · no code, no permissions

This pack ships everything companion mode can take from a plugin, with no `index.ts` at all: nothing runs in the
sandbox, and the Browse page shows no permissions and nothing it can reach.

## Layout

```
readme.md
companion-models/sample-seed/
  model.json  seed-san.vrm  thumbnail.png  background.jpg
  idle-neutral.vrma  talk-idle.vrma  listen.vrma  think.vrma  wave.vrma  laugh.vrma
companion-characters/sample-seed-character/character.json
companion-characters/sample-rose-character/character.json
```

Three keys — `sample-seed`, `sample-seed-character`, `sample-rose-character` — all different, because keys are
unique across the whole package (a character can't share its model's key). The readme uses `version: 1.3`.

## The look and its motions

`model.json` lists the look's motions as `file:` clips in the model's own folder — every base role gets one body
clip, plus one gesture and one reaction:

| Motion | Role | Format | Notes |
| --- | --- | --- | --- |
| `file:idle-neutral.vrma` | `idle` | VRMA | required — every look needs at least one idle |
| `file:talk-idle.vrma` | `talkIdle` | VRMA | required — the loop while speaking; `cues: ["calm"]`, so a calm reply prefers it |
| `file:listen.vrma` | `listen` | VRMA | a nod while the user talks |
| `file:think.vrma` | `think` | VRMA | hand to chin while the agent works |
| `file:wave.vrma` | `gesture` | VRMA | required (at least one gesture) — `cues: ["waving"]`, `strokeSeconds: 0.9` |
| `file:laugh.vrma` | `reaction` | VRMA | `cues: ["laughing", "chuckling"]` — plays on a `[laughing]` line |

- **VRMA clips need nothing else** — no `rig`; they always drive the humanoid body.
- **`cues` and `strokeSeconds`** let the companion land the wave on a greeting at the right beat.
- **A companion wears the look by reference.** Publish a new version and, once a user's installation moves to it,
  every companion wearing the look moves the new way.

```json
{
  "name": "Sample Seed-san",
  "description": "VirtualCast's sample VRM avatar Seed-san, with a small set of its own motions, for testing plugin looks end to end.",
  "avatar": "seed-san.vrm",
  "thumbnail": "thumbnail.png",
  "background": "background.jpg",
  "outfits": [
    { "key": "plain", "name": "Plain", "meshes": ["wear"] },
    { "key": "cyborg", "name": "Robot arm", "meshes": ["wear", "robo_arm"] }
  ],
  "motions": [
    { "id": "file:idle-neutral.vrma", "roles": ["idle"], "name": "Idle", "description": "A long, unhurried neutral standing idle loop." }
  ]
}
```

(The real file lists all six motions.)

- **Outfits** name parts of the VRM by mesh. `robo_arm` is in only one outfit, so it is hidden in `plain` and shown in `cyborg`;
  `wear` is in both, so the clothes stay on either way, and the head, hair and tail (in no outfit) always show. The agent
  switches with `[outfit:cyborg]`; the Companion tab's wardrobe button does it by hand.
- **`background.jpg`** is a soft, out-of-focus room picture (1920×1080) drawn blurred behind her.

## Beyond this sample: cloth, walking and 2.5D

This pack is the smallest one. Two other real packs show the rest of `model.json`:

- **Yuzuki** (`plugins/yuzuki`, published as `agentparley-yuzuki`) is a VRM with four outfits that set body shapes, an ARKit
  face map, tail and ear `partsGesture` clips, and a `walk` clip (`"roles": ["walk"]`) so she paces. Her Casual, Loungewear and
  Swimwear outfits carry `"cloth": []` (no skirt), so only Dress uses the skirt the stage detects. She declares no `framing`,
  `pacing` or `cloth` of her own: the defaults apply.
- **Mira** (`infrastructure/plugins/hello-live2d`) is a 2.5D look in a `.zip` with `avatar:` motions and a `framing` block
  (`defaultPreset: "upperBody"`, a `face` window of `bottom: 0.58`, `top: 0.86`). It declares no `walk`, outfits or cloth, which a 2.5D look can't use.

See [Companion packs](plugin-companion-packs.md) for every field and what the stage does when you leave one out.

## The item pack — Sample Companion Items

A second fixture, `infrastructure/plugins/sample-companion-items`, ships two props as data (no code, no permissions):

```
readme.md
companion-items/mug/       item.json  mug.glb        thumbnail.png
companion-items/party-hat/ item.json  party-hat.glb  thumbnail.png
```

```json
{
  "name": "Mug",
  "description": "A warm mug of coffee, held in the right hand.",
  "model": "mug.glb",
  "thumbnail": "thumbnail.png",
  "attach": { "bone": "rightHand", "position": [-0.03, 0, 0.03], "rotation": [0, 0, -90], "scale": 1 }
}
```

Install it next to the companion pack and the mug and the hat show up in every 3D companion's wardrobe menu; the agent can write
`[hold:mug]`, `[hold:party-hat]` or `[hold:none]`. See [Companion packs](plugin-companion-packs.md#item--itemjson).

## Two characters, two patterns

**Sample Seed** — the pack's own look, plus the built-in Rose look as a second option:

```json
{
  "name": "Sample Seed",
  "vibe": "A test character with a plugin look that brings its own motions.",
  "look": "sample-seed",
  "soul": "You are Sample Seed, a cheerful test companion. You keep answers short and friendly.",
  "instructions": "You talk with the person out loud. Keep replies to a sentence or two and avoid lists.",
  "voice": {
    "description": "A bright, friendly young voice.",
    "suggestions": [
      { "provider": "fish-audio", "voiceId": "8ef4a238714b45718ce04243307c57a7", "voiceName": "Sample voice" },
      { "provider": "elevenlabs", "voiceId": "21m00Tcm4TlvDq8ikWAM", "voiceName": "Rachel", "models": ["eleven_v3"] }
    ]
  }
}
```

**Sample Rose** — the built-in Rose look, only a new personality and voice:

```json
{
  "name": "Sample Rose",
  "vibe": "A test character on the built-in Rose look, with a voice only Deepgram offers.",
  "look": { "builtIn": "rose" },
  "soul": "You are Sample Rose, a calm and precise test companion who keeps people on track.",
  "instructions": "You talk with the person out loud. Be brief and clear.",
  "voice": {
    "description": "A calm, clear woman's voice.",
    "suggestions": [
      { "provider": "deepgram", "voiceId": "aura-2-thalia-en", "voiceName": "Thalia" }
    ]
  }
}
```

- **How she moves is data too.** A character may add a `behaviour` block next to `voice`, for example a calmer listener
  who reacts to her name and to the web-search tool:

  ```json
  "behaviour": {
    "temperament": { "energy": 0.9 },
    "behaviours": { "backchannels": { "strength": 0.75 } },
    "wordReactions": [ { "phrases": ["{name}", "seed"], "reaction": "browRaise" } ],
    "toolActivity": [ { "when": ["builtin:web_search"], "reaction": "reading" } ]
  }
  ```

- **Voice suggestions are ordered.** Sample Seed's companion uses fish.audio if the account has it, else
  ElevenLabs. An account with neither gets captions only, with a notice offering to add a voice provider — which is
  exactly what Sample Rose shows on an account without Deepgram.
- **A character is a look + a suggested agent.** In the Companions gallery (tagged with the plugin's name), **Talk**
  creates a new agent with the suggested soul and instructions; **Talk as…** adds the face to an existing agent.
- **The instructions are written for speech** — short, no lists — because a companion's replies are spoken.

## Publishing it

A pack with binary files is published as a zip (**Publish from a .zip file instead**, or
`POST /plugins/{id}/versions/zip`). Every binary must be referenced by one of the JSON files, or the publish is
refused naming it. See [Companion packs](plugin-companion-packs.md).
