@pouchy_ai/world-sdk 0.22.0 → 0.24.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,41 @@
1
1
  # @pouchy_ai/world-sdk
2
2
 
3
+ ## 0.24.0
4
+
5
+ - **`startNextEpisode` now rides the MACHINE lane** (Secret Key + provider
6
+ signature, id namespace `epn:`) — the same two proofs as `runTurn` — so an
7
+ unattended serial backend can advance its own player's worldline. Before
8
+ this it required the expiring owner token, which made serial UX impossible
9
+ for an integrator: the backend that drives every beat of the story could
10
+ not move it to the next episode (#2998).
11
+ - The method signature is unchanged (`startNextEpisode(environmentId,
12
+ worldInstanceId)`); an optional `{ requestId }` third argument pins the
13
+ signature id slot for callers that want to supply their own.
14
+ - The server still accepts the owner lane, and still exposes no `/admin`
15
+ mirror — an admin key proves the project, never the Provider.
16
+ - Idempotence is untouched: the run id stays server-derived from the
17
+ worldline, the episode and the revision it starts at; `requestId` is a
18
+ signature slot, not an idempotency key.
19
+ - Server-side this is world API 1.30.0.
20
+
21
+ ## 0.23.0
22
+
23
+ - **`runTurn` gains `focusRoles?: string[]`** — narrow one beat to the named
24
+ roles: an interview, a one-on-one scene, a whispered aside. Before this,
25
+ every caller beat ran (and billed) every bound role up to the server cap,
26
+ and a one-on-one scene was impossible to express through the API.
27
+ - **Shrink-only.** The server intersects the focus with the bound story cast,
28
+ mirroring the event lane's subscription routing: it can never widen a beat
29
+ past the roles the story declares or past the server's own cap. A focus
30
+ that matches no bound role is refused as `no selectable role` and spends
31
+ nothing.
32
+ - Roles outside the focus are **not run and not billed**; the result's
33
+ `skippedRoles` records each as `'not focused'` — a new member of the skip
34
+ vocabulary — so the readback stays honest about who was passed over.
35
+ - Absent or empty means every bound role answers, exactly as before. No
36
+ existing call changes behaviour.
37
+ - Server-side this is world API 1.29.0.
38
+
3
39
  ## 0.22.0
4
40
 
5
41
  - **`startNextEpisode(environmentId, worldInstanceId)`** — start the next
package/README.md CHANGED
@@ -45,7 +45,10 @@ const world = new PouchyWorldClient({
45
45
  **Authoring** — `createStoryPackage`, `publishStoryPackage`, `getStoryPackage`,
46
46
  `listStoryPackages`, `createWorld`, `publishWorld`, `getWorld`, `listWorlds`.
47
47
 
48
- **Running** — `createWorldSession`, `runTurn`, `sendEvent`.
48
+ **Running** — `createWorldSession`, `runTurn`, `sendEvent`,
49
+ `startNextEpisode` (machine lane since 0.24.0 — an unattended serial backend
50
+ can advance its own player's worldline to the next episode and receive the
51
+ carryover).
49
52
 
50
53
  **Deliberating** — `deliberate`, `selectCandidate`. Ask for a couple of public
51
54
  directions a beat could take, then commit the one the player picked. Off unless
@@ -115,6 +118,23 @@ await world.runTurn({
115
118
  no single commit for a deterministic write to ride, and sending patches to one
116
119
  answers 422 rather than dropping them quietly.
117
120
 
121
+ **Focusing a beat on some of the cast.** By default every bound role answers a
122
+ beat (up to the server cap). For an interview or a one-on-one scene, pass
123
+ `focusRoles` and only those roles run — the rest are not billed and are
124
+ recorded in `skippedRoles` as `'not focused'`:
125
+
126
+ ```ts
127
+ const beat = await world.runTurn({
128
+ environmentId, worldInstanceId,
129
+ text: 'Amara, what did you see?',
130
+ focusRoles: ['amara']
131
+ });
132
+ ```
133
+
134
+ Shrink-only: the server intersects the focus with the bound story cast, so it
135
+ can never widen a beat, and a focus matching no bound role is refused as
136
+ `no selectable role` without spending anything.
137
+
118
138
  **Resuming after a crash.** `getTurn` returns the same fields the live result
119
139
  did — `nextOptions` included — so a recovered session can offer the audience
120
140
  the choices it was about to. To catch up on beats you missed entirely, store
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * a project is running — which is exactly the field you reach for when a
5
5
  * customer's integration behaves like an older SDK than they say they have.
6
6
  * It sat at '0.1.0' for eight releases before anything compared the two. */
7
- export declare const WORLD_SDK_VERSION = "0.22.0";
7
+ export declare const WORLD_SDK_VERSION = "0.24.0";
8
8
  export declare const DEFAULT_BASE_URL = "https://pouchy.ai/v1";
9
9
  /** One direction a beat could take. The WHOLE of what a deliberation shows a
10
10
  * player: no reasoning, no role secrets, no simulated effects, no scores. */
@@ -1032,10 +1032,17 @@ export declare class PouchyWorldClient {
1032
1032
  * revision it starts at, so a retried call lands on the same run and
1033
1033
  * answers `created: false` rather than forking the story.
1034
1034
  *
1035
- * OWNER TOKEN ONLY no `/admin` mirror, on the same reasoning as
1036
- * `preflightWorld`: the mirror carries reads, and this is the write that
1037
- * decides a story moves on. */
1038
- startNextEpisode(environmentId: string, worldInstanceId: string): Promise<WorldNextEpisodeResponse>;
1035
+ * MACHINE LANE since 0.24.0 (#2998, world API 1.30.0): a Secret Key plus a
1036
+ * provider signature in the `epn:` namespace the same two proofs as
1037
+ * `runTurn`, because the party trusted to author every beat, including the
1038
+ * one that ends an episode, is the party deciding the story moves on. So
1039
+ * an unattended serial backend can now advance its own player's worldline.
1040
+ * (Before 0.24.0 this rode the owner token; the server still accepts that
1041
+ * lane, and still exposes no `/admin` mirror — an admin key proves the
1042
+ * project, never the Provider.) */
1043
+ startNextEpisode(environmentId: string, worldInstanceId: string, opts?: {
1044
+ requestId?: string;
1045
+ }): Promise<WorldNextEpisodeResponse>;
1039
1046
  /** Publish the next world revision. Existing world INSTANCES keep the
1040
1047
  * revision they were created on — a published change reaches new instances
1041
1048
  * only, which is what keeps a running story from changing runtime or rules
@@ -1320,6 +1327,15 @@ export declare class PouchyWorldClient {
1320
1327
  turnId?: string;
1321
1328
  text: string;
1322
1329
  sourceRoleId?: string;
1330
+ /** Narrow this beat to the named roles — an interview, a one-on-one
1331
+ * scene, a whispered aside. Shrink-only: the server intersects it with
1332
+ * the bound story cast, so it can never widen the beat past the roles
1333
+ * the story declares or the server's own cap. Roles outside the focus
1334
+ * are not run and not billed; they appear in the result's
1335
+ * `skippedRoles` as `'not focused'`. A focus that matches no bound
1336
+ * role is refused as "no selectable role" and spends nothing. Absent
1337
+ * or empty means every bound role answers, exactly as before. */
1338
+ focusRoles?: readonly string[];
1323
1339
  traceId?: string;
1324
1340
  /** PROVIDER-authored state changes, committed with this beat.
1325
1341
  *
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import { createHash, createHmac, randomUUID } from 'node:crypto';
23
23
  * a project is running — which is exactly the field you reach for when a
24
24
  * customer's integration behaves like an older SDK than they say they have.
25
25
  * It sat at '0.1.0' for eight releases before anything compared the two. */
26
- export const WORLD_SDK_VERSION = '0.22.0';
26
+ export const WORLD_SDK_VERSION = '0.24.0';
27
27
  export const DEFAULT_BASE_URL = 'https://pouchy.ai/v1';
28
28
  /** Read the commit turn id out of an envelope, so the signature covers the id
29
29
  * the server will commit under. The payload half is base64url JSON; the
@@ -356,11 +356,20 @@ export class PouchyWorldClient {
356
356
  * revision it starts at, so a retried call lands on the same run and
357
357
  * answers `created: false` rather than forking the story.
358
358
  *
359
- * OWNER TOKEN ONLY no `/admin` mirror, on the same reasoning as
360
- * `preflightWorld`: the mirror carries reads, and this is the write that
361
- * decides a story moves on. */
362
- startNextEpisode(environmentId, worldInstanceId) {
363
- return this.owner('POST', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/episodes/next`, {});
359
+ * MACHINE LANE since 0.24.0 (#2998, world API 1.30.0): a Secret Key plus a
360
+ * provider signature in the `epn:` namespace the same two proofs as
361
+ * `runTurn`, because the party trusted to author every beat, including the
362
+ * one that ends an episode, is the party deciding the story moves on. So
363
+ * an unattended serial backend can now advance its own player's worldline.
364
+ * (Before 0.24.0 this rode the owner token; the server still accepts that
365
+ * lane, and still exposes no `/admin` mirror — an admin key proves the
366
+ * project, never the Provider.) */
367
+ startNextEpisode(environmentId, worldInstanceId, opts = {}) {
368
+ const requestId = opts.requestId ?? newTurnId('epn-req');
369
+ return this.signed(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/episodes/next`, { requestId },
370
+ // Namespaced id slot: an episode-start signature can never be
371
+ // replayed onto the turn door, the events ingress or a deliberation.
372
+ `epn:${requestId}`);
364
373
  }
365
374
  /** Publish the next world revision. Existing world INSTANCES keep the
366
375
  * revision they were created on — a published change reaches new instances
@@ -700,6 +709,7 @@ export class PouchyWorldClient {
700
709
  kind: 'user',
701
710
  text: input.text,
702
711
  ...(input.sourceRoleId ? { sourceRoleId: input.sourceRoleId } : {}),
712
+ ...(input.focusRoles?.length ? { focusRoles: [...input.focusRoles] } : {}),
703
713
  ...(input.proposedPatches ? { proposedPatches: input.proposedPatches } : {})
704
714
  },
705
715
  ...(input.traceId ? { traceId: input.traceId } : {})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pouchy_ai/world-sdk",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
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",