@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.1

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.
Files changed (391) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +36 -2
  3. package/dist/bundling/logic-exports.d.ts +34 -0
  4. package/dist/bundling/node-builtins.d.ts +21 -0
  5. package/dist/bundling/transpiler.d.ts +7 -0
  6. package/dist/cli.js +27368 -0
  7. package/dist/client/dev-server.d.ts +59 -0
  8. package/dist/client/index.d.ts +28 -0
  9. package/dist/client/stub-services.d.ts +32 -0
  10. package/dist/client/test-session.d.ts +195 -0
  11. package/dist/client.js +16884 -0
  12. package/dist/core/asset-reference-translator.d.ts +46 -0
  13. package/dist/core/clock.d.ts +57 -0
  14. package/dist/core/faults.d.ts +65 -0
  15. package/dist/core/game-settings.d.ts +65 -0
  16. package/dist/core/history.d.ts +89 -0
  17. package/dist/core/listen-with-fallback.d.ts +30 -0
  18. package/dist/core/mutation-queue.d.ts +61 -0
  19. package/dist/core/package-scenario.d.ts +89 -0
  20. package/dist/core/participant-registry.d.ts +109 -0
  21. package/dist/core/pending-changes.d.ts +14 -0
  22. package/dist/core/protocol.d.ts +83 -0
  23. package/dist/core/scenario-drafts.d.ts +32 -0
  24. package/dist/core/session-endpoints.d.ts +108 -0
  25. package/dist/core/session-gates.d.ts +75 -0
  26. package/dist/core/session-host.d.ts +94 -0
  27. package/dist/core/session-refusals.d.ts +56 -0
  28. package/dist/core/session-rewind.d.ts +32 -0
  29. package/dist/core/session-roster.d.ts +128 -0
  30. package/dist/core/session-surfaces.d.ts +32 -0
  31. package/dist/core/session-ticks.d.ts +18 -0
  32. package/dist/core/session-timers.d.ts +84 -0
  33. package/dist/core/session-writes.d.ts +40 -0
  34. package/dist/core/session.d.ts +385 -0
  35. package/dist/core/settings-locks.d.ts +22 -0
  36. package/dist/core/team-names.d.ts +52 -0
  37. package/dist/core/team-state-store.d.ts +209 -0
  38. package/dist/core/tick-timer.d.ts +75 -0
  39. package/dist/dev/blob-links.d.ts +10 -0
  40. package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
  41. package/dist/dev/console/index.html +12 -0
  42. package/dist/dev/console-path.d.ts +19 -0
  43. package/dist/dev/lockfile.d.ts +124 -0
  44. package/dist/dev/package-watcher.d.ts +21 -0
  45. package/dist/dev/routes.d.ts +67 -0
  46. package/dist/dev/shell.d.ts +260 -0
  47. package/dist/dev/surfaces.d.ts +41 -0
  48. package/dist/dev/tools-identity.d.ts +23 -0
  49. package/dist/dev/view/batch.d.ts +172 -0
  50. package/dist/dev/view/commands.d.ts +75 -0
  51. package/dist/dev/view/route.d.ts +58 -0
  52. package/dist/dev/view/view.d.ts +78 -0
  53. package/dist/dev/view/viewer.d.ts +32 -0
  54. package/dist/discovery/config.d.ts +9 -0
  55. package/dist/discovery/manifest-paths.d.ts +21 -0
  56. package/dist/discovery/removed-fields.d.ts +90 -0
  57. package/dist/docs/api/deck.md +386 -0
  58. package/dist/docs/api/index.md +67 -0
  59. package/dist/docs/api/sdk-commands.md +286 -0
  60. package/dist/docs/api/sdk-e2e.md +433 -0
  61. package/dist/docs/api/sdk-projection.md +207 -0
  62. package/dist/docs/api/sdk-testing.md +214 -0
  63. package/dist/docs/api/sdk-tools-client.md +560 -0
  64. package/dist/docs/api/sdk-ui-testing.md +231 -0
  65. package/dist/docs/api/sdk-ui.md +1269 -0
  66. package/dist/docs/api/sdk.md +2140 -0
  67. package/dist/docs/changelog-0.x.md +1156 -0
  68. package/dist/docs/changelog.md +236 -0
  69. package/dist/docs/cli-and-dev-server.md +217 -0
  70. package/dist/docs/cli-error-codes.md +154 -0
  71. package/dist/docs/cli-reference.md +2488 -0
  72. package/dist/docs/cli-simulation-testing.md +222 -0
  73. package/dist/docs/concepts/index.md +46 -0
  74. package/dist/docs/concepts/terminology.md +322 -0
  75. package/dist/docs/environment-variables.md +102 -0
  76. package/dist/docs/getting-started.md +145 -0
  77. package/dist/docs/guides/ai-facilitation.md +220 -0
  78. package/dist/docs/guides/capabilities.md +460 -0
  79. package/dist/docs/guides/endpoints.md +335 -0
  80. package/dist/docs/guides/engine-testing.md +477 -0
  81. package/dist/docs/guides/engine.md +527 -0
  82. package/dist/docs/guides/file-handling.md +405 -0
  83. package/dist/docs/guides/games.md +176 -0
  84. package/dist/docs/guides/global-settings.md +94 -0
  85. package/dist/docs/guides/guide.md +123 -0
  86. package/dist/docs/guides/hooks.md +183 -0
  87. package/dist/docs/guides/index.md +51 -0
  88. package/dist/docs/guides/internationalization.md +288 -0
  89. package/dist/docs/guides/llm-integration.md +635 -0
  90. package/dist/docs/guides/multi-team-games.md +586 -0
  91. package/dist/docs/guides/patterns.md +391 -0
  92. package/dist/docs/guides/presentation.md +196 -0
  93. package/dist/docs/guides/production-constraints.md +130 -0
  94. package/dist/docs/guides/scenario-editor.md +143 -0
  95. package/dist/docs/guides/scenarios.md +515 -0
  96. package/dist/docs/guides/session.md +972 -0
  97. package/dist/docs/guides/settings.md +458 -0
  98. package/dist/docs/guides/setup.md +193 -0
  99. package/dist/docs/guides/speech-generation.md +233 -0
  100. package/dist/docs/guides/starter-game.md +164 -0
  101. package/dist/docs/guides/testing.md +575 -0
  102. package/dist/docs/guides/tick-controller.md +569 -0
  103. package/dist/docs/guides/type-safety.md +516 -0
  104. package/dist/docs/guides/ui.md +1389 -0
  105. package/dist/docs/guides/upgrading.md +636 -0
  106. package/dist/docs/guides/validators.md +217 -0
  107. package/dist/docs/guides/waiting-room.md +244 -0
  108. package/dist/docs/index.md +41 -0
  109. package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
  110. package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
  111. package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
  112. package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
  113. package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
  114. package/dist/docs/recipes/create-capability.md +66 -0
  115. package/dist/docs/recipes/create-endpoint.md +127 -0
  116. package/dist/docs/recipes/create-engine.md +111 -0
  117. package/dist/docs/recipes/create-game.md +70 -0
  118. package/dist/docs/recipes/create-guide.md +75 -0
  119. package/dist/docs/recipes/create-hook.md +80 -0
  120. package/dist/docs/recipes/create-presentation.md +142 -0
  121. package/dist/docs/recipes/create-scenario-editor.md +102 -0
  122. package/dist/docs/recipes/create-scenario.md +98 -0
  123. package/dist/docs/recipes/create-session-ui.md +116 -0
  124. package/dist/docs/recipes/create-settings.md +88 -0
  125. package/dist/docs/recipes/create-setup.md +115 -0
  126. package/dist/docs/recipes/create-tick-controller.md +65 -0
  127. package/dist/docs/recipes/create-user-interface.md +96 -0
  128. package/dist/docs/recipes/create-validator.md +67 -0
  129. package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
  130. package/dist/docs/recipes/declare-api-uses.md +67 -0
  131. package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
  132. package/dist/docs/recipes/distribute-package.md +80 -0
  133. package/dist/docs/recipes/e2e-testing.md +116 -0
  134. package/dist/docs/recipes/end-game-on-condition.md +154 -0
  135. package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
  136. package/dist/docs/recipes/index.md +86 -0
  137. package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
  138. package/dist/docs/recipes/negotiate-across-teams.md +164 -0
  139. package/dist/docs/recipes/observe-other-teams-state.md +119 -0
  140. package/dist/docs/recipes/publish-a-package.md +139 -0
  141. package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
  142. package/dist/docs/recipes/review-a-past-day.md +73 -0
  143. package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
  144. package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
  145. package/dist/docs/recipes/show-all-teams-participants.md +109 -0
  146. package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
  147. package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
  148. package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
  149. package/dist/docs/recipes/test-user-interface.md +95 -0
  150. package/dist/docs/recipes/test-with-engine.md +115 -0
  151. package/dist/docs/recipes/upload-and-display-files.md +148 -0
  152. package/dist/docs/recipes/use-request-play.md +65 -0
  153. package/dist/docs/recipes/use-request-tick.md +75 -0
  154. package/dist/docs/troubleshooting.md +816 -0
  155. package/dist/execution/blob-inputs.d.ts +57 -0
  156. package/dist/execution/bundle-reload.d.ts +42 -0
  157. package/dist/execution/config-resolver.d.ts +127 -0
  158. package/dist/execution/configuration-builder.d.ts +66 -0
  159. package/dist/execution/context-composition.d.ts +161 -0
  160. package/dist/execution/endpoint-actions.d.ts +9 -0
  161. package/dist/execution/endpoint-payloads.d.ts +32 -0
  162. package/dist/execution/endpoint-runner.d.ts +129 -0
  163. package/dist/execution/engine-state.d.ts +40 -0
  164. package/dist/execution/executor-callbacks.d.ts +56 -0
  165. package/dist/execution/executor.d.ts +436 -0
  166. package/dist/execution/gates.d.ts +45 -0
  167. package/dist/execution/hook-chain.d.ts +25 -0
  168. package/dist/execution/logic-calls.d.ts +190 -0
  169. package/dist/execution/logic-failure.d.ts +84 -0
  170. package/dist/execution/logic-loader.d.ts +54 -0
  171. package/dist/execution/logic-result.d.ts +76 -0
  172. package/dist/execution/tick-controller-call.d.ts +32 -0
  173. package/dist/execution/types.d.ts +48 -0
  174. package/dist/generation/ts-type.d.ts +39 -0
  175. package/dist/guide/locale.d.ts +12 -0
  176. package/dist/guide/video-registry.d.ts +42 -0
  177. package/dist/help-examples.d.ts +125 -0
  178. package/dist/host/adapter.d.ts +67 -0
  179. package/dist/host/host-channel.d.ts +222 -0
  180. package/dist/host/index.d.ts +20 -0
  181. package/dist/host/protocol.d.ts +783 -0
  182. package/dist/host/surfaces.d.ts +42 -0
  183. package/dist/host/theme.d.ts +55 -0
  184. package/dist/host.js +583 -0
  185. package/dist/prompts/index.d.ts +7 -0
  186. package/dist/prompts/schema-builder.d.ts +29 -0
  187. package/dist/prompts/types.d.ts +108 -0
  188. package/dist/prompts/validators.d.ts +32 -0
  189. package/dist/runtime/capability-scope.d.ts +6 -0
  190. package/dist/runtime/context-merge.d.ts +29 -0
  191. package/dist/runtime/context-projection.d.ts +41 -0
  192. package/dist/runtime/guarded-callbacks.d.ts +15 -0
  193. package/dist/runtime/index.d.ts +27 -0
  194. package/dist/runtime/manifest-version.d.ts +55 -0
  195. package/dist/runtime/tick-validation.d.ts +25 -0
  196. package/dist/runtime.js +346 -0
  197. package/dist/schema.json +1546 -0
  198. package/dist/server/vite-plugin.d.ts +46 -0
  199. package/dist/services/tts/client.d.ts +51 -0
  200. package/dist/services/tts/elevenlabs.d.ts +24 -0
  201. package/dist/services/tts/types.d.ts +44 -0
  202. package/dist/storage/asset-resolver.d.ts +49 -0
  203. package/dist/storage/blob-store.d.ts +41 -0
  204. package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
  205. package/dist/templates/endpoint/logic.ts.liquid +25 -0
  206. package/dist/templates/engine/init.json.liquid +7 -0
  207. package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
  208. package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
  209. package/dist/templates/engine/logic.test.ts.liquid +61 -0
  210. package/dist/templates/engine/logic.ts.liquid +32 -0
  211. package/dist/templates/engine/run.json.liquid +7 -0
  212. package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
  213. package/dist/templates/guide/02-preparation.md.liquid +18 -0
  214. package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
  215. package/dist/templates/hook/logic.test.ts.liquid +46 -0
  216. package/dist/templates/hook/logic.ts.liquid +12 -0
  217. package/dist/templates/package/CLAUDE.md.template +338 -0
  218. package/dist/templates/package/DESIGN.md.liquid +144 -0
  219. package/dist/templates/package/README.md.liquid +143 -0
  220. package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
  221. package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
  222. package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
  223. package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
  224. package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
  225. package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
  226. package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
  227. package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
  228. package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
  229. package/dist/templates/package/claude/settings.json.template +16 -0
  230. package/dist/templates/package/gitignore.template +15 -0
  231. package/dist/templates/package/global-setup.ts.template +21 -0
  232. package/dist/templates/package/globals.d.ts.template +2 -0
  233. package/dist/templates/package/oxfmtrc.json.template +14 -0
  234. package/dist/templates/package/oxlintrc.json.template +238 -0
  235. package/dist/templates/package/package.json.liquid +65 -0
  236. package/dist/templates/package/playwright.config.ts.liquid +19 -0
  237. package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
  238. package/dist/templates/package/test-setup.ts.liquid +7 -0
  239. package/dist/templates/package/tsconfig.json.liquid +15 -0
  240. package/dist/templates/package/vite.config.ts.liquid +19 -0
  241. package/dist/templates/package/vitest.config.ts.liquid +25 -0
  242. package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
  243. package/dist/templates/presentation/index.html.liquid +20 -0
  244. package/dist/templates/presentation/src/App.tsx.liquid +49 -0
  245. package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
  246. package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
  247. package/dist/templates/presentation/src/main.tsx.liquid +14 -0
  248. package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
  249. package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
  250. package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
  251. package/dist/templates/session/src/App.test.tsx.liquid +60 -0
  252. package/dist/templates/session/src/App.tsx.liquid +128 -0
  253. package/dist/templates/session/src/i18n.ts.liquid +53 -0
  254. package/dist/templates/session/src/main.tsx.liquid +48 -0
  255. package/dist/templates/session/src/theme.ts.liquid +46 -0
  256. package/dist/templates/settings-editor/index.html.liquid +12 -0
  257. package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
  258. package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
  259. package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
  260. package/dist/templates/setup/src/App.tsx.liquid +102 -0
  261. package/dist/templates/setup/src/i18n.ts.liquid +47 -0
  262. package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
  263. package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
  264. package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
  265. package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
  266. package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
  267. package/dist/templates/starter-game/engine/defaults.ts +81 -0
  268. package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
  269. package/dist/templates/starter-game/engine/logic.test.ts +221 -0
  270. package/dist/templates/starter-game/engine/logic.ts +149 -0
  271. package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
  272. package/dist/templates/starter-game/engine/types.ts +139 -0
  273. package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
  274. package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
  275. package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
  276. package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
  277. package/dist/templates/starter-game/guide/guide.test.ts +108 -0
  278. package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
  279. package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
  280. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
  281. package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
  282. package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
  283. package/dist/templates/starter-game/package.json +68 -0
  284. package/dist/templates/starter-game/playwright.config.ts +19 -0
  285. package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
  286. package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
  287. package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
  288. package/dist/templates/starter-game/session/defaults.ts +80 -0
  289. package/dist/templates/starter-game/session/index.html +12 -0
  290. package/dist/templates/starter-game/session/session.sdk.json +11 -0
  291. package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
  292. package/dist/templates/starter-game/session/src/App.tsx +309 -0
  293. package/dist/templates/starter-game/session/src/Section.tsx +37 -0
  294. package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
  295. package/dist/templates/starter-game/session/src/format.ts +8 -0
  296. package/dist/templates/starter-game/session/src/i18n.ts +51 -0
  297. package/dist/templates/starter-game/session/src/main.tsx +48 -0
  298. package/dist/templates/starter-game/session/src/theme.ts +45 -0
  299. package/dist/templates/starter-game/session/types.ts +135 -0
  300. package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
  301. package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
  302. package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
  303. package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
  304. package/dist/templates/starter-game/test-setup.ts +7 -0
  305. package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
  306. package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
  307. package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
  308. package/dist/templates/starter-game/tsconfig.json +19 -0
  309. package/dist/templates/starter-game/ui/defaults.ts +60 -0
  310. package/dist/templates/starter-game/ui/index.html +12 -0
  311. package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
  312. package/dist/templates/starter-game/ui/src/App.tsx +175 -0
  313. package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
  314. package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
  315. package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
  316. package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
  317. package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
  318. package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
  319. package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
  320. package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
  321. package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
  322. package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
  323. package/dist/templates/starter-game/ui/src/format.ts +12 -0
  324. package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
  325. package/dist/templates/starter-game/ui/src/main.tsx +48 -0
  326. package/dist/templates/starter-game/ui/src/menu.ts +18 -0
  327. package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
  328. package/dist/templates/starter-game/ui/src/theme.ts +45 -0
  329. package/dist/templates/starter-game/ui/src/writes.ts +43 -0
  330. package/dist/templates/starter-game/ui/types.ts +137 -0
  331. package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
  332. package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
  333. package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
  334. package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
  335. package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
  336. package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
  337. package/dist/templates/starter-game/vite.config.ts +19 -0
  338. package/dist/templates/starter-game/vitest.config.ts +15 -0
  339. package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
  340. package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
  341. package/dist/templates/starter-game/waiting_room/index.html +12 -0
  342. package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
  343. package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
  344. package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
  345. package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
  346. package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
  347. package/dist/templates/starter-game/waiting_room/types.ts +59 -0
  348. package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
  349. package/dist/templates/tick-controller/logic.ts.liquid +8 -0
  350. package/dist/templates/ui/index.html.liquid +12 -0
  351. package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
  352. package/dist/templates/ui/src/App.tsx.liquid +103 -0
  353. package/dist/templates/ui/src/i18n.ts.liquid +47 -0
  354. package/dist/templates/ui/src/main.tsx.liquid +48 -0
  355. package/dist/templates/ui/src/theme.ts.liquid +46 -0
  356. package/dist/templates/validator/logic.test.ts.liquid +42 -0
  357. package/dist/templates/validator/logic.ts.liquid +21 -0
  358. package/dist/templates/waiting-room/index.html.liquid +12 -0
  359. package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
  360. package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
  361. package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
  362. package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
  363. package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
  364. package/dist/templating/resource-metadata.d.ts +131 -0
  365. package/dist/types.d.ts +14 -0
  366. package/dist/utils/atomic-write.d.ts +40 -0
  367. package/dist/utils/cli-error.d.ts +64 -0
  368. package/dist/utils/document-parser-client.d.ts +20 -0
  369. package/dist/utils/env-loader.d.ts +40 -0
  370. package/dist/utils/error-codes.d.ts +335 -0
  371. package/dist/utils/errors.d.ts +19 -0
  372. package/dist/utils/global-settings.d.ts +65 -0
  373. package/dist/utils/llm-client.d.ts +34 -0
  374. package/dist/utils/logger.d.ts +36 -0
  375. package/dist/utils/output.d.ts +109 -0
  376. package/dist/utils/platform.d.ts +14 -0
  377. package/dist/utils/prompt.d.ts +36 -0
  378. package/dist/utils/real-path.d.ts +6 -0
  379. package/dist/utils/schema-properties.d.ts +35 -0
  380. package/dist/validation/ajv.d.ts +17 -0
  381. package/dist/validation/facilitator-guide-validator.d.ts +27 -0
  382. package/dist/validation/findings.d.ts +40 -0
  383. package/dist/validation/game-constraints.d.ts +10 -0
  384. package/dist/validation/game-validator.d.ts +34 -0
  385. package/dist/validation/package-validation.d.ts +41 -0
  386. package/dist/validation/package-validator.d.ts +19 -0
  387. package/dist/validation/resource-validator.d.ts +79 -0
  388. package/dist/validation/schema-defaults-validator.d.ts +29 -0
  389. package/dist/validation/schema-validation.d.ts +115 -0
  390. package/dist/validation/semver.d.ts +9 -0
  391. package/package.json +122 -3
