@onlyworlds/sdk 3.1.0 → 4.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # For AI agents using @onlyworlds/sdk
2
2
 
3
+ **Start with [SCHEMA.md](SCHEMA.md)** (in this package): the full generated schema reference —
4
+ every type, every field with its meaning, link directions, families, icons, display sections.
5
+ It is generated from the same canonical YAML as the types, so it cannot drift.
6
+
3
7
  **What this package is**: the canonical typed TypeScript client for the OnlyWorlds v2 API,
4
8
  plus the canonical constants (element types, icons, colour families, field schema).
5
9
  OnlyWorlds is an open standard for portable world data — 22 element types, UUID-linked.
package/CHANGELOG.md CHANGED
@@ -1,44 +1,86 @@
1
- # Changelog
2
-
3
- All notable changes to `@onlyworlds/sdk`. Maintained from 3.1.0 onward (Kael, Assembly);
4
- earlier history lives in git log only.
5
-
6
- ## [3.1.0] — 2026-07-23
7
-
8
- ### Added
9
- - **Canonical element colour palette** (`src/v2/palette.ts`, exported from root):
10
- `ELEMENT_FAMILIES` (type → family), `FAMILY_COLORS` (family → `{light, dark}` hex),
11
- `familyOf(type)`, `elementColor(type, mode)`, `FAMILY_ORDER`, type `ElementFamily`.
12
- Four semantic families (agents / world / abstract / temporal) ruled 2026-07-22 after
13
- CVD-validated measurement (Orrery `product/schema/element-palette-measurements.md`).
14
- Colour carries the FAMILY; `ELEMENT_ICONS` carries the TYPE. Keyed on the v2 slug
15
- union. First proven live in atlas and council, whose local copies become re-exports.
16
- **`ELEMENT_FAMILIES` is GENERATED** from the `family:` key in keel's schema YAML
17
- (keel `c69366b`, a keel presentation-wrapper key — not part of the council-governed
18
- OnlyWorlds standard); hexes are hand-authored design constants beside it. Membership
19
- and hex invariants test-gated (`test/palette.test.mjs`); light hexes render-proven on
20
- the ruled OnlyWorlds light mode (ow-house-light) 2026-07-23.
21
- - **Codegen drift guard**: `python codegen/generate_types.py --check` fails if
22
- `types.generated.ts` doesn't match the schema YAMLs (release gate; mirrors keel's
23
- schema CI job). Codegen also hard-fails on missing/invalid `family:` keys — the
24
- canary for a canonical-refresh that overwrote keel's wrapper layer.
25
- - **CI**: GitHub Action building + running the full suite against the compiled bundle.
26
- - **AGENTS.md** shipped in the tarball — usage guide for AI agents reading the package
27
- locally, including the SDK-vs-MCP division of labor and the wire gotchas.
28
- - README: worked create→link→read-back round-trip, schema-verified field by field
29
- (also fixes the old example patching `'character'` with a location's id); canonical
30
- colour section; SDK-vs-MCP guidance.
31
- - This CHANGELOG.
32
-
33
- ### Fixed
34
- - `FieldType` union now includes `'number'` — the legacy alias that 70 `FIELD_SCHEMA`
35
- entries actually carry (consumers switch on it; data normalization deferred to 4.0).
36
- - `generate_types.py` exits with a clear message when the sibling keel checkout is
37
- absent instead of a bare traceback.
38
-
39
- ## [3.0.0] — 2026-07-18
40
-
41
- - v2-native client (`OwV2Client`) absorbed from Assembly's ow-v2-client v0.9.0 (Kael),
42
- wire-corrected against live staging fixtures (S22). Generated element types from the
43
- canonical keel schema YAML (`src/v2/types.generated.ts`), validated against all 1,086
44
- live W11 elements. v1 surface unchanged and frozen.
1
+ # Changelog
2
+
3
+ All notable changes to `@onlyworlds/sdk`. Maintained from 3.1.0 onward (Kael, Assembly);
4
+ earlier history lives in git log only.
5
+
6
+ ## [Unreleased — 4.0.0] (branch `v4`)
7
+
8
+ **v2-native only.** See `docs/migrating-3-to-4.md`. 3.x stays published forever for
9
+ pinned consumers.
10
+
11
+ ### Removed (BREAKING)
12
+ - The v1 client (`OnlyWorldsClient`), the v1 `ElementType` enum, `icon-utils`.
13
+ `ElementType` at the root is now THE v2 slug union (was `V2ElementType` alias in 3.x).
14
+ - CJS build — the package is ESM-only with a sealed `exports` map (`"type": "module"`,
15
+ `sideEffects: false`).
16
+
17
+ ### Changed
18
+ - Metadata tables (`ELEMENT_ICONS`, `ELEMENT_LABELS`, `ELEMENT_SECTIONS`, `FIELD_SCHEMA`,
19
+ `getElementIcon`, `getElementLabel`) ported onto the v2 slug union — same export names,
20
+ same shapes, contents byte-identical EXCEPT: `FIELD_SCHEMA` `type: 'number'` →
21
+ `'integer'` (70 entries; 'number' dropped from `FieldType`). Migration: consumers
22
+ switching on `'number'` switch on `'integer'`.
23
+ - `TokenResource` now takes a structural `TokenTransport` (`{ request<T>(method, path,
24
+ body?) }`) instead of the removed v1 client. Token surface retained pending the
25
+ wire-fate ruling (RFC-001 §5) — may be removed in a later 4.x.
26
+
27
+ ### Added (post-alpha.0, same night)
28
+ - **`ELEMENT_ICONS` GENERATED** from keel's `icon:` wrapper key (keel `56c124a`).
29
+ - **`ELEMENT_SECTIONS` DERIVED** from the canonical schema's own document structure
30
+ (Skeld's ruling: sections are the standard tier's property groups; document order is
31
+ display order). Divergence check vs the old hand table found and fixed three fossils:
32
+ creature "Behaviour"→"Behavior", pin's triple-listed generic link → `element`,
33
+ relation "Involves" listing a nonexistent `relations` field. Canonical is truth.
34
+ - **`SCHEMA.md`** — full generated schema reference (every field with its canonical
35
+ description, link directions, families, icons, sections), ships in the tarball;
36
+ covered by the codegen drift guard. AGENTS.md points agents at it first.
37
+ - **`OwV2Client.request()` is public** (typed escape hatch; does not sanitize) and
38
+ structurally satisfies `TokenTransport` — `new TokenResource(client)` just works.
39
+ Token ruling (Skeld): keel keeps `/tokens/*` long-term; ported, not deleted.
40
+ - README rewritten v2-native (v1 sections, branded types, and the encrypted-key
41
+ walkthrough removed; bulk partial-failure and idempotency-key hygiene promoted).
42
+
43
+ ### Planned before release (RFC-001 gates)
44
+ - Skeld's live wire-log v1-traffic query (gate 3). Alpha publishes to the `alpha`
45
+ dist-tag only — never `latest`. **No `npm deprecate` on 3.x** (amended per landscape
46
+ research — 3.x stays plainly supported; README framing carries the message).
47
+
48
+ ## [3.1.0] — 2026-07-23
49
+
50
+ ### Added
51
+ - **Canonical element colour palette** (`src/v2/palette.ts`, exported from root):
52
+ `ELEMENT_FAMILIES` (type → family), `FAMILY_COLORS` (family → `{light, dark}` hex),
53
+ `familyOf(type)`, `elementColor(type, mode)`, `FAMILY_ORDER`, type `ElementFamily`.
54
+ Four semantic families (agents / world / abstract / temporal) ruled 2026-07-22 after
55
+ CVD-validated measurement (Orrery `product/schema/element-palette-measurements.md`).
56
+ Colour carries the FAMILY; `ELEMENT_ICONS` carries the TYPE. Keyed on the v2 slug
57
+ union. First proven live in atlas and council, whose local copies become re-exports.
58
+ **`ELEMENT_FAMILIES` is GENERATED** from the `family:` key in keel's schema YAML
59
+ (keel `c69366b`, a keel presentation-wrapper key — not part of the council-governed
60
+ OnlyWorlds standard); hexes are hand-authored design constants beside it. Membership
61
+ and hex invariants test-gated (`test/palette.test.mjs`); light hexes render-proven on
62
+ the ruled OnlyWorlds light mode (ow-house-light) 2026-07-23.
63
+ - **Codegen drift guard**: `python codegen/generate_types.py --check` fails if
64
+ `types.generated.ts` doesn't match the schema YAMLs (release gate; mirrors keel's
65
+ schema CI job). Codegen also hard-fails on missing/invalid `family:` keys — the
66
+ canary for a canonical-refresh that overwrote keel's wrapper layer.
67
+ - **CI**: GitHub Action building + running the full suite against the compiled bundle.
68
+ - **AGENTS.md** shipped in the tarball — usage guide for AI agents reading the package
69
+ locally, including the SDK-vs-MCP division of labor and the wire gotchas.
70
+ - README: worked create→link→read-back round-trip, schema-verified field by field
71
+ (also fixes the old example patching `'character'` with a location's id); canonical
72
+ colour section; SDK-vs-MCP guidance.
73
+ - This CHANGELOG.
74
+
75
+ ### Fixed
76
+ - `FieldType` union now includes `'number'` — the legacy alias that 70 `FIELD_SCHEMA`
77
+ entries actually carry (consumers switch on it; data normalization deferred to 4.0).
78
+ - `generate_types.py` exits with a clear message when the sibling keel checkout is
79
+ absent instead of a bare traceback.
80
+
81
+ ## [3.0.0] — 2026-07-18
82
+
83
+ - v2-native client (`OwV2Client`) absorbed from Assembly's ow-v2-client v0.9.0 (Kael),
84
+ wire-corrected against live staging fixtures (S22). Generated element types from the
85
+ canonical keel schema YAML (`src/v2/types.generated.ts`), validated against all 1,086
86
+ live W11 elements. v1 surface unchanged and frozen.
package/README.md CHANGED
@@ -3,12 +3,13 @@
3
3
  [![npm version](https://badge.fury.io/js/@onlyworlds%2Fsdk.svg)](https://www.npmjs.com/package/@onlyworlds/sdk)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A type-safe TypeScript SDK for building world-building applications with the OnlyWorlds API.
6
+ The canonical typed client for the [OnlyWorlds](https://onlyworlds.github.io) v2 API, plus the
7
+ canonical constants (element types, icons, colour families, field schema) — generated from the
8
+ same schema source the server runs on.
7
9
 
8
- The SDK speaks two dialects of the same API:
9
-
10
- - **v2 (`OwV2Client`)** -- the current, recommended default. Local-first shape: one-shape read/write (no `_ids` suffix), an opaque `/changes` sync feed, atomic bulk writes, and client-side UUID minting for safe retries. Base URL `https://www.onlyworlds.com/api/v2`.
11
- - **v1 (`OnlyWorldsClient`)** -- the original resource-style client. Fully served forever; kept below as the legacy section. Existing 2.x code keeps working unchanged -- v2 is purely additive.
10
+ **4.x is v2-native and ESM-only (Node 18+).** If you need the legacy v1 API dialect
11
+ (`OnlyWorldsClient`) or CommonJS `require()`, stay on 3.x — it remains published and the v1 API
12
+ remains served. See [docs/migrating-3-to-4.md](docs/migrating-3-to-4.md).
12
13
 
13
14
  ## Installation
14
15
 
@@ -16,317 +17,128 @@ The SDK speaks two dialects of the same API:
16
17
  npm install @onlyworlds/sdk
17
18
  ```
18
19
 
19
- ## Quick Start (v2 -- recommended)
20
+ ## Quick Start
20
21
 
21
22
  ```typescript
22
23
  import { OwV2Client } from '@onlyworlds/sdk';
23
24
 
24
- const client = new OwV2Client({
25
- apiKey: 'ow_w_your_world_key', // ow_w_ write / ow_r_ read / ow_a_ account / 10-digit legacy
26
- apiPin: '1234', // needed for writes on a PIN-protected world
27
- });
28
- // baseUrl defaults to https://www.onlyworlds.com/api/v2
29
-
30
- // Read a page, or walk every page of a type
31
- const page = await client.list('character'); // { data, has_more, next_cursor }
32
- for await (const character of client.listAll('character')) {
33
- // ...
34
- }
35
-
36
- // A worked round-trip: two elements, linked, read back. Byte-true v2 dialect:
37
- // link fields use ONE bare name both directions (no _ids suffix — that's v1),
38
- // and you NEVER send a "world" field (identity comes from the key; sending it 422s).
39
- const location = await client.create('location', {
40
- name: 'Dragon Peak',
41
- description: 'A treacherous mountain peak where dragons nest',
42
- }); // id minted client-side when omitted (retries stay idempotent)
43
-
44
- const dragon = await client.create('creature', {
45
- name: 'Vorrath the Ember-Scaled',
46
- location: location.id, // single link: UUID (or null)
47
- });
48
-
49
- const fetched = await client.get('creature', dragon.id);
50
- // fetched.location === location.id — reads the way it writes
51
-
52
- // Partial update (PATCH is destructive on sent fields; arrays replace wholesale)
53
- await client.patch('location', location.id, { supertype: 'Mountain' });
54
-
55
- // For relationships, prefer the atomic link merge -- returns the full updated element
56
- const fireBreath = await client.create('ability', { name: 'Ember Breath' });
57
- await client.editLinks('creature', dragon.id, 'abilities', { add: [fireBreath.id], remove: [] });
58
-
59
- // Bulk write (up to ~1000; partial success by default, atomic:true for all-or-nothing)
60
- const res = await client.bulk([
61
- { type: 'character', element: { name: 'A' } },
62
- { type: 'event', element: { name: 'B' } },
63
- ]);
64
- // res.items[i].status is the per-slot HTTP status; res.errors flags any failure
65
-
66
- // Sync: walk the opaque change feed and persist the final cursor
67
- let cursor;
68
- for await (const change of client.changesAll(cursor)) {
69
- // apply change in order
70
- }
71
- ```
72
-
73
- ### Canonical element colours
74
-
75
- ```typescript
76
- import { elementColor, ELEMENT_FAMILIES, FAMILY_COLORS } from '@onlyworlds/sdk';
77
-
78
- elementColor('character', 'dark'); // '#3987e5' — colour carries the FAMILY
79
- // (agents / world / abstract / temporal); ELEMENT_ICONS carries the TYPE.
80
- // CVD-validated: always pair colour with icon + label, never colour alone.
25
+ const client = new OwV2Client({ apiKey: 'ow_r_your_key' }); // read-only key: that's all you need
26
+ const page = await client.list('character'); // { data, has_more, next_cursor }
81
27
  ```
82
28
 
83
- ### AI-assistant access (MCP) — and when to use which
84
-
85
- An MCP server exists at `https://www.onlyworlds.com/mcp` for AI assistants (Claude and other MCP clients) to read and write worlds directly -- no SDK code required. See the [docs](https://onlyworlds.github.io) for setup.
86
-
87
- Division of labor: for **known, deterministic operations** (CRUD, sync, bulk) use this SDK — typed calls, no tool-schema overhead. For **live exploration of a user's world from a chat/agent context**, use the MCP server. Agents: see `AGENTS.md` in this package.
29
+ Key kinds: `ow_w_` write / `ow_r_` read-only (no PIN — the "give this to your players" key) /
30
+ `ow_a_` account Bearer / 10-digit legacy. Writes on a PIN-protected world also pass `apiPin`.
31
+ Demo keys `0000000000`–`0000000009` read real sample worlds.
88
32
 
89
- ## Legacy: v1 client (`OnlyWorldsClient`)
90
-
91
- The v1 resource-style client remains fully supported and served forever. Prefer `OwV2Client` for new code.
33
+ ## The full round-trip
92
34
 
93
35
  ```typescript
94
- import { OnlyWorldsClient } from '@onlyworlds/sdk';
95
-
96
- const client = new OnlyWorldsClient({
97
- apiKey: 'your-api-key',
98
- apiPin: '1234'
99
- });
100
- // baseUrl defaults to https://www.onlyworlds.com/api/worldapi — only override it
101
- // when pointing at a different host (it must include the full API path)
102
-
103
- // Get your world (API keys are world-scoped)
104
- const world = await client.worlds.get();
105
-
106
- // Fetch characters (paginated)
107
- const characters = await client.characters.list();
36
+ const writer = new OwV2Client({ apiKey: 'ow_w_your_key', apiPin: '1234' });
108
37
 
109
- // Create a new location
110
- const location = await client.locations.create({
38
+ // v2 dialect rules, worth internalizing once:
39
+ // - link fields use ONE bare name both directions (no _ids suffix — that's v1)
40
+ // - NEVER send a "world" field (identity comes from the key; the client strips it anyway)
41
+ const location = await writer.create('location', {
111
42
  name: 'Dragon Peak',
112
43
  description: 'A treacherous mountain peak where dragons nest',
113
- supertype: 'mountain'
114
- });
115
-
116
- // Get a specific element
117
- const character = await client.characters.get('element-id');
118
-
119
- // Update an element
120
- await client.characters.update('element-id', {
121
- name: 'Updated Name'
122
- });
123
-
124
- // Delete an element
125
- await client.characters.delete('element-id');
126
- ```
127
-
128
- ## Features
129
-
130
- - ✅ **Full Type Safety** - Complete TypeScript definitions for all 22 OnlyWorlds element types
131
- - ✅ **Schema-Aligned Types** - Type definitions track the OnlyWorlds schema
132
- - ✅ **CRUD Operations** - Create, read, update, and delete operations for all elements
133
- - ✅ **Token Management** - Built-in support for OnlyWorlds token rating system
134
- - ✅ **Branded Types** - Compile-time safety for element relationships
135
- - ✅ **Zero Runtime Overhead** - Type system has no runtime cost
136
- - ✅ **Modern ESM/CJS** - Supports both ES modules and CommonJS
137
-
138
- ## API Reference
139
-
140
- ### World Endpoint
141
-
142
- The `worlds` resource is special because API keys are world-scoped (one key = one world). The endpoint returns a single `World` object directly, not a paginated list.
143
-
144
- ```typescript
145
- // Get your world
146
- const world = await client.worlds.get();
44
+ }); // id minted client-side when omitted (retries stay idempotent)
147
45
 
148
- // Update your world
149
- const updated = await client.worlds.update({
150
- description: 'A dark fantasy realm'
46
+ const dragon = await writer.create('creature', {
47
+ name: 'Vorrath the Ember-Scaled',
48
+ location: location.id, // single link: UUID (or null)
151
49
  });
152
- ```
153
-
154
- **Note**: The `worlds` resource only has `get()` and `update()` methods (no `list()`, `create()`, or `delete()`) because API keys are world-scoped.
155
-
156
- ### Element Endpoints
157
50
 
158
- All other element types return paginated results:
159
-
160
- ```typescript
161
- // List with pagination
162
- const response = await client.characters.list({
163
- limit: 10,
164
- offset: 0,
165
- ordering: '-created_at',
166
- search: 'dragon'
167
- });
51
+ const fetched = await writer.get('creature', dragon.id);
52
+ // fetched.location === location.id — reads the way it writes
168
53
 
169
- console.log(response.count); // Total count
170
- console.log(response.results); // Array of Characters
171
- console.log(response.next); // URL for next page
172
- console.log(response.previous); // URL for previous page
173
- ```
54
+ // PATCH is destructive on sent fields; arrays replace wholesale
55
+ await writer.patch('location', location.id, { supertype: 'Mountain' });
174
56
 
175
- ## Type-Safe Relationships
57
+ // For relationships, use the atomic link merge — returns the full updated element
58
+ const fireBreath = await writer.create('ability', { name: 'Ember Breath' });
59
+ await writer.editLinks('creature', dragon.id, 'abilities', { add: [fireBreath.id], remove: [] });
176
60
 
177
- ```typescript
178
- import { ElementId, Character, Location } from '@onlyworlds/sdk';
179
-
180
- // Branded types ensure you can't mix up element IDs
181
- const locationId: ElementId<'Location'> = 'some-location-id';
182
- const character: Character = {
183
- name: 'Aragorn',
184
- location: locationId // Type-safe!
185
- };
61
+ // Walk every page of a type
62
+ for await (const character of writer.listAll('character')) { /* ... */ }
186
63
  ```
187
64
 
188
- ## Token Management
189
-
190
- OnlyWorlds provides a token rating system for tracking API usage and enabling AI-powered features. Users get a daily token allowance (default: 10,000 tokens) that resets every day.
65
+ ## Bulk writes — always check `errors`
191
66
 
192
- ### Working Reference Implementation
193
-
194
- See [base-tool/src/llm/token-service.ts](https://github.com/OnlyWorlds/base-tool) for the complete working implementation that this SDK enables.
195
-
196
- ### Check Token Status
67
+ `/bulk` **succeeds partially by default** (HTTP 200 with per-slot statuses). The one mistake to
68
+ never make: treating a 200 as "everything landed."
197
69
 
198
70
  ```typescript
199
- // Get current token status
200
- const status = await client.tokens.getStatus();
201
-
202
- console.log(`Available: ${status.tokens_available_today}/${status.token_rating}`);
203
- console.log(`Used today: ${status.tokens_used_today}`);
204
- console.log(`Active sessions: ${status.sessions_active}`);
205
- console.log(`Last reset: ${status.last_reset}`);
71
+ const res = await writer.bulk(
72
+ [
73
+ { type: 'character', element: { name: 'A' } },
74
+ { type: 'event', element: { name: 'B' } },
75
+ ],
76
+ { idempotencyKey: crypto.randomUUID() }, // mint FRESH per attempt — a failed batch is
77
+ ); // cached under its key; never reuse across retries
78
+ if (res.errors) {
79
+ for (const slot of res.items.filter((s) => s.status >= 400)) {
80
+ console.warn(slot.error?.code, slot.error?.message, slot.error?.doc_url);
81
+ }
82
+ }
83
+ // Pass { atomic: true } for all-or-nothing instead. res.wasReplay flags idempotent replays.
206
84
  ```
207
85
 
208
- ### Consume Tokens
209
-
210
- Report token consumption when your tool uses AI features or other token-tracked services:
86
+ ## Sync
211
87
 
212
88
  ```typescript
213
- // Report token usage
214
- const result = await client.tokens.consume({
215
- amount: 500,
216
- service: 'my_worldbuilding_tool',
217
- metadata: {
218
- feature: 'character_generation',
219
- model: 'gpt-4',
220
- prompt_tokens: 300,
221
- completion_tokens: 200
222
- }
223
- });
224
-
225
- // Check if consumption succeeded
226
- if (result.error) {
227
- console.warn('Token warning:', result.error);
89
+ let cursor; // opaque, never expires; persist it
90
+ for await (const change of client.changesAll(cursor)) {
91
+ // ops arrive in (change_seq, id) order — apply in order → convergence
228
92
  }
229
-
230
- console.log(`Consumed ${result.tokens_consumed} tokens`);
231
- console.log(`${result.tokens_remaining} tokens remaining`);
93
+ // GOTCHA: world-meta edits (name, calendar, public_read) do NOT enter /changes.
94
+ // Poll client.getWorld() and compare updated_at separately.
232
95
  ```
233
96
 
234
- **Important**: The API allows consumption even when exceeding available tokens (tracks as debt), but returns a warning in the `error` field.
97
+ ## Errors
235
98
 
236
- ### Advanced: Encrypted API Key Access
99
+ Every non-2xx throws `OwApiError` carrying the platform envelope: `.status`, `.type`, `.code`,
100
+ `.param` (the exact field that failed), and `.docUrl` — surface `docUrl` in your UX. Transport
101
+ failures throw `OwNetworkError`. `err.isValidationError` is the common branch.
237
102
 
238
- For tools that need direct OpenAI API access using OnlyWorlds tokens:
103
+ ## Canonical constants (all generated or test-gated from schema)
239
104
 
240
105
  ```typescript
241
- // Get encrypted OpenAI key (requires 100+ tokens)
242
- const access = await client.tokens.getAccessKey();
243
-
244
- console.log('Session ID:', access.session_id);
245
- console.log('Expires:', access.expires_at);
246
- console.log('Available tokens:', access.tokens_available);
247
-
248
- // Decrypt the key client-side (see base-tool for full implementation)
249
- // 1. Derive decryption key from world ID using SHA-256
250
- // 2. Use 'fernet' npm package to decrypt
251
- // 3. Use decrypted OpenAI key for direct API calls
252
- // 4. Report usage with session_id
253
-
254
- // See base-tool/src/llm/token-service.ts:99-235 for complete example
106
+ import {
107
+ ELEMENT_TYPES, // the 22 slugs (and the ElementType union)
108
+ ELEMENT_ICONS, // Material Symbols icon per type
109
+ ELEMENT_LABELS, // plural display labels
110
+ ELEMENT_SECTIONS, // canonical field grouping + display order
111
+ FIELD_SCHEMA, // per-field type/target metadata
112
+ elementColor, // canonical colour: family carries COLOUR, icon carries TYPE
113
+ } from '@onlyworlds/sdk';
114
+
115
+ elementColor('character', 'dark'); // '#3987e5' — four CVD-validated families
116
+ // Always pair colour with icon + label; colour alone is not accessible.
255
117
  ```
256
118
 
257
- **Full decryption implementation** (based on base-tool):
119
+ The full schema with per-field meaning lives in [SCHEMA.md](SCHEMA.md) (generated, ships in this
120
+ package). AI agents: read [AGENTS.md](AGENTS.md) first.
258
121
 
259
- ```typescript
260
- import { fernet } from 'fernet';
261
-
262
- // Derive decryption key from world ID
263
- async function deriveKey(worldId: string): Promise<string> {
264
- const salt = 'onlyworlds-token-api-2024-public-salt';
265
- const keyMaterial = `${worldId}:${salt}`;
266
- const encoder = new TextEncoder();
267
- const data = encoder.encode(keyMaterial);
268
- const hashBuffer = await crypto.subtle.digest('SHA-256', data);
269
- const hashArray = Array.from(new Uint8Array(hashBuffer));
270
- const base64 = btoa(String.fromCharCode(...hashArray));
271
- return base64.replace(/\+/g, '-').replace(/\//g, '_');
272
- }
273
-
274
- // Decrypt the API key
275
- async function decryptApiKey(encryptedKey: string, worldId: string): Promise<string> {
276
- const derivedKey = await deriveKey(worldId);
277
- const secret = new fernet.Secret(derivedKey);
278
- const token = new fernet.Token({
279
- secret: secret,
280
- token: encryptedKey,
281
- ttl: 0 // Don't enforce TTL client-side
282
- });
283
- return token.decode();
284
- }
285
-
286
- // Usage
287
- const world = await client.worlds.get();
288
- const access = await client.tokens.getAccessKey();
289
- const apiKey = await decryptApiKey(access.encrypted_key, world.id);
290
-
291
- // Use apiKey for OpenAI API calls, then report usage:
292
- await client.tokens.consume({
293
- amount: tokensUsed,
294
- sessionId: access.session_id,
295
- service: 'direct_openai',
296
- metadata: { model: 'gpt-4', /* ... */ }
297
- });
298
- ```
299
-
300
- ### Session Management
122
+ ## Token rating system
301
123
 
302
124
  ```typescript
303
- // Revoke a specific session
304
- await client.tokens.revokeSession(sessionId);
305
-
306
- // Revoke all sessions (emergency cleanup)
307
- const result = await client.tokens.revokeAllSessions();
308
- console.log(`Revoked ${result.sessions_revoked} sessions`);
125
+ import { TokenResource } from '@onlyworlds/sdk';
126
+ const tokens = new TokenResource(writer); // OwV2Client.request() satisfies TokenTransport
127
+ const status = await tokens.getStatus();
309
128
  ```
310
129
 
311
- ### Encryption Info
312
-
313
- Get public encryption details (no auth required):
130
+ ## AI access — when to use which
314
131
 
315
- ```typescript
316
- const info = await client.tokens.getEncryptionInfo();
317
- console.log('Algorithm:', info.algorithm);
318
- console.log('Key derivation:', info.key_derivation);
319
- console.log('Public salt:', info.salt);
320
- console.log(info.javascript_example);
321
- ```
132
+ For **known, deterministic operations** (CRUD, sync, bulk) use this SDK — typed calls, no
133
+ tool-schema overhead. For **live exploration of a user's world from a chat/agent context**, use
134
+ the MCP server at `https://www.onlyworlds.com/mcp` (same `API-Key`/`API-Pin` headers, 11 tools;
135
+ unaffected by SDK versioning).
322
136
 
323
137
  ## License
324
138
 
325
- MIT License - see [LICENSE](LICENSE) file for details
139
+ MIT
326
140
 
327
141
  ## Links
328
142
 
329
- - [OnlyWorlds Website](https://onlyworlds.com)
330
- - [Documentation](https://onlyworlds.github.io/)
331
- - [NPM Package](https://www.npmjs.com/package/@onlyworlds/sdk)
332
- - [Report Issues](https://github.com/OnlyWorlds/sdk/issues)
143
+ - [OnlyWorlds](https://www.onlyworlds.com) · [Docs](https://onlyworlds.github.io) · [API reference](https://www.onlyworlds.com/api/docs)
144
+ - [Issues](https://github.com/OnlyWorlds/sdk/issues) · [CHANGELOG](CHANGELOG.md) · [Migration 3→4](docs/migrating-3-to-4.md)