@pouchy_ai/world-sdk 0.8.0 → 0.10.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/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # @pouchy_ai/world-sdk
2
2
 
3
+ ## 0.10.0
4
+
5
+ Additive: an unattended backend can now read its own world. World API 1.7.
6
+
7
+ - New `adminKey` client option — a project admin key (`pak_…`), minted once from
8
+ the dashboard, long-lived and machine-held. When present, the world READS
9
+ (`getWorldState`, `listTurns`, `listTurnsSince`, `getTurn`, `getWorldMetrics`,
10
+ `listDeliveries`) go to a new `/v1/admin/environments/**` mirror instead of the
11
+ owner plane.
12
+ - Why this exists: `adminToken` is a Firebase ID token — roughly an hour of life,
13
+ minted by a browser sign-in, with no API-key path. A backend could DRIVE a turn
14
+ with its own credentials (Secret Key + provider signature) and then needed a
15
+ human at a browser to find out what the turn did. Resuming after a crash was
16
+ the same story. That was the single thing keeping a sixty-minute integration
17
+ out of reach, and it was a credential gap, not a missing capability.
18
+ - The mirror is reads only, and every route is GET. Driving a turn still needs
19
+ `secretKey` + `signing`: an admin key proves the PROJECT, never the Provider,
20
+ and the world's turn door requires both proofs. The delivery queue ACTIONS
21
+ (requeue, rehydrate, resolve-gap, drain), ledger archival, and the content loop
22
+ stay on `adminToken` — each decides something about a real reader's experience
23
+ of a story, or shortens the committed record.
24
+ - Both doors answer from one shared server-side read, so which credential you
25
+ hold does not change the answer.
26
+ - No existing method changed shape, and `adminToken` keeps working exactly as
27
+ before.
28
+
29
+ ## 0.9.0
30
+
31
+ Additive: the read-back becomes a real recovery path, and the timeline gets a
32
+ resume cursor. World API 1.6.
33
+
34
+ - `getTurn` now returns a typed `WorldTurnReadback` instead of an opaque
35
+ record, and the server fills in the four turn-time facts the live result
36
+ always had and the read-back never did: `selectedRoles`, `skippedRoles`,
37
+ `repairs` and `nextOptions`. Recovering a lost response used to tell you what
38
+ the world changed and not what it offered next, which made resuming a story
39
+ guesswork. All four are optional — a turn committed before this release
40
+ carries none of them, and absence means UNKNOWN, not "none".
41
+ - Two divergences from `WorldTurnResult` are deliberate and now typed rather
42
+ than implied: a read-back's `completionStatus`/`executionStatus` are the
43
+ constant `'committed'` (a turn that never committed has no entry and answers
44
+ 404), and its `deliveryStatus` adds `'unknown'` (once settled rows are
45
+ reaped, the absence of a queue row is not proof of delivery).
46
+ - New `listTurns(environmentId, worldInstanceId, { limit, sinceSeq })` returning
47
+ a `WorldTimelinePage`. With `sinceSeq` you get the beats AFTER that revision,
48
+ oldest first, plus `nextSinceSeq` to continue; without it you get the
49
+ newest-first window as before. The response names its own `order`.
50
+ - New `listTurnsSince(...)` — follows that cursor to the end. This is the resume
51
+ primitive: a backend that stored the last `seq` it handled gets exactly the
52
+ beats it missed, in order, with no gap at a page boundary and no beat twice.
53
+ Bounded by `maxPages`, because an unbounded follow loop against a busy world
54
+ never returns.
55
+ - The API document is now served at `GET https://pouchy.ai/v1/world/openapi` —
56
+ public, unauthenticated, and previously reachable by nobody.
57
+ - No existing method changed shape.
58
+
3
59
  ## 0.8.0
4
60
 
5
61
  - `WorldDeliveryOpsRow` gained `cleanupRetryCount` and `cleanupStalledAt`.
