@onlyworlds/sdk 4.39.0 → 4.40.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
@@ -106,6 +106,11 @@ against live data). The v1 surface (`OnlyWorldsClient`, the `ElementType` enum)
106
106
  `mcp`, `declared`, `api`, `v1`, `sync`, `server`), and a `/changes` delete carries
107
107
  `deleted_by` and `deleted_via`; `isAgentDoor(door)` is true for the first three. An agent
108
108
  seat's key is recorded as an agent without the option. Never set it in a tool people use.
109
+ - A guest or player reads what its own Character's sheet links to as a **stub**, marked
110
+ `known_of: true` (`isKnownOf(el)`): only `id`, `type`, `name`, `description`, `supertype`,
111
+ `subtype`, `image_url` and the timestamps are real; every other field reads unset, and its
112
+ files are 404. Render it as known of, never merge its empties over a whole copy, and never
113
+ write it back: `upsert`, `patch`, `create` and `bulk` throw a TypeError on one before sending.
109
114
  - A string holding an unpaired surrogate (text cut mid-emoji) is a 422 naming the field.
110
115
  Slice strings by code point, not by UTF-16 unit.
111
116
  - Colour carries the element's FAMILY (`elementColor(type, mode)`); the icon
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ earlier history lives in git log only.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [4.40.0] - 2026-10-20
9
+
10
+ The "known of" stub's client half (keel PR 113, B16; keel #421 §7g), live in production since 2026-10-20 13:47.
11
+
12
+ ### Added
13
+ - **`known_of?: true`** on `OwElementBase` (generated; `SCHEMA.md` says it too): present, and true, only on a
14
+ stub, an element a guest or player reaches only through its own Character's sheet, one step out. A stub
15
+ keeps `id`, `type`, `name`, `description`, `supertype`, `subtype`, `image_url`, `created_at`, `updated_at`
16
+ and `change_seq`; every other field reads unset (`""`, `null`, `[]`), `created_by`, `changed_by` and
17
+ `changed_via` are null, it has no extension fields, and its files are 404. For Lantern and Atlas, which
18
+ render stubs (Temper).
19
+ - **`isKnownOf(el)`**: true only for `known_of: true`; absent or anything else is a whole element.
20
+
21
+ ### Changed
22
+ - **A stub is never written**: `upsert`, `patch`, `create` and `bulk` throw a `TypeError` before sending a
23
+ body with `known_of: true` (`bulk` names the item, and sends none of the batch). Stripping the marker would
24
+ turn keel's 422 into a write that blanks the element for any key that may write it (an owner pushing a
25
+ player's copy). `known_of` joins the strip list, so any other value of it is never sent.
26
+
8
27
  ## [4.39.0] - 2026-10-20
9
28
 
