@pouchy_ai/world-sdk 0.8.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 +142 -0
- package/LICENSE +41 -0
- package/README.md +141 -0
- package/conformance.mjs +279 -0
- package/dist/index.d.ts +631 -0
- package/dist/index.js +560 -0
- package/package.json +54 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
export declare const WORLD_SDK_VERSION = "0.1.0";
|
|
2
|
+
export declare const DEFAULT_BASE_URL = "https://pouchy.ai/v1";
|
|
3
|
+
/** The refusal classes a world call can produce. `unknown` is deliberate: an
|
|
4
|
+
* unrecognized status is never quietly folded into a neighbour. */
|
|
5
|
+
export declare const WORLD_ERROR_CODES: readonly ["unauthorized", "forbidden", "not_found", "conflict", "unprocessable", "rate_limited", "payload_too_large", "server_error", "network", "unknown"];
|
|
6
|
+
export type WorldErrorCode = (typeof WORLD_ERROR_CODES)[number];
|
|
7
|
+
export declare class WorldApiError extends Error {
|
|
8
|
+
readonly code: WorldErrorCode;
|
|
9
|
+
readonly status: number;
|
|
10
|
+
/** The server's own message, when it sent one. */
|
|
11
|
+
readonly detail?: string;
|
|
12
|
+
readonly retryAfterSec?: number;
|
|
13
|
+
constructor(input: {
|
|
14
|
+
code: WorldErrorCode;
|
|
15
|
+
status: number;
|
|
16
|
+
message: string;
|
|
17
|
+
detail?: string;
|
|
18
|
+
retryAfterSec?: number;
|
|
19
|
+
});
|
|
20
|
+
/** Worth trying again with the SAME idempotency key. A 409 is not: it means
|
|
21
|
+
* the world disagreed with the request, and repeating it will disagree
|
|
22
|
+
* again. */
|
|
23
|
+
get retryable(): boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare const SOURCE_SIGNATURE_HEADER = "X-Pouchy-Source-Signature";
|
|
26
|
+
/** Build the `X-Pouchy-Source-Signature` header for one request body.
|
|
27
|
+
*
|
|
28
|
+
* The canonical string is five newline-joined lines — scheme, unix seconds,
|
|
29
|
+
* the declared source, the event/turn id, and the sha256 of the EXACT body
|
|
30
|
+
* bytes. Sign at SEND time, every attempt: a legitimate retry of the same id
|
|
31
|
+
* days later carries a fresh timestamp and passes the ±5 minute skew, because
|
|
32
|
+
* the signature proves origin and never doubles as a dedupe key.
|
|
33
|
+
*
|
|
34
|
+
* Pass the same string you will actually send as the body. Serializing twice
|
|
35
|
+
* (once to sign, once to send) is the classic way to sign bytes you did not
|
|
36
|
+
* send. */
|
|
37
|
+
export declare function signSourceRequest(input: {
|
|
38
|
+
source: string;
|
|
39
|
+
/** eventId for `/events`, turnId for the turns door — the id slot. */
|
|
40
|
+
id: string;
|
|
41
|
+
body: string;
|
|
42
|
+
keyId: string;
|
|
43
|
+
secret: string;
|
|
44
|
+
nowMs?: number;
|
|
45
|
+
}): string;
|
|
46
|
+
/** A fresh turn id for a NEW beat. Retrying a beat means re-sending the SAME
|
|
47
|
+
* id — that is what makes the retry free of a second commit, a second model
|
|
48
|
+
* call and a second message. Generate once, store it with whatever you are
|
|
49
|
+
* about to do, and reuse it until you get an answer. */
|
|
50
|
+
export declare function newTurnId(prefix?: string): string;
|
|
51
|
+
/** `evt:` is the platform's own namespace for turns derived from trusted
|
|
52
|
+
* events; the turns door refuses caller ids in it. */
|
|
53
|
+
export declare function isReservedTurnId(turnId: string): boolean;
|
|
54
|
+
export type WorldCompletionStatus = 'complete' | 'partial' | 'duplicate' | 'conflict' | 'rejected' | 'refused';
|
|
55
|
+
export type WorldExecutionStatus = 'committed' | 'not_committed';
|
|
56
|
+
export type WorldDeliveryStatus = 'delivered' | 'pending' | 'none';
|
|
57
|
+
export interface WorldTurnResult {
|
|
58
|
+
turnId: string;
|
|
59
|
+
worldInstanceId: string;
|
|
60
|
+
beforeStateRevision: number;
|
|
61
|
+
afterStateRevision: number;
|
|
62
|
+
selectedRoles: string[];
|
|
63
|
+
roleMessages: Array<{
|
|
64
|
+
roleId: string;
|
|
65
|
+
message: string;
|
|
66
|
+
fallback?: true;
|
|
67
|
+
}>;
|
|
68
|
+
committedStateDiff: unknown[];
|
|
69
|
+
rejectedEffects: Array<{
|
|
70
|
+
index: number;
|
|
71
|
+
reason: string;
|
|
72
|
+
roleId?: string;
|
|
73
|
+
kind?: string;
|
|
74
|
+
/** Batch 7 — WHY, as a code. `narrative_conflict` is the only one that
|
|
75
|
+
* describes content: two characters wanted incompatible things and the
|
|
76
|
+
* story rule settled it. Every other code is a DEFECT in the story
|
|
77
|
+
* package, the prompt or the integration, and should be treated as one
|
|
78
|
+
* rather than as colour. Absent on turns committed before the taxonomy —
|
|
79
|
+
* read that as unknown, never as narrative. */
|
|
80
|
+
code?: WorldRejectionCode;
|
|
81
|
+
}>;
|
|
82
|
+
skippedRoles: Array<{
|
|
83
|
+
roleId: string;
|
|
84
|
+
reason: string;
|
|
85
|
+
}>;
|
|
86
|
+
nextOptions: Array<{
|
|
87
|
+
branchId: string;
|
|
88
|
+
condition: string;
|
|
89
|
+
}>;
|
|
90
|
+
repairs?: Array<{
|
|
91
|
+
roleId: string;
|
|
92
|
+
outcome: 'repaired' | 'fallback';
|
|
93
|
+
reason?: string;
|
|
94
|
+
}>;
|
|
95
|
+
traceId?: string;
|
|
96
|
+
executionStatus: WorldExecutionStatus;
|
|
97
|
+
deliveryStatus: WorldDeliveryStatus;
|
|
98
|
+
completionStatus: WorldCompletionStatus;
|
|
99
|
+
}
|
|
100
|
+
/** Read a turn result without guessing. The two questions are separate: did
|
|
101
|
+
* the world move, and did the audience hear about it. */
|
|
102
|
+
/** Read a delivery row without guessing what its status means. `blocking` is
|
|
103
|
+
* the field that matters most: PR-A delivers a session's beats in order, so a
|
|
104
|
+
* blocking row is not one failure — it is a reader who has stopped receiving
|
|
105
|
+
* the story until someone acts. */
|
|
106
|
+
/** Why a redacted dead letter could not be rebuilt. A closed vocabulary: an
|
|
107
|
+
* operator deciding whether to resolve a gap is making a judgement call, and a
|
|
108
|
+
* free-text reason is one they cannot compare across incidents. */
|
|
109
|
+
export type WorldRehydrateFailure = 'source_missing' | 'archive_unavailable' | 'hash_mismatch' | 'ledger_drift' | 'identity_mismatch';
|
|
110
|
+
/** What to DO about a stuck delivery, in one call. The three answers are the
|
|
111
|
+
* three verbs, in the order they should be tried: re-send what is still there,
|
|
112
|
+
* rebuild it from the record, or tell the reader the beat is gone. */
|
|
113
|
+
export declare function describeDeliveryResolution(row: WorldDeliveryOpsRow): {
|
|
114
|
+
action: 'none' | 'requeue' | 'rehydrate' | 'resolve_gap' | 'wait';
|
|
115
|
+
blocking: boolean;
|
|
116
|
+
summary: string;
|
|
117
|
+
};
|
|
118
|
+
export declare function describeDelivery(row: WorldDeliveryOpsRow): {
|
|
119
|
+
state: 'landed' | 'in_flight' | 'waiting' | 'stuck';
|
|
120
|
+
blocking: boolean;
|
|
121
|
+
summary: string;
|
|
122
|
+
};
|
|
123
|
+
export declare function describeTurn(result: WorldTurnResult): {
|
|
124
|
+
worldMoved: boolean;
|
|
125
|
+
audienceHeard: boolean;
|
|
126
|
+
/** Sending the SAME turnId again is safe and correct. */
|
|
127
|
+
shouldRetrySameTurn: boolean;
|
|
128
|
+
/** The request itself needs changing before it can succeed. */
|
|
129
|
+
needsDifferentRequest: boolean;
|
|
130
|
+
summary: string;
|
|
131
|
+
};
|
|
132
|
+
export interface WorldReplayReport {
|
|
133
|
+
worldInstanceId: string;
|
|
134
|
+
verdict: 'consistent' | 'drift' | 'missing_entries' | 'illegal_patch' | 'incomplete' | 'unavailable';
|
|
135
|
+
detail?: string;
|
|
136
|
+
fromRevision: number;
|
|
137
|
+
rebuiltRevision: number;
|
|
138
|
+
rebuiltHash: string;
|
|
139
|
+
currentRevision: number | null;
|
|
140
|
+
currentHash: string | null;
|
|
141
|
+
entriesScanned: number;
|
|
142
|
+
cursor?: string;
|
|
143
|
+
checkpointWritten?: number;
|
|
144
|
+
saturated: boolean;
|
|
145
|
+
elapsedMs: number;
|
|
146
|
+
dryRun: true;
|
|
147
|
+
}
|
|
148
|
+
export interface ScriptDraftRow {
|
|
149
|
+
draftId: string;
|
|
150
|
+
jobId: string;
|
|
151
|
+
worldInstanceId: string;
|
|
152
|
+
environmentId: string;
|
|
153
|
+
content: Record<string, unknown>;
|
|
154
|
+
status: 'draft' | 'reviewed';
|
|
155
|
+
createdAt: string;
|
|
156
|
+
createdBy: string;
|
|
157
|
+
reviewedAt?: string;
|
|
158
|
+
reviewedBy?: string;
|
|
159
|
+
reviewNote?: string;
|
|
160
|
+
}
|
|
161
|
+
/** The EDITORIAL layer: a model's reading of an evidence draft, and the human
|
|
162
|
+
* review state attached to it. `suggestionRatio` is the share of lines the
|
|
163
|
+
* server could NOT match to a committed line — read it before you read the
|
|
164
|
+
* prose. */
|
|
165
|
+
export interface EditorialDraftRow {
|
|
166
|
+
editorialId: string;
|
|
167
|
+
draftId: string;
|
|
168
|
+
worldInstanceId: string;
|
|
169
|
+
environmentId: string;
|
|
170
|
+
content: {
|
|
171
|
+
contractVersion: number;
|
|
172
|
+
title: string;
|
|
173
|
+
scenes: Array<Record<string, unknown>>;
|
|
174
|
+
provenance: Record<string, unknown>;
|
|
175
|
+
suggestionRatio: number;
|
|
176
|
+
humanReviewRequired: true;
|
|
177
|
+
};
|
|
178
|
+
status: 'draft' | 'under_review' | 'changes_requested' | 'approved' | 'rejected' | 'exported';
|
|
179
|
+
sceneDecisions?: Record<string, Record<string, unknown>>;
|
|
180
|
+
createdAt: string;
|
|
181
|
+
createdBy: string;
|
|
182
|
+
updatedAt?: string;
|
|
183
|
+
statusBy?: string;
|
|
184
|
+
statusNote?: string;
|
|
185
|
+
exportedAt?: string;
|
|
186
|
+
}
|
|
187
|
+
/** Operator metrics for one world instance. Two families, kept apart on
|
|
188
|
+
* purpose: one word answering both is how a delivery outage reads as a
|
|
189
|
+
* content problem. */
|
|
190
|
+
/** The shared refusal taxonomy (Batch 7). Only `narrative_conflict` is content. */
|
|
191
|
+
export type WorldRejectionCode = 'narrative_conflict' | 'policy_rejection' | 'schema_rejection' | 'canon_rejection' | 'concurrency_rejection' | 'repair_failure';
|
|
192
|
+
/** True for every code except `narrative_conflict`. */
|
|
193
|
+
export declare function isDefectRejection(code: WorldRejectionCode | undefined): boolean;
|
|
194
|
+
export interface WorldInstanceMetrics {
|
|
195
|
+
worldInstanceId: string;
|
|
196
|
+
generatedAt: number;
|
|
197
|
+
stateRevision: number | null;
|
|
198
|
+
delivery: {
|
|
199
|
+
byStatus: Record<string, number>;
|
|
200
|
+
retried: number;
|
|
201
|
+
attemptsTotal: number;
|
|
202
|
+
dead: number;
|
|
203
|
+
oldestUndeliveredAgeMs: number | null;
|
|
204
|
+
/** Sessions whose oldest unlanded line cannot move. Delivery is ordered
|
|
205
|
+
* per session, so this is the number of readers currently missing a
|
|
206
|
+
* beat — usually a dead letter holding the stream. */
|
|
207
|
+
blockedSessions: number;
|
|
208
|
+
/** Batch 7.1 — receipt growth on a long-running world, and how much of it
|
|
209
|
+
* is collectable. A receipt exists for as long as any legal path can put
|
|
210
|
+
* its delivery back in flight; `eligibleForCleanup` counts the delivered
|
|
211
|
+
* rows past retention, which is the only population whose receipts go. */
|
|
212
|
+
receiptCount: number;
|
|
213
|
+
eligibleForCleanup: number;
|
|
214
|
+
latencyP50Ms: number | null;
|
|
215
|
+
latencyP95Ms: number | null;
|
|
216
|
+
scanned: number;
|
|
217
|
+
truncated: boolean;
|
|
218
|
+
};
|
|
219
|
+
turns: {
|
|
220
|
+
turns: number;
|
|
221
|
+
effectsProposed: number;
|
|
222
|
+
effectsAccepted: number;
|
|
223
|
+
effectsRejected: number;
|
|
224
|
+
effectRejectionRate: number;
|
|
225
|
+
/** Batch 7 — refusals split by the shared taxonomy. Judge a world by
|
|
226
|
+
* these, not by the aggregate above: the aggregate mixes drama with
|
|
227
|
+
* defects. */
|
|
228
|
+
rejectionsByCode: Record<WorldRejectionCode, number>;
|
|
229
|
+
narrativeConflictRate: number;
|
|
230
|
+
policyRejectionRate: number;
|
|
231
|
+
schemaRejectionRate: number;
|
|
232
|
+
canonRejectionRate: number;
|
|
233
|
+
concurrencyConflictRate: number;
|
|
234
|
+
repairAttemptRate: number;
|
|
235
|
+
repairSuccessRate: number;
|
|
236
|
+
neutralFallbackRate: number;
|
|
237
|
+
turnsWithRejectedEffects: number;
|
|
238
|
+
repairTurnRate: number;
|
|
239
|
+
linesDelivered: number;
|
|
240
|
+
fallbackLines: number;
|
|
241
|
+
fallbackRate: number;
|
|
242
|
+
linesByRole: Record<string, number>;
|
|
243
|
+
rejectionsByRole: Record<string, number>;
|
|
244
|
+
scanned: number;
|
|
245
|
+
truncated: boolean;
|
|
246
|
+
};
|
|
247
|
+
archive: {
|
|
248
|
+
floor: number;
|
|
249
|
+
prunedThrough: number;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/** A quality suite's report. `notApplicable` metrics are excluded from
|
|
253
|
+
* `passRate` — a metric with no data is a finding, not a pass. */
|
|
254
|
+
export interface WorldEvalReport {
|
|
255
|
+
contractVersion: number;
|
|
256
|
+
suite: 'drama' | 'npc';
|
|
257
|
+
worldInstanceId: string;
|
|
258
|
+
turns: number;
|
|
259
|
+
metrics: Array<{
|
|
260
|
+
id: string;
|
|
261
|
+
label: string;
|
|
262
|
+
direction: 'higher' | 'lower' | 'exact' | 'band';
|
|
263
|
+
value: number;
|
|
264
|
+
threshold: number;
|
|
265
|
+
thresholdMax?: number;
|
|
266
|
+
pass: boolean;
|
|
267
|
+
notApplicable?: boolean;
|
|
268
|
+
detail: string;
|
|
269
|
+
}>;
|
|
270
|
+
passed: number;
|
|
271
|
+
failed: number;
|
|
272
|
+
notApplicable: number;
|
|
273
|
+
passRate: number;
|
|
274
|
+
ok: boolean;
|
|
275
|
+
}
|
|
276
|
+
/** One row of the operator queue view. No message text, no signing material,
|
|
277
|
+
* no payload hash — a queue question, not a transcript. */
|
|
278
|
+
export interface WorldDeliveryOpsRow {
|
|
279
|
+
deliveryId: string;
|
|
280
|
+
turnId: string;
|
|
281
|
+
roleId: string;
|
|
282
|
+
uid: string;
|
|
283
|
+
sessionId: string;
|
|
284
|
+
ledgerSeq?: number;
|
|
285
|
+
sequence?: number;
|
|
286
|
+
status: 'pending' | 'delivering' | 'delivered' | 'retry_wait' | 'dead' | 'rehydrating' | 'unrecoverable' | 'resolved_gap';
|
|
287
|
+
attempts: number;
|
|
288
|
+
ageMs: number;
|
|
289
|
+
nextAttemptAt: number;
|
|
290
|
+
createdAt: number;
|
|
291
|
+
deliveredAt?: number;
|
|
292
|
+
lastErrorClass?: string;
|
|
293
|
+
payloadRedactedAt?: number;
|
|
294
|
+
requeueCount?: number;
|
|
295
|
+
rehydratedAt?: number;
|
|
296
|
+
rehydratedFrom?: 'ledger' | 'archive';
|
|
297
|
+
unrecoverableReason?: WorldRehydrateFailure;
|
|
298
|
+
resolvedGapAt?: number;
|
|
299
|
+
replacesDeliveryId?: string;
|
|
300
|
+
/** How many times the settled row's atomic cleanup has failed, and when it
|
|
301
|
+
* crossed the threshold at which it becomes something to look at. Present
|
|
302
|
+
* only once a cleanup has actually failed — an ordinary row omits both.
|
|
303
|
+
* They are cleanup bookkeeping, never a delivery status: a row carrying
|
|
304
|
+
* them is still `delivered` and still terminal. A persistently non-zero
|
|
305
|
+
* `cleanupRetryCount` across a session means its receipt store is refusing
|
|
306
|
+
* writes and the receipts are not shrinking. */
|
|
307
|
+
cleanupRetryCount?: number;
|
|
308
|
+
cleanupStalledAt?: number;
|
|
309
|
+
/** This row is the oldest unlanded line of its session AND cannot move — so
|
|
310
|
+
* it is why that reader is waiting. */
|
|
311
|
+
blocking: boolean;
|
|
312
|
+
}
|
|
313
|
+
export interface WorldDeliveryPage {
|
|
314
|
+
rows: WorldDeliveryOpsRow[];
|
|
315
|
+
cursor?: string;
|
|
316
|
+
truncated: boolean;
|
|
317
|
+
scanned: number;
|
|
318
|
+
}
|
|
319
|
+
export interface WorldDeliveryDetail extends WorldDeliveryOpsRow {
|
|
320
|
+
channel: string;
|
|
321
|
+
payloadType: string;
|
|
322
|
+
/** Only when `includePayload` was passed — and that read was audited. */
|
|
323
|
+
payload?: {
|
|
324
|
+
text: string;
|
|
325
|
+
userText: string;
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
export interface WorldDrainReport {
|
|
329
|
+
scanned: number;
|
|
330
|
+
claimed: number;
|
|
331
|
+
delivered: number;
|
|
332
|
+
failed: number;
|
|
333
|
+
dead: number;
|
|
334
|
+
/** Sessions still owing their oldest line after this pass. */
|
|
335
|
+
blocked: number;
|
|
336
|
+
saturated: boolean;
|
|
337
|
+
}
|
|
338
|
+
/** The production hand-off (Batch 7). Every line keeps the origin the SERVER
|
|
339
|
+
* verified: `evidence` with the turn it came from, or `suggestion` because
|
|
340
|
+
* nothing committed matched it — whatever the model claimed. A suggestion is
|
|
341
|
+
* never laundered into a fact downstream. */
|
|
342
|
+
export interface ApprovedScriptExportRow {
|
|
343
|
+
exportId: string;
|
|
344
|
+
draftId: string;
|
|
345
|
+
editorialId: string;
|
|
346
|
+
worldInstanceId: string;
|
|
347
|
+
environmentId: string;
|
|
348
|
+
content: {
|
|
349
|
+
contractVersion: number;
|
|
350
|
+
exportId: string;
|
|
351
|
+
title: string;
|
|
352
|
+
lineage: Record<string, unknown>;
|
|
353
|
+
review: {
|
|
354
|
+
reviewer: string;
|
|
355
|
+
approvedAt: string;
|
|
356
|
+
sceneDecisions: Record<string, unknown>;
|
|
357
|
+
};
|
|
358
|
+
characters: Array<{
|
|
359
|
+
roleId: string;
|
|
360
|
+
storyRoleId?: string;
|
|
361
|
+
lines: number;
|
|
362
|
+
}>;
|
|
363
|
+
scenes: Array<Record<string, unknown>>;
|
|
364
|
+
stateChanges: Array<{
|
|
365
|
+
summary: string;
|
|
366
|
+
sourceTurnId: string;
|
|
367
|
+
}>;
|
|
368
|
+
suggestionRatio: number;
|
|
369
|
+
exportDigest: string;
|
|
370
|
+
};
|
|
371
|
+
createdAt: string;
|
|
372
|
+
createdBy: string;
|
|
373
|
+
deliveredAt?: string;
|
|
374
|
+
deliveryAttempted?: number;
|
|
375
|
+
deliveryOk?: number;
|
|
376
|
+
}
|
|
377
|
+
export interface WorldClientOptions {
|
|
378
|
+
/** The project this client acts for. */
|
|
379
|
+
projectId: string;
|
|
380
|
+
/** An OWNER-plane credential (a signed-in admin's ID token) for the control
|
|
381
|
+
* plane: story packages, world definitions, drafts, replay, reads. */
|
|
382
|
+
adminToken?: string;
|
|
383
|
+
/** A project Secret Key (`pchy_sk_…`) for the MACHINE lane: world sessions,
|
|
384
|
+
* turns and trusted events. It also carries the test/live axis. */
|
|
385
|
+
secretKey?: string;
|
|
386
|
+
/** The event-source signing key pair the turns door and `/events` verify.
|
|
387
|
+
* `source` must be the world's own `providerRef`. */
|
|
388
|
+
signing?: {
|
|
389
|
+
source: string;
|
|
390
|
+
keyId: string;
|
|
391
|
+
secret: string;
|
|
392
|
+
};
|
|
393
|
+
baseUrl?: string;
|
|
394
|
+
fetch?: typeof globalThis.fetch;
|
|
395
|
+
/** Per-request timeout. A coordinated turn is a multi-model act — the
|
|
396
|
+
* default is generous on purpose, and lower is usually wrong. */
|
|
397
|
+
timeoutMs?: number;
|
|
398
|
+
}
|
|
399
|
+
export declare class PouchyWorldClient {
|
|
400
|
+
readonly projectId: string;
|
|
401
|
+
private readonly baseUrl;
|
|
402
|
+
private readonly doFetch;
|
|
403
|
+
private readonly adminToken?;
|
|
404
|
+
private readonly secretKey?;
|
|
405
|
+
private readonly signing?;
|
|
406
|
+
private readonly timeoutMs;
|
|
407
|
+
constructor(options: WorldClientOptions);
|
|
408
|
+
listStoryPackages(): Promise<{
|
|
409
|
+
packages: unknown[];
|
|
410
|
+
}>;
|
|
411
|
+
createStoryPackage(content: unknown): Promise<{
|
|
412
|
+
packageId: string;
|
|
413
|
+
revision: number;
|
|
414
|
+
contentHash: string;
|
|
415
|
+
}>;
|
|
416
|
+
getStoryPackage(packageId: string): Promise<unknown>;
|
|
417
|
+
/** Publish the next IMMUTABLE story revision. Idempotent on content: the
|
|
418
|
+
* same bytes return the existing revision rather than minting a twin. */
|
|
419
|
+
publishStoryPackage(packageId: string, content: unknown): Promise<{
|
|
420
|
+
packageId: string;
|
|
421
|
+
revision: number;
|
|
422
|
+
contentHash: string;
|
|
423
|
+
created: boolean;
|
|
424
|
+
}>;
|
|
425
|
+
listWorlds(): Promise<{
|
|
426
|
+
environments: unknown[];
|
|
427
|
+
}>;
|
|
428
|
+
createWorld(definition: unknown): Promise<{
|
|
429
|
+
environmentId: string;
|
|
430
|
+
environmentRevision: number;
|
|
431
|
+
contentHash: string;
|
|
432
|
+
}>;
|
|
433
|
+
getWorld(environmentId: string): Promise<unknown>;
|
|
434
|
+
/** Publish the next world revision. Existing world INSTANCES keep the
|
|
435
|
+
* revision they were created on — a published change reaches new instances
|
|
436
|
+
* only, which is what keeps a running story from changing runtime or rules
|
|
437
|
+
* underneath its players. */
|
|
438
|
+
publishWorld(environmentId: string, definition: unknown): Promise<{
|
|
439
|
+
environmentId: string;
|
|
440
|
+
environmentRevision: number;
|
|
441
|
+
contentHash: string;
|
|
442
|
+
created: boolean;
|
|
443
|
+
}>;
|
|
444
|
+
getWorldState(environmentId: string, worldInstanceId: string): Promise<{
|
|
445
|
+
worldInstanceId: string;
|
|
446
|
+
environmentRevision: number;
|
|
447
|
+
state: Record<string, unknown>;
|
|
448
|
+
}>;
|
|
449
|
+
/** Read back a COMMITTED turn. The recovery path when a response was lost:
|
|
450
|
+
* it re-runs nothing, and a 404 means the turn never committed. */
|
|
451
|
+
getTurn(environmentId: string, worldInstanceId: string, turnId: string): Promise<Record<string, unknown>>;
|
|
452
|
+
/** Verify that the materialized state is what the committed ledger says.
|
|
453
|
+
* Always a dry run — it reports, it never repairs. Pass the previous
|
|
454
|
+
* report's `cursor` to continue a run that came back `incomplete`. */
|
|
455
|
+
replayLedger(environmentId: string, worldInstanceId: string, options?: {
|
|
456
|
+
cursor?: string;
|
|
457
|
+
maxEntries?: number;
|
|
458
|
+
}): Promise<WorldReplayReport>;
|
|
459
|
+
/** Walk a replay to completion, page by page. Bounded by `maxPages` so a
|
|
460
|
+
* caller can never spin: an unfinished walk returns its last report, and
|
|
461
|
+
* the cursor is in it. */
|
|
462
|
+
replayLedgerToEnd(environmentId: string, worldInstanceId: string, options?: {
|
|
463
|
+
maxPages?: number;
|
|
464
|
+
}): Promise<WorldReplayReport>;
|
|
465
|
+
/** The delivery queue for one instance (Batch 7). Statuses, timings, attempt
|
|
466
|
+
* counts, error classes, and which row is blocking its session — never the
|
|
467
|
+
* message bodies. */
|
|
468
|
+
listDeliveries(environmentId: string, worldInstanceId: string, filters?: {
|
|
469
|
+
status?: 'pending' | 'delivering' | 'delivered' | 'retry_wait' | 'dead';
|
|
470
|
+
sessionId?: string;
|
|
471
|
+
turnId?: string;
|
|
472
|
+
cursor?: string;
|
|
473
|
+
limit?: number;
|
|
474
|
+
}): Promise<WorldDeliveryPage>;
|
|
475
|
+
/** One delivery. `includePayload` returns the line itself and writes an audit
|
|
476
|
+
* row naming you — pass it deliberately, not by default. */
|
|
477
|
+
getDelivery(environmentId: string, worldInstanceId: string, deliveryId: string, options?: {
|
|
478
|
+
includePayload?: boolean;
|
|
479
|
+
}): Promise<WorldDeliveryDetail>;
|
|
480
|
+
/** Run one bounded drain now — for the moment after a requeue. */
|
|
481
|
+
drainDeliveries(environmentId: string, worldInstanceId: string): Promise<WorldDrainReport>;
|
|
482
|
+
/** Put a dead letter back in the queue. There is deliberately no payload
|
|
483
|
+
* parameter: the beat was committed by the coordinator, and supplying a
|
|
484
|
+
* different line here would be authoring world history through the delivery
|
|
485
|
+
* plane. Idempotent — an already-queued row is a no-op. */
|
|
486
|
+
requeueDelivery(environmentId: string, worldInstanceId: string, deliveryId: string): Promise<{
|
|
487
|
+
ok: boolean;
|
|
488
|
+
status?: string;
|
|
489
|
+
reason?: string;
|
|
490
|
+
}>;
|
|
491
|
+
/** Turn an APPROVED editorial draft into a versioned production hand-off
|
|
492
|
+
* (Batch 7). Idempotent on content — the same approved review exports to
|
|
493
|
+
* the same id forever, so replaying one is a no-op rather than a second
|
|
494
|
+
* script. `notify` also sends `world.script_approved` to the project's
|
|
495
|
+
* webhooks, carrying lineage and identifiers but never the script body. */
|
|
496
|
+
createApprovedExport(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string, options?: {
|
|
497
|
+
notify?: boolean;
|
|
498
|
+
}): Promise<ApprovedScriptExportRow>;
|
|
499
|
+
listApprovedExports(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string): Promise<{
|
|
500
|
+
exports: unknown[];
|
|
501
|
+
}>;
|
|
502
|
+
/** The next Story Package as a CANDIDATE — validated, and returned rather
|
|
503
|
+
* than published. Publishing it is a separate act by a person, through
|
|
504
|
+
* `createStoryPackage`. Only evidence-origin material becomes canon. */
|
|
505
|
+
deriveStoryPackageCandidate(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string, exportId: string, options?: {
|
|
506
|
+
name?: string;
|
|
507
|
+
summary?: string;
|
|
508
|
+
}): Promise<{
|
|
509
|
+
exportId: string;
|
|
510
|
+
status: 'candidate';
|
|
511
|
+
publishWith: string;
|
|
512
|
+
droppedSuggestions: number;
|
|
513
|
+
content: Record<string, unknown>;
|
|
514
|
+
}>;
|
|
515
|
+
/** Rebuild a dead letter whose text was redacted, FROM THE RECORD (Batch
|
|
516
|
+
* 7.1). Takes no body — nobody types a replacement line and no model
|
|
517
|
+
* regenerates one. Accepted only if the rebuild re-derives the same delivery
|
|
518
|
+
* id and the same content digest captured before the text was destroyed;
|
|
519
|
+
* otherwise it refuses with a code and the row becomes `unrecoverable`,
|
|
520
|
+
* which still blocks the session. */
|
|
521
|
+
rehydrateDelivery(environmentId: string, worldInstanceId: string, deliveryId: string): Promise<{
|
|
522
|
+
ok: boolean;
|
|
523
|
+
status?: string;
|
|
524
|
+
from?: 'ledger' | 'archive';
|
|
525
|
+
error?: WorldRehydrateFailure;
|
|
526
|
+
}>;
|
|
527
|
+
/** Account for a beat that cannot be recovered. The only exit from
|
|
528
|
+
* `unrecoverable`, and it takes NO text: the notice is generated
|
|
529
|
+
* server-side from a fixed catalogue and rendered in the reader's language.
|
|
530
|
+
* The original resolves when the notice LANDS, not when you call this. */
|
|
531
|
+
resolveDeliveryGap(environmentId: string, worldInstanceId: string, deliveryId: string): Promise<{
|
|
532
|
+
ok: boolean;
|
|
533
|
+
noticeDeliveryId?: string;
|
|
534
|
+
created?: boolean;
|
|
535
|
+
noticeKey?: string;
|
|
536
|
+
}>;
|
|
537
|
+
/** Operator metrics for one instance (Batch 6): delivery and turn families,
|
|
538
|
+
* kept apart because "the lines arrived" and "the world played well" are
|
|
539
|
+
* different questions. */
|
|
540
|
+
getWorldMetrics(environmentId: string, worldInstanceId: string): Promise<WorldInstanceMetrics>;
|
|
541
|
+
/** Score the committed history against a quality suite. Deterministic — no
|
|
542
|
+
* model judges the output, so a score is something you can regress. */
|
|
543
|
+
evaluateWorld(environmentId: string, worldInstanceId: string, options?: {
|
|
544
|
+
suite?: 'drama' | 'npc';
|
|
545
|
+
replayVerdict?: string;
|
|
546
|
+
}): Promise<WorldEvalReport>;
|
|
547
|
+
/** Ledger archival. The default action is `plan`, which writes nothing;
|
|
548
|
+
* `execute` copies and never deletes; `prune` is the only call in this
|
|
549
|
+
* client that destroys a row, and it refuses without `confirm` — and again
|
|
550
|
+
* unless the archive verifies and has outlived its retention. */
|
|
551
|
+
archiveLedger(environmentId: string, worldInstanceId: string, options?: {
|
|
552
|
+
action?: 'plan' | 'execute' | 'verify' | 'prune';
|
|
553
|
+
confirm?: boolean;
|
|
554
|
+
}): Promise<Record<string, unknown>>;
|
|
555
|
+
listScriptDrafts(environmentId: string, worldInstanceId: string): Promise<{
|
|
556
|
+
drafts: unknown[];
|
|
557
|
+
}>;
|
|
558
|
+
/** Generate (or return) the draft for this instance's committed history.
|
|
559
|
+
* Idempotent on the ledger range: the same range is the same draft. */
|
|
560
|
+
createScriptDraft(environmentId: string, worldInstanceId: string): Promise<ScriptDraftRow>;
|
|
561
|
+
getScriptDraft(environmentId: string, worldInstanceId: string, draftId: string): Promise<ScriptDraftRow>;
|
|
562
|
+
/** Mark a draft reviewed. The reviewer is the SIGNED-IN human whose token
|
|
563
|
+
* this client carries — never a field in the body. */
|
|
564
|
+
reviewScriptDraft(environmentId: string, worldInstanceId: string, draftId: string, note?: string): Promise<ScriptDraftRow>;
|
|
565
|
+
/** The reviewed draft as standard JSON. Refuses with 409 until a human has
|
|
566
|
+
* reviewed it — that gate is the product, not an obstacle. */
|
|
567
|
+
exportScriptDraft(environmentId: string, worldInstanceId: string, draftId: string): Promise<Record<string, unknown>>;
|
|
568
|
+
/** The EDITORIAL layer over an evidence draft (Batch 6). The evidence draft
|
|
569
|
+
* is deterministic and never changes; this is a model's reading of it, and
|
|
570
|
+
* every line it claims came from the story was re-checked server-side. */
|
|
571
|
+
listEditorialDrafts(environmentId: string, worldInstanceId: string, draftId: string): Promise<{
|
|
572
|
+
editorials: unknown[];
|
|
573
|
+
}>;
|
|
574
|
+
/** Generate (or return) an editorial reading. Idempotent per model + prompt
|
|
575
|
+
* version + input digest, so asking twice is not a second opinion. */
|
|
576
|
+
createEditorialDraft(environmentId: string, worldInstanceId: string, draftId: string, model?: string): Promise<EditorialDraftRow>;
|
|
577
|
+
getEditorialDraft(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string): Promise<EditorialDraftRow>;
|
|
578
|
+
/** Move an editorial draft through its review lifecycle. `exported` is not
|
|
579
|
+
* settable here — `exportEditorialDraft` writes it. */
|
|
580
|
+
setEditorialStatus(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string, status: 'under_review' | 'changes_requested' | 'approved' | 'rejected', note?: string): Promise<EditorialDraftRow>;
|
|
581
|
+
/** One human verdict on one scene. `edited` carries the editor's own text —
|
|
582
|
+
* the only way text in this layer changes after generation. */
|
|
583
|
+
decideEditorialScene(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string, input: {
|
|
584
|
+
sceneId: string;
|
|
585
|
+
decision: 'accepted' | 'edited' | 'rejected';
|
|
586
|
+
editedText?: string;
|
|
587
|
+
note?: string;
|
|
588
|
+
}): Promise<EditorialDraftRow>;
|
|
589
|
+
/** Export an APPROVED editorial draft. `preview: true` reads the same payload
|
|
590
|
+
* without stamping it exported, so a reviewer can see what would leave. */
|
|
591
|
+
exportEditorialDraft(environmentId: string, worldInstanceId: string, draftId: string, editorialId: string, options?: {
|
|
592
|
+
preview?: boolean;
|
|
593
|
+
}): Promise<Record<string, unknown>>;
|
|
594
|
+
/** Mint a world SESSION for one end user in one role. The returned token is
|
|
595
|
+
* what your own frontend uses with the companion SDK; it is scoped to that
|
|
596
|
+
* instance and role and carries no project credential. */
|
|
597
|
+
createWorldSession(input: {
|
|
598
|
+
environment: string;
|
|
599
|
+
role: string;
|
|
600
|
+
externalUserId: string;
|
|
601
|
+
worldInstance?: string;
|
|
602
|
+
requestId?: string;
|
|
603
|
+
}): Promise<Record<string, unknown>>;
|
|
604
|
+
/** Drive ONE coordinated beat. `turnId` is the idempotency key: re-send the
|
|
605
|
+
* same one to retry, mint a new one for a new beat. */
|
|
606
|
+
runTurn(input: {
|
|
607
|
+
environmentId: string;
|
|
608
|
+
worldInstanceId: string;
|
|
609
|
+
turnId?: string;
|
|
610
|
+
text: string;
|
|
611
|
+
sourceRoleId?: string;
|
|
612
|
+
traceId?: string;
|
|
613
|
+
}): Promise<WorldTurnResult>;
|
|
614
|
+
/** Send a trusted EVENT into a world. On a `coordinated` world this becomes
|
|
615
|
+
* one coordinator turn; on an `actor` world it wakes each subscribed role.
|
|
616
|
+
* Either way `eventId` is the dedupe key — re-send it freely. */
|
|
617
|
+
sendEvent(input: {
|
|
618
|
+
name: string;
|
|
619
|
+
eventId?: string;
|
|
620
|
+
environment: string;
|
|
621
|
+
worldInstance: string;
|
|
622
|
+
data: Record<string, unknown>;
|
|
623
|
+
schemaVersion?: number;
|
|
624
|
+
occurredAt?: number;
|
|
625
|
+
}): Promise<Record<string, unknown>>;
|
|
626
|
+
private owner;
|
|
627
|
+
/** The machine lane: Secret Key AND a source signature over the EXACT bytes
|
|
628
|
+
* being sent — the two proofs the world requires of a backend. */
|
|
629
|
+
private signed;
|
|
630
|
+
private request;
|
|
631
|
+
}
|