package/README.md CHANGED
@@ -47,7 +47,50 @@ const world = new PouchyWorldClient({
47
47
 
48
48
  **Running** — `createWorldSession`, `runTurn`, `sendEvent`.
49
49
 
50
- **Reading** — `getWorldState`, `getTurn`, `replayLedger`, `replayLedgerToEnd`.
50
+ **Reading** — `getWorldState`, `getTurn`, `listTurns`, `listTurnsSince`,
51
+ `replayLedger`, `replayLedgerToEnd`.
52
+
53
+ **Which credential does what.** This matters more than it looks: one of them
54
+ expires within the hour.
55
+
56
+ | you hold | you can | you cannot |
57
+ |---|---|---|
58
+ | `secretKey` + `signing` | mint sessions, drive turns, send events | read anything back |
59
+ | `adminKey` (`pak_…`, long-lived) | read the world: overview, state, timeline, turn read-back, metrics, delivery queue | drive a turn, act on the queue, author |
60
+ | `adminToken` (Firebase ID token, ~1h) | everything above plus authoring and the content loop | outlive the hour |
61
+
62
+ A server holds `secretKey` + `signing` + `adminKey` and needs no browser login
63
+ anywhere in its deployment. `adminToken` is for a person, or for a script a
64
+ person is watching.
65
+
66
+ ```ts
67
+ const world = new PouchyWorldClient({
68
+ projectId: process.env.POUCHY_PROJECT_ID!,
69
+ secretKey: process.env.POUCHY_SECRET_KEY!, // drive
70
+ adminKey: process.env.POUCHY_ADMIN_KEY!, // read back — does not expire
71
+ signing: { source: 'drama-backend', keyId: …, secret: … }
72
+ });
73
+ ```
74
+
75
+ An admin key proves the PROJECT, never the Provider, so it cannot drive a turn —
76
+ the turn door requires a Secret Key *and* a signature over the exact bytes. And
77
+ the delivery queue ACTIONS stay on `adminToken` on purpose: requeue, rehydrate
78
+ and resolve-gap each decide what happens to a reader who is missing a beat, and
79
+ resolve-gap tells them it is never coming.
80
+
81
+ **Resuming after a crash.** `getTurn` returns the same fields the live result
82
+ did — `nextOptions` included — so a recovered session can offer the audience
83
+ the choices it was about to. To catch up on beats you missed entirely, store
84
+ the last `seq` you processed and call `listTurnsSince`:
85
+
86
+ ```ts
87
+ const missed = await world.listTurnsSince(envId, instanceId, lastSeqIHandled);
88
+ for (const beat of missed) render(beat); // in order, no gap, no repeat
89
+ ```
90
+
91
+ Both are additive: a turn committed before world API 1.6 carries none of the
92
+ four turn-time facts (`selectedRoles`, `skippedRoles`, `repairs`,
93
+ `nextOptions`). Absent means UNKNOWN, never "none".
51
94
 
52
95
  **Content return** — `createScriptDraft`, `getScriptDraft`, `listScriptDrafts`,
53
96
  `reviewScriptDraft`, `exportScriptDraft`.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const WORLD_SDK_VERSION = "0.1.0";
1
+ export declare const WORLD_SDK_VERSION = "0.10.0";
2
2
  export declare const DEFAULT_BASE_URL = "https://pouchy.ai/v1";
3
3
  /** The refusal classes a world call can produce. `unknown` is deliberate: an
4
4
  * unrecognized status is never quietly folded into a neighbour. */
@@ -106,6 +106,102 @@ export interface WorldTurnResult {
106
106
  /** Why a redacted dead letter could not be rebuilt. A closed vocabulary: an
107
107
  * operator deciding whether to resolve a gap is making a judgement call, and a
108
108
  * free-text reason is one they cannot compare across incidents. */
109
+ /** What `GET …/turns/{turnId}` answers. The live result's fields, plus what
110
+ * only the record knows (when it committed, which revision it bound to, how
111
+ * its lines were delivered).
112
+ *
113
+ * Two deliberate differences from `WorldTurnResult`, both because a read-back
114
+ * exists ONLY for a turn that committed:
115
+ *
116
+ * · `completionStatus` / `executionStatus` are the constant `'committed'`,
117
+ * not a member of the live union. A turn that never committed has no entry
118
+ * and answers 404 — there is no status to report;
119
+ * · `deliveryStatus` adds `'unknown'`, which is a real answer once settled
120
+ * rows have been reaped: no queue row is not proof of delivery.
121
+ *
122
+ * The four turn-time facts are optional because rows committed before world
123
+ * API 1.6 do not carry them. Absent means UNKNOWN. */
124
+ export interface WorldTurnReadback {
125
+ turnId: string;
126
+ worldInstanceId: string;
127
+ beforeStateRevision: number;
128
+ afterStateRevision: number;
129
+ roleMessages: Array<{
130
+ roleId: string;
131
+ message: string;
132
+ }>;
133
+ committedStateDiff: unknown[];
134
+ rejectedEffects: Array<{
135
+ index: number;
136
+ reason: string;
137
+ roleId?: string;
138
+ kind?: string;
139
+ }>;
140
+ selectedRoles?: string[];
141
+ skippedRoles?: Array<{
142
+ roleId: string;
143
+ reason: string;
144
+ }>;
145
+ repairs?: Array<{
146
+ roleId: string;
147
+ outcome: 'repaired' | 'fallback';
148
+ reason?: string;
149
+ }>;
150
+ nextOptions?: Array<{
151
+ branchId: string;
152
+ condition: string;
153
+ }>;
154
+ committedAt: number;
155
+ environmentRevision: number;
156
+ storyPackageRef?: {
157
+ packageId: string;
158
+ revision: number;
159
+ contentHash: string;
160
+ };
161
+ eventId?: string;
162
+ traceId?: string;
163
+ completionStatus: 'committed';
164
+ executionStatus: 'committed';
165
+ deliveryStatus: WorldDeliveryStatus | 'unknown';
166
+ deliveryGaps?: unknown[];
167
+ deliveryCounts?: {
168
+ total: number;
169
+ delivered: number;
170
+ [k: string]: number;
171
+ };
172
+ }
173
+ /** One page of the committed timeline. `order` says which way it runs, because
174
+ * the two calls answer different questions: without a cursor you get the
175
+ * newest beats first, with one you get everything after it, oldest first. */
176
+ export interface WorldTimelinePage {
177
+ beats: Array<{
178
+ entryId: string;
179
+ seq: number;
180
+ kind: string;
181
+ actorRoleId: string | null;
182
+ beforeRevision: number;
183
+ afterRevision: number;
184
+ patchCount: number;
185
+ committedAt: number;
186
+ turnId: string | null;
187
+ messages: Array<{
188
+ roleId: string;
189
+ text: string;
190
+ fallback?: true;
191
+ }>;
192
+ effects: Array<{
193
+ kind: string;
194
+ roleId: string;
195
+ target: string | null;
196
+ accepted: boolean;
197
+ code?: WorldRejectionCode;
198
+ reason?: string;
199
+ }>;
200
+ }>;
201
+ order: 'asc' | 'desc';
202
+ /** Present only on a cursor read: pass it back as `sinceSeq` to continue. */
203
+ nextSinceSeq?: number | null;
204
+ }
109
205
  export type WorldRehydrateFailure = 'source_missing' | 'archive_unavailable' | 'hash_mismatch' | 'ledger_drift' | 'identity_mismatch';
110
206
  /** What to DO about a stuck delivery, in one call. The three answers are the
111
207
  * three verbs, in the order they should be tried: re-send what is still there,
@@ -378,8 +474,23 @@ export interface WorldClientOptions {
378
474
  /** The project this client acts for. */
379
475
  projectId: string;
380
476
  /** An OWNER-plane credential (a signed-in admin's ID token) for the control
381
- * plane: story packages, world definitions, drafts, replay, reads. */
477
+ * plane: story packages, world definitions, drafts, replay, reads.
478
+ *
479
+ * Short-lived — a Firebase ID token, roughly an hour — and minted by a
480
+ * browser sign-in. Fine for a script a person is watching; wrong for a
481
+ * server. For a server, use `adminKey`. */
382
482
  adminToken?: string;
483
+ /** A project ADMIN key (`pak_…`), minted once from the dashboard.
484
+ *
485
+ * Long-lived and machine-held: this is what an unattended backend uses.
486
+ * When present, the world READS (overview, state, timeline, turn read-back,
487
+ * metrics, deliveries) go to the `/admin` mirror instead of the owner
488
+ * plane, and no browser login is involved anywhere in your deployment.
489
+ *
490
+ * It proves the PROJECT, never the Provider, so it cannot drive a turn —
491
+ * that still needs `secretKey` + `signing`. Authoring calls and the content
492
+ * loop still need `adminToken`; see the README for which is which. */
493
+ adminKey?: string;
383
494
  /** A project Secret Key (`pchy_sk_…`) for the MACHINE lane: world sessions,
384
495
  * turns and trusted events. It also carries the test/live axis. */
385
496
  secretKey?: string;
@@ -401,6 +512,7 @@ export declare class PouchyWorldClient {
401
512
  private readonly baseUrl;
402
513
  private readonly doFetch;
403
514
  private readonly adminToken?;
515
+ private readonly adminKey?;
404
516
  private readonly secretKey?;
405
517
  private readonly signing?;
406
518
  private readonly timeoutMs;
@@ -448,10 +560,38 @@ export declare class PouchyWorldClient {
448
560
  }>;
449
561
  /** Read back a COMMITTED turn. The recovery path when a response was lost:
450
562
  * it re-runs nothing, and a 404 means the turn never committed. */
451
- getTurn(environmentId: string, worldInstanceId: string, turnId: string): Promise<Record<string, unknown>>;
563
+ /** Read a COMMITTED turn back the recovery path when a response was lost.
564
+ *
565
+ * As of world API 1.6 this carries the same fields the live result did,
566
+ * `nextOptions` included, so a resumed session can offer the audience the
567
+ * same choices it would have. Rows committed before that carry none of the
568
+ * four turn-time facts: absent means UNKNOWN, never "none". */
569
+ getTurn(environmentId: string, worldInstanceId: string, turnId: string): Promise<WorldTurnReadback>;
452
570
  /** Verify that the materialized state is what the committed ledger says.
453
571
  * Always a dry run — it reports, it never repairs. Pass the previous
454
572
  * report's `cursor` to continue a run that came back `incomplete`. */
573
+ /** ONE page of the committed timeline.
574
+ *
575
+ * Pass `sinceSeq` to resume: you get the beats after it, oldest first, and
576
+ * a `nextSinceSeq` to continue with. Without it you get the newest-first
577
+ * window, which is the "what happened lately" view and cannot be paged
578
+ * past its cap. */
579
+ listTurns(environmentId: string, worldInstanceId: string, opts?: {
580
+ limit?: number;
581
+ sinceSeq?: number;
582
+ }): Promise<WorldTimelinePage>;
583
+ /** Walk the timeline FORWARD from a revision already processed, following
584
+ * the cursor to the end. The resume primitive: a backend that stored the
585
+ * last `seq` it handled calls this once on restart and receives exactly the
586
+ * beats it missed, in order, with no gap at a page boundary and no beat
587
+ * twice.
588
+ *
589
+ * `maxPages` is a stop, not a tuning knob — an unbounded follow loop
590
+ * against a busy world never returns. */
591
+ listTurnsSince(environmentId: string, worldInstanceId: string, sinceSeq: number, opts?: {
592
+ pageSize?: number;
593
+ maxPages?: number;
594
+ }): Promise<WorldTimelinePage['beats']>;
455
595
  replayLedger(environmentId: string, worldInstanceId: string, options?: {
456
596
  cursor?: string;
457
597
  maxEntries?: number;
@@ -623,6 +763,13 @@ export declare class PouchyWorldClient {
623
763
  schemaVersion?: number;
624
764
  occurredAt?: number;
625
765
  }): Promise<Record<string, unknown>>;
766
+ /** A world READ. Prefers the machine lane when an admin key is present.
767
+ *
768
+ * `ownerPath` and `adminPath` address the same read through two doors; the
769
+ * server answers both from one shared function, so which door you came in
770
+ * by does not change the answer. The admin door drops `projectId` from the
771
+ * path because the key already names the project. */
772
+ private read;
626
773
  private owner;
627
774
  /** The machine lane: Secret Key AND a source signature over the EXACT bytes
628
775
  * being sent — the two proofs the world requires of a backend. */
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@
17
17
  // SIGNING a request the way the server verifies it, and choosing turn ids that
18
18
  // make a retry idempotent instead of a second beat.
19
19
  import { createHash, createHmac, randomUUID } from 'node:crypto';
20
- export const WORLD_SDK_VERSION = '0.1.0';
20
+ export const WORLD_SDK_VERSION = '0.10.0';
21
21
  export const DEFAULT_BASE_URL = 'https://pouchy.ai/v1';
22
22
  // ── errors ─────────────────────────────────────────────────────────────────
23
23
  /** The refusal classes a world call can produce. `unknown` is deliberate: an
@@ -222,6 +222,7 @@ export class PouchyWorldClient {
222
222
  baseUrl;
223
223
  doFetch;
224
224
  adminToken;
225
+ adminKey;
225
226
  secretKey;
226
227
  signing;
227
228
  timeoutMs;
@@ -233,6 +234,8 @@ export class PouchyWorldClient {
233
234
  this.doFetch = options.fetch ?? globalThis.fetch;
234
235
  if (options.adminToken !== undefined)
235
236
  this.adminToken = options.adminToken;
237
+ if (options.adminKey !== undefined)
238
+ this.adminKey = options.adminKey;
236
239
  if (options.secretKey !== undefined)
237
240
  this.secretKey = options.secretKey;
238
241
  if (options.signing !== undefined)
@@ -271,16 +274,67 @@ export class PouchyWorldClient {
271
274
  return this.owner('PATCH', `/projects/${this.projectId}/environments/${environmentId}`, definition);
272
275
  }
273
276
  getWorldState(environmentId, worldInstanceId) {
274
- return this.owner('GET', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/state`);
277
+ return this.read(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/state`, `/admin/environments/${environmentId}/instances/${worldInstanceId}/state`);
275
278
  }
276
279
  /** Read back a COMMITTED turn. The recovery path when a response was lost:
277
280
  * it re-runs nothing, and a 404 means the turn never committed. */
281
+ /** Read a COMMITTED turn back — the recovery path when a response was lost.
282
+ *
283
+ * As of world API 1.6 this carries the same fields the live result did,
284
+ * `nextOptions` included, so a resumed session can offer the audience the
285
+ * same choices it would have. Rows committed before that carry none of the
286
+ * four turn-time facts: absent means UNKNOWN, never "none". */
278
287
  getTurn(environmentId, worldInstanceId, turnId) {
279
- return this.owner('GET', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/turns/${encodeURIComponent(turnId)}`);
288
+ return this.read(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/turns/${encodeURIComponent(turnId)}`, `/admin/environments/${environmentId}/instances/${worldInstanceId}/turns/${encodeURIComponent(turnId)}`);
280
289
  }
281
290
  /** Verify that the materialized state is what the committed ledger says.
282
291
  * Always a dry run — it reports, it never repairs. Pass the previous
283
292
  * report's `cursor` to continue a run that came back `incomplete`. */
293
+ /** ONE page of the committed timeline.
294
+ *
295
+ * Pass `sinceSeq` to resume: you get the beats after it, oldest first, and
296
+ * a `nextSinceSeq` to continue with. Without it you get the newest-first
297
+ * window, which is the "what happened lately" view and cannot be paged
298
+ * past its cap. */
299
+ listTurns(environmentId, worldInstanceId, opts) {
300
+ const q = new URLSearchParams();
301
+ if (opts?.limit !== undefined)
302
+ q.set('limit', String(opts.limit));
303
+ if (opts?.sinceSeq !== undefined)
304
+ q.set('sinceSeq', String(opts.sinceSeq));
305
+ const suffix = q.toString() ? `?${q}` : '';
306
+ return this.read(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/turns`, `/admin/environments/${environmentId}/instances/${worldInstanceId}/turns`, suffix);
307
+ }
308
+ /** Walk the timeline FORWARD from a revision already processed, following
309
+ * the cursor to the end. The resume primitive: a backend that stored the
310
+ * last `seq` it handled calls this once on restart and receives exactly the
311
+ * beats it missed, in order, with no gap at a page boundary and no beat
312
+ * twice.
313
+ *
314
+ * `maxPages` is a stop, not a tuning knob — an unbounded follow loop
315
+ * against a busy world never returns. */
316
+ async listTurnsSince(environmentId, worldInstanceId, sinceSeq, opts) {
317
+ const pageSize = opts?.pageSize ?? 50;
318
+ const maxPages = opts?.maxPages ?? 20;
319
+ const out = [];
320
+ let cursor = sinceSeq;
321
+ for (let page = 0; page < maxPages; page++) {
322
+ const got = await this.listTurns(environmentId, worldInstanceId, {
323
+ limit: pageSize,
324
+ sinceSeq: cursor
325
+ });
326
+ if (!got.beats.length)
327
+ break;
328
+ out.push(...got.beats);
329
+ const next = got.nextSinceSeq;
330
+ // Defensive: a server that stopped advancing the cursor would other-
331
+ // wise spin here re-reading the same page until maxPages.
332
+ if (typeof next !== 'number' || next <= cursor)
333
+ break;
334
+ cursor = next;
335
+ }
336
+ return out;
337
+ }
284
338
  replayLedger(environmentId, worldInstanceId, options = {}) {
285
339
  return this.owner('POST', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/ledger/replay`, options);
286
340
  }
@@ -304,7 +358,7 @@ export class PouchyWorldClient {
304
358
  if (v !== undefined)
305
359
  q.set(k, String(v));
306
360
  const suffix = q.toString() ? `?${q}` : '';
307
- return this.owner('GET', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/deliveries${suffix}`);
361
+ return this.read(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/deliveries`, `/admin/environments/${environmentId}/instances/${worldInstanceId}/deliveries`, suffix);
308
362
  }
309
363
  /** One delivery. `includePayload` returns the line itself and writes an audit
310
364
  * row naming you — pass it deliberately, not by default. */
@@ -360,7 +414,7 @@ export class PouchyWorldClient {
360
414
  * kept apart because "the lines arrived" and "the world played well" are
361
415
  * different questions. */
362
416
  getWorldMetrics(environmentId, worldInstanceId) {
363
- return this.owner('GET', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/metrics`);
417
+ return this.read(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/metrics`, `/admin/environments/${environmentId}/instances/${worldInstanceId}/metrics`);
364
418
  }
365
419
  /** Score the committed history against a quality suite. Deterministic — no
366
420
  * model judges the output, so a score is something you can regress. */
@@ -476,9 +530,25 @@ export class PouchyWorldClient {
476
530
  return this.signed(`/projects/${this.projectId}/events`, body, eventId);
477
531
  }
478
532
  // ── transport ────────────────────────────────────────────────────────────
533
+ /** A world READ. Prefers the machine lane when an admin key is present.
534
+ *
535
+ * `ownerPath` and `adminPath` address the same read through two doors; the
536
+ * server answers both from one shared function, so which door you came in
537
+ * by does not change the answer. The admin door drops `projectId` from the
538
+ * path because the key already names the project. */
539
+ async read(ownerPath, adminPath, query = '') {
540
+ if (this.adminKey) {
541
+ return this.request('GET', `${adminPath}${query}`, {
542
+ headers: { authorization: `Bearer ${this.adminKey}` }
543
+ });
544
+ }
545
+ return this.owner('GET', `${ownerPath}${query}`);
546
+ }
479
547
  async owner(method, path, body) {
480
548
  if (!this.adminToken) {
481
- throw new Error(`${method} ${path} needs an owner token (adminToken)`);
549
+ throw new Error(`${method} ${path} needs an owner token (adminToken) — or, for a world READ ` +
550
+ `from an unattended backend, a project admin key (adminKey), which routes ` +
551
+ `the read through the /admin mirror and does not expire`);
482
552
  }
483
553
  return this.request(method, path, {
484
554
  headers: { authorization: `Bearer ${this.adminToken}` },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pouchy_ai/world-sdk",
3
- "version": "0.8.0",
4
- "description": "Server-side TypeScript client for Pouchy World story packages, world definitions, world sessions, coordinated turns, trusted events, replay verification and script drafts. Node only: it holds a project Secret Key and a source signing key, which never belong in a browser or a mobile app.",
3
+ "version": "0.10.0",
4
+ "description": "Server-side TypeScript client for Pouchy World \u2014 story packages, world definitions, world sessions, coordinated turns, trusted events, replay verification and script drafts. Node only: it holds a project Secret Key and a source signing key, which never belong in a browser or a mobile app.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "homepage": "https://pouchy.ai/sdk",