10
29
  The party link (keel #422 PR 2, keel PR 110), live in production since 2026-10-20 13:15.
package/SCHEMA.md CHANGED
@@ -7,6 +7,8 @@ Written for both humans and AI agents reading this package locally.
7
7
 
8
8
  **The shape rules** (v2 wire dialect): every element carries `id` (UUID), `name`, optional
9
9
  `description`/`supertype`/`subtype`/`image_url`, server-managed `type`/`created_at`/`updated_at`/`change_seq`/`created_by`,
10
+ `known_of: true` only on a stub (an element a guest knows of through its Character's sheet:
11
+ its other fields read unset and are not values; never write a stub back),
10
12
  and namespaced extension fields (`x_*` etc.) returned verbatim. Link fields use ONE bare
11
13
  name in both read and write (no `_ids` suffix). Single links are `UUID | null`; multi links
12
14
  are `UUID[]`. **Links are owned one-way**: the type listed below owns the field (e.g.
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  * pass-through fields (atlas_* / shadow_* / x_*) returned verbatim by the server.
3
3
  * Derived from base_properties.yaml: `World` is dropped (the API rejects it in
4
4
  * bodies -- the key determines the world) and the seven server-managed fields are
5
- * added, since they ride every wire body and appear in no element YAML. */
5
+ * added, since they ride every wire body and appear in no element YAML; `known_of`
6
+ * rides only a stub. */
6
7
  interface OwElementBase {
7
8
  /** Element type slug (server-managed, read-only). */
8
9
  type: string;
@@ -30,6 +31,8 @@ interface OwElementBase {
30
31
  changed_by?: string | null;
31
32
  /** The door of the last write (server-managed, read-only): `seat` (an agent seat), `mcp`, `declared` (a client that sent X-OW-Agent), then `api`, `v1`, `sync`, or `server` (keel's own writes). The first three are agent-made. Null for anything last changed before writes recorded it. */
32
33
  changed_via?: 'seat' | 'mcp' | 'declared' | 'api' | 'v1' | 'sync' | 'server' | null;
34
+ /** Present, and true, only on a stub (server-managed, read-only): an element a guest or player reaches only through its own Character's sheet. A stub keeps id, type, name, description, supertype, subtype, image_url, created_at, updated_at and change_seq; every other field reads unset ("", null, []), created_by/changed_by/changed_via null, no extension fields, and its files are 404. Its empties are not values and not a deletion: never write a stub back. Absent: the element is whole. See isKnownOf(). */
35
+ known_of?: true;
33
36
  /** Namespaced extension fields (atlas_* / shadow_* / x_*), returned verbatim. */
34
37
  [ext: string]: unknown;
35
38
  }
@@ -1955,6 +1958,19 @@ type OwWriteDoor = NonNullable<OwElementBase['changed_via']>;
1955
1958
  declare const AGENT_DOORS: readonly OwWriteDoor[];
1956
1959
  /** Whether a write came through an agent's door (`changed_via` or `deleted_via`); false for null. */
1957
1960
  declare function isAgentDoor(door: OwWriteDoor | null | undefined): boolean;
1961
+ /**
1962
+ * Whether an element body is a stub the reader only knows of (keel PR 113, B16; keel #421
1963
+ * §7g): a guest or player reaches it only through its own Character's sheet, one step out.
1964
+ * A stub keeps `id`, `type`, `name`, `description`, `supertype`, `subtype`, `image_url`,
1965
+ * `created_at`, `updated_at` and `change_seq`; every other field reads unset (`""`, `null`,
1966
+ * `[]`), `created_by`, `changed_by` and `changed_via` are null, it carries no extension
1967
+ * fields, and its files answer 404. Those empties are not the element's values and not a
1968
+ * deletion: never merge them over a whole copy, and never write a stub back (this client
1969
+ * refuses to). True only for `known_of: true`; absent or anything else is a whole element.
1970
+ */
1971
+ declare function isKnownOf(element: {
1972
+ known_of?: unknown;
1973
+ } | null | undefined): boolean;
1958
1974
  /**
1959
1975
  * /changes response. Wire shape verified in keel source (core/changes.py) and
1960
1976
  * pinned here: {cursor, changes, has_more, head}.
@@ -2941,7 +2957,11 @@ declare class OwV2Client {
2941
2957
  create(type: ElementType | string, element: OwElement | Record<string, unknown>, opts?: {
2942
2958
  idempotencyKey?: string;
2943
2959
  }): Promise<OwElement>;
2944
- /** PUT /{type}/{id}/ -- upsert-by-client-id. The local-first write primitive. */
2960
+ /**
2961
+ * PUT /{type}/{id}/ -- upsert-by-client-id. The local-first write primitive. A known-of
2962
+ * stub (`known_of: true`, see isKnownOf) is a TypeError before sending, here and on every
2963
+ * element write: a PUT of one would blank every field it does not keep.
2964
+ */
2945
2965
  upsert(type: ElementType | string, id: string, element: OwElement | Record<string, unknown>): Promise<OwElement>;
2946
2966
  /**
2947
2967
  * PATCH /{type}/{id}/ -- partial update. DESTRUCTIVE on sent fields: arrays
@@ -4383,4 +4403,4 @@ declare class TokenResource {
4383
4403
  getEncryptionInfo(): Promise<EncryptionInfo>;
4384
4404
  }
4385
4405
 
4386
- export { AGENT_DOORS, ASK_LIMITS, type AccessKeyResponse, CALENDAR_FIELD, CALENDAR_SUPERTYPE, type CalendarConfig, type CalendarEra, type CalendarIntercalary, type CalendarMonths, type CalendarWeek, ELEMENT_FAMILIES, ELEMENT_ICONS, ELEMENT_LABELS, ELEMENT_SECTIONS, ELEMENT_TYPES, type ElementFamily, type ElementType, type EncryptionInfo, type EraDomain, FAMILY_COLORS, FAMILY_ORDER, FIELD_ACRONYMS, FIELD_SCHEMA, FILE_EXTENSIONS, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, type OwAgentJoined, type OwAllowance, OwApiError, type OwAskAnswer, type OwAskContext, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwCalendarElement, type OwChange, type OwChangeDelete, type OwChangeUpsert, type OwChangesPage, type OwClientConfig, type OwCreatedWorldLink, type OwDeleteReason, type OwElement, type OwElementBase, type OwErrorBody, type OwFileAddedBy, type OwFileDownload, type OwFileKind, type OwFileRow, type OwFileTicket, type OwJoinPreview, type OwJoined, type OwJoinedMembership, type OwKeyKind, type OwLinkEdit, type OwLookupElement, type OwLookupHit, type OwLookupLink, type OwLookupOptions, type OwLookupReason, type OwMe, type OwMediaTicket, type OwMember, type OwMemberRole, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, type OwPlayerJoined, OwPreconditionFailedError, type OwPreparedPhoto, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwResyncReason, type OwSeat, type OwSeatCreated, OwSignInError, type OwSignInReason, type OwSignedIn, type OwSortAddToRow, type OwSortAnswer, type OwSortAskRow, type OwSortLinkRow, type OwSortMakeRow, type OwSortNote, type OwSortRef, type OwSortRow, type OwUploadedFile, type OwUploadedImage, OwV2Client, type OwWorldFileEntry, OwWorldFileError, type OwWorldFileReason, type OwWorldFileRow, type OwWorldFiles, type OwWorldIndex, type OwWorldLink, type OwWorldMeta, type OwWriteDoor, PHOTO_MAX_BYTES, PHOTO_TYPES, type ParseError, type ParseOptions, type ParseResult, type RevokeAllSessionsResponse, type RevokeSessionResponse, SPATIAL_TYPES, type SectionInfo, type StepBreakdown, type TokenConsumeParams, type TokenConsumeResponse, TokenResource, type TokenStatus, type TokenTransport, WORLD_FILE_MAX_BYTES, WORLD_FILE_MAX_ENTRIES, type WorldCalendar, absYearForEraYear, absoluteYear, approxYearLength, breakdownStep, buildAskContext, buildWorldIndex, detectKeyKind, displayYear, elementColor, eraForStep, errorFromResponse, familyOf, fieldLabel, fileKindOf, floorDiv, floorMod, formatDuration, formatMidLabel, formatStep, formatYearLabel, getElementIcon, getElementLabel, isAgentDoor, isDemoKey, isLeapYear, isParseError, kindCanWrite, lookupWorld, monthStartSteps, parseDate, parseErrorEnvelope, pinExpectation, planPhoto, preparePhoto, readCalendarConfig, readCalendarValue, readWorldFile, renderElement, resolveCalendarLens, resolveEraDomains, validateCalendarConfig, weekdayForStep, worldCalendars, writeWorldFile, yearLength, yearStartStep };
4406
+ export { AGENT_DOORS, ASK_LIMITS, type AccessKeyResponse, CALENDAR_FIELD, CALENDAR_SUPERTYPE, type CalendarConfig, type CalendarEra, type CalendarIntercalary, type CalendarMonths, type CalendarWeek, ELEMENT_FAMILIES, ELEMENT_ICONS, ELEMENT_LABELS, ELEMENT_SECTIONS, ELEMENT_TYPES, type ElementFamily, type ElementType, type EncryptionInfo, type EraDomain, FAMILY_COLORS, FAMILY_ORDER, FIELD_ACRONYMS, FIELD_SCHEMA, FILE_EXTENSIONS, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, type OwAgentJoined, type OwAllowance, OwApiError, type OwAskAnswer, type OwAskContext, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwCalendarElement, type OwChange, type OwChangeDelete, type OwChangeUpsert, type OwChangesPage, type OwClientConfig, type OwCreatedWorldLink, type OwDeleteReason, type OwElement, type OwElementBase, type OwErrorBody, type OwFileAddedBy, type OwFileDownload, type OwFileKind, type OwFileRow, type OwFileTicket, type OwJoinPreview, type OwJoined, type OwJoinedMembership, type OwKeyKind, type OwLinkEdit, type OwLookupElement, type OwLookupHit, type OwLookupLink, type OwLookupOptions, type OwLookupReason, type OwMe, type OwMediaTicket, type OwMember, type OwMemberRole, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, type OwPlayerJoined, OwPreconditionFailedError, type OwPreparedPhoto, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwResyncReason, type OwSeat, type OwSeatCreated, OwSignInError, type OwSignInReason, type OwSignedIn, type OwSortAddToRow, type OwSortAnswer, type OwSortAskRow, type OwSortLinkRow, type OwSortMakeRow, type OwSortNote, type OwSortRef, type OwSortRow, type OwUploadedFile, type OwUploadedImage, OwV2Client, type OwWorldFileEntry, OwWorldFileError, type OwWorldFileReason, type OwWorldFileRow, type OwWorldFiles, type OwWorldIndex, type OwWorldLink, type OwWorldMeta, type OwWriteDoor, PHOTO_MAX_BYTES, PHOTO_TYPES, type ParseError, type ParseOptions, type ParseResult, type RevokeAllSessionsResponse, type RevokeSessionResponse, SPATIAL_TYPES, type SectionInfo, type StepBreakdown, type TokenConsumeParams, type TokenConsumeResponse, TokenResource, type TokenStatus, type TokenTransport, WORLD_FILE_MAX_BYTES, WORLD_FILE_MAX_ENTRIES, type WorldCalendar, absYearForEraYear, absoluteYear, approxYearLength, breakdownStep, buildAskContext, buildWorldIndex, detectKeyKind, displayYear, elementColor, eraForStep, errorFromResponse, familyOf, fieldLabel, fileKindOf, floorDiv, floorMod, formatDuration, formatMidLabel, formatStep, formatYearLabel, getElementIcon, getElementLabel, isAgentDoor, isDemoKey, isKnownOf, isLeapYear, isParseError, kindCanWrite, lookupWorld, monthStartSteps, parseDate, parseErrorEnvelope, pinExpectation, planPhoto, preparePhoto, readCalendarConfig, readCalendarValue, readWorldFile, renderElement, resolveCalendarLens, resolveEraDomains, validateCalendarConfig, weekdayForStep, worldCalendars, writeWorldFile, yearLength, yearStartStep };
package/dist/index.js CHANGED
@@ -429,6 +429,7 @@ var OwV2Client = class {
429
429
  * `null` are accepted and stored as `''` (nameless Markers exist).
430
430
  */
431
431
  async create(type, element, opts = {}) {
432
+ refuseKnownOf(element, "create");
432
433
  const body = sanitizePayload(element);
433
434
  if (body.id === void 0 || body.id === null || body.id === "") {
434
435
  body.id = opts.idempotencyKey ? keyedId(opts.idempotencyKey, 0) : mintUuid();
@@ -439,8 +440,13 @@ var OwV2Client = class {
439
440
  idempotencyKey: opts.idempotencyKey
440
441
  });
441
442
  }
442
- /** PUT /{type}/{id}/ -- upsert-by-client-id. The local-first write primitive. */
443
+ /**
444
+ * PUT /{type}/{id}/ -- upsert-by-client-id. The local-first write primitive. A known-of
445
+ * stub (`known_of: true`, see isKnownOf) is a TypeError before sending, here and on every
446
+ * element write: a PUT of one would blank every field it does not keep.
447
+ */
443
448
  async upsert(type, id, element) {
449
+ refuseKnownOf(element, "upsert");
444
450
  return this.request("PUT", `/${type}/${id}/`, { write: true, body: sanitizePayload(element) });
445
451
  }
446
452
  /**
@@ -454,6 +460,7 @@ var OwV2Client = class {
454
460
  * it for today's unconditional write.
455
461
  */
456
462
  async patch(type, id, partial, opts = {}) {
463
+ refuseKnownOf(partial, "patch");
457
464
  return this.request("PATCH", `/${type}/${id}/`, { write: true, body: sanitizePayload(partial), ifMatch: opts.ifMatch });
458
465
  }
459
466
  /**
@@ -492,6 +499,7 @@ var OwV2Client = class {
492
499
  * fresh UUIDv7. Ids you supply are kept.
493
500
  */
494
501
  async bulk(items, opts = {}) {
502
+ items.forEach((it, index) => refuseKnownOf(it.element, `bulk: items[${index}]`));
495
503
  const body = {
496
504
  items: items.map((it, index) => {
497
505
  const element = sanitizePayload(it.element);
@@ -1191,13 +1199,20 @@ var OwV2Client = class {
1191
1199
  return parsed;
1192
1200
  }
1193
1201
  };
1194
- var READ_ONLY_FIELDS = ["world", "type", "created_at", "updated_at", "change_seq"];
1202
+ var READ_ONLY_FIELDS = ["world", "type", "created_at", "updated_at", "change_seq", "known_of"];
1195
1203
  function sanitizePayload(payload) {
1196
1204
  if (!payload || typeof payload !== "object") return payload;
1197
1205
  const rest = { ...payload };
1198
1206
  for (const field of READ_ONLY_FIELDS) delete rest[field];
1199
1207
  return rest;
1200
1208
  }
1209
+ function refuseKnownOf(payload, where) {
1210
+ if (payload && typeof payload === "object" && payload.known_of === true) {
1211
+ throw new TypeError(
1212
+ `${where}: this body is a known-of stub (known_of: true), not the element: its empty fields are not its values, and writing it would blank them. Write from a whole copy, or send only the fields you mean to change.`
1213
+ );
1214
+ }
1215
+ }
1201
1216
  async function endAuthSession(fetchImpl, auth, session) {
1202
1217
  const abort = new AbortController();
1203
1218
  const timer = setTimeout(() => abort.abort(), 5e3);
@@ -2072,6 +2087,9 @@ var AGENT_DOORS = ["seat", "mcp", "declared"];
2072
2087
  function isAgentDoor(door) {
2073
2088
  return door != null && AGENT_DOORS.includes(door);
2074
2089
  }
2090
+ function isKnownOf(element) {
2091
+ return element != null && element.known_of === true;
2092
+ }
2075
2093
 
2076
2094
  // src/v2/palette.ts
2077
2095
  var FAMILY_COLORS = {
@@ -3567,6 +3585,7 @@ export {
3567
3585
  getElementLabel,
3568
3586
  isAgentDoor,
3569
3587
  isDemoKey,
3588
+ isKnownOf,
3570
3589
  isLeapYear,
3571
3590
  isParseError,
3572
3591
  kindCanWrite,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlyworlds/sdk",
3
- "version": "4.39.0",
3
+ "version": "4.40.0",
4
4
  "description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
5
5
  "type": "module",
6
6
  "exports": {