# Plethora Bit Agent Context

Version: plethora-agent-context-2026-06-20.1

Plethora Bits are tiny mobile-first interactive objects: games, puzzles, interactive art, fidget toys, music toys, educational activities, sensory experiences, and other small playful systems. Agents should be able to create them with no local starter repo as long as they follow this contract.

## Current Flow

1. Fetch /v1/agent/context.md or /v1/agent/context.json before creating code.
2. Build a mobile-first bit using only the SDK and contract described by the agent resources.
3. Use approved, exact-version Plethora Library Registry pins for external libraries.
4. Validate the manifest and source locally if the agent has a validator; otherwise keep to the schema endpoint exactly.
5. Pair with the creator before uploading drafts. Public publish is intentionally manual from the app or dashboard.

## Available Endpoints

- GET /v1/agent (available, auth: none) - Discover the creator-agent workflow and all agent resources.
- GET /v1/agent/context.md (available, auth: none) - Fetch the complete standalone bit-making instructions.
- GET /v1/agent/schema.json (available, auth: none) - Fetch the current public bit contract schema.
- GET /v1/agent/libraries.json (available, auth: none) - Fetch approved library and font metadata.
- POST /v1/agent/pair/sessions (available, auth: none) - Create a short-lived pairing code for an agent without exposing a Supabase token.
- POST /v1/agent/pair/claim (available, auth: user_session) - Approve pairing from the mobile app or web dashboard.
- POST /v1/agent/pair/lookup (available, auth: user_session) - Show the creator the pending agent identity and requested scopes before approval.
- POST /v1/agent/pair/sessions/:sessionId/exchange (available, auth: agent_pairing_secret) - Poll/exchange an approved pairing session using the session secret. On approval, read data.accessToken exactly once.
- POST /v1/agent/bits/drafts (available, auth: agent_access_token) - Upload or update a draft bit for the paired user. Publish remains manual.
- GET /v1/agent/tokens (available, auth: user_session) - List paired coding agents for the signed-in creator.
- DELETE /v1/agent/tokens/:tokenId (available, auth: user_session) - Revoke a paired coding agent token. Publishing remains manual.

## Pairing And Upload Auth

Agents must pair before uploading drafts. The exchange endpoint is one-time:

1. Call `POST /v1/agent/pair/sessions`.
2. Show `pairingCode` or `pairingUrl` to the creator.
3. Poll `POST /v1/agent/pair/sessions/:sessionId/exchange` with the private `sessionSecret`.
4. When the response status is `approved`, save `data.accessToken` immediately.
5. Use `Authorization: Plethora-Agent <data.accessToken>` for draft upload.

Do not look for `agentToken`. The token field is exactly `accessToken`, nested under `data` in the standard API envelope. The approved exchange consumes the session, so a second exchange call will not return the token again.

Approved exchange response shape:

```json
{
  "ok": true,
  "data": {
    "status": "approved",
    "sessionId": "11111111-1111-4111-8111-111111111111",
    "expiresAt": "2026-06-20T22:00:00.000Z",
    "accessToken": "plag_example_one_time_visible_token",
    "tokenId": "22222222-2222-4222-8222-222222222222",
    "tokenExpiresAt": "2026-06-21T22:00:00.000Z",
    "scopes": [
      "bits:draft:write"
    ]
  },
  "requestId": "request-id",
  "serverTime": "2026-06-20T21:00:00.000Z"
}
```

Pending exchange response shape:

```json
{
  "ok": true,
  "data": {
    "status": "pending",
    "sessionId": "11111111-1111-4111-8111-111111111111",
    "expiresAt": "2026-06-20T22:00:00.000Z",
    "scopes": [
      "bits:draft:write"
    ]
  },
  "requestId": "request-id",
  "serverTime": "2026-06-20T21:00:00.000Z"
}
```

## Manifest Contract

- Runtime: plethora-bit@1
- Runtime global: `window.plethoraBit`
- Entry default: `main.js`
- Package size limit: 2097152 bytes
- Packaged assets: max 0
- Approved library host: libs.plethora.studio
- Permissions: `audio`, `backgroundMusic`, `haptics`, `microphone`, `motion`, `networkFetch`, `storage`

Minimal `plethora.json`:

```json
{
  "schemaVersion": 1,
  "runtime": "plethora-bit@1",
  "entry": "main.js",
  "title": "Touch Sketch",
  "description": "A tiny touch-reactive Plethora Bit.",
  "tags": [
    "creative",
    "touch"
  ],
  "permissions": [
    "haptics"
  ],
  "dependencies": []
}
```

## SDK

Define `window.plethoraBit = { meta?, async init(ctx) {} }`.

Surfaces:
- ctx.createRoot(options?) -> DOM root inside the bit container, automatically removed on cleanup.
- ctx.createCanvas2D(options?) -> DPR-correct 2D canvas inside the bit container.
- ctx.createCanvas(options?) -> canvas for WebGL/custom renderers.

Lifecycle:
- ctx.platform.ready() after the bit has mounted.
- ctx.platform.start() from the first real user gesture.
- ctx.platform.interact({ type }) for meaningful taps, drags, swipes, choices, or other actions.
- ctx.platform.complete(payload?) for natural endings.
- ctx.platform.fail(payload?) for game-over/failure states.
- ctx.platform.setScore(number) for score-bearing games.
- ctx.platform.setProgress(numberBetween0And1) for progress-bearing experiences.
- ctx.platform.haptic('light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error') with haptics permission.