@@ -0,0 +1,46 @@
1
+ import { SDKUserInterface, SDKScenarioEditor, SDKSetup, SDKSessionUserInterface, SDKWaitingRoomUserInterface, SDKPresentation } from '@seriousgaming/sdk';
2
+ import type { Plugin } from 'vite';
3
+ export interface SeriousGamingUiPluginSurfaces {
4
+ userInterfaces: SDKUserInterface[];
5
+ setups: SDKSetup[];
6
+ scenarioEditors: SDKScenarioEditor[];
7
+ sessionUserInterfaces: SDKSessionUserInterface[];
8
+ waitingRoomUserInterfaces: SDKWaitingRoomUserInterface[];
9
+ /** Optional so a caller predating the slot need not name it. */
10
+ presentations?: SDKPresentation[];
11
+ }
12
+ /**
13
+ * The surfaces to route, as a fixed set or as a function the plugin calls on
14
+ * each request. A dev server passes the function, so a surface added while it
15
+ * runs is routed without a restart (#640).
16
+ */
17
+ export type SeriousGamingUiPluginOptions = {
18
+ workingDir: string;
19
+ } & (SeriousGamingUiPluginSurfaces | {
20
+ surfaces: () => SeriousGamingUiPluginSurfaces;
21
+ });
22
+ /**
23
+ * Rewrite a document's relative asset references to root-relative ones.
24
+ *
25
+ * A surface's HTML is authored beside its sources (`src="./src/main.tsx"`) and
26
+ * served from under a prefix, so the browser resolves that against the prefixed
27
+ * document URL while Vite's module graph knows the same file by its
28
+ * root-relative path. The module was then fetched under both, evaluated twice,
29
+ * and every surface called `createRoot` on the same container twice.
30
+ *
31
+ * Rewriting here makes the document name the module the way Vite names it, so
32
+ * there is one identity and one evaluation.
33
+ */
34
+ export declare function toRootRelative(html: string, documentDirectory: string): string;
35
+ /**
36
+ * Module id of the dev-only recovery guard injected into every served surface.
37
+ *
38
+ * Virtual, so it needs no file on disk and no route of its own: Vite resolves
39
+ * it through the plugin and serves it under `/@id/`, which means it goes
40
+ * through the transform pipeline and gets an `import.meta.hot` like any other
41
+ * module.
42
+ */
43
+ export declare const RECOVERY_MODULE_ID = "virtual:seriousgaming-surface-recovery";
44
+ /** How the injected tag names it. Vite spells a leading NUL `__x00__` in a URL. */
45
+ export declare const RECOVERY_MODULE_URL = "/@id/__x00__virtual:seriousgaming-surface-recovery";
46
+ export declare function seriousgamingUiPlugin(options: SeriousGamingUiPluginOptions): Plugin;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * TTS Client - dispatcher that wraps the configured TtsProvider.
3
+ *
4
+ * Resolves the provider adapter, the tier-to-model mapping, the preset-to-voice
5
+ * mapping, clamps `speed` to the provider's supported range, and surfaces a
6
+ * single `generateSpeech` entry point to the rest of the runtime.
7
+ *
8
+ * v1 ships with the ElevenLabs adapter only; additional providers (OpenAI,
9
+ * Google, Azure, Polly) will land behind a future `TTS_PROVIDER` env var.
10
+ * Adding them is a non-breaking change for game code.
11
+ */
12
+ import type { GenerateSpeechRequest, GenerateSpeechResponse } from '@seriousgaming/sdk';
13
+ import type { TtsProvider } from './types';
14
+ export declare class TtsClientError extends Error {
15
+ constructor(message: string);
16
+ }
17
+ export interface TtsClientConfig {
18
+ /** ElevenLabs API key. When omitted, the client throws on first call. */
19
+ elevenLabsApiKey?: string;
20
+ /** Tier-to-model mapping. Missing tiers fall back toward `quick`. */
21
+ quickModel?: string;
22
+ standardModel?: string;
23
+ expertModel?: string;
24
+ /** Preset-to-voice mapping. Missing presets fall back to `neutral`, then provider default. */
25
+ voicePresets?: {
26
+ neutral?: string;
27
+ narrator?: string;
28
+ announcer?: string;
29
+ executive?: string;
30
+ coach?: string;
31
+ [preset: string]: string | undefined;
32
+ };
33
+ /** Optional custom provider — used only by tests. */
34
+ provider?: TtsProvider;
35
+ }
36
+ export declare class TtsClient {
37
+ private readonly provider;
38
+ private readonly quickModel;
39
+ private readonly standardModel?;
40
+ private readonly expertModel?;
41
+ private readonly voicePresets;
42
+ constructor(config: TtsClientConfig);
43
+ /** Whether the client is configured to make TTS calls. */
44
+ isConfigured(): boolean;
45
+ generateSpeech(requestOrText: GenerateSpeechRequest | string): Promise<GenerateSpeechResponse & {
46
+ audio: Uint8Array;
47
+ }>;
48
+ private resolveModel;
49
+ private resolveVoice;
50
+ private buildConfigurationError;
51
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ElevenLabs adapter for the TTS dispatcher. v1 of `generateSpeech` ships
3
+ * with this provider only; OpenAI, Google, Azure and Polly adapters will
4
+ * follow behind a future `TTS_PROVIDER` env var.
5
+ */
6
+ import type { NormalisedTtsRequest, TtsProvider, TtsSynthesisResult } from './types';
7
+ export declare const ELEVENLABS_DEFAULT_MODELS: {
8
+ readonly quick: "eleven_flash_v2_5";
9
+ readonly standard: "eleven_turbo_v2_5";
10
+ readonly expert: "eleven_multilingual_v2";
11
+ };
12
+ export interface ElevenLabsAdapterConfig {
13
+ apiKey: string;
14
+ fetchImpl?: typeof globalThis.fetch;
15
+ }
16
+ export declare class ElevenLabsAdapter implements TtsProvider {
17
+ readonly name = "elevenlabs";
18
+ private readonly apiKey;
19
+ private readonly fetchImpl;
20
+ constructor(config: ElevenLabsAdapterConfig);
21
+ defaultVoice(): string;
22
+ clampSpeed(speed: number): number;
23
+ synthesize(request: NormalisedTtsRequest): Promise<TtsSynthesisResult>;
24
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Provider-agnostic text-to-speech contract used by the TTS dispatcher.
3
+ *
4
+ * Adapters (`elevenlabs.ts`, future `openai.ts`, etc.) implement
5
+ * {@link TtsProvider}. The dispatcher resolves the configured adapter, maps
6
+ * tier → model and preset → provider voice, clamps `speed` to the supported
7
+ * range, and calls `synthesize` with a normalised request.
8
+ */
9
+ import type { GenerateSpeechFormat, LLMTier } from '@seriousgaming/sdk';
10
+ export interface NormalisedTtsRequest {
11
+ /** Plain-text utterance. */
12
+ text: string;
13
+ /** Provider-specific voice ID resolved from the preset. */
14
+ voice: string;
15
+ /** Provider-specific model ID resolved from the tier. */
16
+ model: string;
17
+ /** Output audio container. */
18
+ format: GenerateSpeechFormat;
19
+ /** Optional BCP-47 hint passed through to the provider. */
20
+ languageHint?: string;
21
+ /** Speed multiplier already clamped to the provider's supported range. */
22
+ speed?: number;
23
+ /** Tier that selected the model — useful for adapters that branch behaviour. */
24
+ tier: LLMTier;
25
+ }
26
+ export interface TtsSynthesisResult {
27
+ audio: Uint8Array;
28
+ mimeType: string;
29
+ durationMs?: number;
30
+ charactersBilled?: number;
31
+ }
32
+ export interface TtsProvider {
33
+ readonly name: string;
34
+ /**
35
+ * Synthesise audio for `request`. Throws when the provider cannot service
36
+ * the call (missing config, transport error, etc.); the dispatcher converts
37
+ * the error into a callback-level failure for game code.
38
+ */
39
+ synthesize(request: NormalisedTtsRequest): Promise<TtsSynthesisResult>;
40
+ /** Provider-default voice ID, used when no preset (including `neutral`) is configured. */
41
+ defaultVoice(): string;
42
+ /** Clamp `speed` to the provider's supported range. */
43
+ clampSpeed(speed: number): number;
44
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Resolve an asset reference under the unified URI scheme.
3
+ *
4
+ * `asset:<path>` — package-bundled file, resolved against the active
5
+ * scenario's `assets/` directory.
6
+ * `shared:<path>` — package-level file, resolved against the package's
7
+ * top-level `shared-assets/` directory. Resolvable even
8
+ * when no scenario is active.
9
+ * `blob:<id>` — runtime-uploaded file, resolved against the dev
10
+ * server's blob store.
11
+ *
12
+ * The dispatch lives here so every engine-side and endpoint-side caller
13
+ * (`readAsset`, `extractText`) shares a single implementation.
14
+ */
15
+ export interface ResolvedAsset {
16
+ data: Buffer;
17
+ mimeType: string;
18
+ name: string;
19
+ }
20
+ /**
21
+ * Returns true when `value` is a string starting with one of the known
22
+ * asset URI scheme prefixes.
23
+ */
24
+ export declare function isAssetReference(value: unknown): value is string;
25
+ /**
26
+ * Resolve an `asset:` reference to a file under the scenario's `assets/`
27
+ * directory. The path is normalised and validated against directory traversal.
28
+ */
29
+ export declare function resolveAsset(scenarioRootDirectory: string, path: string): Promise<ResolvedAsset>;
30
+ /**
31
+ * Resolve a `shared:` reference to a file under the package's top-level
32
+ * `shared-assets/` directory. Unlike `asset:`, shared assets are package-level
33
+ * and resolvable with no active scenario. The path is normalised and validated
34
+ * against directory traversal relative to the `shared-assets/` root.
35
+ */
36
+ export declare function resolveSharedAsset(packagePath: string, path: string): Promise<ResolvedAsset>;
37
+ /**
38
+ * Resolve an `asset:<path>`, `shared:<path>`, or `blob:<id>` reference and
39
+ * return the underlying bytes, MIME type, and display name.
40
+ * `scenarioRootDirectory` is required for `asset:` references; pass `null` if
41
+ * no scenario is active and the resolver should throw on `asset:` references.
42
+ * `shared:` references are package-level and resolve regardless of the active
43
+ * scenario.
44
+ *
45
+ * Relative `scenarioRootDirectory` values resolve against `packagePath`
46
+ * (rather than the process CWD) so callers don't need to absolutize the
47
+ * scenario root before passing it in.
48
+ */
49
+ export declare function readAssetByReference(packagePath: string, scenarioRootDirectory: string | null, reference: string): Promise<ResolvedAsset>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Blob storage for game assets uploaded during configuration or play.
3
+ *
4
+ * Blobs are stored as pairs of files under .seriousgaming-sdk/blobs/:
5
+ * <uuid>.bin — raw binary data
6
+ * <uuid>.meta.json — { name, mimeType, retention }
7
+ *
8
+ * Retention (§16.2) is a deletion-lifecycle bucket, not an access dimension:
9
+ * "settings" — survives a session reset; cleared on a re-initialise
10
+ * "game" — cleared on a session reset or a re-initialise
11
+ */
12
+ export type BlobRetention = 'settings' | 'game';
13
+ export interface BlobMeta {
14
+ name: string;
15
+ mimeType: string;
16
+ retention: BlobRetention;
17
+ }
18
+ export interface BlobData extends BlobMeta {
19
+ data: Buffer;
20
+ }
21
+ /**
22
+ * Keep a package's blobs in `dir` rather than in the package, until the
23
+ * returned function is called. For an isolated dev server, which must neither
24
+ * read the developer's uploads nor leave its own beside them (#640).
25
+ */
26
+ export declare function redirectBlobs(packagePath: string, dir: string): () => void;
27
+ /**
28
+ * Store a blob on disk. Returns the generated blob ID.
29
+ * Throws if data exceeds the 20 MB limit.
30
+ */
31
+ export declare function storeBlob(packagePath: string, data: Buffer, name: string, mimeType: string, retention: BlobRetention): Promise<string>;
32
+ /**
33
+ * Read a blob from disk by ID.
34
+ * Throws if the blob does not exist.
35
+ */
36
+ export declare function readBlob(packagePath: string, id: string): Promise<BlobData>;
37
+ /**
38
+ * Delete every blob held under one of `retentions` (§16.2). A deleted blob is
39
+ * read as an unknown one: {@link readBlob} throws, and the asset route 404s.
40
+ */
41
+ export declare function deleteBlobs(packagePath: string, retentions: readonly BlobRetention[]): Promise<void>;
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Handle } from './logic';
3
+ import type { Request, Response, Context, Callbacks } from '../types';
4
+ import type { EndpointCaller, EndpointSession, {% unless sessionScoped %}TeamInfo, {% endunless %}LogicMeta } from '@seriousgaming/sdk';
5
+
6
+ // The run-wide facts the host supplies: the session locale, its random seed,
7
+ // whether this is the game being played or a walkthrough beside it, and when
8
+ // the operation started.
9
+ const meta: LogicMeta = { locale: 'en', seed: 'test-seed', runKind: 'session', now: Date.UTC(2026, 0, 1) };
10
+ // Where the session stands when the call is made: the ticks it has completed,
11
+ // whether the waiting room is open, and every team's standing.
12
+ const session: EndpointSession = {
13
+ mode: 'independent',
14
+ completedTicks: 1,
15
+ maxTicks: null,
16
+ waitingRoomOpen: false,
17
+ complete: false,
18
+ teams: [],
19
+ };
20
+ const callbacks = {} as Callbacks;
21
+ const caller: EndpointCaller = {
22
+ sessionId: 'test-session',
23
+ // A participant's own team; a facilitator's or external call carries none.
24
+ teamId: 'team-1',
25
+ role: 'participant',
26
+ userInterfaceIdentifier: '{{ identifier }}',
27
+ };
28
+ {% unless sessionScoped %}// The team the call runs for, which the runtime always supplies on team scope.
29
+ const team: TeamInfo = { teamId: 'team-1', index: 0, name: 'Team 1' };
30
+ {% endunless %}
31
+ describe('{{ identifier }}', () => {
32
+ describe('Handle', () => {
33
+ it('returns a valid response', async () => {
34
+ const request: Request = {
35
+ // TODO: populate request fields from requestSchema
36
+ } as Request;
37
+ const context: Context = {
38
+ // TODO: populate context based on declared reads
39
+ } as Context;
40
+
41
+ // The settings resources this endpoint lists in `settings`, keyed by
42
+ // identifier. Empty until it lists some.
43
+ const settings = {} as Parameters<typeof Handle>[0]['settings'];
44
+
45
+ const result = await Handle({
46
+ request,
47
+ context,
48
+ settings,
49
+ callbacks,
50
+ caller,
51
+ {% unless sessionScoped %} team,
52
+ {% endunless %} meta,
53
+ participants: [],
54
+ session,
55
+ });
56
+
57
+ // TODO: assert result matches responseSchema
58
+ expect(result).toBeDefined();
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,25 @@
1
+ import type { Handler } from '../types';
2
+
3
+ export const Handle: Handler = async ({ request, context, callbacks, caller }) => {
4
+ // The runtime validates `request` against the requestSchema before this
5
+ // runs, and the response against the responseSchema before it reaches the
6
+ // caller.
7
+ {%- if writes %}
8
+ // This endpoint declares `writes`, so it returns the response and the
9
+ // capabilities it changed. Only those in `writes` may appear in
10
+ // `capabilities`.
11
+ return {
12
+ response: {
13
+ // TODO: build the response shape declared in responseSchema
14
+ },
15
+ capabilities: {
16
+ // TODO: the capabilities this call changes
17
+ },
18
+ } as Awaited<ReturnType<Handler>>;
19
+ {%- else %}
20
+ // It declares no `writes`, so it cannot change the game: return the response.
21
+ return {
22
+ // TODO: build the response shape declared in responseSchema
23
+ } as Awaited<ReturnType<Handler>>;
24
+ {%- endif %}
25
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "blocks": {
3
+ "languageVersion": 0,
4
+ "blocks": []
5
+ },
6
+ "code": ""
7
+ }
@@ -0,0 +1,90 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import type { Participant, TeamRunMeta } from '@seriousgaming/sdk';
3
+ import { defaultSettings, defaultContext, defaultSharedContext, defaultTeamContext } from './defaults';
4
+ import { Init, InitAggregate, RunAggregate } from './logic';
5
+ import type { Context, Settings, Callbacks, TeamContext } from './types';
6
+
7
+ // `teamId` and `index` name the team whose context a per-team call receives,
8
+ // `seed` is the session's random seed (a fixed one here keeps an assertion on
9
+ // derived randomness stable), `runKind` says whether this is the game being
10
+ // played or a walkthrough beside it, and `now` is when the operation started
11
+ // (fixed here, as `seed` is, so a test asserting on it is stable). All of them come from the runtime in a
12
+ // real session, so a test has to state them.
13
+ const meta: TeamRunMeta = {
14
+ tick: 0,
15
+ locale: 'en',
16
+ teamId: 'team-1',
17
+ index: 0,
18
+ seed: 'test-seed',
19
+ runKind: 'session',
20
+ now: Date.UTC(2026, 0, 1),
21
+ };
22
+ // The participants on the team, taken when the operation started. Empty here;
23
+ // add records to test logic that reads the roster.
24
+ const participants: Participant[] = [];
25
+ const callbacks = {} as Callbacks;
26
+
27
+ // `defaultContext` and `defaultSettings` are generated beside the types, from
28
+ // the very schemas they satisfy: the values a session opens with. Spread
29
+ // them and name only what the case is about, and a capability added later
30
+ // arrives here already populated rather than breaking every fixture. An
31
+ // aggregate function receives them split: the shared store once, and each
32
+ // team's own capabilities, so its fixtures use `defaultSharedContext` and
33
+ // `defaultTeamContext`.
34
+ const shared = { context: { ...defaultSharedContext } };
35
+
36
+ describe('{{ identifier }}', () => {
37
+ describe('Init', () => {
38
+ it('should initialize each team context', async () => {
39
+ // TODO: override the capabilities this case is about
40
+ const context: Context = { ...defaultContext };
41
+ // TODO: override the settings this case is about
42
+ const settings: Settings = { ...defaultSettings };
43
+
44
+ const { capabilities } = await Init({ context, settings, meta, participants, callbacks });
45
+
46
+ // TODO: Add assertions for initialized values
47
+ expect(capabilities).toBeDefined();
48
+ });
49
+ });
50
+
51
+ describe('InitAggregate', () => {
52
+ it('should seed shared state across all teams', async () => {
53
+ const teams = [
54
+ {
55
+ teamId: 'team-1',
56
+ index: 0,
57
+ // TODO: override the capabilities this case is about
58
+ context: { ...defaultTeamContext } satisfies TeamContext,
59
+ },
60
+ ];
61
+ // TODO: override the settings this case is about
62
+ const settings: Settings = { ...defaultSettings };
63
+
64
+ const result = await InitAggregate({ shared, teams, settings, meta, participants, callbacks });
65
+
66
+ // TODO: Add assertions for shared and per-team values
67
+ expect(result.teams).toHaveLength(teams.length);
68
+ });
69
+ });
70
+
71
+ describe('RunAggregate', () => {
72
+ it('should update shared state on each tick', async () => {
73
+ const teams = [
74
+ {
75
+ teamId: 'team-1',
76
+ index: 0,
77
+ // TODO: override the capabilities this case is about
78
+ context: { ...defaultTeamContext } satisfies TeamContext,
79
+ },
80
+ ];
81
+ // TODO: override the settings this case is about
82
+ const settings: Settings = { ...defaultSettings };
83
+
84
+ const result = await RunAggregate({ shared, teams, settings, meta, participants, callbacks });
85
+
86
+ // TODO: Add assertions for shared and per-team values
87
+ expect(result.teams).toHaveLength(teams.length);
88
+ });
89
+ });
90
+ });
@@ -0,0 +1,46 @@
1
+ import { InitializationFunction, InitAggregateFunction, RunAggregateFunction } from '@seriousgaming/sdk';
2
+ import type { Context, Settings, Callbacks, SharedContext, TeamContext } from './types';
3
+
4
+ export const Init: InitializationFunction<Context, Settings, Callbacks> = async ({ context, settings, callbacks }) => {
5
+ // Initialise each team's context here. Return the capabilities it writes
6
+ // under `capabilities`: a patch, merged into the team's context. `Init` is
7
+ // optional: delete it and every team starts from the capabilities' defaults.
8
+ return {
9
+ capabilities: {
10
+ ...context
11
+ }
12
+ };
13
+ };
14
+
15
+ export const InitAggregate: InitAggregateFunction<TeamContext, SharedContext, Settings, Callbacks> = async ({ shared, teams, settings, callbacks }) => {
16
+ // Seed shared state from all teams' initial contexts here.
17
+ // `shared.context` is the session's shared store, once; each team's
18
+ // `context` holds only its own capabilities. Write shared capabilities under
19
+ // `shared.capabilities`, and each team's under its `teams` entry, in the
20
+ // same order as the input. A shared capability in a `teams` entry fails.
21
+ return {
22
+ shared: { capabilities: {} },
23
+ teams: teams.map((team) => ({
24
+ capabilities: {
25
+ ...team.context
26
+ }
27
+ }))
28
+ };
29
+ };
30
+
31
+ export const RunAggregate: RunAggregateFunction<TeamContext, SharedContext, Settings, Callbacks> = async ({ shared, teams, settings, callbacks }) => {
32
+ // Run once across all teams each tick, reading the shared store from
33
+ // `shared.context` rather than from any one team
34
+ // Callbacks are narrowed by the engine's `uses` array. To call generateText
35
+ // or extractText, declare them in engine.sdk.json's `uses` field — the
36
+ // runtime throws on any callback that isn't declared. A declared one is
37
+ // always present, so call it directly, without `?.`.
38
+ return {
39
+ shared: { capabilities: {} },
40
+ teams: teams.map((team) => ({
41
+ capabilities: {
42
+ ...team.context
43
+ }
44
+ }))
45
+ };
46
+ };
@@ -0,0 +1,61 @@
1
+ import type { Participant, TeamRunMeta } from '@seriousgaming/sdk';
2
+ import { describe, it, expect } from 'vitest';
3
+
4
+ import { defaultSettings, defaultContext } from './defaults';
5
+ import { Init, Run } from './logic';
6
+ import type { Context, Settings, Callbacks } from './types';
7
+
8
+ // `teamId` and `index` name the team whose context a per-team call receives,
9
+ // `seed` is the session's random seed (a fixed one here keeps an assertion on
10
+ // derived randomness stable), `runKind` says whether this is the game being
11
+ // played or a walkthrough beside it, and `now` is when the operation started
12
+ // (fixed here, as `seed` is, so a test asserting on it is stable). All of them come from the runtime in a
13
+ // real session, so a test has to state them.
14
+ const meta: TeamRunMeta = {
15
+ tick: 0,
16
+ locale: 'en',
17
+ teamId: 'team-1',
18
+ index: 0,
19
+ seed: 'test-seed',
20
+ runKind: 'session',
21
+ now: Date.UTC(2026, 0, 1),
22
+ };
23
+ // The participants on the team, taken when the operation started. Empty here;
24
+ // add records to test logic that reads the roster.
25
+ const participants: Participant[] = [];
26
+ const callbacks = {} as Callbacks;
27
+
28
+ // `defaultContext` and `defaultSettings` are generated beside the types, from
29
+ // the very schemas they satisfy: the values a session opens with. Spread
30
+ // them and name only what the case is about, and a capability added later
31
+ // arrives here already populated rather than breaking every fixture.
32
+
33
+ describe('{{ identifier }}', () => {
34
+ describe('Init', () => {
35
+ it('should initialize context variables', async () => {
36
+ // TODO: override the capabilities this case is about
37
+ const context: Context = { ...defaultContext };
38
+ // TODO: override the settings this case is about
39
+ const settings: Settings = { ...defaultSettings };
40
+
41
+ const { capabilities } = await Init({ context, settings, meta, participants, callbacks });
42
+
43
+ // TODO: Add assertions for initialized values
44
+ expect(capabilities).toBeDefined();
45
+ });
46
+ });
47
+
48
+ describe('Run', () => {
49
+ it('should update context on each tick', async () => {
50
+ // TODO: override the capabilities this case is about
51
+ const context: Context = { ...defaultContext };
52
+ // TODO: override the settings this case is about
53
+ const settings: Settings = { ...defaultSettings };
54
+
55
+ const { capabilities } = await Run({ context, settings, meta, participants, callbacks });
56
+
57
+ // TODO: Add assertions for updated values
58
+ expect(capabilities).toBeDefined();
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,32 @@
1
+ import { InitializationFunction, RunFunction } from '@seriousgaming/sdk';
2
+ import type { Context, Settings, Callbacks } from './types';
3
+
4
+ export const Init: InitializationFunction<Context, Settings, Callbacks> = async ({ context, settings, callbacks }) => {
5
+ // Initialise your engine here. Return the capabilities it writes under
6
+ // `capabilities`: a patch, merged into the team's context. `Init` is
7
+ // optional: delete it and every team starts from the capabilities' defaults.
8
+ //
9
+ // Memory only the engine needs between ticks can go under `state` beside
10
+ // `capabilities` instead of in a capability: no surface ever sees it, and
11
+ // undo rewinds it. Type it with the fourth parameter, for example
12
+ // `RunFunction<Context, Settings, Callbacks, EngineState>`, and `Run` receives
13
+ // it back as `state`.
14
+ return {
15
+ capabilities: {
16
+ ...context
17
+ }
18
+ };
19
+ };
20
+
21
+ export const Run: RunFunction<Context, Settings, Callbacks> = async ({ context, settings, callbacks }) => {
22
+ // Run your engine logic here
23
+ // Callbacks are narrowed by the engine's `uses` array. To call generateText
24
+ // or extractText, declare them in engine.sdk.json's `uses` field — the
25
+ // runtime throws on any callback that isn't declared. A declared one is
26
+ // always present, so call it directly, without `?.`.
27
+ return {
28
+ capabilities: {
29
+ ...context
30
+ }
31
+ };
32
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "blocks": {
3
+ "languageVersion": 0,
4
+ "blocks": []
5
+ },
6
+ "code": ""
7
+ }
@@ -0,0 +1,19 @@
1
+ # {{ name }} at a glance
2
+
3
+ A one-page summary a facilitator can keep next to the laptop. Replace the placeholders with the real shape of your session.
4
+
5
+ :::timing
6
+ Total session length: about 90 minutes.
7
+ :::
8
+
9
+ | Phase | Duration | Facilitator focus |
10
+ | --- | --- | --- |
11
+ | Briefing | 15 minutes | Introduce the simulation and form teams |
12
+ | Play | 60 minutes | Run ticks and coach teams between decisions |
13
+ | Debrief | 15 minutes | Connect outcomes to the learning objectives |
14
+
15
+ ## Materials
16
+
17
+ - One laptop or tablet per team
18
+ - A room layout that lets teams talk without overhearing each other
19
+ - This guide, printed or on a second screen
@@ -0,0 +1,18 @@
1
+ # Preparing for the session
2
+
3
+ Everything to do before participants arrive.
4
+
5
+ ## Before the day
6
+
7
+ - Read this guide end to end
8
+ - Run the simulation once yourself so the decisions feel familiar
9
+ - Decide team composition and prepare name cards if needed
10
+
11
+ ## On the day
12
+
13
+ - Start the session and confirm every team device can reach it
14
+ - Check the room layout gives each team space to confer
15
+
16
+ :::note
17
+ A facilitator new to this simulation should be able to run the session from this guide alone. If a step relies on knowledge that is only in your head, write it down here.
18
+ :::