@pouchy_ai/world-sdk 0.15.0 → 0.18.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 +80 -0
- package/README.md +22 -6
- package/dist/index.d.ts +256 -30
- package/dist/index.js +53 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# @pouchy_ai/world-sdk
|
|
2
2
|
|
|
3
|
+
## 0.18.0
|
|
4
|
+
|
|
5
|
+
- `createApprovedExport({ version: 2 })` — the V2 production hand-off: a
|
|
6
|
+
synopsis, character notes, an episode split, the beats where the plot moved,
|
|
7
|
+
committed relation changes, and where the run diverged from its story.
|
|
8
|
+
- **Your existing calls do not change.** `version` defaults to 1, and omitting
|
|
9
|
+
it sends no `version` at all, so a caller written before this release receives
|
|
10
|
+
a byte-identical V1 export.
|
|
11
|
+
- **V1 is frozen.** `exportId` is a content hash you may be keying on for
|
|
12
|
+
idempotency; nothing was added to V1, no key was reordered in its canonical
|
|
13
|
+
string, and its `approvedscript1` domain prefix is unchanged. Golden vectors
|
|
14
|
+
generated before V2 existed hold the line.
|
|
15
|
+
- `ApprovedScriptExportRow.content` is now a union discriminated on
|
|
16
|
+
`contractVersion`. **Narrow before reading version-specific fields** — if you
|
|
17
|
+
read `content.synopsis` without checking, TypeScript will now tell you. A
|
|
18
|
+
stored row keeps the version it was written with, forever; nothing is
|
|
19
|
+
migrated.
|
|
20
|
+
- The two contracts hash under separate domains, so one approval exported both
|
|
21
|
+
ways yields two different, individually stable ids. That is correct: they
|
|
22
|
+
carry different content.
|
|
23
|
+
- An unrecognised `version` is a **400**, not a quiet fall back to 1 — a caller
|
|
24
|
+
who believes they asked for V2 must not silently ship against V1.
|
|
25
|
+
- Every V2 field is derived from committed material and **no model is called**.
|
|
26
|
+
`synopsis` is assembled from scene headings rather than written; a character
|
|
27
|
+
note quotes only an evidence-origin line; `episodes` is a mechanical split at
|
|
28
|
+
a fixed size, not an editorial judgement; `relationChanges` carries the
|
|
29
|
+
evidence layer's own wording rather than a role pair parsed out of prose.
|
|
30
|
+
- A rejected scene stays rejected: `keyMoments` and `relationChanges` cover only
|
|
31
|
+
turns belonging to surviving scenes. `divergence` and `lineage` are facts
|
|
32
|
+
about the run and are not filtered.
|
|
33
|
+
- `deriveStoryPackageCandidate` takes **version 1 exports only** and refuses a
|
|
34
|
+
V2 export with 422. It is the one path from an export into published canon,
|
|
35
|
+
and which V2 fields may become canon has not been decided.
|
|
36
|
+
- `world.script_approved` webhooks gain `contractVersion`. Still identifiers and
|
|
37
|
+
lineage only — never the script.
|
|
38
|
+
|
|
39
|
+
## 0.17.0
|
|
40
|
+
|
|
41
|
+
- `scanConsistency({ layers })` — opt into the MODEL-ASSISTED layer: suspicions
|
|
42
|
+
the deterministic checks cannot prove.
|
|
43
|
+
- **Your existing calls do not change.** Without `layers` the response is
|
|
44
|
+
byte-identical to 0.16.0 — no new keys, and zero model calls server-side.
|
|
45
|
+
- **Suspicions arrive in `modelAssistedFindings`, never in `findings`.**
|
|
46
|
+
`WorldConsistencyFindingCode` is frozen. If you test
|
|
47
|
+
`severity === 'blocking'` over `findings`, that check keeps meaning exactly
|
|
48
|
+
what it meant; model-assisted severity has no `blocking` member at all.
|
|
49
|
+
- Three gates, all required: a server flag, a declaration on the published world
|
|
50
|
+
revision, and `layers`. `modelAssistedNote` tells you which one was shut.
|
|
51
|
+
- Findings are `potential` until a person decides. `findingId` is stable across
|
|
52
|
+
scans for the same judgement — derived from the code and the server-normalised
|
|
53
|
+
evidence, not from the model's wording — so a dismissal sticks.
|
|
54
|
+
- At most five model calls per scan, no retry, a wall-clock deadline, and
|
|
55
|
+
`modelAssistedNote: 'incomplete'` when the budget stopped it early. Rate
|
|
56
|
+
limited fail-closed: one scan per minute per project.
|
|
57
|
+
- `suspected_cross_ip_contamination` is an editorial opinion even once
|
|
58
|
+
confirmed. It is not evidence of isolation and should not gate a build, block
|
|
59
|
+
an export, or reject content.
|
|
60
|
+
|
|
61
|
+
## 0.16.0
|
|
62
|
+
|
|
63
|
+
- `scanConsistency()` — what a world's own record proves about itself. Ten
|
|
64
|
+
deterministic findings, each a comparison between committed facts and the
|
|
65
|
+
pinned story package. Read-only: it commits nothing, asks no model, and
|
|
66
|
+
repairs nothing.
|
|
67
|
+
- **`code` is a closed set that may grow.** A published member is never removed,
|
|
68
|
+
renamed, or widened in meaning; a criterion found to be wrong is deprecated
|
|
69
|
+
and replaced beside it. Handle an unknown code as informational rather than
|
|
70
|
+
failing on it — that is what makes a minor bump safe to take.
|
|
71
|
+
- **Read `coverage` before trusting an empty `findings`.** Some checks read
|
|
72
|
+
fields older ledger rows do not carry, and those entries are counted there
|
|
73
|
+
rather than skipped in silence. A clean report with non-empty coverage is
|
|
74
|
+
clean about less than the whole world.
|
|
75
|
+
- `incomplete` is a status, not an empty result: the walk is capped, a `cursor`
|
|
76
|
+
comes back, and the state- and chain-level checks do not run on a partial
|
|
77
|
+
walk. `entriesScanned` stays honest even once `findingsTruncated` is true.
|
|
78
|
+
- Rate limited on two axes (project and IP, 30/min each, both required).
|
|
79
|
+
`status: 'rate_limited'` arrives with HTTP 429 and `retryAfterSec`. If the
|
|
80
|
+
shared limiter is unreachable the scan still runs — a read-only diagnostic
|
|
81
|
+
should not vanish when a world is being investigated.
|
|
82
|
+
|
|
3
83
|
## 0.15.0
|
|
4
84
|
|
|
5
85
|
- `getProgress()` — where a story is and whether it can go on, as a DERIVED
|
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ directions a beat could take, then commit the one the player picked. Off unless
|
|
|
52
52
|
the world's published revision declares it; the `envelope` is server-side only.
|
|
53
53
|
|
|
54
54
|
**Reading** — `getWorldState`, `getProgress`, `getTurn`, `listTurns`,
|
|
55
|
-
`listTurnsSince`, `replayLedger`, `replayLedgerToEnd`.
|
|
55
|
+
`listTurnsSince`, `replayLedger`, `replayLedgerToEnd`, `scanConsistency`.
|
|
56
56
|
|
|
57
57
|
**Which credential does what.** This matters more than it looks: one of them
|
|
58
58
|
expires within the hour.
|
|
@@ -161,11 +161,27 @@ copies, and `prune` — the one call here that deletes — refuses without
|
|
|
161
161
|
|
|
162
162
|
**Production hand-off** — `createApprovedExport`, `listApprovedExports`,
|
|
163
163
|
`deriveStoryPackageCandidate`. An approved editorial draft becomes a versioned
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Idempotent on content, so replaying an export is a no-op.
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
approved script export carrying the whole chain: original story package → world
|
|
165
|
+
instance → ledger range → evidence draft → editorial draft → reviewer.
|
|
166
|
+
Idempotent on content, so replaying an export is a no-op.
|
|
167
|
+
|
|
168
|
+
`version` picks the contract and **defaults to 1**. `ApprovedScriptExportV1` is
|
|
169
|
+
frozen: its `exportId` is a content hash integrators key on, so nothing is added
|
|
170
|
+
to it, ever. `version: 2` adds what a production workflow needs beyond the
|
|
171
|
+
scenes — a synopsis assembled from scene headings, character notes quoting only
|
|
172
|
+
committed lines, a mechanical episode split, the beats that completed a story
|
|
173
|
+
node, committed relation changes, and where the run diverged from its story. All
|
|
174
|
+
of it derived from committed material; **no model is called for any of it**. The
|
|
175
|
+
two contracts hash under separate domains, so one approval exported both ways
|
|
176
|
+
yields two different, individually stable ids. `content` is a union — narrow on
|
|
177
|
+
`contractVersion` before reading version-specific fields, and expect an
|
|
178
|
+
unrecognised `version` to be a 400 rather than a quiet fall back to 1.
|
|
179
|
+
|
|
180
|
+
The Story Package candidate is validated and *returned*; publishing it is a
|
|
181
|
+
separate act by a person, and only evidence-origin material becomes canon. It
|
|
182
|
+
takes **version 1 exports only** and refuses a V2 export with 422 — it is the
|
|
183
|
+
one path from an export into published canon, and which V2 fields may become
|
|
184
|
+
canon has not been decided.
|
|
169
185
|
|
|
170
186
|
**Editorial layer** — `createEditorialDraft`, `getEditorialDraft`,
|
|
171
187
|
`listEditorialDrafts`, `setEditorialStatus`, `decideEditorialScene`,
|
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.
|
|
7
|
+
export declare const WORLD_SDK_VERSION = "0.18.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. */
|
|
@@ -528,39 +528,128 @@ export interface WorldDrainReport {
|
|
|
528
528
|
blocked: number;
|
|
529
529
|
saturated: boolean;
|
|
530
530
|
}
|
|
531
|
-
/** The production hand-off (Batch 7). Every line keeps the
|
|
532
|
-
* verified: `evidence` with the turn it came from, or
|
|
533
|
-
* nothing committed matched it
|
|
534
|
-
* never laundered into a fact downstream.
|
|
531
|
+
/** The production hand-off, version 1 (Batch 7) — FROZEN. Every line keeps the
|
|
532
|
+
* origin the SERVER verified: `evidence` with the turn it came from, or
|
|
533
|
+
* `suggestion` because nothing committed matched it, whatever the model
|
|
534
|
+
* claimed. A suggestion is never laundered into a fact downstream.
|
|
535
|
+
*
|
|
536
|
+
* Frozen means frozen: `exportId` is a content hash, integrators key on it for
|
|
537
|
+
* idempotency, and a field added here would change the id of every export
|
|
538
|
+
* minted from an approval that was already exported. New material goes to V2. */
|
|
539
|
+
export interface ApprovedScriptExportContentV1 {
|
|
540
|
+
contractVersion: 1;
|
|
541
|
+
exportId: string;
|
|
542
|
+
title: string;
|
|
543
|
+
lineage: Record<string, unknown>;
|
|
544
|
+
review: {
|
|
545
|
+
reviewer: string;
|
|
546
|
+
approvedAt: string;
|
|
547
|
+
sceneDecisions: Record<string, unknown>;
|
|
548
|
+
};
|
|
549
|
+
characters: Array<{
|
|
550
|
+
roleId: string;
|
|
551
|
+
storyRoleId?: string;
|
|
552
|
+
lines: number;
|
|
553
|
+
}>;
|
|
554
|
+
scenes: Array<Record<string, unknown>>;
|
|
555
|
+
stateChanges: Array<{
|
|
556
|
+
summary: string;
|
|
557
|
+
sourceTurnId: string;
|
|
558
|
+
}>;
|
|
559
|
+
suggestionRatio: number;
|
|
560
|
+
exportDigest: string;
|
|
561
|
+
}
|
|
562
|
+
/** Version 2 (Batch 10) — what a production workflow needs beyond the scenes.
|
|
563
|
+
*
|
|
564
|
+
* Every added field is DERIVED from material the pipeline already committed;
|
|
565
|
+
* no model is called to produce any of it. In particular `synopsis` is
|
|
566
|
+
* ASSEMBLED from the surviving scene headings rather than written, and a
|
|
567
|
+
* character note quotes only an evidence-origin line.
|
|
568
|
+
*
|
|
569
|
+
* Declared field by field rather than extending V1. The two contracts hash
|
|
570
|
+
* under separate domains (`approvedscript1` / `approvedscript2`), so one
|
|
571
|
+
* approval exported both ways yields two different, individually stable ids —
|
|
572
|
+
* which is correct, since the two carry different content. */
|
|
573
|
+
export interface ApprovedScriptExportContentV2 {
|
|
574
|
+
contractVersion: 2;
|
|
575
|
+
exportId: string;
|
|
576
|
+
title: string;
|
|
577
|
+
lineage: Record<string, unknown>;
|
|
578
|
+
review: {
|
|
579
|
+
reviewer: string;
|
|
580
|
+
approvedAt: string;
|
|
581
|
+
/** Whether `approvedAt` was stamped when the draft entered `approved`, or
|
|
582
|
+
* stood in from the row's last-touch time because it predates the stamp. */
|
|
583
|
+
approvalTimeSource: 'stamped' | 'legacy_updated_at';
|
|
584
|
+
sceneDecisions: Record<string, unknown>;
|
|
585
|
+
};
|
|
586
|
+
characters: Array<{
|
|
587
|
+
roleId: string;
|
|
588
|
+
storyRoleId?: string;
|
|
589
|
+
lines: number;
|
|
590
|
+
}>;
|
|
591
|
+
scenes: Array<Record<string, unknown>>;
|
|
592
|
+
stateChanges: Array<{
|
|
593
|
+
summary: string;
|
|
594
|
+
sourceTurnId: string;
|
|
595
|
+
}>;
|
|
596
|
+
suggestionRatio: number;
|
|
597
|
+
/** Scene headings in order. Assembled, not authored. */
|
|
598
|
+
synopsis?: string;
|
|
599
|
+
characterNotes?: Array<{
|
|
600
|
+
roleId: string;
|
|
601
|
+
storyRoleId?: string;
|
|
602
|
+
lines: number;
|
|
603
|
+
sceneIds: string[];
|
|
604
|
+
/** Their first COMMITTED line. Absent when they only ever spoke suggested
|
|
605
|
+
* lines — a model's connective sentence must not read as characterisation. */
|
|
606
|
+
firstLine?: string;
|
|
607
|
+
}>;
|
|
608
|
+
/** A MECHANICAL split at a fixed number of scenes, not an editorial one: a
|
|
609
|
+
* reproducible starting point for a producer to move. */
|
|
610
|
+
episodes?: Array<{
|
|
611
|
+
episodeId: string;
|
|
612
|
+
title: string;
|
|
613
|
+
sceneIds: string[];
|
|
614
|
+
synopsis: string;
|
|
615
|
+
}>;
|
|
616
|
+
/** Beats that completed at least one story node — where the plot moved. Not
|
|
617
|
+
* "moments somebody found dramatic": that judgement stays a person's. */
|
|
618
|
+
keyMoments?: Array<{
|
|
619
|
+
sourceTurnId: string;
|
|
620
|
+
seq: number;
|
|
621
|
+
completedNodeIds: string[];
|
|
622
|
+
summary: string;
|
|
623
|
+
}>;
|
|
624
|
+
/** Committed `set_relation` changes, in the evidence layer's own wording and
|
|
625
|
+
* with the turn they came from. Not parsed back into a role pair: inventing
|
|
626
|
+
* structure from prose is the guessing this pipeline reserves for people. */
|
|
627
|
+
relationChanges?: Array<{
|
|
628
|
+
summary: string;
|
|
629
|
+
sourceTurnId: string;
|
|
630
|
+
}>;
|
|
631
|
+
/** Where the run stands against the story it started from. Facts only — a
|
|
632
|
+
* node never completed, or a branch condition reached. Never what "should"
|
|
633
|
+
* have happened. */
|
|
634
|
+
divergence?: Array<{
|
|
635
|
+
kind: 'unresolved_objective' | 'branch_reached';
|
|
636
|
+
nodeId?: string;
|
|
637
|
+
branchId?: string;
|
|
638
|
+
description: string;
|
|
639
|
+
}>;
|
|
640
|
+
exportDigest: string;
|
|
641
|
+
}
|
|
642
|
+
/** Both contracts, discriminated on `content.contractVersion`. Narrow before
|
|
643
|
+
* reading anything version-specific; a stored row keeps the version it was
|
|
644
|
+
* written with, forever. */
|
|
645
|
+
export type ApprovedScriptExportContent = ApprovedScriptExportContentV1 | ApprovedScriptExportContentV2;
|
|
535
646
|
export interface ApprovedScriptExportRow {
|
|
536
647
|
exportId: string;
|
|
537
648
|
draftId: string;
|
|
538
649
|
editorialId: string;
|
|
539
650
|
worldInstanceId: string;
|
|
540
651
|
environmentId: string;
|
|
541
|
-
content:
|
|
542
|
-
contractVersion: number;
|
|
543
|
-
exportId: string;
|
|
544
|
-
title: string;
|
|
545
|
-
lineage: Record<string, unknown>;
|
|
546
|
-
review: {
|
|
547
|
-
reviewer: string;
|
|
548
|
-
approvedAt: string;
|
|
549
|
-
sceneDecisions: Record<string, unknown>;
|
|
550
|
-
};
|
|
551
|
-
characters: Array<{
|
|
552
|
-
roleId: string;
|
|
553
|
-
storyRoleId?: string;
|
|
554
|
-
lines: number;
|
|
555
|
-
}>;
|
|
556
|
-
scenes: Array<Record<string, unknown>>;
|
|
557
|
-
stateChanges: Array<{
|
|
558
|
-
summary: string;
|
|
559
|
-
sourceTurnId: string;
|
|
560
|
-
}>;
|
|
561
|
-
suggestionRatio: number;
|
|
562
|
-
exportDigest: string;
|
|
563
|
-
};
|
|
652
|
+
content: ApprovedScriptExportContent;
|
|
564
653
|
createdAt: string;
|
|
565
654
|
createdBy: string;
|
|
566
655
|
deliveredAt?: string;
|
|
@@ -641,6 +730,107 @@ export interface WorldClientOptions {
|
|
|
641
730
|
* default is generous on purpose, and lower is usually wrong. */
|
|
642
731
|
timeoutMs?: number;
|
|
643
732
|
}
|
|
733
|
+
/** What a world's own record proves about itself. World API 1.19.
|
|
734
|
+
*
|
|
735
|
+
* DETERMINISTIC ONLY. Every finding is a comparison between committed facts
|
|
736
|
+
* and the story package the instance pins — never a judgement about writing.
|
|
737
|
+
* Nothing here repairs anything, and nothing here is a suggestion.
|
|
738
|
+
*
|
|
739
|
+
* `code` is a CLOSED set that may GROW. A published member is never removed,
|
|
740
|
+
* renamed, or widened in meaning; a criterion later found to be wrong is
|
|
741
|
+
* deprecated and replaced beside it. So handle an unknown code as
|
|
742
|
+
* informational rather than failing on it — that is what makes a minor bump
|
|
743
|
+
* safe to take. */
|
|
744
|
+
export type WorldConsistencyFindingCode = 'revision_mismatch' | 'ledger_drift' | 'ledger_missing_entries' | 'unrecorded_beat_claim' | 'orphan_branch_reference' | 'node_prereq_violation' | 'effect_without_grant' | 'flag_schema_violation' | 'relation_change_without_cause' | 'verbatim_secret_disclosure';
|
|
745
|
+
export interface WorldConsistencyEvidence {
|
|
746
|
+
turnId?: string;
|
|
747
|
+
seq?: number;
|
|
748
|
+
roleId?: string;
|
|
749
|
+
sceneId?: string;
|
|
750
|
+
nodeId?: string;
|
|
751
|
+
branchId?: string;
|
|
752
|
+
flagKey?: string;
|
|
753
|
+
effectKind?: string;
|
|
754
|
+
/** Only on verbatim matches, and only the matched fragment — never a line. */
|
|
755
|
+
quote?: string;
|
|
756
|
+
/** Passed through from a committed rejection AS EVIDENCE. It is not this
|
|
757
|
+
* finding's classification; the two vocabularies stay separate. */
|
|
758
|
+
ledgerRejectionCode?: string;
|
|
759
|
+
}
|
|
760
|
+
export interface WorldConsistencyFinding {
|
|
761
|
+
code: WorldConsistencyFindingCode;
|
|
762
|
+
layer: 'deterministic';
|
|
763
|
+
status: 'proven';
|
|
764
|
+
severity: 'blocking' | 'warning' | 'info';
|
|
765
|
+
/** Never empty. A finding nobody can check is an accusation. */
|
|
766
|
+
evidence: WorldConsistencyEvidence[];
|
|
767
|
+
}
|
|
768
|
+
/** What a check could NOT examine. Read this before trusting an empty
|
|
769
|
+
* `findings`: a clean report with non-empty coverage is clean about less than
|
|
770
|
+
* the whole world. Absence of a finding is not proof of absence. */
|
|
771
|
+
export interface WorldConsistencyCoverage {
|
|
772
|
+
code: WorldConsistencyFindingCode;
|
|
773
|
+
skipped: number;
|
|
774
|
+
reason: 'evidence_field_absent' | 'scan_incomplete';
|
|
775
|
+
}
|
|
776
|
+
/** The MODEL-ASSISTED layer. World API 1.20.
|
|
777
|
+
*
|
|
778
|
+
* A SEPARATE closed set from `WorldConsistencyFindingCode`, which stays frozen
|
|
779
|
+
* as the deterministic vocabulary. Keeping them apart is not tidiness: code
|
|
780
|
+
* written against 1.19 reasonably does `if (f.severity === 'blocking') fail()`
|
|
781
|
+
* over `findings`, and a suspicion about a character's voice must never be
|
|
782
|
+
* able to fail that build. Suspicions arrive in their own array, and their
|
|
783
|
+
* severity has no `'blocking'` member at all.
|
|
784
|
+
*
|
|
785
|
+
* These are judgements, not proofs. Nothing here should gate a merge, block an
|
|
786
|
+
* export, or reject content — `suspected_cross_ip_contamination` least of all:
|
|
787
|
+
* even confirmed it is an editorial opinion, not evidence of isolation. */
|
|
788
|
+
export type WorldModelAssistedFindingCode = 'suspected_canon_conflict' | 'suspected_revival' | 'suspected_persona_conflict' | 'suspected_secret_leak' | 'suspected_cross_ip_contamination';
|
|
789
|
+
export interface WorldModelAssistedFinding {
|
|
790
|
+
code: WorldModelAssistedFindingCode;
|
|
791
|
+
layer: 'model_assisted';
|
|
792
|
+
/** `potential` until a person decides. Confirming records an opinion against
|
|
793
|
+
* the finding; it changes nothing about the story, canon, ledger or state. */
|
|
794
|
+
status: 'potential' | 'confirmed' | 'dismissed';
|
|
795
|
+
severity: 'warning' | 'info';
|
|
796
|
+
/** Stable across scans for the same judgement, so a dismissal sticks. */
|
|
797
|
+
findingId: string;
|
|
798
|
+
/** Assembled server-side from committed rows. The model picks which rows;
|
|
799
|
+
* it never writes evidence. */
|
|
800
|
+
evidence: Array<{
|
|
801
|
+
turnId?: string;
|
|
802
|
+
roleId?: string;
|
|
803
|
+
}>;
|
|
804
|
+
/** The model's own words, bounded, and typed apart from evidence. */
|
|
805
|
+
suggestion?: {
|
|
806
|
+
kind: 'author_note';
|
|
807
|
+
text: string;
|
|
808
|
+
};
|
|
809
|
+
reviewedBy?: string;
|
|
810
|
+
reviewedAt?: string;
|
|
811
|
+
}
|
|
812
|
+
/** Why the model layer produced less than a full pass, when it was asked for. */
|
|
813
|
+
export type WorldModelLayerNote = 'disabled_server' | 'not_declared' | 'rate_limited' | 'unavailable' | 'incomplete';
|
|
814
|
+
export type WorldConsistencyReport = {
|
|
815
|
+
status: 'complete' | 'incomplete';
|
|
816
|
+
contractVersion: number;
|
|
817
|
+
findings: WorldConsistencyFinding[];
|
|
818
|
+
coverage: WorldConsistencyCoverage[];
|
|
819
|
+
entriesScanned: number;
|
|
820
|
+
findingsTruncated: boolean;
|
|
821
|
+
/** Present only when `status` is `incomplete`. Pass it back to continue. */
|
|
822
|
+
cursor?: number;
|
|
823
|
+
/** Present ONLY when you asked for `model_assisted`. A default call
|
|
824
|
+
* carries neither this key nor `modelAssistedNote`. */
|
|
825
|
+
modelAssistedFindings?: WorldModelAssistedFinding[];
|
|
826
|
+
modelAssistedNote?: WorldModelLayerNote;
|
|
827
|
+
} | {
|
|
828
|
+
status: 'rate_limited';
|
|
829
|
+
retryAfterSec: number;
|
|
830
|
+
} | {
|
|
831
|
+
status: 'unavailable';
|
|
832
|
+
reason: 'world_unavailable';
|
|
833
|
+
};
|
|
644
834
|
export declare class PouchyWorldClient {
|
|
645
835
|
readonly projectId: string;
|
|
646
836
|
private readonly baseUrl;
|
|
@@ -692,6 +882,29 @@ export declare class PouchyWorldClient {
|
|
|
692
882
|
environmentRevision: number;
|
|
693
883
|
state: Record<string, unknown>;
|
|
694
884
|
}>;
|
|
885
|
+
/** What this world's own record proves about itself. World API 1.19.
|
|
886
|
+
*
|
|
887
|
+
* A read-only scan: it commits nothing, asks no model, and repairs nothing.
|
|
888
|
+
* Findings are deterministic — each one is two recorded things that cannot
|
|
889
|
+
* both be true — so they are safe to act on without a human re-reading the
|
|
890
|
+
* story, which is exactly what the model-assisted layer is not.
|
|
891
|
+
*
|
|
892
|
+
* Check `status` first. `incomplete` means the walk was capped or the
|
|
893
|
+
* datastore stopped answering: pass `cursor` back to continue, and do NOT
|
|
894
|
+
* read an empty `findings` on that arm as a clean bill of health.
|
|
895
|
+
*
|
|
896
|
+
* READ `coverage` BEFORE TRUSTING AN EMPTY `findings`. Some checks depend
|
|
897
|
+
* on fields that older ledger rows do not carry, and those entries are
|
|
898
|
+
* counted there rather than skipped in silence. A clean report that could
|
|
899
|
+
* not examine half the history is not the same as a clean world.
|
|
900
|
+
*
|
|
901
|
+
* Rate limited on two axes, project and IP. `status: 'rate_limited'`
|
|
902
|
+
* arrives with HTTP 429 and `retryAfterSec`.
|
|
903
|
+
*/
|
|
904
|
+
scanConsistency(environmentId: string, worldInstanceId: string, opts?: {
|
|
905
|
+
cursor?: number;
|
|
906
|
+
layers?: Array<'deterministic' | 'model_assisted'>;
|
|
907
|
+
}): Promise<WorldConsistencyReport>;
|
|
695
908
|
/** Where the story is, and whether it can go on. World API 1.18.
|
|
696
909
|
*
|
|
697
910
|
* A DERIVED checkpoint: nothing is stored to answer it, no model is asked,
|
|
@@ -786,16 +999,29 @@ export declare class PouchyWorldClient {
|
|
|
786
999
|
* (Batch 7). Idempotent on content — the same approved review exports to
|
|
787
1000
|
* the same id forever, so replaying one is a no-op rather than a second
|
|
788
1001
|
* script. `notify` also sends `world.script_approved` to the project's
|
|
789
|
-
* webhooks, carrying lineage and
|
|
1002
|
+
* webhooks, carrying lineage, identifiers and `contractVersion` but never
|
|
1003
|
+
* the script body.
|
|
1004
|
+
*
|
|
1005
|
+
* `version` DEFAULTS TO 1 and omitting it sends no `version` at all, so a
|
|
1006
|
+
* caller written before V2 existed keeps receiving byte-identical exports.
|
|
1007
|
+
* Pass `version: 2` for the production contract (synopsis, character notes,
|
|
1008
|
+
* episodes, key moments, relation changes, divergence). Any other value is
|
|
1009
|
+
* refused by the server rather than coerced. */
|
|
790
1010
|
createApprovedExport(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string, options?: {
|
|
791
1011
|
notify?: boolean;
|
|
1012
|
+
version?: 1 | 2;
|
|
792
1013
|
}): Promise<ApprovedScriptExportRow>;
|
|
793
1014
|
listApprovedExports(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string): Promise<{
|
|
794
1015
|
exports: unknown[];
|
|
795
1016
|
}>;
|
|
796
1017
|
/** The next Story Package as a CANDIDATE — validated, and returned rather
|
|
797
1018
|
* than published. Publishing it is a separate act by a person, through
|
|
798
|
-
* `createStoryPackage`. Only evidence-origin material becomes canon.
|
|
1019
|
+
* `createStoryPackage`. Only evidence-origin material becomes canon.
|
|
1020
|
+
*
|
|
1021
|
+
* VERSION 1 EXPORTS ONLY. A version 2 export is refused with 422: this is
|
|
1022
|
+
* the one path from an export into published canon, and which V2 fields may
|
|
1023
|
+
* become canon has not been decided. Export the approval as version 1 and
|
|
1024
|
+
* derive from that. */
|
|
799
1025
|
deriveStoryPackageCandidate(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string, exportId: string, options?: {
|
|
800
1026
|
name?: string;
|
|
801
1027
|
summary?: string;
|
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.
|
|
26
|
+
export const WORLD_SDK_VERSION = '0.18.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
|
|
@@ -297,6 +297,40 @@ export class PouchyWorldClient {
|
|
|
297
297
|
getWorldState(environmentId, worldInstanceId) {
|
|
298
298
|
return this.read(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/state`, `/admin/environments/${environmentId}/instances/${worldInstanceId}/state`);
|
|
299
299
|
}
|
|
300
|
+
/** What this world's own record proves about itself. World API 1.19.
|
|
301
|
+
*
|
|
302
|
+
* A read-only scan: it commits nothing, asks no model, and repairs nothing.
|
|
303
|
+
* Findings are deterministic — each one is two recorded things that cannot
|
|
304
|
+
* both be true — so they are safe to act on without a human re-reading the
|
|
305
|
+
* story, which is exactly what the model-assisted layer is not.
|
|
306
|
+
*
|
|
307
|
+
* Check `status` first. `incomplete` means the walk was capped or the
|
|
308
|
+
* datastore stopped answering: pass `cursor` back to continue, and do NOT
|
|
309
|
+
* read an empty `findings` on that arm as a clean bill of health.
|
|
310
|
+
*
|
|
311
|
+
* READ `coverage` BEFORE TRUSTING AN EMPTY `findings`. Some checks depend
|
|
312
|
+
* on fields that older ledger rows do not carry, and those entries are
|
|
313
|
+
* counted there rather than skipped in silence. A clean report that could
|
|
314
|
+
* not examine half the history is not the same as a clean world.
|
|
315
|
+
*
|
|
316
|
+
* Rate limited on two axes, project and IP. `status: 'rate_limited'`
|
|
317
|
+
* arrives with HTTP 429 and `retryAfterSec`.
|
|
318
|
+
*/
|
|
319
|
+
scanConsistency(environmentId, worldInstanceId, opts = {}) {
|
|
320
|
+
// The query rides the third argument, not the path template. Both are
|
|
321
|
+
// sent identically; keeping the path a pure template is what lets the
|
|
322
|
+
// admin-lane gate check that every `/admin/...` the SDK sends is a route
|
|
323
|
+
// that actually exists.
|
|
324
|
+
const q = new URLSearchParams();
|
|
325
|
+
if (opts.cursor !== undefined)
|
|
326
|
+
q.set('cursor', String(opts.cursor));
|
|
327
|
+
// Omitted means deterministic only, with zero model calls — the default
|
|
328
|
+
// stays what it was, so upgrading the SDK changes no behaviour.
|
|
329
|
+
if (opts.layers?.length)
|
|
330
|
+
q.set('layers', opts.layers.join(','));
|
|
331
|
+
const suffix = q.toString() ? `?${q}` : '';
|
|
332
|
+
return this.read(`/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/consistency`, `/admin/environments/${environmentId}/instances/${worldInstanceId}/consistency`, suffix);
|
|
333
|
+
}
|
|
300
334
|
/** Where the story is, and whether it can go on. World API 1.18.
|
|
301
335
|
*
|
|
302
336
|
* A DERIVED checkpoint: nothing is stored to answer it, no model is asked,
|
|
@@ -424,16 +458,31 @@ export class PouchyWorldClient {
|
|
|
424
458
|
* (Batch 7). Idempotent on content — the same approved review exports to
|
|
425
459
|
* the same id forever, so replaying one is a no-op rather than a second
|
|
426
460
|
* script. `notify` also sends `world.script_approved` to the project's
|
|
427
|
-
* webhooks, carrying lineage and
|
|
461
|
+
* webhooks, carrying lineage, identifiers and `contractVersion` but never
|
|
462
|
+
* the script body.
|
|
463
|
+
*
|
|
464
|
+
* `version` DEFAULTS TO 1 and omitting it sends no `version` at all, so a
|
|
465
|
+
* caller written before V2 existed keeps receiving byte-identical exports.
|
|
466
|
+
* Pass `version: 2` for the production contract (synopsis, character notes,
|
|
467
|
+
* episodes, key moments, relation changes, divergence). Any other value is
|
|
468
|
+
* refused by the server rather than coerced. */
|
|
428
469
|
createApprovedExport(environmentId, worldInstanceId, draftId, editorialId, options = {}) {
|
|
429
|
-
return this.owner('POST', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/script-drafts/${draftId}/editorial/${editorialId}/approved-export`,
|
|
470
|
+
return this.owner('POST', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/script-drafts/${draftId}/editorial/${editorialId}/approved-export`, {
|
|
471
|
+
...(options.notify ? { notify: true } : {}),
|
|
472
|
+
...(options.version === undefined ? {} : { version: options.version })
|
|
473
|
+
});
|
|
430
474
|
}
|
|
431
475
|
listApprovedExports(environmentId, worldInstanceId, draftId, editorialId) {
|
|
432
476
|
return this.owner('GET', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/script-drafts/${draftId}/editorial/${editorialId}/approved-export`);
|
|
433
477
|
}
|
|
434
478
|
/** The next Story Package as a CANDIDATE — validated, and returned rather
|
|
435
479
|
* than published. Publishing it is a separate act by a person, through
|
|
436
|
-
* `createStoryPackage`. Only evidence-origin material becomes canon.
|
|
480
|
+
* `createStoryPackage`. Only evidence-origin material becomes canon.
|
|
481
|
+
*
|
|
482
|
+
* VERSION 1 EXPORTS ONLY. A version 2 export is refused with 422: this is
|
|
483
|
+
* the one path from an export into published canon, and which V2 fields may
|
|
484
|
+
* become canon has not been decided. Export the approval as version 1 and
|
|
485
|
+
* derive from that. */
|
|
437
486
|
deriveStoryPackageCandidate(environmentId, worldInstanceId, draftId, editorialId, exportId, options = {}) {
|
|
438
487
|
return this.owner('POST', `/projects/${this.projectId}/environments/${environmentId}/instances/${worldInstanceId}/script-drafts/${draftId}/editorial/${editorialId}/approved-export/candidate?exportId=${encodeURIComponent(exportId)}`, options);
|
|
439
488
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pouchy_ai/world-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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",
|