@seriousgaming/sdk 0.0.0-stage → 1.0.0-alpha.2
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/LICENSE +202 -0
- package/NOTICE +1 -0
- package/README.md +33 -2
- package/lib/build/callbacks.d.ts +998 -0
- package/lib/build/callbacks.js +32 -0
- package/lib/build/context-merge.d.ts +59 -0
- package/lib/build/context-merge.js +118 -0
- package/lib/build/document.d.ts +36 -0
- package/lib/build/document.js +8 -0
- package/lib/build/encoding.d.ts +26 -0
- package/lib/build/encoding.js +78 -0
- package/lib/build/endpoints.d.ts +421 -0
- package/lib/build/endpoints.js +46 -0
- package/lib/build/hooks.d.ts +74 -0
- package/lib/build/hooks.js +1 -0
- package/lib/build/index.d.ts +18 -0
- package/lib/build/index.js +16 -0
- package/lib/build/llm.d.ts +166 -0
- package/lib/build/llm.js +17 -0
- package/lib/build/resource-schema.d.ts +1048 -0
- package/lib/build/resource-schema.js +7 -0
- package/lib/build/scheduling.d.ts +112 -0
- package/lib/build/scheduling.js +217 -0
- package/lib/build/seed.d.ts +51 -0
- package/lib/build/seed.js +89 -0
- package/lib/build/signatures.d.ts +372 -0
- package/lib/build/signatures.js +1 -0
- package/lib/build/speech.d.ts +102 -0
- package/lib/build/speech.js +11 -0
- package/lib/build/tick-validation.d.ts +23 -0
- package/lib/build/tick-validation.js +96 -0
- package/lib/build/timer.d.ts +74 -0
- package/lib/build/timer.js +1 -0
- package/lib/build/validators.d.ts +116 -0
- package/lib/build/validators.js +1 -0
- package/lib/commands.d.ts +357 -0
- package/lib/commands.js +14 -0
- package/lib/e2e.d.ts +238 -0
- package/lib/e2e.js +248 -0
- package/lib/projection.d.ts +266 -0
- package/lib/projection.js +23 -0
- package/lib/testing/index.d.ts +13 -0
- package/lib/testing/index.js +13 -0
- package/lib/testing/logic-args.d.ts +177 -0
- package/lib/testing/logic-args.js +229 -0
- package/lib/testing/stub-services.d.ts +41 -0
- package/lib/testing/stub-services.js +176 -0
- package/lib/ui/CrossTeamDataContext.d.ts +36 -0
- package/lib/ui/CrossTeamDataContext.js +32 -0
- package/lib/ui/PresentationContext.d.ts +72 -0
- package/lib/ui/PresentationContext.js +13 -0
- package/lib/ui/PresentationWrapper.d.ts +12 -0
- package/lib/ui/PresentationWrapper.js +78 -0
- package/lib/ui/SessionUserInterfaceCallbacksContext.d.ts +3 -0
- package/lib/ui/SessionUserInterfaceCallbacksContext.js +7 -0
- package/lib/ui/SessionUserInterfaceDataContext.d.ts +103 -0
- package/lib/ui/SessionUserInterfaceDataContext.js +17 -0
- package/lib/ui/SessionUserInterfaceHookFailuresContext.d.ts +44 -0
- package/lib/ui/SessionUserInterfaceHookFailuresContext.js +15 -0
- package/lib/ui/SessionUserInterfaceHostOverlaysContext.d.ts +23 -0
- package/lib/ui/SessionUserInterfaceHostOverlaysContext.js +23 -0
- package/lib/ui/SessionUserInterfaceLocaleContext.d.ts +11 -0
- package/lib/ui/SessionUserInterfaceLocaleContext.js +13 -0
- package/lib/ui/SessionUserInterfaceMetaContext.d.ts +39 -0
- package/lib/ui/SessionUserInterfaceMetaContext.js +13 -0
- package/lib/ui/SessionUserInterfaceParticipantsContext.d.ts +12 -0
- package/lib/ui/SessionUserInterfaceParticipantsContext.js +16 -0
- package/lib/ui/SessionUserInterfaceTeamConfigurationContext.d.ts +53 -0
- package/lib/ui/SessionUserInterfaceTeamConfigurationContext.js +23 -0
- package/lib/ui/SessionUserInterfaceTeamLinksContext.d.ts +56 -0
- package/lib/ui/SessionUserInterfaceTeamLinksContext.js +12 -0
- package/lib/ui/SessionUserInterfaceTeamsContext.d.ts +13 -0
- package/lib/ui/SessionUserInterfaceTeamsContext.js +13 -0
- package/lib/ui/SessionUserInterfaceThemeContext.d.ts +9 -0
- package/lib/ui/SessionUserInterfaceThemeContext.js +7 -0
- package/lib/ui/SessionUserInterfaceThemePreferenceContext.d.ts +3 -0
- package/lib/ui/SessionUserInterfaceThemePreferenceContext.js +7 -0
- package/lib/ui/SessionUserInterfaceWalkthroughLinkContext.d.ts +42 -0
- package/lib/ui/SessionUserInterfaceWalkthroughLinkContext.js +26 -0
- package/lib/ui/SessionUserInterfaceWrapper.d.ts +8 -0
- package/lib/ui/SessionUserInterfaceWrapper.js +321 -0
- package/lib/ui/SettingsEditorCallbacksContext.d.ts +3 -0
- package/lib/ui/SettingsEditorCallbacksContext.js +7 -0
- package/lib/ui/SettingsEditorContext.d.ts +33 -0
- package/lib/ui/SettingsEditorContext.js +16 -0
- package/lib/ui/SettingsEditorLocaleContext.d.ts +13 -0
- package/lib/ui/SettingsEditorLocaleContext.js +15 -0
- package/lib/ui/SettingsEditorThemeContext.d.ts +9 -0
- package/lib/ui/SettingsEditorThemeContext.js +7 -0
- package/lib/ui/SettingsEditorThemePreferenceContext.d.ts +3 -0
- package/lib/ui/SettingsEditorThemePreferenceContext.js +7 -0
- package/lib/ui/SettingsEditorWrapper.d.ts +13 -0
- package/lib/ui/SettingsEditorWrapper.js +74 -0
- package/lib/ui/UserInterfaceCallbacksContext.d.ts +3 -0
- package/lib/ui/UserInterfaceCallbacksContext.js +7 -0
- package/lib/ui/UserInterfaceDataContext.d.ts +78 -0
- package/lib/ui/UserInterfaceDataContext.js +11 -0
- package/lib/ui/UserInterfaceLocaleContext.d.ts +13 -0
- package/lib/ui/UserInterfaceLocaleContext.js +15 -0
- package/lib/ui/UserInterfaceOwnTeamContext.d.ts +25 -0
- package/lib/ui/UserInterfaceOwnTeamContext.js +26 -0
- package/lib/ui/UserInterfaceOwnTeamJoinLinkContext.d.ts +52 -0
- package/lib/ui/UserInterfaceOwnTeamJoinLinkContext.js +13 -0
- package/lib/ui/UserInterfaceParticipantContext.d.ts +16 -0
- package/lib/ui/UserInterfaceParticipantContext.js +24 -0
- package/lib/ui/UserInterfaceParticipantsContext.d.ts +12 -0
- package/lib/ui/UserInterfaceParticipantsContext.js +13 -0
- package/lib/ui/UserInterfaceRunKindContext.d.ts +31 -0
- package/lib/ui/UserInterfaceRunKindContext.js +31 -0
- package/lib/ui/UserInterfaceTeamsContext.d.ts +12 -0
- package/lib/ui/UserInterfaceTeamsContext.js +13 -0
- package/lib/ui/UserInterfaceThemeContext.d.ts +9 -0
- package/lib/ui/UserInterfaceThemeContext.js +7 -0
- package/lib/ui/UserInterfaceThemePreferenceContext.d.ts +3 -0
- package/lib/ui/UserInterfaceThemePreferenceContext.js +7 -0
- package/lib/ui/UserInterfaceTimerContext.d.ts +10 -0
- package/lib/ui/UserInterfaceTimerContext.js +12 -0
- package/lib/ui/UserInterfaceValidationContext.d.ts +25 -0
- package/lib/ui/UserInterfaceValidationContext.js +14 -0
- package/lib/ui/UserInterfaceViewerRoleContext.d.ts +14 -0
- package/lib/ui/UserInterfaceViewerRoleContext.js +17 -0
- package/lib/ui/UserInterfaceWrapper.d.ts +8 -0
- package/lib/ui/UserInterfaceWrapper.js +219 -0
- package/lib/ui/expectedParentOrigin.d.ts +10 -0
- package/lib/ui/expectedParentOrigin.js +32 -0
- package/lib/ui/index.d.ts +50 -0
- package/lib/ui/index.js +52 -0
- package/lib/ui/internal/SurfaceGate.d.ts +19 -0
- package/lib/ui/internal/SurfaceGate.js +17 -0
- package/lib/ui/internal/SurfaceScope.d.ts +17 -0
- package/lib/ui/internal/SurfaceScope.js +16 -0
- package/lib/ui/internal/createContexts.d.ts +50 -0
- package/lib/ui/internal/createContexts.js +32 -0
- package/lib/ui/internal/document-attributes.d.ts +9 -0
- package/lib/ui/internal/document-attributes.js +87 -0
- package/lib/ui/internal/surface-timer.d.ts +3 -0
- package/lib/ui/internal/surface-timer.js +11 -0
- package/lib/ui/internal/useLatestRef.d.ts +7 -0
- package/lib/ui/internal/useLatestRef.js +11 -0
- package/lib/ui/internal/useOptimisticMerge.d.ts +19 -0
- package/lib/ui/internal/useOptimisticMerge.js +28 -0
- package/lib/ui/internal/useSurfaceControls.d.ts +52 -0
- package/lib/ui/internal/useSurfaceControls.js +19 -0
- package/lib/ui/internal/useTypedPastState.d.ts +17 -0
- package/lib/ui/internal/useTypedPastState.js +17 -0
- package/lib/ui/mutation-commit.d.ts +32 -0
- package/lib/ui/mutation-commit.js +1 -0
- package/lib/ui/mutation-outcome.d.ts +66 -0
- package/lib/ui/mutation-outcome.js +49 -0
- package/lib/ui/request-id.d.ts +18 -0
- package/lib/ui/request-id.js +44 -0
- package/lib/ui/testing.d.ts +365 -0
- package/lib/ui/testing.js +572 -0
- package/lib/ui/tick-snapshot.d.ts +28 -0
- package/lib/ui/tick-snapshot.js +34 -0
- package/lib/ui/ui-protocol.d.ts +799 -0
- package/lib/ui/ui-protocol.js +50 -0
- package/lib/ui/useAsset.d.ts +80 -0
- package/lib/ui/useAsset.js +139 -0
- package/lib/ui/useEndpointCallbacks.d.ts +31 -0
- package/lib/ui/useEndpointCallbacks.js +68 -0
- package/lib/ui/useHostChannel.d.ts +90 -0
- package/lib/ui/useHostChannel.js +338 -0
- package/lib/ui/useMutationReporter.d.ts +100 -0
- package/lib/ui/useMutationReporter.js +97 -0
- package/lib/ui/usePastState.d.ts +39 -0
- package/lib/ui/usePastState.js +63 -0
- package/lib/ui/useViewerStorage.d.ts +54 -0
- package/lib/ui/useViewerStorage.js +141 -0
- package/package.json +89 -3
|
@@ -0,0 +1,998 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback interfaces for SDK components
|
|
3
|
+
*
|
|
4
|
+
* Defines the callbacks available to the engine, user interfaces, and configuration
|
|
5
|
+
* user interfaces. LLM-specific types are imported from ./llm.
|
|
6
|
+
*
|
|
7
|
+
* Asset references — every callback below that takes or returns a file
|
|
8
|
+
* identifier uses the unified asset URI scheme:
|
|
9
|
+
*
|
|
10
|
+
* `asset:<path>` — package-bundled file. The path is relative to the
|
|
11
|
+
* active scenario's `assets/` directory.
|
|
12
|
+
* `blob:<id>` — runtime-uploaded file. The id is whatever the runtime
|
|
13
|
+
* assigns when the blob was stored.
|
|
14
|
+
*
|
|
15
|
+
* Any string starting with `asset:` or `blob:` is unambiguously an asset
|
|
16
|
+
* reference. No other strings are.
|
|
17
|
+
*/
|
|
18
|
+
import { ExtractTextFunction } from './document.js';
|
|
19
|
+
import { LLMGenerateFunction } from './llm.js';
|
|
20
|
+
import { ApiUse } from './resource-schema.js';
|
|
21
|
+
import { GenerateSpeechFunction } from './speech.js';
|
|
22
|
+
/**
|
|
23
|
+
* Shape returned by `readAsset` for both `asset:` and `blob:` references.
|
|
24
|
+
*/
|
|
25
|
+
export interface ReadAssetResponse {
|
|
26
|
+
/** Raw bytes of the referenced file. */
|
|
27
|
+
data: Uint8Array;
|
|
28
|
+
/** MIME type of the referenced file (e.g. `application/pdf`, `image/png`). */
|
|
29
|
+
mimeType: string;
|
|
30
|
+
/**
|
|
31
|
+
* Display name of the referenced file. For `blob:` references this is the
|
|
32
|
+
* original name supplied at upload time. For `asset:` references this is
|
|
33
|
+
* the basename of the path.
|
|
34
|
+
*/
|
|
35
|
+
name: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Who a team is: the fields every team entry in the SDK carries (§5.6).
|
|
39
|
+
*
|
|
40
|
+
* One shape wherever a team is listed or addressed, so a renderer written for
|
|
41
|
+
* one list serves every other: the team lists a surface receives, a cross-team
|
|
42
|
+
* or past-state row, an endpoint's `team` and `session.teams`, the state
|
|
43
|
+
* projection and a presentation's teams all extend it.
|
|
44
|
+
*/
|
|
45
|
+
export interface TeamInfo {
|
|
46
|
+
/** Stable `team-N` id (§5.6). */
|
|
47
|
+
teamId: string;
|
|
48
|
+
/**
|
|
49
|
+
* Platform-assigned, session-stable team index. Assigned at team creation
|
|
50
|
+
* (dense 0..N-1 at session start) and never reassigned: removals leave a
|
|
51
|
+
* gap, later-added teams take the lowest unused index. The same team has
|
|
52
|
+
* the same index everywhere it appears. `teams.defaultNames` entries are
|
|
53
|
+
* applied by this index.
|
|
54
|
+
*/
|
|
55
|
+
index: number;
|
|
56
|
+
/** The team's display name, resolved per §5.4 (a facilitator rename included). */
|
|
57
|
+
name: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* One team's state, live. Returned per team by the `useUserInterfaceCrossTeamData` hook.
|
|
61
|
+
*
|
|
62
|
+
* {@link PastStateEntry} is this plus *when*, which is the only thing that
|
|
63
|
+
* actually differs between watching a team now and reviewing it later. Sharing
|
|
64
|
+
* the base is what lets one cell renderer serve both: `entry.name` and
|
|
65
|
+
* `entry.context.ledger` read the same either way.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* One team's state at one committed past tick, as returned by `getPastState`.
|
|
69
|
+
*
|
|
70
|
+
* One entry per (tick, team) pair, which is what lets a single call answer
|
|
71
|
+
* every historical question the surfaces used to need four for: one round for
|
|
72
|
+
* one team, a range for one team, one round across every team, a range across
|
|
73
|
+
* every team. Group by `tick` for a results table, filter by `teamId` for a
|
|
74
|
+
* trend line.
|
|
75
|
+
*
|
|
76
|
+
* `context` is composed exactly like the live `data` a surface receives — the
|
|
77
|
+
* team's own context with the shared store as it stood at that tick folded in
|
|
78
|
+
* — so a review screen renders a historical board through the surface's
|
|
79
|
+
* *current* config, and is then narrowed to `capabilityIds` when the query
|
|
80
|
+
* named any.
|
|
81
|
+
*
|
|
82
|
+
* Entries are point-in-time and immutable: the value for a given tick never
|
|
83
|
+
* changes within a session (an undo or reset removes it, it does not rewrite
|
|
84
|
+
* it), so a consumer can cache what it has fetched.
|
|
85
|
+
*/
|
|
86
|
+
export interface TeamStateEntry<T = Record<string, unknown>> extends TeamInfo {
|
|
87
|
+
/**
|
|
88
|
+
* The team's context, composed like the live `data` a surface receives and
|
|
89
|
+
* narrowed to what this surface may see: its declared `crossTeam` reads for
|
|
90
|
+
* another team, its whole declared set for its own.
|
|
91
|
+
*/
|
|
92
|
+
context: T;
|
|
93
|
+
}
|
|
94
|
+
export interface PastStateEntry<T = Record<string, unknown>> extends TeamStateEntry<T> {
|
|
95
|
+
/**
|
|
96
|
+
* The snapshot's number: snapshot N is the state after tick N (after
|
|
97
|
+
* `Init`, for snapshot 0), with the decisions round N + 1 entered on it.
|
|
98
|
+
*/
|
|
99
|
+
tick: number;
|
|
100
|
+
/** When the snapshot was taken (epoch milliseconds). */
|
|
101
|
+
recordedAt: number;
|
|
102
|
+
/** Whether the tick controller had signalled game complete at that tick. */
|
|
103
|
+
complete: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* What to read from the past.
|
|
107
|
+
*
|
|
108
|
+
* Both tick bounds are inclusive and `toTick` is clamped to the last committed
|
|
109
|
+
* past tick, so `{ fromTick: 0, toTick: completedTicks }` means 'everything
|
|
110
|
+
* reviewable' with no head arithmetic. Omitting `toTick` reads the single tick
|
|
111
|
+
* `fromTick`.
|
|
112
|
+
*/
|
|
113
|
+
export interface PastStateQuery<T = Record<string, unknown>> {
|
|
114
|
+
/** First tick to read, inclusive. */
|
|
115
|
+
fromTick: number;
|
|
116
|
+
/**
|
|
117
|
+
* Last tick to read, inclusive; clamped to the last committed past tick.
|
|
118
|
+
* Omit to read only `fromTick`.
|
|
119
|
+
*/
|
|
120
|
+
toTick?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Which teams to read.
|
|
123
|
+
*
|
|
124
|
+
* `'own'` (the default) is the caller's own team on a game surface. `'all'`
|
|
125
|
+
* is every team. A team id reads that one team, which is how a session
|
|
126
|
+
* surface addresses a team it is not scoped to. Team ids are opaque and
|
|
127
|
+
* stable: take one from `SessionData` or `setTeamList`, never build one
|
|
128
|
+
* from an index. A runtime never assigns `'own'` or `'all'` as a team id
|
|
129
|
+
* (§5.6), so the sentinels cannot collide with one.
|
|
130
|
+
*/
|
|
131
|
+
teams?: 'own' | 'all' | string;
|
|
132
|
+
/**
|
|
133
|
+
* Capabilities to include in each returned `context`. Omit for the
|
|
134
|
+
* surface's whole declared set.
|
|
135
|
+
*
|
|
136
|
+
* This is what makes a range read cheap: a results chart over sixty rounds
|
|
137
|
+
* wants one field per round, not sixty whole boards, and a growing log kept
|
|
138
|
+
* in context to avoid the round trips is delivered to every surface on
|
|
139
|
+
* every push for the rest of the session.
|
|
140
|
+
*/
|
|
141
|
+
capabilityIds?: (keyof T & string)[];
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A participant the platform tracks for some team in the session (§13.1).
|
|
145
|
+
*
|
|
146
|
+
* The one participant type, wherever a participant appears: the caller's own
|
|
147
|
+
* identity (`useUserInterfaceParticipant`), the live roster for the caller's
|
|
148
|
+
* team (`useUserInterfaceParticipants`) and for every team
|
|
149
|
+
* (`useSessionUserInterfaceParticipants`), the `getAllTeamsParticipants`
|
|
150
|
+
* callback, hook payloads, a validator's `actor`, an endpoint's roster and
|
|
151
|
+
* the state projection. The `teamId` groups a flat list by team; join it
|
|
152
|
+
* against the team list for each team's display name and index.
|
|
153
|
+
*
|
|
154
|
+
* `connected` is separate from existence: a participant record lives for the
|
|
155
|
+
* whole session once created, so an absent participant is `connected: false`
|
|
156
|
+
* rather than a missing entry.
|
|
157
|
+
*/
|
|
158
|
+
export interface Participant {
|
|
159
|
+
/** Stable participant id, unique within its team. */
|
|
160
|
+
id: string;
|
|
161
|
+
/** The participant's display name. */
|
|
162
|
+
name: string;
|
|
163
|
+
/** Zero-based join order within the team. */
|
|
164
|
+
index: number;
|
|
165
|
+
/**
|
|
166
|
+
* True while the participant has a live connection to the platform.
|
|
167
|
+
*
|
|
168
|
+
* Informative wherever it appears, including in a hook payload: a
|
|
169
|
+
* facilitator can move a participant who is offline, so a `participantMove`
|
|
170
|
+
* hook sees `false` there and can act on it.
|
|
171
|
+
*/
|
|
172
|
+
connected: boolean;
|
|
173
|
+
/** The team this participant belongs to (e.g. `team-1`). */
|
|
174
|
+
teamId: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The host's theme preference. `'system'` means the host follows the
|
|
178
|
+
* operating system's colour scheme; `'light'` and `'dark'` are explicit
|
|
179
|
+
* choices. Distinct from the resolved palette delivered to UIs through the
|
|
180
|
+
* theme channel — a UI reading `'system'` knows the host has not overridden
|
|
181
|
+
* the OS, which the resolved light/dark mode alone cannot tell it.
|
|
182
|
+
*/
|
|
183
|
+
export type ThemePreference = 'light' | 'dark' | 'system';
|
|
184
|
+
/**
|
|
185
|
+
* A host-rendered overlay a session surface may ask the host to open
|
|
186
|
+
* (SPECIFICATION.md §6.4).
|
|
187
|
+
*
|
|
188
|
+
* These stay host-owned: the host renders them in its own chrome, above the
|
|
189
|
+
* surface, where the surface can neither restyle nor suppress them. Only the
|
|
190
|
+
* trigger is delegated, so a facilitator in front of a surface that owns the
|
|
191
|
+
* whole canvas does not have to leave it to reach the guide or a print sheet.
|
|
192
|
+
*
|
|
193
|
+
* The enum is extensible. Treat an unknown member as an overlay you cannot
|
|
194
|
+
* offer rather than an error, and render a control only for a member the host
|
|
195
|
+
* actually reports (see `openHostOverlay`).
|
|
196
|
+
*/
|
|
197
|
+
export type HostOverlay = 'facilitator-guide' | 'print-sheets';
|
|
198
|
+
/**
|
|
199
|
+
* Every callback a runtime may offer logic: the engine, hooks, tick
|
|
200
|
+
* controllers and endpoints.
|
|
201
|
+
*
|
|
202
|
+
* The four `ApiUse` callbacks (`generateText`, `extractText`,
|
|
203
|
+
* `generateSpeech`, `writeBlob`) are services, gated by the resource's `uses`
|
|
204
|
+
* (§6.11). This interface is the superset, which is why three of them carry a
|
|
205
|
+
* `?`: whether one is there depends on what the resource declared. Type a
|
|
206
|
+
* resource's callbacks with its generated `Callbacks` ({@link LogicCallbacks})
|
|
207
|
+
* instead, where a declared service is present, so it is called without `?.`
|
|
208
|
+
* or `!`, and an undeclared one is absent from the type (and throws
|
|
209
|
+
* {@link UndeclaredApiUseError} if reached anyway). A runtime that cannot
|
|
210
|
+
* provide a service a package declares refuses the package at load.
|
|
211
|
+
*/
|
|
212
|
+
export interface SignatureCallbacks {
|
|
213
|
+
/**
|
|
214
|
+
* Generate text using an LLM
|
|
215
|
+
*
|
|
216
|
+
* Examples:
|
|
217
|
+
* ```typescript
|
|
218
|
+
* // Simple string prompt
|
|
219
|
+
* const response = await callbacks.generateText("Summarize this: " + text);
|
|
220
|
+
*
|
|
221
|
+
* // Options object with prompt
|
|
222
|
+
* const response = await callbacks.generateText({
|
|
223
|
+
* prompt: "Summarize this: " + text
|
|
224
|
+
* });
|
|
225
|
+
*
|
|
226
|
+
* // With conversation history
|
|
227
|
+
* const response = await callbacks.generateText({
|
|
228
|
+
* messages: [
|
|
229
|
+
* { role: 'user', content: 'What is 2+2?' },
|
|
230
|
+
* { role: 'assistant', content: '4' },
|
|
231
|
+
* { role: 'user', content: 'What about 3+3?' }
|
|
232
|
+
* ]
|
|
233
|
+
* });
|
|
234
|
+
*
|
|
235
|
+
* // Structured output with JSON Schema
|
|
236
|
+
* const response = await callbacks.generateText({
|
|
237
|
+
* prompt: "Extract the name and age from: John is 30 years old",
|
|
238
|
+
* jsonSchema: {
|
|
239
|
+
* type: 'object',
|
|
240
|
+
* properties: {
|
|
241
|
+
* name: { type: 'string' },
|
|
242
|
+
* age: { type: 'integer' }
|
|
243
|
+
* },
|
|
244
|
+
* required: ['name', 'age']
|
|
245
|
+
* }
|
|
246
|
+
* });
|
|
247
|
+
*
|
|
248
|
+
* // With custom system prompt
|
|
249
|
+
* const response = await callbacks.generateText({
|
|
250
|
+
* prompt: "Translate to Spanish: Hello",
|
|
251
|
+
* systemPrompt: "You are a professional translator. Respond only with the translation."
|
|
252
|
+
* });
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
generateText: LLMGenerateFunction;
|
|
256
|
+
/**
|
|
257
|
+
* Read the bytes of an asset by reference. Accepts either an
|
|
258
|
+
* `asset:<path>` reference (resolved against the active scenario's
|
|
259
|
+
* `assets/` directory) or a `blob:<id>` reference (resolved against the
|
|
260
|
+
* runtime's blob store).
|
|
261
|
+
*
|
|
262
|
+
* Example:
|
|
263
|
+
* ```typescript
|
|
264
|
+
* const file = await callbacks.readAsset("blob:abc123");
|
|
265
|
+
* const briefing = await callbacks.readAsset("asset:briefing.pdf");
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
readAsset: (reference: string) => Promise<ReadAssetResponse>;
|
|
269
|
+
/**
|
|
270
|
+
* Read a committed past tick, without keeping a copy of it in context.
|
|
271
|
+
*
|
|
272
|
+
* The same history a surface reads through its own `getPastState` (§12.5),
|
|
273
|
+
* offered to logic on the same terms. An endpoint answering a facilitator's
|
|
274
|
+
* 'how has this team's profit moved' has no other route to it: unlike a
|
|
275
|
+
* surface, it cannot ask a UI to fetch the rounds and pass them in, because
|
|
276
|
+
* the caller it exists for may be an agent with no surface at all.
|
|
277
|
+
*
|
|
278
|
+
* It is also what lets an engine stop carrying a round-keyed ledger. A
|
|
279
|
+
* value the next round needs had to live in context, where it rides every
|
|
280
|
+
* push to every participant for the rest of the session (§12.4.2); read it
|
|
281
|
+
* back from here instead and it rides nothing.
|
|
282
|
+
*
|
|
283
|
+
* **What is reachable.** Every round committed before this call into logic
|
|
284
|
+
* began. For an engine that is every round strictly before the one its tick
|
|
285
|
+
* is closing — the round being closed is the board in `context`, and it is
|
|
286
|
+
* not recorded until the tick commits. A hook or endpoint runs outside a
|
|
287
|
+
* tick and reaches the head. Ticks outside that are omitted rather than
|
|
288
|
+
* returned as holes, exactly as for a surface, so a short result is normal.
|
|
289
|
+
*
|
|
290
|
+
* **Whose rounds.** `teams: 'own'` (the default) is the team this call is
|
|
291
|
+
* scoped to. A session-scoped endpoint (§6.12) has no team of its own, so
|
|
292
|
+
* `'own'` reads as the whole session there, which is the rule a session
|
|
293
|
+
* surface already follows. Entries carry the capabilities this resource
|
|
294
|
+
* declared in `reads` and `writes`, projected exactly as its live context
|
|
295
|
+
* is (§7.4).
|
|
296
|
+
*
|
|
297
|
+
* Not gated by `uses`: it reads state this resource is already entitled to
|
|
298
|
+
* see, one tick further back.
|
|
299
|
+
*
|
|
300
|
+
* Always present, like `readAsset`, the other callback `uses` does not
|
|
301
|
+
* gate.
|
|
302
|
+
*
|
|
303
|
+
* Example:
|
|
304
|
+
* ```typescript
|
|
305
|
+
* // what this team submitted last round
|
|
306
|
+
* const [previous] = await callbacks.getPastState<Context>({
|
|
307
|
+
* fromTick: meta.tick - 1,
|
|
308
|
+
* });
|
|
309
|
+
*
|
|
310
|
+
* // one field across every completed round, rather than a ledger in context
|
|
311
|
+
* const trend = await callbacks.getPastState<Context>({
|
|
312
|
+
* fromTick: 0,
|
|
313
|
+
* toTick: meta.tick - 1,
|
|
314
|
+
* capabilityIds: ['profit'],
|
|
315
|
+
* });
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
getPastState: <T = Record<string, unknown>>(query: PastStateQuery<T>) => Promise<PastStateEntry<T>[]>;
|
|
319
|
+
/**
|
|
320
|
+
* Extract text content from a document referenced under the unified
|
|
321
|
+
* asset URI scheme. Accepts either an `asset:` or `blob:` reference.
|
|
322
|
+
* Returns the extracted text, suitable for passing to an LLM.
|
|
323
|
+
*
|
|
324
|
+
* Currently supports PDF files.
|
|
325
|
+
*
|
|
326
|
+
* Example:
|
|
327
|
+
* ```typescript
|
|
328
|
+
* const result = await callbacks.extractText("blob:abc123");
|
|
329
|
+
* const summary = await callbacks.generateText("Summarize: " + result.content);
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
extractText?: ExtractTextFunction;
|
|
333
|
+
/**
|
|
334
|
+
* Synthesise speech from text. Returns a `blob:`-prefixed reference for
|
|
335
|
+
* the generated audio, which can be played in a UI via `getAssetUrl` or
|
|
336
|
+
* read in the engine via `readAsset`.
|
|
337
|
+
*
|
|
338
|
+
* Example:
|
|
339
|
+
* ```typescript
|
|
340
|
+
* const audio = await callbacks.generateSpeech({
|
|
341
|
+
* text: 'Welcome to the simulation.',
|
|
342
|
+
* voice: 'narrator',
|
|
343
|
+
* });
|
|
344
|
+
* // audio.reference can be passed to a UI for playback
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
generateSpeech?: GenerateSpeechFunction;
|
|
348
|
+
/**
|
|
349
|
+
* Write raw bytes to the runtime blob store. Returns a `blob:`-prefixed
|
|
350
|
+
* reference that can be returned to a caller (e.g. an endpoint response)
|
|
351
|
+
* and later resolved via `getAssetUrl`, `readAsset`, or `extractText`.
|
|
352
|
+
*
|
|
353
|
+
* The optional `options.retention` controls when the blob is deleted: `'game'`
|
|
354
|
+
* (default) blobs are wiped on game reset; `'settings'` blobs survive resets
|
|
355
|
+
* and are wiped only when the session is re-initialised.
|
|
356
|
+
*
|
|
357
|
+
* Example:
|
|
358
|
+
* ```typescript
|
|
359
|
+
* const reference = await callbacks.writeBlob(buffer, {
|
|
360
|
+
* name: 'summary.pdf',
|
|
361
|
+
* mimeType: 'application/pdf',
|
|
362
|
+
* });
|
|
363
|
+
* return { reportRef: reference };
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
writeBlob?: (data: Uint8Array | ArrayBuffer, options: {
|
|
367
|
+
name: string;
|
|
368
|
+
mimeType: string;
|
|
369
|
+
retention?: 'settings' | 'game';
|
|
370
|
+
}) => Promise<string>;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Callbacks available to user interface components
|
|
374
|
+
*/
|
|
375
|
+
export interface UserInterfaceCallbacks {
|
|
376
|
+
/**
|
|
377
|
+
* Request the game to advance to the next tick.
|
|
378
|
+
* Equivalent to the player clicking the next-tick button.
|
|
379
|
+
*
|
|
380
|
+
* Note this does not submit anything: writes reach the team's context when
|
|
381
|
+
* `setData` is called, not when a tick is requested. Advancing the tick
|
|
382
|
+
* runs the engine over what is already there.
|
|
383
|
+
*
|
|
384
|
+
* Returns a promise that resolves once the tick has finished and context is
|
|
385
|
+
* updated, and rejects with a `MutationError` when the tick cannot run (e.g.
|
|
386
|
+
* the tick limit is reached). Safe to ignore for fire-and-forget.
|
|
387
|
+
*
|
|
388
|
+
* Example:
|
|
389
|
+
* ```typescript
|
|
390
|
+
* await callbacks.requestTick();
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
requestTick: () => Promise<void>;
|
|
394
|
+
/**
|
|
395
|
+
* Rename the team this surface belongs to.
|
|
396
|
+
*
|
|
397
|
+
* Names no team, because there is nothing to name: the host resolves which
|
|
398
|
+
* team the caller is on from its own bookkeeping, so a surface can only ever
|
|
399
|
+
* rename its own. The new name is the team's real name everywhere, reaching
|
|
400
|
+
* the host's own chrome and print sheets as well as every game surface, so a
|
|
401
|
+
* game has no reason to keep a display name of its own alongside it.
|
|
402
|
+
*
|
|
403
|
+
* Available on every player surface, participants and observers alike, with
|
|
404
|
+
* nothing to declare. The facilitator can rename the same team at the same
|
|
405
|
+
* moment from the session surface, so read the result back from
|
|
406
|
+
* `useUserInterfaceOwnTeam()` rather than trusting the write: the last change wins and the
|
|
407
|
+
* pushed value is the only authority.
|
|
408
|
+
*
|
|
409
|
+
* Resolves once the new roster is persisted and broadcast. Rejects with a
|
|
410
|
+
* `MutationError` when the host refuses: `denied` for a frozen session or a
|
|
411
|
+
* name it will not accept (blank, or past the host's length cap), which is
|
|
412
|
+
* terminal, and `error` for anything transient.
|
|
413
|
+
*
|
|
414
|
+
* Example:
|
|
415
|
+
* ```typescript
|
|
416
|
+
* await callbacks.renameOwnTeam('The Cartographers');
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
renameOwnTeam: (name: string) => Promise<void>;
|
|
420
|
+
/**
|
|
421
|
+
* Upload a file as a blob. Returns a `blob:`-prefixed reference that can
|
|
422
|
+
* be stored in context/config and later resolved via `getAssetUrl`,
|
|
423
|
+
* `readAsset`, or `extractText`.
|
|
424
|
+
*
|
|
425
|
+
* The blob's deletion lifecycle is assigned by the host from the UI type,
|
|
426
|
+
* not chosen by the bundle: uploads from the setup UI or the scenario
|
|
427
|
+
* editor survive game resets and are wiped only when the session is
|
|
428
|
+
* re-initialised; uploads from every other UI surface are wiped on game
|
|
429
|
+
* reset.
|
|
430
|
+
*
|
|
431
|
+
* Example:
|
|
432
|
+
* ```typescript
|
|
433
|
+
* const reference = await callbacks.uploadBlob(file);
|
|
434
|
+
* // Store in context: setData({ attachment: reference })
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
uploadBlob: (file: File) => Promise<string>;
|
|
438
|
+
/**
|
|
439
|
+
* Resolve an asset reference to a URL that can be used directly in HTML
|
|
440
|
+
* elements. Accepts either an `asset:<path>` or `blob:<id>` reference.
|
|
441
|
+
*
|
|
442
|
+
* Example:
|
|
443
|
+
* ```typescript
|
|
444
|
+
* const url = callbacks.getAssetUrl("blob:abc123");
|
|
445
|
+
* const logo = callbacks.getAssetUrl("asset:shared/logo.svg");
|
|
446
|
+
* // <img src={url} />
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
getAssetUrl: (reference: string) => string;
|
|
450
|
+
/**
|
|
451
|
+
* Read the bytes of an asset by reference. Accepts either an
|
|
452
|
+
* `asset:<path>` reference (resolved against the active scenario's
|
|
453
|
+
* `assets/` directory) or a `blob:<id>` reference.
|
|
454
|
+
*
|
|
455
|
+
* Example:
|
|
456
|
+
* ```typescript
|
|
457
|
+
* const file = await callbacks.readAsset("blob:abc123");
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
readAsset: (reference: string) => Promise<ReadAssetResponse>;
|
|
461
|
+
/**
|
|
462
|
+
* Read committed past state: one round or a range, this team or every
|
|
463
|
+
* team, whole boards or named capabilities.
|
|
464
|
+
*
|
|
465
|
+
* Always resolves to a list, ordered by `tick` then by stable team index,
|
|
466
|
+
* with one {@link PastStateEntry} per (tick, team) pair. A single round is
|
|
467
|
+
* a list of one, and an empty list means nothing in that range is
|
|
468
|
+
* reviewable — the same answer for a range past the head, an undone range,
|
|
469
|
+
* and a reset session. There is no separate `null`: 'nothing here' has one
|
|
470
|
+
* spelling.
|
|
471
|
+
*
|
|
472
|
+
* Reviewable history is scoped to the session. A reset bulk-deletes the
|
|
473
|
+
* snapshots, so every past round a player could scrub to disappears.
|
|
474
|
+
*
|
|
475
|
+
* Ticks that are not committed past ticks are **omitted** rather than
|
|
476
|
+
* returned as holes: a tick at or above the current head (read it from
|
|
477
|
+
* `useUserInterfaceData`'s `completedTicks`), an undone or reset tick, and any
|
|
478
|
+
* negative, non-integer or out-of-range tick. A host MAY return fewer
|
|
479
|
+
* entries than the range implies, so treat a short result as normal and
|
|
480
|
+
* read on from the last tick you received. It rejects with a
|
|
481
|
+
* {@link TickSnapshotError} only on a genuine transport or host fault.
|
|
482
|
+
*
|
|
483
|
+
* **Authorisation.** Not gated by the bundle's `uses`. Own-team entries
|
|
484
|
+
* carry the capabilities this surface declared in `reads` and `writes`.
|
|
485
|
+
* `teams: 'all'` additionally requires `crossTeam: true` on a capability
|
|
486
|
+
* for it to appear in *another* team's entry (§6.2), which is the same
|
|
487
|
+
* boundary the live `useUserInterfaceCrossTeamData` hook enforces: reading another
|
|
488
|
+
* team's history grants nothing reading it live would not.
|
|
489
|
+
*
|
|
490
|
+
* Debounce scrubbing and cache what you fetch: an entry is immutable until
|
|
491
|
+
* the head decreases, so re-visiting a round is free.
|
|
492
|
+
*
|
|
493
|
+
* Example:
|
|
494
|
+
* ```typescript
|
|
495
|
+
* // this round, this team
|
|
496
|
+
* const [board] = await callbacks.getPastState<Context>({ fromTick: day });
|
|
497
|
+
* if (board) renderBoard(board.context);
|
|
498
|
+
*
|
|
499
|
+
* // a profit trend over every completed round, one field per round
|
|
500
|
+
* const trend = await callbacks.getPastState<Context>({
|
|
501
|
+
* fromTick: 0,
|
|
502
|
+
* toTick: completedTicks,
|
|
503
|
+
* capabilityIds: ['profit'],
|
|
504
|
+
* });
|
|
505
|
+
*
|
|
506
|
+
* // every team's ledger at one past round
|
|
507
|
+
* const settlement = await callbacks.getPastState<Context>({
|
|
508
|
+
* fromTick: day,
|
|
509
|
+
* teams: 'all',
|
|
510
|
+
* capabilityIds: ['ledger'],
|
|
511
|
+
* });
|
|
512
|
+
* ```
|
|
513
|
+
*/
|
|
514
|
+
getPastState: <T = Record<string, unknown>>(query: PastStateQuery<T>) => Promise<PastStateEntry<T>[]>;
|
|
515
|
+
/**
|
|
516
|
+
* Read the current participant roster of every team in the session, for
|
|
517
|
+
* building a cross-team directory (who is on each team) in a negotiation or
|
|
518
|
+
* trading game. Returns one flat entry per participant across all teams,
|
|
519
|
+
* each tagged with its `teamId`; join against {@link useUserInterfaceTeams}
|
|
520
|
+
* for team names and indexes.
|
|
521
|
+
*
|
|
522
|
+
* The result is a point-in-time snapshot, not a live subscription: it does
|
|
523
|
+
* not update as participants join or leave, so re-call it to refresh. The
|
|
524
|
+
* caller's own team stays live through the {@link useUserInterfaceParticipants}
|
|
525
|
+
* hook.
|
|
526
|
+
*
|
|
527
|
+
* Only meaningful in shared-state games, and gated by the bundle's
|
|
528
|
+
* `crossTeamParticipants` declaration: a UI that did not declare it resolves
|
|
529
|
+
* to an empty array. Rejects only on a genuine transport or host fault.
|
|
530
|
+
*
|
|
531
|
+
* Example:
|
|
532
|
+
* ```typescript
|
|
533
|
+
* const everyone = await callbacks.getAllTeamsParticipants();
|
|
534
|
+
* const byTeam = Map.groupBy(everyone, (p) => p.teamId);
|
|
535
|
+
* ```
|
|
536
|
+
*/
|
|
537
|
+
getAllTeamsParticipants: () => Promise<Participant[]>;
|
|
538
|
+
/**
|
|
539
|
+
* Server-side endpoints declared by this UI's bundle. Each entry calls a
|
|
540
|
+
* named, author-written handler; the host validates the request against
|
|
541
|
+
* the endpoint's `requestSchema`, invokes the handler, and validates the
|
|
542
|
+
* response before returning it.
|
|
543
|
+
*
|
|
544
|
+
* Generated `types.ts` narrows this to a strongly-typed map of the
|
|
545
|
+
* endpoints the UI declared in its `endpoints` array.
|
|
546
|
+
*
|
|
547
|
+
* Example:
|
|
548
|
+
* ```typescript
|
|
549
|
+
* const { summary } = await callbacks.endpoints['com.example.summarise']({ text });
|
|
550
|
+
* ```
|
|
551
|
+
*/
|
|
552
|
+
endpoints: Record<string, (request: unknown) => Promise<unknown>>;
|
|
553
|
+
/**
|
|
554
|
+
* Set the host's theme preference. The host applies the new preference,
|
|
555
|
+
* persists it, and broadcasts the change back to every UI, so the value
|
|
556
|
+
* returned by {@link useUserInterfaceThemePreference} updates everywhere.
|
|
557
|
+
* Pass `'system'` to follow the operating system.
|
|
558
|
+
*
|
|
559
|
+
* Read the current preference reactively with
|
|
560
|
+
* {@link useUserInterfaceThemePreference}; this callback only sets it.
|
|
561
|
+
*
|
|
562
|
+
* Example:
|
|
563
|
+
* ```typescript
|
|
564
|
+
* callbacks.setThemePreference('dark');
|
|
565
|
+
* ```
|
|
566
|
+
*/
|
|
567
|
+
setThemePreference: (preference: ThemePreference) => void;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Callbacks available to waiting room user interface components.
|
|
571
|
+
*
|
|
572
|
+
* A waiting room UI is a game UI shown in a different display context, so it
|
|
573
|
+
* receives the player callback surface unchanged rather than a reduced one.
|
|
574
|
+
* The alias exists so generated `types.ts` files name the surface they consume.
|
|
575
|
+
*/
|
|
576
|
+
export type WaitingRoomUserInterfaceCallbacks = UserInterfaceCallbacks;
|
|
577
|
+
/**
|
|
578
|
+
* Callbacks available to session user interface components.
|
|
579
|
+
*
|
|
580
|
+
* This is the game's whole-session facilitator surface, so the callbacks are
|
|
581
|
+
* session-level across every team rather than scoped to one. Game flow
|
|
582
|
+
* callbacks accept an optional `teamId` to target a specific team; omitting
|
|
583
|
+
* it applies the action to all teams (or is ignored in shared-state mode).
|
|
584
|
+
*/
|
|
585
|
+
export interface SessionUserInterfaceCallbacks {
|
|
586
|
+
/**
|
|
587
|
+
* Upload a file as a blob. Returns a `blob:`-prefixed reference. The host
|
|
588
|
+
* assigns the blob's deletion lifecycle from the UI type (see
|
|
589
|
+
* `UserInterfaceCallbacks.uploadBlob`).
|
|
590
|
+
*
|
|
591
|
+
* Example:
|
|
592
|
+
* ```typescript
|
|
593
|
+
* const reference = await callbacks.uploadBlob(file);
|
|
594
|
+
* ```
|
|
595
|
+
*/
|
|
596
|
+
uploadBlob: (file: File) => Promise<string>;
|
|
597
|
+
/**
|
|
598
|
+
* Resolve an asset reference to a URL. Accepts either an
|
|
599
|
+
* `asset:<path>` or `blob:<id>` reference.
|
|
600
|
+
*
|
|
601
|
+
* Example:
|
|
602
|
+
* ```typescript
|
|
603
|
+
* const url = callbacks.getAssetUrl("blob:abc123");
|
|
604
|
+
* ```
|
|
605
|
+
*/
|
|
606
|
+
getAssetUrl: (reference: string) => string;
|
|
607
|
+
/**
|
|
608
|
+
* Read the bytes of an asset by reference.
|
|
609
|
+
*
|
|
610
|
+
* Example:
|
|
611
|
+
* ```typescript
|
|
612
|
+
* const file = await callbacks.readAsset("blob:abc123");
|
|
613
|
+
* ```
|
|
614
|
+
*/
|
|
615
|
+
readAsset: (reference: string) => Promise<ReadAssetResponse>;
|
|
616
|
+
/**
|
|
617
|
+
* Read committed past state, the same call the player surface has, with
|
|
618
|
+
* two differences that follow from a session UI not being scoped to a
|
|
619
|
+
* team.
|
|
620
|
+
*
|
|
621
|
+
* `teams` defaults to `'all'` here rather than `'own'`, because a
|
|
622
|
+
* facilitator surface has no own team, and it accepts a `team-N` id to
|
|
623
|
+
* read one named team. And there is no `crossTeam` gate: a session UI
|
|
624
|
+
* already receives every team's declared context live through
|
|
625
|
+
* `SessionData`, so reading the same declared fields shifted in time
|
|
626
|
+
* grants it nothing it did not have. It is bounded by this surface's own
|
|
627
|
+
* declared `reads` and `writes`, and never returns a `private` capability
|
|
628
|
+
* (§6.1).
|
|
629
|
+
*
|
|
630
|
+
* Everything else matches: a list ordered by `tick` then stable team
|
|
631
|
+
* index, one entry per (tick, team) pair, unreviewable ticks omitted, an
|
|
632
|
+
* empty list for a range holding nothing, and a rejection only on a
|
|
633
|
+
* genuine fault.
|
|
634
|
+
*
|
|
635
|
+
* Example:
|
|
636
|
+
* ```typescript
|
|
637
|
+
* // a results table: rows are rounds, entries are teams
|
|
638
|
+
* const rows = await callbacks.getPastState<Context>({
|
|
639
|
+
* fromTick: 0,
|
|
640
|
+
* toTick: head,
|
|
641
|
+
* capabilityIds: ['profit'],
|
|
642
|
+
* });
|
|
643
|
+
*
|
|
644
|
+
* // one team's board at one past round
|
|
645
|
+
* const [board] = await callbacks.getPastState<Context>({
|
|
646
|
+
* fromTick: day,
|
|
647
|
+
* teams: 'team-1',
|
|
648
|
+
* });
|
|
649
|
+
* ```
|
|
650
|
+
*/
|
|
651
|
+
getPastState: <T = Record<string, unknown>>(query: PastStateQuery<T>) => Promise<PastStateEntry<T>[]>;
|
|
652
|
+
/**
|
|
653
|
+
* Request a tick. Pass a `teamId` to tick a specific team (independent
|
|
654
|
+
* mode) or omit to tick all teams.
|
|
655
|
+
*
|
|
656
|
+
* Returns a promise that resolves once the tick(s) have finished and rejects
|
|
657
|
+
* with a `MutationError` when the tick cannot run. Safe to ignore.
|
|
658
|
+
*
|
|
659
|
+
* Example:
|
|
660
|
+
* ```typescript
|
|
661
|
+
* await callbacks.requestTick(); // all teams
|
|
662
|
+
* await callbacks.requestTick("team-1"); // specific team
|
|
663
|
+
* ```
|
|
664
|
+
*/
|
|
665
|
+
requestTick: (teamId?: string) => Promise<void>;
|
|
666
|
+
/**
|
|
667
|
+
* Pause automatic ticking. Pass a `teamId` to pause a specific team
|
|
668
|
+
* or omit to pause all teams. Resolves once the host has applied it, and
|
|
669
|
+
* rejects with a `MutationError` when the host refuses it.
|
|
670
|
+
*/
|
|
671
|
+
pauseTimer: (teamId?: string) => Promise<void>;
|
|
672
|
+
/**
|
|
673
|
+
* Resume automatic ticking after a pause. Pass a `teamId` to resume a
|
|
674
|
+
* specific team or omit to resume all teams. Resolves once the host has
|
|
675
|
+
* applied it, and rejects with a `MutationError` when the host refuses it.
|
|
676
|
+
*/
|
|
677
|
+
resumeTimer: (teamId?: string) => Promise<void>;
|
|
678
|
+
/**
|
|
679
|
+
* Reset the game back to its initial state. Pass a `teamId` to reset a
|
|
680
|
+
* specific team or omit to reset the entire session. Returns a promise that
|
|
681
|
+
* resolves once the reset has completed and rejects with a `MutationError`
|
|
682
|
+
* on failure.
|
|
683
|
+
*/
|
|
684
|
+
resetGame: (teamId?: string) => Promise<void>;
|
|
685
|
+
/**
|
|
686
|
+
* Undo the last tick. Pass a `teamId` to undo for a specific team or
|
|
687
|
+
* omit to undo for all teams. Returns a promise that resolves once the undo
|
|
688
|
+
* has completed and rejects with a `MutationError` on failure.
|
|
689
|
+
*/
|
|
690
|
+
undoTick: (teamId?: string) => Promise<void>;
|
|
691
|
+
/**
|
|
692
|
+
* Move the deadline of the currently scheduled tick by `byMs`
|
|
693
|
+
* milliseconds. Positive buys the room more time; negative brings the deadline forward, clamped at
|
|
694
|
+
* now rather than firing retroactively. Pass a `teamId` to adjust one team
|
|
695
|
+
* or omit to adjust every team.
|
|
696
|
+
*
|
|
697
|
+
* This moves *this* tick only. The interval the tick controller returns for
|
|
698
|
+
* the ticks after it is untouched, so a session does not drift because a
|
|
699
|
+
* facilitator gave one round an extra two minutes. Read the result back
|
|
700
|
+
* from `useUserInterfaceTimer().extendedMs` rather than assuming the call
|
|
701
|
+
* won.
|
|
702
|
+
*
|
|
703
|
+
* Deliberately not bounded by the §11.8 tick floor and ceiling: those bound
|
|
704
|
+
* the cadence a tick controller may schedule, and a facilitator already
|
|
705
|
+
* reaches outside both with `requestTick` (immediate) and `pauseTimer`
|
|
706
|
+
* (indefinite).
|
|
707
|
+
*
|
|
708
|
+
* Rejects with a `MutationError` carrying `{ kind: 'denied' }` when there is
|
|
709
|
+
* no tick to move — automatic ticking is disabled, the game is complete, or
|
|
710
|
+
* the timer is paused — and when the host cannot schedule the result. A
|
|
711
|
+
* denial is terminal.
|
|
712
|
+
*
|
|
713
|
+
* Example:
|
|
714
|
+
* ```typescript
|
|
715
|
+
* await callbacks.extendTick(120_000); // two more minutes, every team
|
|
716
|
+
* await callbacks.extendTick(120_000, 'team-1'); // just this team
|
|
717
|
+
* await callbacks.extendTick(-30_000); // pull the deadline in 30 seconds
|
|
718
|
+
* ```
|
|
719
|
+
*/
|
|
720
|
+
extendTick: (byMs: number, teamId?: string) => Promise<void>;
|
|
721
|
+
/**
|
|
722
|
+
* Ask the host to open one of its own overlays: the facilitator guide, a
|
|
723
|
+
* team's print sheet. The host renders it in its own chrome, above the
|
|
724
|
+
* surface, so this delegates the trigger and nothing else.
|
|
725
|
+
*
|
|
726
|
+
* `target` is the overlay's argument. A guide identifier for
|
|
727
|
+
* `'facilitator-guide'`, a team id for `'print-sheets'`; omitting it means
|
|
728
|
+
* the whole thing — the game's first declared guide, every team.
|
|
729
|
+
*
|
|
730
|
+
* Render a control only for an overlay in
|
|
731
|
+
* `useSessionUserInterfaceHostOverlays()`. Which overlays exist is the
|
|
732
|
+
* host's answer: a production host and the SDK's dev server do not offer
|
|
733
|
+
* the same set, and a button for one the host does not have is a button
|
|
734
|
+
* that cannot work.
|
|
735
|
+
*
|
|
736
|
+
* Rejects with a `MutationError` carrying `{ kind: 'denied' }` for an
|
|
737
|
+
* overlay this host does not offer, and for a `target` the active game does
|
|
738
|
+
* not declare. A denial is terminal.
|
|
739
|
+
*
|
|
740
|
+
* Example:
|
|
741
|
+
* ```typescript
|
|
742
|
+
* await callbacks.openHostOverlay('facilitator-guide');
|
|
743
|
+
* await callbacks.openHostOverlay('print-sheets', 'team-1');
|
|
744
|
+
* ```
|
|
745
|
+
*/
|
|
746
|
+
openHostOverlay: (overlay: HostOverlay, target?: string) => Promise<void>;
|
|
747
|
+
/**
|
|
748
|
+
* Open or close the session's waiting room. While it is open, every player
|
|
749
|
+
* sees the game's waiting room UI instead of its game UIs; closing it swaps
|
|
750
|
+
* them back with no reload. Game state is untouched in both directions —
|
|
751
|
+
* per-team context, current tick, and timers all survive the toggle, which
|
|
752
|
+
* is the point: a facilitator can park a live session for a discussion and
|
|
753
|
+
* resume exactly where teams were.
|
|
754
|
+
*
|
|
755
|
+
* Alone among the session callbacks it takes no `teamId`: the waiting room
|
|
756
|
+
* is one flag for the whole session, so there is no per-team variant to
|
|
757
|
+
* target. Opening it does not stop the tick clock (see
|
|
758
|
+
* `TickControllerFunctionArgs.waitingRoomOpen` for how to do that).
|
|
759
|
+
*
|
|
760
|
+
* The host usually renders a waiting-room control in its own chrome too, so
|
|
761
|
+
* both writers exist by design. Read the resulting flag back from
|
|
762
|
+
* `useSessionUserInterfaceMeta().waitingRoomOpen` rather than assuming this
|
|
763
|
+
* call won.
|
|
764
|
+
*
|
|
765
|
+
* Example:
|
|
766
|
+
* ```typescript
|
|
767
|
+
* await callbacks.setWaitingRoom(true); // park the session
|
|
768
|
+
* ```
|
|
769
|
+
*/
|
|
770
|
+
setWaitingRoom: (open: boolean) => Promise<void>;
|
|
771
|
+
/**
|
|
772
|
+
* Throw the walkthrough away and start it again at tick 0 (§5.7).
|
|
773
|
+
*
|
|
774
|
+
* The walkthrough is a disposable second run of the game, used to
|
|
775
|
+
* demonstrate it: one team, one participant, isolated from this session in
|
|
776
|
+
* both directions. Resetting it is how a facilitator clears the decisions
|
|
777
|
+
* they entered while talking the room through, and since it always begins
|
|
778
|
+
* at tick 0 there is nothing else to undo.
|
|
779
|
+
*
|
|
780
|
+
* Rejects with a `MutationError` carrying `{ kind: 'denied' }` while the
|
|
781
|
+
* session has no walkthrough yet, since opening its link is what creates
|
|
782
|
+
* one and there is nothing to reset before then.
|
|
783
|
+
*
|
|
784
|
+
* Example:
|
|
785
|
+
* ```typescript
|
|
786
|
+
* await callbacks.resetWalkthrough();
|
|
787
|
+
* ```
|
|
788
|
+
*/
|
|
789
|
+
resetWalkthrough: () => Promise<void>;
|
|
790
|
+
/**
|
|
791
|
+
* Add a team to the session. The host assigns the lowest unused team index
|
|
792
|
+
* and resolves the default name for that index when `name` is omitted.
|
|
793
|
+
*
|
|
794
|
+
* Rejects with a `MutationError` carrying `{ kind: 'denied' }` when the
|
|
795
|
+
* session is already at its maximum team count, when the session is
|
|
796
|
+
* shared-state (whose team count is fixed at creation), or when the
|
|
797
|
+
* session is read-only. The SDK's dev server relaxes the shared-state rule
|
|
798
|
+
* before the first tick as an authoring convenience; do not rely on it.
|
|
799
|
+
* A denial is terminal: retrying cannot succeed. Read
|
|
800
|
+
* `useSessionUserInterfaceTeamConfiguration()` to disable the control before
|
|
801
|
+
* the facilitator reaches it.
|
|
802
|
+
*
|
|
803
|
+
* Example:
|
|
804
|
+
* ```typescript
|
|
805
|
+
* await callbacks.createTeam('Marketing');
|
|
806
|
+
* ```
|
|
807
|
+
*/
|
|
808
|
+
createTeam: (name?: string) => Promise<void>;
|
|
809
|
+
/**
|
|
810
|
+
* Rename a team. The new name reaches every surface through the team list.
|
|
811
|
+
*
|
|
812
|
+
* Example:
|
|
813
|
+
* ```typescript
|
|
814
|
+
* await callbacks.renameTeam('team-1', 'Marketing');
|
|
815
|
+
* ```
|
|
816
|
+
*/
|
|
817
|
+
renameTeam: (teamId: string, name: string) => Promise<void>;
|
|
818
|
+
/**
|
|
819
|
+
* Rename a participant. The new name reaches every surface through the
|
|
820
|
+
* participant list.
|
|
821
|
+
*
|
|
822
|
+
* Example:
|
|
823
|
+
* ```typescript
|
|
824
|
+
* await callbacks.renameParticipant('participant-3', 'Sam');
|
|
825
|
+
* ```
|
|
826
|
+
*/
|
|
827
|
+
renameParticipant: (participantId: string, name: string) => Promise<void>;
|
|
828
|
+
/**
|
|
829
|
+
* Reassign a participant to another team.
|
|
830
|
+
*
|
|
831
|
+
* The participant keeps their id but takes a fresh index within the
|
|
832
|
+
* destination team, and their open surfaces are remounted against that team
|
|
833
|
+
* (§13.2). Two consequences, which are easy to confuse:
|
|
834
|
+
*
|
|
835
|
+
* - **Their browser loses local component state**, because the surface
|
|
836
|
+
* re-renders from scratch: a half-typed field, an uncommitted slider.
|
|
837
|
+
* Ordinary remount behaviour, and rarely worth warning anyone about.
|
|
838
|
+
* - **Their earlier writes stay with the source team.** Anything they wrote
|
|
839
|
+
* through `setData` is in that team's context and does not travel, so
|
|
840
|
+
* they arrive with none of their own state and the source team keeps work
|
|
841
|
+
* done by someone no longer on it.
|
|
842
|
+
*
|
|
843
|
+
* The second is what a game has to handle, and the mechanism is the
|
|
844
|
+
* `participantMove` hook: seed the destination on `phase: 'arrival'` the
|
|
845
|
+
* way `participantJoin` seeds a new participant. Note there is no transfer
|
|
846
|
+
* channel — a hook returns one team's context, so the departure hook's
|
|
847
|
+
* return value is invisible to arrival. A shared-state game carries state
|
|
848
|
+
* across through the shared store; an independent-mode game re-derives it
|
|
849
|
+
* from the payload and its own configuration. Where it genuinely cannot be
|
|
850
|
+
* re-derived, that is the case worth telling a facilitator about.
|
|
851
|
+
*
|
|
852
|
+
* A move does not reset either team: the source team's context, including
|
|
853
|
+
* every pending pre-tick decision in it, is untouched.
|
|
854
|
+
*
|
|
855
|
+
* Any write of theirs already in flight against the old team fails as
|
|
856
|
+
* denied rather than landing on either team.
|
|
857
|
+
*
|
|
858
|
+
* Example:
|
|
859
|
+
* ```typescript
|
|
860
|
+
* await callbacks.moveParticipant('participant-3', 'team-2');
|
|
861
|
+
* ```
|
|
862
|
+
*/
|
|
863
|
+
moveParticipant: (participantId: string, teamId: string) => Promise<void>;
|
|
864
|
+
/**
|
|
865
|
+
* Delete a team from an independent-mode session. No other team's index or
|
|
866
|
+
* id changes, and the deleted team's index is left as a gap that the next
|
|
867
|
+
* `createTeam` takes back as a new team.
|
|
868
|
+
*
|
|
869
|
+
* Only an empty team can go: move every participant out with
|
|
870
|
+
* `moveParticipant` first, connected or not, because a participant is
|
|
871
|
+
* never erased. Rejects with a `MutationError` carrying `{ kind: 'denied' }`
|
|
872
|
+
* while the team has participants, in a shared-state session (whose
|
|
873
|
+
* cohort is fixed for the session), for the last team, for a team the
|
|
874
|
+
* session does not hold, and when the session is read-only. A denial is
|
|
875
|
+
* terminal: retrying cannot succeed. Read
|
|
876
|
+
* `useSessionUserInterfaceTeamConfiguration().mode` to offer the control
|
|
877
|
+
* only in independent mode.
|
|
878
|
+
*
|
|
879
|
+
* Example:
|
|
880
|
+
* ```typescript
|
|
881
|
+
* await callbacks.deleteTeam('team-3');
|
|
882
|
+
* ```
|
|
883
|
+
*/
|
|
884
|
+
deleteTeam: (teamId: string) => Promise<void>;
|
|
885
|
+
/**
|
|
886
|
+
* Server-side endpoints declared by this UI's bundle. See
|
|
887
|
+
* {@link UserInterfaceCallbacks.endpoints} for details.
|
|
888
|
+
*/
|
|
889
|
+
endpoints: Record<string, (request: unknown) => Promise<unknown>>;
|
|
890
|
+
/**
|
|
891
|
+
* Set the host's theme preference. See
|
|
892
|
+
* {@link UserInterfaceCallbacks.setThemePreference} for details. Read the
|
|
893
|
+
* current value reactively with the matching theme-preference hook.
|
|
894
|
+
*/
|
|
895
|
+
setThemePreference: (preference: ThemePreference) => void;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Error thrown when a resource invokes a callback it did not declare in its
|
|
899
|
+
* bundle's `uses` array. Carries the offending resource identifier and the
|
|
900
|
+
* callback name so a runtime can surface both to the operator.
|
|
901
|
+
*/
|
|
902
|
+
export declare class UndeclaredApiUseError extends Error {
|
|
903
|
+
readonly resourceIdentifier: string;
|
|
904
|
+
readonly api: ApiUse;
|
|
905
|
+
constructor(resourceIdentifier: string, api: ApiUse);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* The {@link SignatureCallbacks} members that `uses` does not gate (§6.11).
|
|
909
|
+
*
|
|
910
|
+
* Runtime plumbing, and reads of state the resource already declared, rather
|
|
911
|
+
* than externally-visible APIs an operator gates access to. They are always
|
|
912
|
+
* present, so a generated `Callbacks` type has to carry them: without this the
|
|
913
|
+
* runtime passed them and no author could see them, which is how `readAsset`
|
|
914
|
+
* came to be reachable in principle and used nowhere.
|
|
915
|
+
*/
|
|
916
|
+
export type UngatedSignatureCallbacks = Pick<SignatureCallbacks, 'readAsset' | 'getPastState'>;
|
|
917
|
+
/**
|
|
918
|
+
* The callbacks a logic-bearing resource actually receives: the gated ones it
|
|
919
|
+
* declared in `uses`, plus the ungated ones it gets regardless.
|
|
920
|
+
*
|
|
921
|
+
* Every declared service is required here, never optional: a runtime that
|
|
922
|
+
* cannot provide one refuses the package at load (§6.11), so a resource that
|
|
923
|
+
* declared `generateText` calls `callbacks.generateText(...)` directly. A
|
|
924
|
+
* service it did not declare is not in the type at all.
|
|
925
|
+
*
|
|
926
|
+
* This is what each resource's generated `types.ts` aliases its `Callbacks` to.
|
|
927
|
+
* It exists as a named helper rather than being spelled out as an intersection
|
|
928
|
+
* at every call site because the spelled-out form is what an author reads first
|
|
929
|
+
* when asking what their handler is handed, and 'a Pick intersected with a
|
|
930
|
+
* Required Pick' answers that question badly.
|
|
931
|
+
*/
|
|
932
|
+
export type LogicCallbacks<Uses extends ApiUse = never> = Required<Pick<SignatureCallbacks, Uses>> & UngatedSignatureCallbacks;
|
|
933
|
+
/**
|
|
934
|
+
* Callbacks available to a settings editor: the `setup` and `scenario_editor`
|
|
935
|
+
* slots, which share one wrapper (§6.5).
|
|
936
|
+
*/
|
|
937
|
+
export interface SettingsEditorCallbacks {
|
|
938
|
+
/**
|
|
939
|
+
* Upload a file as a blob. Returns a `blob:`-prefixed reference that can
|
|
940
|
+
* be stored in a setting and later resolved via `getAssetUrl` or
|
|
941
|
+
* `readAsset`. Uploads from a settings editor survive game resets and are
|
|
942
|
+
* wiped only when the session is re-initialised; the host assigns this
|
|
943
|
+
* lifecycle from the UI type.
|
|
944
|
+
*
|
|
945
|
+
* Example:
|
|
946
|
+
* ```typescript
|
|
947
|
+
* const reference = await callbacks.uploadBlob(file);
|
|
948
|
+
* ```
|
|
949
|
+
*/
|
|
950
|
+
uploadBlob: (file: File) => Promise<string>;
|
|
951
|
+
/**
|
|
952
|
+
* Resolve an asset reference to a URL. Accepts either an
|
|
953
|
+
* `asset:<path>` or `blob:<id>` reference.
|
|
954
|
+
*
|
|
955
|
+
* Example:
|
|
956
|
+
* ```typescript
|
|
957
|
+
* const url = callbacks.getAssetUrl("blob:abc123");
|
|
958
|
+
* // <img src={url} />
|
|
959
|
+
* ```
|
|
960
|
+
*/
|
|
961
|
+
getAssetUrl: (reference: string) => string;
|
|
962
|
+
/**
|
|
963
|
+
* Read the bytes of an asset by reference.
|
|
964
|
+
*
|
|
965
|
+
* Example:
|
|
966
|
+
* ```typescript
|
|
967
|
+
* const file = await callbacks.readAsset("blob:abc123");
|
|
968
|
+
* ```
|
|
969
|
+
*/
|
|
970
|
+
readAsset: (reference: string) => Promise<ReadAssetResponse>;
|
|
971
|
+
/**
|
|
972
|
+
* Request the game to leave the configuration step and start play.
|
|
973
|
+
* Equivalent to the operator clicking the host-side Play button, which is
|
|
974
|
+
* always rendered (SPECIFICATION.md §8.3). Send this from a finish control
|
|
975
|
+
* of your own when the form wants one; it is never the only way out, so a
|
|
976
|
+
* bug here cannot strand a facilitator on the configuration step.
|
|
977
|
+
*
|
|
978
|
+
* Meaningful in the `setup` slot only. A scenario editor has no session
|
|
979
|
+
* to start, so the host refuses it from there (§6.5).
|
|
980
|
+
*
|
|
981
|
+
* Example:
|
|
982
|
+
* ```typescript
|
|
983
|
+
* callbacks.requestPlay();
|
|
984
|
+
* ```
|
|
985
|
+
*/
|
|
986
|
+
requestPlay: () => void;
|
|
987
|
+
/**
|
|
988
|
+
* Server-side endpoints declared by this UI's bundle. See
|
|
989
|
+
* {@link UserInterfaceCallbacks.endpoints} for details.
|
|
990
|
+
*/
|
|
991
|
+
endpoints: Record<string, (request: unknown) => Promise<unknown>>;
|
|
992
|
+
/**
|
|
993
|
+
* Set the host's theme preference. See
|
|
994
|
+
* {@link UserInterfaceCallbacks.setThemePreference} for details. Read the
|
|
995
|
+
* current value reactively with the matching theme-preference hook.
|
|
996
|
+
*/
|
|
997
|
+
setThemePreference: (preference: ThemePreference) => void;
|
|
998
|
+
}
|