Events and animation:
- ctx.listen(target, eventName, handler, options?) registers cleanup-owned event listeners.
- ctx.onFrame(cb) registers one persistent runtime-owned frame callback. Call once during init, not from inside cb.
- ctx.raf(cb) exists only as a legacy alias. New bits should use ctx.onFrame(cb).

Libraries:
- ctx.loadScript(url) loads classic script dependencies from the approved registry.
- ctx.importModule(url) imports ES module dependencies from the approved registry.
- Both require networkFetch permission and a matching dependency pin.

Audio:
- ctx.music is for runtime-managed background music beds and requires backgroundMusic permission.
- ctx.audio, AudioContext, and new Audio() are for action sounds and require audio permission.
- Start audio from a user gesture so mobile autoplay rules are respected.

Memory:
- ctx.memory.local("save").get() and .set(value) store viewer-private progress declared in manifest.memory.local.
- ctx.memory.record("score").submit(value, { label?, dimensions? }) submits high scores, best times, streaks, or completions declared in manifest.memory.records.
- ctx.memory.record("score").leaderboard({ scope, period, dimensions }) reads the standard Plethora leaderboard DTO.
- ctx.memory.tally("favorite").choose(value) and .results() power surveys, polls, votes, and reactions declared in manifest.memory.tallies.
- ctx.memory.world("mural").get() and .mutate(mutation) use bounded shared spaces declared in manifest.memory.worlds.
- Declare memory channels in plethora.json first. Bits cannot create ad hoc channels at runtime.

## Hard Safety Rules

- Public bits run in a fixed WebView sandbox owned by Plethora.
- Bits cannot load arbitrary public CDNs or arbitrary remote code.
- External libraries must be approved, version-pinned registry dependencies served from libs.plethora.studio.
- Packaged bit assets are currently disabled: maxAssets is 0.
- Camera input is not in the current public bit contract; microphone input requires the microphone permission and OS/user grant.
- Publish stays manual so the creator can review AI feedback and platform moderation before release.

## Best Practices

- Keep the bottom of the screen light; it is an unsafe area for heavy controls and core gameplay.
- Add an instructions button with point-wise instructions when the bit is not self-explanatory.
- Provide tap-to-replay or a replay button for games and finite experiences.
- Check that buttons do not overlap each other on phone-sized screens.
- Add tasteful background music and short action sounds when they improve the feel.
- Make the bit polished, tactile, and visually memorable.
- Update manifest permissions accurately whenever APIs change.
- Call ctx.platform.ready() at the end of init and ctx.platform.start() on first real interaction.
- Use ctx.onFrame() once during init for animation loops.
- Use ctx.memory records for scores/times that should appear in Plethora leaderboards.
- Use ctx.memory tallies for surveys/polls and ctx.memory worlds for co-creation.
- Never mount to document.body, create raw canvas elements for the main surface, or create script tags manually.

## Approved Library Pins

- three@0.164.1
- three-global@0.134.0
- three-global@0.128.0
- playcanvas@1.74.0
- mediapipe-hands@0.4.1675469240
- html2canvas@1.4.1
- ammo.js@0.0.10
- d3@7.9.0
- vfx-js-core@0.11.1
- animejs@3.2.1
- p5@2.2.3
- pixi.js@8.18.1
- paper@0.12.18
- regl@2.1.1
- ogl@1.0.11
- two.js@0.8.23
- pts@0.12.9
- twgl.js@7.0.0
- zdog@1.1.3
- oimo@1.0.9
- phaser@4.1.0
- fabric@7.3.1
- makerjs@0.19.2
- sketch-js@1.1.3
- picogl@0.17.9
- babylonjs@9.7.0
- css-doodle@0.51.0
- hydra-synth@1.4.0
- @jscad/modeling@2.13.0
- @luma.gl/core@9.3.3
- @theatre/core@0.7.2
- canvas-sketch@0.7.7
- @thi.ng/geom@8.3.30
- shader-park-core@0.2.8

Fetch `/v1/agent/libraries.json` for exact URLs, SHA-256 hashes, sizes, MIME types, and font metadata.

## Minimal Single-File Bit

```js
window.plethoraBit = {
  meta: {
    title: "Touch Sketch",
    runtime: "plethora-bit@1",
    tags: ["creative", "touch"],
    permissions: ["haptics"]
  },

  async init(ctx) {
    const canvas = ctx.createCanvas2D();
    const g = canvas.getContext("2d");
    const touches = [];

    function draw() {
      g.clearRect(0, 0, ctx.width, ctx.height);
      g.fillStyle = "#f2c94c";

      for (const touch of touches) {
        g.beginPath();
        g.arc(touch.x, touch.y, touch.r, 0, Math.PI * 2);
        g.fill();
        touch.r += 2;
      }

      while (touches.length && touches[0].r > 96) {
        touches.shift();
      }
    }

    ctx.listen(canvas, "touchstart", event => {
      event.preventDefault();
      ctx.platform.start();
      ctx.platform.haptic("light");
      const rect = canvas.getBoundingClientRect();

      for (const touch of event.changedTouches) {
        touches.push({
          x: touch.clientX - rect.left,
          y: touch.clientY - rect.top,
          r: 12
        });
      }
    }, { passive: false });

    ctx.onFrame(draw);
    ctx.platform.ready();
  }
};
```
