@game-infra/game-state-schemas 0.1.0 → 0.3.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/README.md CHANGED
@@ -24,7 +24,7 @@ Keeping both in one resource meant one listing, one delete sweep and one retenti
24
24
  // pnpm sibling checkout
25
25
  "@game-infra/game-state-schemas": "link:../../game-infra/packages/schemas/game-state-schemas"
26
26
  // or from npm
27
- "@game-infra/game-state-schemas": "^0.1.0"
27
+ "@game-infra/game-state-schemas": "^0.2.0"
28
28
  ```
29
29
 
30
30
  `valibot` is a peer dependency (`^1.4.0`); the consumer provides it. `@game-infra/api-schemas-core` and `@game-infra/event-schemas` are runtime dependencies — the first for the success-response wrapper, the second for the `ActivationDTO` effect vocabulary a freeform turn shares with an authored story branch.
@@ -75,12 +75,26 @@ await sendByApiContract(http, gameJournalTruncateContract, {
75
75
 
76
76
  Story mode stores a tree of pregenerated beats addressed by the choices taken to reach them. Freeform play follows the player's own words, so it stores a linear journal instead: one session, one entry per turn. Replaying the journal rebuilds the whole run — play state, cast and memory — so nothing else is stored.
77
77
 
78
- `FreeformTurnPayloadSchema` is what goes inside an entry's `data`: the player's input, the narration, the neurons the adjudicator judged relevant (`focus`), the check it rolled (`ResolvedCheckDTOSchema` — rolled once, recorded forever, never re-rolled on replay), effects in the same `ActivationDTO` vocabulary a story branch carries, the characters the turn introduced (`IntroducedEntityDTOSchema`, id and name guaranteed and the game's own definition passed through), the memories it formed and the consolidations it performed. `FreeformSessionPayloadSchema` is the session header's payload.
78
+ `FreeformTurnPayloadSchema` is what goes inside an entry's `data`: the player's input, the narration, the neurons the adjudicator judged relevant (`focus`), the check it rolled (`ResolvedCheckDTOSchema` — rolled once, recorded forever, never re-rolled on replay), effects in the same `ActivationDTO` vocabulary a story branch carries, the characters the turn introduced (`IntroducedEntityDTOSchema`, id and name guaranteed and the game's own definition passed through), the memories it formed and the consolidations it performed. It also retains `timings` for each generation stage and `stageModels` for the effective canonical model behind each model-backed stage, so a client can show saved timing breakdowns and derive later ETAs from comparable historical runs. `FreeformSessionPayloadSchema` is the session header's payload.
79
79
 
80
- Memory is a synapse graph. Each `MemoryDTO` connects to **neurons** — `character:tallow`, `faction:ash-wardens`, or a domain such as `domain:war` (see `FREEFORM_DOMAINS`, and `neuronKey`/`parseNeuronKey` for the spelling) — and recall walks those connections rather than the whole journal. Over time small memories consolidate: a `ConsolidationDTO` names the entries a single summary supersedes, so five small grievances become "many past grievances" on replay exactly as they did in play.
80
+ Memory is a synapse graph. Each `MemoryDTO` connects to **neurons** — `character:tallow`, `faction:ash-wardens`, or a domain such as `domain:war` (see `FREEFORM_DOMAINS`, and `neuronKey`/`parseNeuronKey` for the spelling) — and recall walks those connections rather than the whole journal. Over time small memories consolidate: a `ConsolidationDTO` names the entries a single summary supersedes, so five small grievances become "many past grievances" on replay exactly as they did in play. A client may write that consolidation back after the visible turn finishes; saving the same journal sequence replaces its payload without adding a second turn.
81
81
 
82
82
  None of these payload schemas is attached to an endpoint — the service stores `data` opaquely — but two clients reading the same session must agree on the shapes.
83
83
 
84
+ ## Narrative threads — several scenes at once
85
+
86
+ A linear journal answers "what happened" with one list, which works until a player wants two things happening at once: a negotiation in the capital and a raid on the coast, each worth its own tab. Two of those cannot append to one list — whichever finished second would be narrated as though the first had already happened, and neither player intent was ever "and meanwhile, knowing that".
87
+
88
+ `NarrativeThreadPayloadSchema` makes a thread a **fork of the journal**. `baseSequence` is the whole of the fork: the number of canon turns the thread could see when it opened. Everything before it is shared; everything the thread writes lands after it and is invisible to its siblings until a merge. The trunk is the degenerate case — `CANON_THREAD_ID`, no parent, base zero. A turn records the thread that wrote it (`threadId`) and the index it held there before a merge renumbered it (`originSequence`); both are optional, so a journal written before threads existed still reads.
89
+
90
+ Convergence is where the honesty has to be. Two scenes written against the same past will sometimes contradict — a character killed in one and bargaining in the other, an item spent twice. `ThreadMergeReviewPayloadSchema` records the third answer between concatenating and refusing: the `ContradictionDTO`s found, graded `cosmetic` / `continuity` / `blocking`, and the `RetconDTO` amendments that reconcile them. A retcon may only `amend-narration`, `annotate`, `supersede-memory` or `reorder` — a merge agent that may rewrite anything can quietly delete a turn the player was proud of. Retcons are stored beside the journal rather than applied into it, so a reader sees both what was narrated and what was changed to make two scenes one story. `order` is the canon sequence the merge chose, recorded rather than re-derived: model finish times say when a call returned, not when a thing happened.
91
+
92
+ ## Runs in flight
93
+
94
+ Resolving one turn is many model calls over many seconds, and a client that owns that work loses it whenever the browser reloads. `TurnRunPayloadSchema` is the run as a **server-owned, stage-checkpointed record**: `stages` in execution order with a `RunStageDTO` status each, and every partial output published the moment it exists — `check` as soon as the dice are rolled, `narration` as soon as the prose lands, `effects` once the consequences are interpreted, `memories` once the chronicler has been. A client renders whatever the record already carries and reattaches to a run in flight rather than restarting it.
95
+
96
+ `status` distinguishes the three states a progressive client actually needs: `running` (nothing to show yet), `narrated` (there is prose on screen) and `settling` (the prose is final, the bookkeeping is still going). `settling` is the one worth spelling out — chronicling and consolidation happen after the player is already reading, so the turn looks finished, but the next turn in the same thread must not start until they land or it is adjudicated against a memory graph missing the turn before it. `leaseUntil` tells a stalled run from a slow one: without it, a worker that dies mid-run leaves the record `running` forever and nothing else dares touch it.
97
+
84
98
  ## Savegames
85
99
 
86
100
  Slot snapshots are `game-state-service`'s other half, but their contracts predate this package and stay where they are, in `@game-infra/api-schemas-core`: `savegameSaveContract`, `savegameLoadContract`, `savegameListSlotsContract`, at `/users/:userId/games/:gameId/saves/:saveSlot`. A published shape does not move just to sit next to its neighbours.
@@ -342,6 +342,23 @@ export declare const FreeformTurnPayloadSchema: import("valibot").ObjectSchema<{
342
342
  }, undefined>, undefined>;
343
343
  /** The model that resolved the turn, so a re-read can say what produced it. */
344
344
  readonly model: import("valibot").StringSchema<undefined>;
345
+ /** Milliseconds spent in each named generation-pipeline step. */
346
+ readonly timings: import("valibot").OptionalSchema<import("valibot").RecordSchema<import("valibot").StringSchema<undefined>, import("valibot").NumberSchema<undefined>, undefined>, undefined>;
347
+ /** Effective canonical model used for each model-backed pipeline step. */
348
+ readonly stageModels: import("valibot").OptionalSchema<import("valibot").RecordSchema<import("valibot").StringSchema<undefined>, import("valibot").StringSchema<undefined>, undefined>, undefined>;
349
+ /**
350
+ * The narrative thread that wrote the turn, when the session runs more than
351
+ * one. Absent on a single-threaded journal, which is what every session
352
+ * written before threads existed is, so a reader treats absence as the canon
353
+ * thread rather than as a turn from nowhere.
354
+ */
355
+ readonly threadId: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
356
+ /**
357
+ * The index the turn held in its own thread, kept after a merge renumbers it
358
+ * into the canon. Without it a retcon addressed to `{ threadId, sequence }`
359
+ * stops naming anything the moment the merge lands.
360
+ */
361
+ readonly originSequence: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
345
362
  }, undefined>;
346
363
  export type FreeformTurnPayload = InferOutput<typeof FreeformTurnPayloadSchema>;
347
364
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"freeformPayloads.d.ts","sourceRoot":"","sources":["../src/freeformPayloads.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"freeformPayloads.d.ts","sourceRoot":"","sources":["../src/freeformPayloads.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { ActivationDTOSchema } from "@game-infra/event-schemas";
2
- import { array, looseObject, number, object, optional, picklist, string, } from "valibot";
2
+ import { array, looseObject, number, object, optional, picklist, record, string, } from "valibot";
3
3
  /**
4
4
  * What goes inside a session's `data` and its journal entries' for **freeform
5
5
  * mode**: play that follows the player's own words instead of authored
@@ -294,6 +294,23 @@ export const FreeformTurnPayloadSchema = object({
294
294
  consolidations: array(ConsolidationDTOSchema),
295
295
  /** The model that resolved the turn, so a re-read can say what produced it. */
296
296
  model: string(),
297
+ /** Milliseconds spent in each named generation-pipeline step. */
298
+ timings: optional(record(string(), number())),
299
+ /** Effective canonical model used for each model-backed pipeline step. */
300
+ stageModels: optional(record(string(), string())),
301
+ /**
302
+ * The narrative thread that wrote the turn, when the session runs more than
303
+ * one. Absent on a single-threaded journal, which is what every session
304
+ * written before threads existed is, so a reader treats absence as the canon
305
+ * thread rather than as a turn from nowhere.
306
+ */
307
+ threadId: optional(string()),
308
+ /**
309
+ * The index the turn held in its own thread, kept after a merge renumbers it
310
+ * into the canon. Without it a retcon addressed to `{ threadId, sequence }`
311
+ * stops naming anything the moment the merge lands.
312
+ */
313
+ originSequence: optional(number()),
297
314
  });
298
315
  /**
299
316
  * A freeform session's header payload — what goes inside a
@@ -1 +1 @@
1
- {"version":3,"file":"freeformPayloads.js","sourceRoot":"","sources":["../src/freeformPayloads.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"freeformPayloads.js","sourceRoot":"","sources":["../src/freeformPayloads.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -35,5 +35,7 @@ export { GameJournalAppendRequestSchema, GameJournalAppendResponseSchema, GameJo
35
35
  export type { GameJournalAppendRequest, GameJournalAppendResponse, GameJournalEntryRecord, GameJournalListResponse, GameJournalTruncateResponse, GameSessionDeleteResponse, GameSessionListResponse, GameSessionLoadResponse, GameSessionRecord, GameSessionSaveRequest, GameSessionSaveResponse, JournalTruncation, } from "./sessionState.js";
36
36
  export { CheckModifierDTOSchema, ConsolidationDTOSchema, FREEFORM_COMPLEXITIES, FREEFORM_DOMAINS, FREEFORM_OUTCOMES, FreeformComplexitySchema, FreeformOutcomeSchema, FreeformSessionPayloadSchema, FreeformTurnPayloadSchema, IntroducedEntityDTOSchema, MemoryDTOSchema, NEURON_KEY_SEPARATOR, ResolvedCheckDTOSchema, neuronKey, parseNeuronKey, } from "./freeformPayloads.js";
37
37
  export type { CheckModifierDTO, ConsolidationDTO, FreeformComplexity, FreeformOutcome, FreeformSessionPayload, FreeformTurnPayload, IntroducedEntityDTO, MemoryDTO, ResolvedCheckDTO, } from "./freeformPayloads.js";
38
+ export { CANON_THREAD_ID, CONTRADICTION_SEVERITIES, ContradictionDTOSchema, ContradictionSeveritySchema, NarrativeThreadPayloadSchema, RETCON_KINDS, RUN_STAGE_STATUSES, RetconDTOSchema, RetconKindSchema, RunStageDTOSchema, RunStageStatusSchema, THREAD_STATUSES, TURN_RUN_STATUSES, ThreadMergeReviewPayloadSchema, ThreadStatusSchema, ThreadTurnRefSchema, TurnRunPayloadSchema, TurnRunStatusSchema, } from "./narrativeThreads.js";
39
+ export type { ContradictionDTO, ContradictionSeverity, NarrativeThreadPayload, RetconDTO, RetconKind, RunStageDTO, RunStageStatus, ThreadMergeReviewPayload, ThreadStatus, ThreadTurnRef, TurnRunPayload, TurnRunStatus, } from "./narrativeThreads.js";
38
40
  export { gameJournalAppendContract, gameJournalListContract, gameJournalTruncateContract, gameSessionDeleteContract, gameSessionListContract, gameSessionLoadContract, gameSessionSaveContract, } from "./contracts.js";
39
41
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,EACL,8BAA8B,EAC9B,+BAA+B,EAC/B,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,+BAA+B,EAC/B,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,SAAS,EACT,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,EACL,8BAA8B,EAC9B,+BAA+B,EAC/B,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,+BAA+B,EAC/B,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,SAAS,EACT,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,SAAS,EACT,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,4BAA4B,EAC5B,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,8BAA8B,EAC9B,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -38,6 +38,10 @@ export { GameJournalAppendRequestSchema, GameJournalAppendResponseSchema, GameJo
38
38
  // synapse memory graph — memories connected to neurons, consolidated over time
39
39
  // — that decides what a narrator is reminded of.
40
40
  export { CheckModifierDTOSchema, ConsolidationDTOSchema, FREEFORM_COMPLEXITIES, FREEFORM_DOMAINS, FREEFORM_OUTCOMES, FreeformComplexitySchema, FreeformOutcomeSchema, FreeformSessionPayloadSchema, FreeformTurnPayloadSchema, IntroducedEntityDTOSchema, MemoryDTOSchema, NEURON_KEY_SEPARATOR, ResolvedCheckDTOSchema, neuronKey, parseNeuronKey, } from "./freeformPayloads.js";
41
+ // Narrative threads: several scenes of one playthrough advancing at once, the
42
+ // merge that converges them (with the retcons that keep the canon honest), and
43
+ // the server-owned, stage-checkpointed record of the work in flight.
44
+ export { CANON_THREAD_ID, CONTRADICTION_SEVERITIES, ContradictionDTOSchema, ContradictionSeveritySchema, NarrativeThreadPayloadSchema, RETCON_KINDS, RUN_STAGE_STATUSES, RetconDTOSchema, RetconKindSchema, RunStageDTOSchema, RunStageStatusSchema, THREAD_STATUSES, TURN_RUN_STATUSES, ThreadMergeReviewPayloadSchema, ThreadStatusSchema, ThreadTurnRefSchema, TurnRunPayloadSchema, TurnRunStatusSchema, } from "./narrativeThreads.js";
41
45
  // Typed endpoint contracts (toad-contracts). Each contract carries its own
42
46
  // `pathResolver`; wire them into a client via @toad-contracts/frontend-http-client
43
47
  // and into a Hono service via @toad-contracts/hono.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,2EAA2E;AAC3E,sDAAsD;AACtD,OAAO,EACL,8BAA8B,EAC9B,+BAA+B,EAC/B,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,+BAA+B,EAC/B,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAgB3B,0EAA0E;AAC1E,+EAA+E;AAC/E,iDAAiD;AACjD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,SAAS,EACT,cAAc,GACf,MAAM,uBAAuB,CAAC;AAa/B,2EAA2E;AAC3E,mFAAmF;AACnF,oDAAoD;AACpD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,2EAA2E;AAC3E,sDAAsD;AACtD,OAAO,EACL,8BAA8B,EAC9B,+BAA+B,EAC/B,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,+BAA+B,EAC/B,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAgB3B,0EAA0E;AAC1E,+EAA+E;AAC/E,iDAAiD;AACjD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,EACzB,yBAAyB,EACzB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,SAAS,EACT,cAAc,GACf,MAAM,uBAAuB,CAAC;AAa/B,8EAA8E;AAC9E,+EAA+E;AAC/E,qEAAqE;AACrE,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,4BAA4B,EAC5B,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,8BAA8B,EAC9B,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAgB/B,2EAA2E;AAC3E,mFAAmF;AACnF,oDAAoD;AACpD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,450 @@
1
+ import { type InferOutput } from "valibot";
2
+ /**
3
+ * **Narrative threads**: several scenes of one playthrough advancing at once,
4
+ * and the durable record of the work that advances them.
5
+ *
6
+ * A linear journal answers "what happened" with one list, which is the right
7
+ * shape right up until a player wants two things happening at once — a
8
+ * negotiation in the capital and a raid on the coast, each worth its own tab.
9
+ * Two of those cannot append to one list: whichever turn finished second would
10
+ * be narrated as though the first had already happened, and neither player
11
+ * intent was ever "and meanwhile, knowing that".
12
+ *
13
+ * So a thread is a **fork of the journal**, not a copy of the run. It records
14
+ * the point in the shared journal it forked from ({@link
15
+ * NarrativeThreadPayload.baseSequence}) and appends its own turns after it.
16
+ * Every thread reads the same state up to that point and none of them sees
17
+ * another's turns while they are still being written. Threads converge later,
18
+ * by a **merge**, which is the only moment their outputs meet.
19
+ *
20
+ * Convergence is where the honesty has to be. Two scenes written against the
21
+ * same past will sometimes contradict — a character killed in one and
22
+ * bargaining in the other, an item spent twice, two people in one room a
23
+ * province apart. A merge that simply concatenates produces a canon that reads
24
+ * as broken, and a merge that refuses produces a game that cannot use threads
25
+ * at all. {@link ThreadMergeReviewPayloadSchema} records the third answer: the
26
+ * contradictions found, and the {@link RetconDTOSchema} amendments that
27
+ * reconcile them, each naming the turn it rewrites and why. Retcons are stored
28
+ * rather than applied in place, so the journal keeps what was originally
29
+ * narrated and a reader can always see what was changed to make the two scenes
30
+ * one story.
31
+ *
32
+ * The third shape here is not about threads at all but about *time*. Resolving
33
+ * one turn is many model calls over many seconds, and a client that owns that
34
+ * work loses it whenever the browser reloads. {@link TurnRunPayloadSchema} is
35
+ * the run as a **server-owned, stage-checkpointed record**: every stage's
36
+ * status, and every partial output the moment it exists — the roll before the
37
+ * prose, the prose before the consequences, the chronicle after both. A client
38
+ * renders whatever the record already carries and reattaches to a run in
39
+ * flight rather than restarting it.
40
+ *
41
+ * As everywhere else in this package these are payload shapes, not endpoints:
42
+ * `game-state-service` stores `data` opaquely. They live here because two
43
+ * clients reading one playthrough have to agree on what a thread is.
44
+ */
45
+ /** The trunk every playthrough starts on, and the thread a merge lands in. */
46
+ export declare const CANON_THREAD_ID = "canon";
47
+ /**
48
+ * Where a thread is in its life.
49
+ *
50
+ * `open` accepts turns. `merging` is under review by the merge agent and
51
+ * accepts none — a turn written against a base that is being rewritten is a
52
+ * contradiction manufactured after the fact. `merged` has been folded into the
53
+ * canon and is kept for the record. `abandoned` was closed without merging, and
54
+ * its turns are read as what-ifs rather than as things that happened.
55
+ */
56
+ export declare const THREAD_STATUSES: readonly ["open", "merging", "merged", "abandoned"];
57
+ export declare const ThreadStatusSchema: import("valibot").PicklistSchema<readonly ["open", "merging", "merged", "abandoned"], undefined>;
58
+ export type ThreadStatus = InferOutput<typeof ThreadStatusSchema>;
59
+ /**
60
+ * One thread of a playthrough: a tab in the story.
61
+ *
62
+ * `baseSequence` is the whole of the fork: the number of canon turns the thread
63
+ * could see when it opened. Everything before it is shared with every other
64
+ * thread; everything the thread writes lands after it and is invisible to its
65
+ * siblings until a merge. The canon thread itself is the degenerate case —
66
+ * {@link CANON_THREAD_ID}, no parent, base zero.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { CANON_THREAD_ID, type NarrativeThreadPayload } from '@game-infra/game-state-schemas'
71
+ *
72
+ * const raid: NarrativeThreadPayload = {
73
+ * id: 'the-coast-raid',
74
+ * title: 'The coast raid',
75
+ * parentThreadId: CANON_THREAD_ID,
76
+ * baseSequence: 12,
77
+ * status: 'open',
78
+ * createdAt: '2026-08-31T09:00:00.000Z',
79
+ * }
80
+ * ```
81
+ */
82
+ export declare const NarrativeThreadPayloadSchema: import("valibot").ObjectSchema<{
83
+ readonly id: import("valibot").StringSchema<undefined>;
84
+ /** What to call the tab. */
85
+ readonly title: import("valibot").StringSchema<undefined>;
86
+ /** The thread this one forked from. Absent on the canon thread. */
87
+ readonly parentThreadId: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
88
+ /** Canon turns visible to this thread. Its own turns are numbered from here. */
89
+ readonly baseSequence: import("valibot").NumberSchema<undefined>;
90
+ readonly status: import("valibot").PicklistSchema<readonly ["open", "merging", "merged", "abandoned"], undefined>;
91
+ readonly createdAt: import("valibot").StringSchema<undefined>;
92
+ /** Canon length after the merge that closed this thread, when one has. */
93
+ readonly mergedAtSequence: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
94
+ }, undefined>;
95
+ export type NarrativeThreadPayload = InferOutput<typeof NarrativeThreadPayloadSchema>;
96
+ /** One turn, addressed the way a merge has to address it: thread, then index. */
97
+ export declare const ThreadTurnRefSchema: import("valibot").ObjectSchema<{
98
+ readonly threadId: import("valibot").StringSchema<undefined>;
99
+ readonly sequence: import("valibot").NumberSchema<undefined>;
100
+ }, undefined>;
101
+ export type ThreadTurnRef = InferOutput<typeof ThreadTurnRefSchema>;
102
+ /**
103
+ * How badly two threads disagree.
104
+ *
105
+ * `cosmetic` is a difference a reader would not notice and a merge may keep as
106
+ * it is. `continuity` is a fact stated two ways, which reads as sloppy and is
107
+ * what a retcon is for. `blocking` is a fact stated two ways where *both* are
108
+ * load-bearing — a character who cannot be both dead and negotiating — and the
109
+ * merge is not honest until something is amended.
110
+ */
111
+ export declare const CONTRADICTION_SEVERITIES: readonly ["cosmetic", "continuity", "blocking"];
112
+ export declare const ContradictionSeveritySchema: import("valibot").PicklistSchema<readonly ["cosmetic", "continuity", "blocking"], undefined>;
113
+ export type ContradictionSeverity = InferOutput<typeof ContradictionSeveritySchema>;
114
+ /**
115
+ * One disagreement between threads, named so a reader can check the judgement.
116
+ *
117
+ * `between` carries the turns that disagree rather than a prose description of
118
+ * them, so a client can link straight to both sides instead of asking the
119
+ * reader to find them.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * import type { ContradictionDTO } from '@game-infra/game-state-schemas'
124
+ *
125
+ * const clash: ContradictionDTO = {
126
+ * id: 'k1',
127
+ * severity: 'blocking',
128
+ * description: 'Warden Ilse dies on the coast and opens the ledger in the capital.',
129
+ * between: [
130
+ * { threadId: 'the-coast-raid', sequence: 3 },
131
+ * { threadId: 'the-ledger', sequence: 2 },
132
+ * ],
133
+ * }
134
+ * ```
135
+ */
136
+ export declare const ContradictionDTOSchema: import("valibot").ObjectSchema<{
137
+ readonly id: import("valibot").StringSchema<undefined>;
138
+ readonly severity: import("valibot").PicklistSchema<readonly ["cosmetic", "continuity", "blocking"], undefined>;
139
+ /** What the two turns each claim, in one sentence. */
140
+ readonly description: import("valibot").StringSchema<undefined>;
141
+ /** The turns that disagree. At least two, or there is no disagreement. */
142
+ readonly between: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
143
+ readonly threadId: import("valibot").StringSchema<undefined>;
144
+ readonly sequence: import("valibot").NumberSchema<undefined>;
145
+ }, undefined>, undefined>;
146
+ }, undefined>;
147
+ export type ContradictionDTO = InferOutput<typeof ContradictionDTOSchema>;
148
+ /**
149
+ * What a retcon is allowed to do.
150
+ *
151
+ * The list is short on purpose. A merge agent that may rewrite anything is one
152
+ * that can quietly delete a turn the player is proud of, and "the story changed
153
+ * while you were reading it" is worse than the contradiction it fixed.
154
+ *
155
+ * - `amend-narration` rewrites one turn's prose, keeping its check, effects and
156
+ * memories exactly as they were rolled and recorded.
157
+ * - `annotate` adds a reconciling sentence without touching what is already
158
+ * written — the answer when both turns are fine and only their meeting needs
159
+ * explaining.
160
+ * - `supersede-memory` retires memories that the merged canon has made false,
161
+ * which is the memory graph's own vocabulary rather than a new one.
162
+ * - `reorder` states that a turn belongs elsewhere in the canon order, which
163
+ * resolves most "meanwhile" contradictions without changing a word.
164
+ */
165
+ export declare const RETCON_KINDS: readonly ["amend-narration", "annotate", "supersede-memory", "reorder"];
166
+ export declare const RetconKindSchema: import("valibot").PicklistSchema<readonly ["amend-narration", "annotate", "supersede-memory", "reorder"], undefined>;
167
+ export type RetconKind = InferOutput<typeof RetconKindSchema>;
168
+ /**
169
+ * One amendment the merge agent proposes, against one turn.
170
+ *
171
+ * Stored beside the journal rather than applied into it. The turn keeps what it
172
+ * originally said, the retcon says what it says now, and a reader can see both
173
+ * — which is the difference between a story that was reconciled and a story
174
+ * that was silently edited.
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * import type { RetconDTO } from '@game-infra/game-state-schemas'
179
+ *
180
+ * const patch: RetconDTO = {
181
+ * id: 'r1',
182
+ * kind: 'amend-narration',
183
+ * target: { threadId: 'the-coast-raid', sequence: 3 },
184
+ * reason: 'Ilse cannot die here and open the ledger in the capital two turns later.',
185
+ * narration: 'The blade takes her shoulder, not her throat; she goes down cursing you.',
186
+ * resolves: ['k1'],
187
+ * }
188
+ * ```
189
+ */
190
+ export declare const RetconDTOSchema: import("valibot").ObjectSchema<{
191
+ readonly id: import("valibot").StringSchema<undefined>;
192
+ readonly kind: import("valibot").PicklistSchema<readonly ["amend-narration", "annotate", "supersede-memory", "reorder"], undefined>;
193
+ /** The turn being amended. */
194
+ readonly target: import("valibot").ObjectSchema<{
195
+ readonly threadId: import("valibot").StringSchema<undefined>;
196
+ readonly sequence: import("valibot").NumberSchema<undefined>;
197
+ }, undefined>;
198
+ /** Why the amendment is needed, in the merge agent's own words. */
199
+ readonly reason: import("valibot").StringSchema<undefined>;
200
+ /** Replacement prose, for `amend-narration`. */
201
+ readonly narration: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
202
+ /** The reconciling sentence, for `annotate`. */
203
+ readonly note: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
204
+ /** Memories the merged canon has made false, for `supersede-memory`. */
205
+ readonly memoryIds: import("valibot").OptionalSchema<import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>, undefined>;
206
+ /** Canon position the turn should hold, for `reorder`. */
207
+ readonly sequence: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
208
+ /** The {@link ContradictionDTO} ids this amendment answers. */
209
+ readonly resolves: import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>;
210
+ }, undefined>;
211
+ export type RetconDTO = InferOutput<typeof RetconDTOSchema>;
212
+ /**
213
+ * One merge: the threads that converged, what disagreed, what was amended, and
214
+ * the order the canon ended up in.
215
+ *
216
+ * `order` is the whole merge decision made explicit. Threads produce turns
217
+ * concurrently and their timestamps say only when a model finished, which is
218
+ * not when a thing happened; the merge agent decides the sequence the canon
219
+ * reads in and records it, so a replay is deterministic without re-asking a
220
+ * model what order the day went in.
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * import type { ThreadMergeReviewPayload } from '@game-infra/game-state-schemas'
225
+ *
226
+ * const review: ThreadMergeReviewPayload = {
227
+ * id: 'merge-0003',
228
+ * threadIds: ['the-coast-raid', 'the-ledger'],
229
+ * baseSequence: 12,
230
+ * order: [
231
+ * { threadId: 'the-ledger', sequence: 0 },
232
+ * { threadId: 'the-coast-raid', sequence: 0 },
233
+ * ],
234
+ * contradictions: [],
235
+ * retcons: [],
236
+ * summary: 'Two scenes, one afternoon, nothing in conflict.',
237
+ * model: 'aion',
238
+ * createdAt: '2026-08-31T09:31:00.000Z',
239
+ * }
240
+ * ```
241
+ */
242
+ export declare const ThreadMergeReviewPayloadSchema: import("valibot").ObjectSchema<{
243
+ readonly id: import("valibot").StringSchema<undefined>;
244
+ /** The threads folded in, in the order they were opened. */
245
+ readonly threadIds: import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>;
246
+ /** Canon length the threads shared before the merge. */
247
+ readonly baseSequence: import("valibot").NumberSchema<undefined>;
248
+ /** The canon order the merge chose, one entry per turn folded in. */
249
+ readonly order: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
250
+ readonly threadId: import("valibot").StringSchema<undefined>;
251
+ readonly sequence: import("valibot").NumberSchema<undefined>;
252
+ }, undefined>, undefined>;
253
+ readonly contradictions: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
254
+ readonly id: import("valibot").StringSchema<undefined>;
255
+ readonly severity: import("valibot").PicklistSchema<readonly ["cosmetic", "continuity", "blocking"], undefined>;
256
+ /** What the two turns each claim, in one sentence. */
257
+ readonly description: import("valibot").StringSchema<undefined>;
258
+ /** The turns that disagree. At least two, or there is no disagreement. */
259
+ readonly between: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
260
+ readonly threadId: import("valibot").StringSchema<undefined>;
261
+ readonly sequence: import("valibot").NumberSchema<undefined>;
262
+ }, undefined>, undefined>;
263
+ }, undefined>, undefined>;
264
+ readonly retcons: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
265
+ readonly id: import("valibot").StringSchema<undefined>;
266
+ readonly kind: import("valibot").PicklistSchema<readonly ["amend-narration", "annotate", "supersede-memory", "reorder"], undefined>;
267
+ /** The turn being amended. */
268
+ readonly target: import("valibot").ObjectSchema<{
269
+ readonly threadId: import("valibot").StringSchema<undefined>;
270
+ readonly sequence: import("valibot").NumberSchema<undefined>;
271
+ }, undefined>;
272
+ /** Why the amendment is needed, in the merge agent's own words. */
273
+ readonly reason: import("valibot").StringSchema<undefined>;
274
+ /** Replacement prose, for `amend-narration`. */
275
+ readonly narration: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
276
+ /** The reconciling sentence, for `annotate`. */
277
+ readonly note: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
278
+ /** Memories the merged canon has made false, for `supersede-memory`. */
279
+ readonly memoryIds: import("valibot").OptionalSchema<import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>, undefined>;
280
+ /** Canon position the turn should hold, for `reorder`. */
281
+ readonly sequence: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
282
+ /** The {@link ContradictionDTO} ids this amendment answers. */
283
+ readonly resolves: import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>;
284
+ }, undefined>, undefined>;
285
+ /** What the merge did, for a reader who does not want to read every retcon. */
286
+ readonly summary: import("valibot").StringSchema<undefined>;
287
+ /** The model that reviewed the merge. */
288
+ readonly model: import("valibot").StringSchema<undefined>;
289
+ readonly createdAt: import("valibot").StringSchema<undefined>;
290
+ }, undefined>;
291
+ export type ThreadMergeReviewPayload = InferOutput<typeof ThreadMergeReviewPayloadSchema>;
292
+ /**
293
+ * Where a run is. The three middle values are what a progressive client needs:
294
+ * they are the difference between "nothing to show yet", "there is prose on
295
+ * screen" and "the prose is final and the bookkeeping is still going".
296
+ *
297
+ * `settling` is the one worth spelling out. Chronicling and consolidation
298
+ * happen after the player is already reading, so the turn looks finished; but
299
+ * the next turn in the same thread must not start until they land, or it is
300
+ * adjudicated against a memory graph missing the turn before it.
301
+ */
302
+ export declare const TURN_RUN_STATUSES: readonly ["queued", "running", "narrated", "settling", "completed", "failed", "cancelled"];
303
+ export declare const TurnRunStatusSchema: import("valibot").PicklistSchema<readonly ["queued", "running", "narrated", "settling", "completed", "failed", "cancelled"], undefined>;
304
+ export type TurnRunStatus = InferOutput<typeof TurnRunStatusSchema>;
305
+ /** Where one stage of a run is. `skipped` is a stage the turn did not need. */
306
+ export declare const RUN_STAGE_STATUSES: readonly ["pending", "running", "completed", "skipped", "failed"];
307
+ export declare const RunStageStatusSchema: import("valibot").PicklistSchema<readonly ["pending", "running", "completed", "skipped", "failed"], undefined>;
308
+ export type RunStageStatus = InferOutput<typeof RunStageStatusSchema>;
309
+ /**
310
+ * One stage of a run, as the server checkpoints it.
311
+ *
312
+ * `attempt` is stored rather than derived because a resumed run has to know
313
+ * whether it is retrying or starting: a stage that has already burnt its
314
+ * retries should fail the run rather than loop for as long as the storage
315
+ * keeps the record.
316
+ */
317
+ export declare const RunStageDTOSchema: import("valibot").ObjectSchema<{
318
+ readonly id: import("valibot").StringSchema<undefined>;
319
+ /** What to show a player waiting on it. */
320
+ readonly label: import("valibot").StringSchema<undefined>;
321
+ /** Which agent does the work — or the engine, for a local stage. */
322
+ readonly agent: import("valibot").StringSchema<undefined>;
323
+ /** The canonical model behind it, or `local` for a stage that asks nobody. */
324
+ readonly model: import("valibot").StringSchema<undefined>;
325
+ readonly status: import("valibot").PicklistSchema<readonly ["pending", "running", "completed", "skipped", "failed"], undefined>;
326
+ /** Epoch milliseconds; absent until the stage starts. */
327
+ readonly startedAt: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
328
+ readonly durationMs: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
329
+ readonly attempt: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
330
+ readonly error: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
331
+ }, undefined>;
332
+ export type RunStageDTO = InferOutput<typeof RunStageDTOSchema>;
333
+ /**
334
+ * One turn's adjudication, owned by the server and readable at any moment.
335
+ *
336
+ * This is the record that makes a reload survivable. The work belongs to the
337
+ * service rather than to the tab that asked for it, every stage is written down
338
+ * as it finishes, and each output is published the moment it exists rather than
339
+ * at the end: `check` as soon as the dice are rolled, `narration` as soon as
340
+ * the prose lands, `effects` once the consequences are interpreted, `memories`
341
+ * once the chronicler has been. A client that reconnects renders whatever is
342
+ * already here and waits only for what is genuinely still missing.
343
+ *
344
+ * `leaseUntil` is how a stalled run is told from a slow one. A worker that dies
345
+ * mid-run leaves the record `running` forever otherwise, and nothing else dares
346
+ * touch it; with a lease, whoever finds it expired may resume from the last
347
+ * completed stage.
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * import type { TurnRunPayload } from '@game-infra/game-state-schemas'
352
+ *
353
+ * const run: TurnRunPayload = {
354
+ * id: 'run-8f2c',
355
+ * threadId: 'the-coast-raid',
356
+ * sequence: 3,
357
+ * input: 'I put my shoulder to the door and shout for Ilse.',
358
+ * status: 'narrated',
359
+ * stages: [
360
+ * { id: 'assessment', label: 'Choosing whether and how to roll',
361
+ * agent: 'Assessor', model: 'aion-mini', status: 'completed', durationMs: 1840 },
362
+ * ],
363
+ * traceId: 'turn-8f2c',
364
+ * startedAt: '2026-08-31T09:12:00.000Z',
365
+ * updatedAt: '2026-08-31T09:12:09.000Z',
366
+ * }
367
+ * ```
368
+ */
369
+ export declare const TurnRunPayloadSchema: import("valibot").ObjectSchema<{
370
+ readonly id: import("valibot").StringSchema<undefined>;
371
+ readonly threadId: import("valibot").StringSchema<undefined>;
372
+ /** The index this turn takes in its thread. */
373
+ readonly sequence: import("valibot").NumberSchema<undefined>;
374
+ /** What the player wrote. Empty on a session's opening turn. */
375
+ readonly input: import("valibot").StringSchema<undefined>;
376
+ readonly status: import("valibot").PicklistSchema<readonly ["queued", "running", "narrated", "settling", "completed", "failed", "cancelled"], undefined>;
377
+ /** Every stage, in execution order, whether or not it has run. */
378
+ readonly stages: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
379
+ readonly id: import("valibot").StringSchema<undefined>;
380
+ /** What to show a player waiting on it. */
381
+ readonly label: import("valibot").StringSchema<undefined>;
382
+ /** Which agent does the work — or the engine, for a local stage. */
383
+ readonly agent: import("valibot").StringSchema<undefined>;
384
+ /** The canonical model behind it, or `local` for a stage that asks nobody. */
385
+ readonly model: import("valibot").StringSchema<undefined>;
386
+ readonly status: import("valibot").PicklistSchema<readonly ["pending", "running", "completed", "skipped", "failed"], undefined>;
387
+ /** Epoch milliseconds; absent until the stage starts. */
388
+ readonly startedAt: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
389
+ readonly durationMs: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
390
+ readonly attempt: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
391
+ readonly error: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
392
+ }, undefined>, undefined>;
393
+ /** Joins every model call this run made. */
394
+ readonly traceId: import("valibot").StringSchema<undefined>;
395
+ readonly startedAt: import("valibot").StringSchema<undefined>;
396
+ readonly updatedAt: import("valibot").StringSchema<undefined>;
397
+ /** Epoch milliseconds after which an unfinished run may be resumed by anyone. */
398
+ readonly leaseUntil: import("valibot").OptionalSchema<import("valibot").NumberSchema<undefined>, undefined>;
399
+ /** The neurons the assessor judged relevant. */
400
+ readonly focus: import("valibot").OptionalSchema<import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>, undefined>;
401
+ /** The roll — available before a word of prose has been written. */
402
+ readonly check: import("valibot").OptionalSchema<import("valibot").ObjectSchema<{
403
+ readonly skillId: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
404
+ readonly statId: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
405
+ readonly complexity: import("valibot").PicklistSchema<readonly ["trivial", "easy", "moderate", "hard", "formidable"], undefined>;
406
+ readonly difficulty: import("valibot").NumberSchema<undefined>;
407
+ readonly roll: import("valibot").NumberSchema<undefined>;
408
+ readonly modifiers: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
409
+ readonly label: import("valibot").StringSchema<undefined>;
410
+ readonly amount: import("valibot").NumberSchema<undefined>;
411
+ }, undefined>, undefined>;
412
+ readonly total: import("valibot").NumberSchema<undefined>;
413
+ readonly outcome: import("valibot").PicklistSchema<readonly ["critical-success", "success", "failure", "critical-failure"], undefined>;
414
+ }, undefined>, undefined>;
415
+ readonly title: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
416
+ readonly narration: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
417
+ readonly effects: import("valibot").OptionalSchema<import("valibot").ArraySchema<import("valibot").ObjectSchema<{
418
+ readonly type: import("valibot").StringSchema<undefined>;
419
+ readonly params: import("valibot").RecordSchema<import("valibot").StringSchema<undefined>, import("valibot").UnknownSchema, undefined>;
420
+ }, undefined>, undefined>, undefined>;
421
+ readonly introduced: import("valibot").OptionalSchema<import("valibot").ArraySchema<import("valibot").LooseObjectSchema<{
422
+ readonly id: import("valibot").StringSchema<undefined>;
423
+ readonly name: import("valibot").StringSchema<undefined>;
424
+ }, undefined>, undefined>, undefined>;
425
+ readonly memories: import("valibot").OptionalSchema<import("valibot").ArraySchema<import("valibot").ObjectSchema<{
426
+ readonly id: import("valibot").StringSchema<undefined>;
427
+ readonly text: import("valibot").StringSchema<undefined>;
428
+ readonly neuronIds: import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>;
429
+ readonly turn: import("valibot").NumberSchema<undefined>;
430
+ readonly weight: import("valibot").NumberSchema<undefined>;
431
+ }, undefined>, undefined>, undefined>;
432
+ readonly consolidations: import("valibot").OptionalSchema<import("valibot").ArraySchema<import("valibot").ObjectSchema<{
433
+ readonly memoryIds: import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>;
434
+ readonly memory: import("valibot").ObjectSchema<{
435
+ readonly id: import("valibot").StringSchema<undefined>;
436
+ readonly text: import("valibot").StringSchema<undefined>;
437
+ readonly neuronIds: import("valibot").ArraySchema<import("valibot").StringSchema<undefined>, undefined>;
438
+ readonly turn: import("valibot").NumberSchema<undefined>;
439
+ readonly weight: import("valibot").NumberSchema<undefined>;
440
+ }, undefined>;
441
+ }, undefined>, undefined>, undefined>;
442
+ /** The model that narrated, once one has. */
443
+ readonly model: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
444
+ /** Milliseconds per named stage, as they complete. */
445
+ readonly timings: import("valibot").OptionalSchema<import("valibot").RecordSchema<import("valibot").StringSchema<undefined>, import("valibot").NumberSchema<undefined>, undefined>, undefined>;
446
+ /** Why the run failed, when it did. */
447
+ readonly error: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
448
+ }, undefined>;
449
+ export type TurnRunPayload = InferOutput<typeof TurnRunPayloadSchema>;
450
+ //# sourceMappingURL=narrativeThreads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narrativeThreads.d.ts","sourceRoot":"","sources":["../src/narrativeThreads.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,363 @@
1
+ import { ActivationDTOSchema } from "@game-infra/event-schemas";
2
+ import { array, number, object, optional, picklist, record, string, } from "valibot";
3
+ import { ConsolidationDTOSchema, IntroducedEntityDTOSchema, MemoryDTOSchema, ResolvedCheckDTOSchema, } from "./freeformPayloads.js";
4
+ /**
5
+ * **Narrative threads**: several scenes of one playthrough advancing at once,
6
+ * and the durable record of the work that advances them.
7
+ *
8
+ * A linear journal answers "what happened" with one list, which is the right
9
+ * shape right up until a player wants two things happening at once — a
10
+ * negotiation in the capital and a raid on the coast, each worth its own tab.
11
+ * Two of those cannot append to one list: whichever turn finished second would
12
+ * be narrated as though the first had already happened, and neither player
13
+ * intent was ever "and meanwhile, knowing that".
14
+ *
15
+ * So a thread is a **fork of the journal**, not a copy of the run. It records
16
+ * the point in the shared journal it forked from ({@link
17
+ * NarrativeThreadPayload.baseSequence}) and appends its own turns after it.
18
+ * Every thread reads the same state up to that point and none of them sees
19
+ * another's turns while they are still being written. Threads converge later,
20
+ * by a **merge**, which is the only moment their outputs meet.
21
+ *
22
+ * Convergence is where the honesty has to be. Two scenes written against the
23
+ * same past will sometimes contradict — a character killed in one and
24
+ * bargaining in the other, an item spent twice, two people in one room a
25
+ * province apart. A merge that simply concatenates produces a canon that reads
26
+ * as broken, and a merge that refuses produces a game that cannot use threads
27
+ * at all. {@link ThreadMergeReviewPayloadSchema} records the third answer: the
28
+ * contradictions found, and the {@link RetconDTOSchema} amendments that
29
+ * reconcile them, each naming the turn it rewrites and why. Retcons are stored
30
+ * rather than applied in place, so the journal keeps what was originally
31
+ * narrated and a reader can always see what was changed to make the two scenes
32
+ * one story.
33
+ *
34
+ * The third shape here is not about threads at all but about *time*. Resolving
35
+ * one turn is many model calls over many seconds, and a client that owns that
36
+ * work loses it whenever the browser reloads. {@link TurnRunPayloadSchema} is
37
+ * the run as a **server-owned, stage-checkpointed record**: every stage's
38
+ * status, and every partial output the moment it exists — the roll before the
39
+ * prose, the prose before the consequences, the chronicle after both. A client
40
+ * renders whatever the record already carries and reattaches to a run in
41
+ * flight rather than restarting it.
42
+ *
43
+ * As everywhere else in this package these are payload shapes, not endpoints:
44
+ * `game-state-service` stores `data` opaquely. They live here because two
45
+ * clients reading one playthrough have to agree on what a thread is.
46
+ */
47
+ /** The trunk every playthrough starts on, and the thread a merge lands in. */
48
+ export const CANON_THREAD_ID = "canon";
49
+ /**
50
+ * Where a thread is in its life.
51
+ *
52
+ * `open` accepts turns. `merging` is under review by the merge agent and
53
+ * accepts none — a turn written against a base that is being rewritten is a
54
+ * contradiction manufactured after the fact. `merged` has been folded into the
55
+ * canon and is kept for the record. `abandoned` was closed without merging, and
56
+ * its turns are read as what-ifs rather than as things that happened.
57
+ */
58
+ export const THREAD_STATUSES = ["open", "merging", "merged", "abandoned"];
59
+ export const ThreadStatusSchema = picklist(THREAD_STATUSES);
60
+ /**
61
+ * One thread of a playthrough: a tab in the story.
62
+ *
63
+ * `baseSequence` is the whole of the fork: the number of canon turns the thread
64
+ * could see when it opened. Everything before it is shared with every other
65
+ * thread; everything the thread writes lands after it and is invisible to its
66
+ * siblings until a merge. The canon thread itself is the degenerate case —
67
+ * {@link CANON_THREAD_ID}, no parent, base zero.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import { CANON_THREAD_ID, type NarrativeThreadPayload } from '@game-infra/game-state-schemas'
72
+ *
73
+ * const raid: NarrativeThreadPayload = {
74
+ * id: 'the-coast-raid',
75
+ * title: 'The coast raid',
76
+ * parentThreadId: CANON_THREAD_ID,
77
+ * baseSequence: 12,
78
+ * status: 'open',
79
+ * createdAt: '2026-08-31T09:00:00.000Z',
80
+ * }
81
+ * ```
82
+ */
83
+ export const NarrativeThreadPayloadSchema = object({
84
+ id: string(),
85
+ /** What to call the tab. */
86
+ title: string(),
87
+ /** The thread this one forked from. Absent on the canon thread. */
88
+ parentThreadId: optional(string()),
89
+ /** Canon turns visible to this thread. Its own turns are numbered from here. */
90
+ baseSequence: number(),
91
+ status: ThreadStatusSchema,
92
+ createdAt: string(),
93
+ /** Canon length after the merge that closed this thread, when one has. */
94
+ mergedAtSequence: optional(number()),
95
+ });
96
+ /** One turn, addressed the way a merge has to address it: thread, then index. */
97
+ export const ThreadTurnRefSchema = object({
98
+ threadId: string(),
99
+ sequence: number(),
100
+ });
101
+ /**
102
+ * How badly two threads disagree.
103
+ *
104
+ * `cosmetic` is a difference a reader would not notice and a merge may keep as
105
+ * it is. `continuity` is a fact stated two ways, which reads as sloppy and is
106
+ * what a retcon is for. `blocking` is a fact stated two ways where *both* are
107
+ * load-bearing — a character who cannot be both dead and negotiating — and the
108
+ * merge is not honest until something is amended.
109
+ */
110
+ export const CONTRADICTION_SEVERITIES = ["cosmetic", "continuity", "blocking"];
111
+ export const ContradictionSeveritySchema = picklist(CONTRADICTION_SEVERITIES);
112
+ /**
113
+ * One disagreement between threads, named so a reader can check the judgement.
114
+ *
115
+ * `between` carries the turns that disagree rather than a prose description of
116
+ * them, so a client can link straight to both sides instead of asking the
117
+ * reader to find them.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import type { ContradictionDTO } from '@game-infra/game-state-schemas'
122
+ *
123
+ * const clash: ContradictionDTO = {
124
+ * id: 'k1',
125
+ * severity: 'blocking',
126
+ * description: 'Warden Ilse dies on the coast and opens the ledger in the capital.',
127
+ * between: [
128
+ * { threadId: 'the-coast-raid', sequence: 3 },
129
+ * { threadId: 'the-ledger', sequence: 2 },
130
+ * ],
131
+ * }
132
+ * ```
133
+ */
134
+ export const ContradictionDTOSchema = object({
135
+ id: string(),
136
+ severity: ContradictionSeveritySchema,
137
+ /** What the two turns each claim, in one sentence. */
138
+ description: string(),
139
+ /** The turns that disagree. At least two, or there is no disagreement. */
140
+ between: array(ThreadTurnRefSchema),
141
+ });
142
+ /**
143
+ * What a retcon is allowed to do.
144
+ *
145
+ * The list is short on purpose. A merge agent that may rewrite anything is one
146
+ * that can quietly delete a turn the player is proud of, and "the story changed
147
+ * while you were reading it" is worse than the contradiction it fixed.
148
+ *
149
+ * - `amend-narration` rewrites one turn's prose, keeping its check, effects and
150
+ * memories exactly as they were rolled and recorded.
151
+ * - `annotate` adds a reconciling sentence without touching what is already
152
+ * written — the answer when both turns are fine and only their meeting needs
153
+ * explaining.
154
+ * - `supersede-memory` retires memories that the merged canon has made false,
155
+ * which is the memory graph's own vocabulary rather than a new one.
156
+ * - `reorder` states that a turn belongs elsewhere in the canon order, which
157
+ * resolves most "meanwhile" contradictions without changing a word.
158
+ */
159
+ export const RETCON_KINDS = ["amend-narration", "annotate", "supersede-memory", "reorder"];
160
+ export const RetconKindSchema = picklist(RETCON_KINDS);
161
+ /**
162
+ * One amendment the merge agent proposes, against one turn.
163
+ *
164
+ * Stored beside the journal rather than applied into it. The turn keeps what it
165
+ * originally said, the retcon says what it says now, and a reader can see both
166
+ * — which is the difference between a story that was reconciled and a story
167
+ * that was silently edited.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * import type { RetconDTO } from '@game-infra/game-state-schemas'
172
+ *
173
+ * const patch: RetconDTO = {
174
+ * id: 'r1',
175
+ * kind: 'amend-narration',
176
+ * target: { threadId: 'the-coast-raid', sequence: 3 },
177
+ * reason: 'Ilse cannot die here and open the ledger in the capital two turns later.',
178
+ * narration: 'The blade takes her shoulder, not her throat; she goes down cursing you.',
179
+ * resolves: ['k1'],
180
+ * }
181
+ * ```
182
+ */
183
+ export const RetconDTOSchema = object({
184
+ id: string(),
185
+ kind: RetconKindSchema,
186
+ /** The turn being amended. */
187
+ target: ThreadTurnRefSchema,
188
+ /** Why the amendment is needed, in the merge agent's own words. */
189
+ reason: string(),
190
+ /** Replacement prose, for `amend-narration`. */
191
+ narration: optional(string()),
192
+ /** The reconciling sentence, for `annotate`. */
193
+ note: optional(string()),
194
+ /** Memories the merged canon has made false, for `supersede-memory`. */
195
+ memoryIds: optional(array(string())),
196
+ /** Canon position the turn should hold, for `reorder`. */
197
+ sequence: optional(number()),
198
+ /** The {@link ContradictionDTO} ids this amendment answers. */
199
+ resolves: array(string()),
200
+ });
201
+ /**
202
+ * One merge: the threads that converged, what disagreed, what was amended, and
203
+ * the order the canon ended up in.
204
+ *
205
+ * `order` is the whole merge decision made explicit. Threads produce turns
206
+ * concurrently and their timestamps say only when a model finished, which is
207
+ * not when a thing happened; the merge agent decides the sequence the canon
208
+ * reads in and records it, so a replay is deterministic without re-asking a
209
+ * model what order the day went in.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * import type { ThreadMergeReviewPayload } from '@game-infra/game-state-schemas'
214
+ *
215
+ * const review: ThreadMergeReviewPayload = {
216
+ * id: 'merge-0003',
217
+ * threadIds: ['the-coast-raid', 'the-ledger'],
218
+ * baseSequence: 12,
219
+ * order: [
220
+ * { threadId: 'the-ledger', sequence: 0 },
221
+ * { threadId: 'the-coast-raid', sequence: 0 },
222
+ * ],
223
+ * contradictions: [],
224
+ * retcons: [],
225
+ * summary: 'Two scenes, one afternoon, nothing in conflict.',
226
+ * model: 'aion',
227
+ * createdAt: '2026-08-31T09:31:00.000Z',
228
+ * }
229
+ * ```
230
+ */
231
+ export const ThreadMergeReviewPayloadSchema = object({
232
+ id: string(),
233
+ /** The threads folded in, in the order they were opened. */
234
+ threadIds: array(string()),
235
+ /** Canon length the threads shared before the merge. */
236
+ baseSequence: number(),
237
+ /** The canon order the merge chose, one entry per turn folded in. */
238
+ order: array(ThreadTurnRefSchema),
239
+ contradictions: array(ContradictionDTOSchema),
240
+ retcons: array(RetconDTOSchema),
241
+ /** What the merge did, for a reader who does not want to read every retcon. */
242
+ summary: string(),
243
+ /** The model that reviewed the merge. */
244
+ model: string(),
245
+ createdAt: string(),
246
+ });
247
+ /**
248
+ * Where a run is. The three middle values are what a progressive client needs:
249
+ * they are the difference between "nothing to show yet", "there is prose on
250
+ * screen" and "the prose is final and the bookkeeping is still going".
251
+ *
252
+ * `settling` is the one worth spelling out. Chronicling and consolidation
253
+ * happen after the player is already reading, so the turn looks finished; but
254
+ * the next turn in the same thread must not start until they land, or it is
255
+ * adjudicated against a memory graph missing the turn before it.
256
+ */
257
+ export const TURN_RUN_STATUSES = [
258
+ "queued",
259
+ "running",
260
+ "narrated",
261
+ "settling",
262
+ "completed",
263
+ "failed",
264
+ "cancelled",
265
+ ];
266
+ export const TurnRunStatusSchema = picklist(TURN_RUN_STATUSES);
267
+ /** Where one stage of a run is. `skipped` is a stage the turn did not need. */
268
+ export const RUN_STAGE_STATUSES = ["pending", "running", "completed", "skipped", "failed"];
269
+ export const RunStageStatusSchema = picklist(RUN_STAGE_STATUSES);
270
+ /**
271
+ * One stage of a run, as the server checkpoints it.
272
+ *
273
+ * `attempt` is stored rather than derived because a resumed run has to know
274
+ * whether it is retrying or starting: a stage that has already burnt its
275
+ * retries should fail the run rather than loop for as long as the storage
276
+ * keeps the record.
277
+ */
278
+ export const RunStageDTOSchema = object({
279
+ id: string(),
280
+ /** What to show a player waiting on it. */
281
+ label: string(),
282
+ /** Which agent does the work — or the engine, for a local stage. */
283
+ agent: string(),
284
+ /** The canonical model behind it, or `local` for a stage that asks nobody. */
285
+ model: string(),
286
+ status: RunStageStatusSchema,
287
+ /** Epoch milliseconds; absent until the stage starts. */
288
+ startedAt: optional(number()),
289
+ durationMs: optional(number()),
290
+ attempt: optional(number()),
291
+ error: optional(string()),
292
+ });
293
+ /**
294
+ * One turn's adjudication, owned by the server and readable at any moment.
295
+ *
296
+ * This is the record that makes a reload survivable. The work belongs to the
297
+ * service rather than to the tab that asked for it, every stage is written down
298
+ * as it finishes, and each output is published the moment it exists rather than
299
+ * at the end: `check` as soon as the dice are rolled, `narration` as soon as
300
+ * the prose lands, `effects` once the consequences are interpreted, `memories`
301
+ * once the chronicler has been. A client that reconnects renders whatever is
302
+ * already here and waits only for what is genuinely still missing.
303
+ *
304
+ * `leaseUntil` is how a stalled run is told from a slow one. A worker that dies
305
+ * mid-run leaves the record `running` forever otherwise, and nothing else dares
306
+ * touch it; with a lease, whoever finds it expired may resume from the last
307
+ * completed stage.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * import type { TurnRunPayload } from '@game-infra/game-state-schemas'
312
+ *
313
+ * const run: TurnRunPayload = {
314
+ * id: 'run-8f2c',
315
+ * threadId: 'the-coast-raid',
316
+ * sequence: 3,
317
+ * input: 'I put my shoulder to the door and shout for Ilse.',
318
+ * status: 'narrated',
319
+ * stages: [
320
+ * { id: 'assessment', label: 'Choosing whether and how to roll',
321
+ * agent: 'Assessor', model: 'aion-mini', status: 'completed', durationMs: 1840 },
322
+ * ],
323
+ * traceId: 'turn-8f2c',
324
+ * startedAt: '2026-08-31T09:12:00.000Z',
325
+ * updatedAt: '2026-08-31T09:12:09.000Z',
326
+ * }
327
+ * ```
328
+ */
329
+ export const TurnRunPayloadSchema = object({
330
+ id: string(),
331
+ threadId: string(),
332
+ /** The index this turn takes in its thread. */
333
+ sequence: number(),
334
+ /** What the player wrote. Empty on a session's opening turn. */
335
+ input: string(),
336
+ status: TurnRunStatusSchema,
337
+ /** Every stage, in execution order, whether or not it has run. */
338
+ stages: array(RunStageDTOSchema),
339
+ /** Joins every model call this run made. */
340
+ traceId: string(),
341
+ startedAt: string(),
342
+ updatedAt: string(),
343
+ /** Epoch milliseconds after which an unfinished run may be resumed by anyone. */
344
+ leaseUntil: optional(number()),
345
+ // Partial outputs, each published the moment it exists.
346
+ /** The neurons the assessor judged relevant. */
347
+ focus: optional(array(string())),
348
+ /** The roll — available before a word of prose has been written. */
349
+ check: optional(ResolvedCheckDTOSchema),
350
+ title: optional(string()),
351
+ narration: optional(string()),
352
+ effects: optional(array(ActivationDTOSchema)),
353
+ introduced: optional(array(IntroducedEntityDTOSchema)),
354
+ memories: optional(array(MemoryDTOSchema)),
355
+ consolidations: optional(array(ConsolidationDTOSchema)),
356
+ /** The model that narrated, once one has. */
357
+ model: optional(string()),
358
+ /** Milliseconds per named stage, as they complete. */
359
+ timings: optional(record(string(), number())),
360
+ /** Why the run failed, when it did. */
361
+ error: optional(string()),
362
+ });
363
+ //# sourceMappingURL=narrativeThreads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narrativeThreads.js","sourceRoot":"","sources":["../src/narrativeThreads.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@game-infra/game-state-schemas",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Play-state API contract: the sessions a player has in progress, the journal each one is rebuilt from, and freeform mode's turn and memory payloads",
5
5
  "homepage": "https://github.com/kibertoad/game-infra/tree/main/packages/schemas/game-state-schemas#readme",
6
6
  "bugs": {