@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,133 @@
1
+ # Adapt a game for walkthroughs
2
+
3
+ **Keywords:** walkthrough, runKind, meta.runKind, useRunKind, demonstrate the game, demo run, rehearse, facilitator walkthrough, show the room, spoiler, solo run, resetWalkthrough, walkthrough link
4
+
5
+ A walkthrough is a disposable second run of your game beside the session, for demonstrating it: one team, one participant, always from tick 0, and isolated from the session in both directions. It is what a facilitator puts on a projector while talking a room through the game before they play it.
6
+
7
+ Most games need no changes. Reach for this when a solo run is missing something a group has, or when a tick reaches outside for something you would rather not pay for or wait on in front of a room.
8
+
9
+ ## Prerequisites
10
+
11
+ - A game with an engine (see [Create an engine](create-engine.md))
12
+ - The dev server running (`pnpm run sdk dev`)
13
+
14
+ ## Steps
15
+
16
+ 1. Open the walkthrough and see what your game looks like in it. It is at `/walkthrough`, and in the dev drawer's Surfaces tab:
17
+
18
+ ```bash
19
+ pnpm run sdk dev
20
+ # then open http://localhost:3000/walkthrough
21
+ ```
22
+
23
+ The run is built the first time you open it. Nothing to create, nothing to clean up.
24
+
25
+ 2. Read the run kind in logic. `meta.runKind` is `'session'` or `'walkthrough'`:
26
+
27
+ ```typescript
28
+ export const Run: RunFunction<Context, Settings> = async ({ context, meta, callbacks }) => {
29
+ if (meta.runKind === 'walkthrough') {
30
+ // ...
31
+ }
32
+ };
33
+ ```
34
+
35
+ Hooks, endpoints and validators read it the same way, from their own `meta`.
36
+
37
+ 3. Read it in a surface with `useRunKind()`:
38
+
39
+ ```tsx
40
+ import { useRunKind } from '@seriousgaming/sdk/ui';
41
+
42
+ const runKind = useRunKind();
43
+ ```
44
+
45
+ It is the same value your engine reads, and neither changes for the life of the run, so a decision taken here cannot disagree with one taken in logic.
46
+
47
+ 4. Fill in what a solo run genuinely lacks. A walkthrough has one team, so anything that compares teams has nobody to compare against:
48
+
49
+ ```tsx
50
+ function Leaderboard() {
51
+ const runKind = useRunKind();
52
+ const data = useUserInterfaceData();
53
+
54
+ // One row is worse than no board: it teaches the room the wrong shape.
55
+ const rows = runKind === 'walkthrough' ? demonstrationRows : data.market.Standings;
56
+
57
+ return <Board rows={rows} />;
58
+ }
59
+ ```
60
+
61
+ 5. Suppress what reaches outside. A model call is real spend, real latency, and a different answer every time, none of which you want on a projector:
62
+
63
+ ```typescript
64
+ const commentary =
65
+ meta.runKind === 'walkthrough'
66
+ ? writtenCommentary(context)
67
+ : getText(await callbacks.generateText(prompt(context)));
68
+ ```
69
+
70
+ Prefer a path your game already has. If it falls back to written text when the model is unavailable, route the walkthrough through that same fallback, so the demonstration is not a third behaviour nobody tests.
71
+
72
+ 6. Offer the walkthrough from your session UI, if you have one. Gate the control on the link, so it never appears on a host that has no walkthrough to open:
73
+
74
+ ```tsx
75
+ function WalkthroughControls() {
76
+ const walkthrough = useSessionUserInterfaceWalkthroughLink();
77
+ const { resetWalkthrough } = useSessionUserInterfaceCallbacks();
78
+
79
+ if (!walkthrough) return null;
80
+
81
+ return (
82
+ <>
83
+ <Button href={walkthrough.url} target="_blank" rel="noreferrer">
84
+ Walkthrough
85
+ </Button>
86
+ <Button onClick={() => void resetWalkthrough()}>Start it again</Button>
87
+ </>
88
+ );
89
+ }
90
+ ```
91
+
92
+ 7. Test both paths. `runKind` is an ordinary field on `meta`, so build the arguments with `logicArgs`, and the render helpers take it as an option:
93
+
94
+ ```typescript
95
+ import { logicArgs } from '@seriousgaming/sdk/testing';
96
+
97
+ import { defaultContext, defaultSettings } from './defaults';
98
+
99
+ const result = await Run(
100
+ logicArgs.run({
101
+ context: defaultContext,
102
+ settings: defaultSettings,
103
+ meta: { runKind: 'walkthrough' },
104
+ })
105
+ );
106
+ ```
107
+
108
+ ```tsx
109
+ renderUserInterface(<Leaderboard />, { runKind: 'walkthrough' });
110
+ ```
111
+
112
+ Hold one line throughout: fill in the social context a solo run lacks, and suppress spoilers and outside calls, but never change how the game plays. If the walkthrough invents a figure the game would not produce, it has gone too far.
113
+
114
+ ## Verify
115
+
116
+ - `pnpm run sdk view --walkthrough` reports `"runKind": "walkthrough"`, one team, and tick 0
117
+ - `pnpm run sdk tick --walkthrough` advances the walkthrough and leaves `pnpm run sdk view` on the session unchanged
118
+ - The walkthrough never shows the waiting room, even for a game with `waitingRoom.openOnStart`
119
+ - `pnpm run sdk reset --walkthrough` returns it to tick 0
120
+ - A game that reads `runKind` nowhere behaves exactly as it did before
121
+
122
+ ## Troubleshooting
123
+
124
+ - **`meta.runKind` is always `'session'`**: you are looking at the session, not the walkthrough. In `sdk dev`, open `/walkthrough` or pass `--walkthrough`
125
+ - **The walkthrough asks for configuration**: it should not. It resolves settings exactly as its session does. If your game has a `setup` slot, commit the session's configuration first, then open the walkthrough
126
+ - **A shared-state game runs the walkthrough with one team**: that is deliberate. `teams.min` says what the game needs to be _played_, and a walkthrough is not play. Use `runKind` to fill in the teams that are not there
127
+ - **The walkthrough's generated content differs from the session's**: the two runs draw separate seeds, so anything derived from `meta.seed` differs between them. Rely on the shape of the screen rather than on a specific generated value when rehearsing
128
+
129
+ ## Related
130
+
131
+ - [Engines guide, walkthroughs](../guides/engine.md#walkthroughs)
132
+ - [Building user interfaces guide, walkthroughs](../guides/ui.md#walkthroughs)
133
+ - [Adapt a UI for observers](adapt-ui-for-observers.md)
@@ -0,0 +1,74 @@
1
+ # Adapt a UI for observers
2
+
3
+ **Keywords:** observer, observe team, viewer role, useUserInterfaceViewerRole, facilitator peek, debrief, null participant, own team, useUserInterfaceOwnTeam, which team am I
4
+
5
+ An observer is a facilitator watching one team's live game UI without joining as a team member. It is useful for seeing how a team is playing, troubleshooting, or running a debrief. Your game UI is rendered for observers as well as participants; most UIs work unchanged, and you reach for this only when you want the game UI to behave differently under observation.
6
+
7
+ ## Prerequisites
8
+
9
+ - A game UI (see [Create a user interface](create-user-interface.md))
10
+ - The dev server running (`pnpm run sdk dev`) with at least one team
11
+
12
+ ## Steps
13
+
14
+ 1. Read the viewer role with `useUserInterfaceViewerRole()`. It returns `'participant'` or `'observer'` and is always present:
15
+
16
+ ```tsx
17
+ import { useUserInterfaceViewerRole } from '@seriousgaming/sdk/ui';
18
+
19
+ const isObserver = useUserInterfaceViewerRole() === 'observer';
20
+ ```
21
+
22
+ 2. Guard reads of the participant. An observer has no caller identity, so `useUserInterfaceParticipant()` returns `null`:
23
+
24
+ ```tsx
25
+ import { useUserInterfaceParticipant } from '@seriousgaming/sdk/ui';
26
+
27
+ const participant = useUserInterfaceParticipant(); // null when observing
28
+ ```
29
+
30
+ 3. Do **not** reach for the participant to find out which team you are rendering. Use `useUserInterfaceOwnTeam()`, which is populated for observers and participants alike:
31
+
32
+ ```tsx
33
+ import { useUserInterfaceOwnTeam } from '@seriousgaming/sdk/ui';
34
+
35
+ const ownTeam = useUserInterfaceOwnTeam(); // { teamId, index, name }, not null under observation
36
+ ```
37
+
38
+ This is the read most often missed. `participant.teamId` is the obvious route and it silently becomes `null` when observed, so anything keyed off it (splitting a `useUserInterfaceCrossTeamData()` result into 'mine' and 'theirs', labelling a board, picking a row to highlight) breaks only in the observer view. Note that `useUserInterfaceTeams()` is the plural hook: it lists every team in the session and does not mark which is yours.
39
+
40
+ 4. Adapt the game UI. Hide participant-only controls (anything keyed by participant id has no id to write under), reveal state you keep hidden during play, or present a read-only debrief view:
41
+
42
+ ```tsx
43
+ function Decisions() {
44
+ const isObserver = useUserInterfaceViewerRole() === 'observer';
45
+
46
+ return (
47
+ <>
48
+ {isObserver && <Banner>Observing: watching this team, not playing</Banner>}
49
+ <PriceInput readOnly={isObserver} />
50
+ {!isObserver && <SubmitNoteForm />}
51
+ </>
52
+ );
53
+ }
54
+ ```
55
+
56
+ 5. Open the observer view at `/team/<n>/observe`. It renders the same game UI with no participant identity and no presence.
57
+
58
+ ## Verify
59
+
60
+ - The observer view shows the same live state as the participant view, and updates as the game ticks
61
+ - `useUserInterfaceViewerRole()` reads `'observer'` and `useUserInterfaceParticipant()` is `null` in the observer view
62
+ - `useUserInterfaceOwnTeam()` still returns the observed team, so anything labelled 'yours' stays correct
63
+ - The observer does not appear in the team roster (`useUserInterfaceParticipants()`) and no presence indicator lights up for them
64
+
65
+ ## Troubleshooting
66
+
67
+ - **The UI crashes reading `participant.name` when observing**: `useUserInterfaceParticipant()` is `null` for an observer. Guard for `null` before reading its fields
68
+ - **Cross-team lists stop marking which team is yours when observing**: you are deriving own-team from `participant.teamId`, which is `null` under observation. Switch to `useUserInterfaceOwnTeam()`
69
+ - **`useUserInterfaceOwnTeam()` returns `null`**: the host is not sending an own team and there is no participant to fall back on. Against the dev server this should not happen on a game UI; on a hosted runtime that does not send the own team, an observer reads `null`
70
+
71
+ ## Related
72
+
73
+ - [User interfaces guide: observers](../guides/ui.md#observers)
74
+ - [Observe other teams' state](observe-other-teams-state.md)
@@ -0,0 +1,109 @@
1
+ # Call an endpoint from a UI
2
+
3
+ **Keywords:** call endpoint, invoke endpoint, callbacks.endpoints, UI endpoint, request response, EndpointError, endpoint failure
4
+
5
+ ## Prerequisites
6
+
7
+ - An [endpoint](create-endpoint.md) already scaffolded in the same package (or installed via another package)
8
+ - A user interface, session UI, setup screen, or scenario editor to call it from
9
+
10
+ ## Steps
11
+
12
+ 1. Declare the endpoint on the UI bundle. Edit the UI's bundle (`ui/ui.sdk.json`, `session/session.sdk.json`, `waiting_room/waiting_room.sdk.json`, `setup/setup.sdk.json`, or `scenario_editor/scenario_editor.sdk.json`) and add the endpoint identifier to the `endpoints` array:
13
+
14
+ ```json
15
+ {
16
+ "definition": {
17
+ "endpoints": ["summarise"]
18
+ }
19
+ }
20
+ ```
21
+
22
+ Run `pnpm run sdk generate` (or any `new`/`update` resource command) to regenerate the UI's `types.ts`. The generator merges a typed `Endpoints` interface into the UI's `Callbacks` type.
23
+
24
+ 2. Use the endpoint from the UI. Access it on the callbacks context exposed by the wrapper:
25
+
26
+ ```tsx
27
+ import { useUserInterfaceCallbacks } from '@seriousgaming/sdk/ui';
28
+ import type { Callbacks } from '../types';
29
+
30
+ function ReportButton({ reportText }: { reportText: string }) {
31
+ // The generated Callbacks narrows `endpoints` to the ones this UI declares
32
+ const { endpoints } = useUserInterfaceCallbacks() as unknown as Callbacks;
33
+
34
+ return (
35
+ <button
36
+ onClick={async () => {
37
+ const { summary } = await endpoints['summarise']({
38
+ text: reportText,
39
+ });
40
+ console.log(summary);
41
+ }}
42
+ >
43
+ Summarise
44
+ </button>
45
+ );
46
+ }
47
+ ```
48
+
49
+ `request` is typed from the endpoint's `requestSchema`. The returned promise resolves with the value validated against `responseSchema`.
50
+
51
+ 3. Handle errors. A failed call rejects with an `EndpointError`. Its `kind` says why, and its `applied` lists the effects that had already landed before it stopped:
52
+
53
+ | `kind` | What happened |
54
+ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
55
+ | `denied` | The UI does not list the endpoint, or the call was refused before the request was read. Nothing changed |
56
+ | `invalid-request` | The request does not match `requestSchema`. The handler did not run |
57
+ | `handler-error` | The handler threw, or ran past its time limit |
58
+ | `invalid-response` | The handler returned something its declaration forbids, such as a response that does not match `responseSchema` |
59
+ | `conflict` | A round started while the handler ran, so nothing was applied. A person may try again |
60
+ | `tick-failed`, or another step's own kind | One of the endpoint's actions failed once applied. `applied` lists what landed before it, and `failedStep` names it |
61
+ | `error` | Anything else |
62
+
63
+ ```tsx
64
+ import { EndpointError } from '@seriousgaming/sdk/ui';
65
+
66
+ try {
67
+ const { summary } = await endpoints['summarise']({ text: reportText });
68
+ setSummary(summary);
69
+ } catch (error) {
70
+ if (error instanceof EndpointError && error.applied.length > 0) {
71
+ // Part of the endpoint's work landed before it stopped
72
+ setNotice(`Some changes were saved, then: ${error.message}`);
73
+ } else {
74
+ setNotice(error instanceof Error ? error.message : String(error));
75
+ }
76
+ }
77
+ ```
78
+
79
+ An empty `applied` means nothing changed. A non-empty one means the session moved, so do not tell the person their action had no effect.
80
+
81
+ 4. (Optional) Return binary data. If the endpoint produces audio, images, or files, it returns a `blob:<id>` reference in the JSON response. Resolve it the same way as any blob reference:
82
+
83
+ ```typescript
84
+ const { getAssetUrl } = useUserInterfaceCallbacks();
85
+ const { endpoints } = useUserInterfaceCallbacks() as unknown as Callbacks;
86
+
87
+ const { audioRef } = await endpoints['narrate']({ text });
88
+ const src = getAssetUrl?.(audioRef);
89
+ <audio src={src} controls autoPlay />
90
+ ```
91
+
92
+ `getAssetUrl` comes from the untyped callbacks, since the generated `Callbacks` carries `endpoints` alone.
93
+
94
+ ## Verify
95
+
96
+ - TypeScript: removing the endpoint identifier from the UI's `endpoints` array makes `callbacks.endpoints['summarise']` a type error
97
+ - Runtime: removing the endpoint identifier from the UI's `endpoints` array makes the call reject with an `EndpointError` of kind `denied` and the message _'"ui" does not list endpoint "summarise" in its endpoints'_
98
+
99
+ ## Troubleshooting
100
+
101
+ - _'"ui" does not list endpoint "X" in its endpoints'_: add the endpoint identifier to the UI's `endpoints` array, then regenerate types
102
+ - _'Endpoint "X" rejected the request: ...'_ (`invalid-request`): your payload does not match `requestSchema`. Compare your call against the endpoint's request schema
103
+ - _'Endpoint "X" returned a response its schema forbids: ...'_ (`invalid-response`): the endpoint handler is returning a value that does not match `responseSchema`. Fix the handler or widen the schema
104
+ - _'Endpoint "X" stopped at action 1 ("tick"): ...'_: the endpoint's write and earlier actions landed, then an action failed. `error.applied` and `error.failedStep` say which
105
+
106
+ ## Related
107
+
108
+ - [Create an endpoint](create-endpoint.md)
109
+ - [Endpoints (concept)](../guides/endpoints.md)
@@ -0,0 +1,117 @@
1
+ # Check a package from a script
2
+
3
+ Validate a package and play a few ticks from CI or an AI agent, and turn each failure into the file and line to fix.
4
+
5
+ **Keywords:** ci, agent, script, json, exit code, diagnostics, validate, automation, error code, pointer, source map
6
+
7
+ ## What you'll build
8
+
9
+ A check that runs `build --validate-only` and a couple of ticks, branches on the exit code, and reads `diagnostics[]` to report each problem where it is: a JSON pointer into a bundle, or a line and column in your TypeScript.
10
+
11
+ ## Prerequisites
12
+
13
+ - A package with `seriousgaming.sdk.json` in the working directory
14
+ - `jq`, or any language that parses JSON
15
+
16
+ ## Steps
17
+
18
+ ### 1. Ask for one JSON document
19
+
20
+ Pass `--json`, or set `SERIOUSGAMING_JSON=1` once for the whole job, and every command writes exactly one envelope to stdout. Run it through `pnpm run --silent` so pnpm's banner does not land on stdout before it.
21
+
22
+ ```bash
23
+ export SERIOUSGAMING_JSON=1
24
+ pnpm run --silent sdk build --validate-only > validate.json
25
+ status=$?
26
+ ```
27
+
28
+ Setting `SERIOUSGAMING_JSON` (or `CI`) also turns prompts off: a command that would have to ask fails with exit code 2 and `PROMPT_REQUIRED` instead of waiting.
29
+
30
+ ### 2. Branch on the exit code
31
+
32
+ 0 is success and 3 an invalid package; the others are in [Exit codes](../cli-reference.md#exit-codes).
33
+
34
+ ```bash
35
+ case $status in
36
+ 0) echo "Package is valid" ;;
37
+ 3) echo "Package is invalid" ;;
38
+ *) jq -r '.error.code + ": " + .error.message' validate.json; exit $status ;;
39
+ esac
40
+ ```
41
+
42
+ ### 3. Read the diagnostics
43
+
44
+ Each diagnostic names a `file` relative to the package root, and either a `pointer` (RFC 6901, into a JSON file) or a `line` and `column` (in a source file). Branch on `code`; the `message` is for people.
45
+
46
+ ```bash
47
+ jq -r '.diagnostics[]
48
+ | "\(.severity) \(.code) \(.file // "-")\(if .pointer then "#" + .pointer elif .line then ":\(.line):\(.column)" else "" end)\n \(.message)\(if .hint then "\n hint: " + .hint else "" end)"' validate.json
49
+ ```
50
+
51
+ ```
52
+ error SETTINGS_UNKNOWN_KEY scenarios/hard/scenario.sdk.json#/definition/settings/engine/BaseDemnd
53
+ Overrides "BaseDemnd", which settings "engine" does not declare
54
+ hint: Did you mean 'BaseDemand'?
55
+ ```
56
+
57
+ A passing build can still have warnings. They are in the success envelope's `diagnostics`, with `"severity": "warning"`.
58
+
59
+ ### 4. Play a tick and catch a logic error
60
+
61
+ ```bash
62
+ pnpm run --silent sdk tick --no-server > tick.json || jq '.diagnostics[] | select(.code == "LOGIC_ERROR")' tick.json
63
+ ```
64
+
65
+ When the engine throws, `tick` exits 4 with `error.code` `TICK_FAILED`, and the first diagnostic says where, read back through the bundle's source map, with the tick and team:
66
+
67
+ ```json
68
+ {
69
+ "severity": "error",
70
+ "code": "LOGIC_ERROR",
71
+ "message": "the first round has no stock",
72
+ "file": "engine/logic.ts",
73
+ "line": 13,
74
+ "column": 15,
75
+ "resource": { "type": "engine", "identifier": "engine" },
76
+ "tick": 1,
77
+ "team": { "teamId": "team-1", "index": 0 }
78
+ }
79
+ ```
80
+
81
+ `--no-server` runs the session in the command's own process and leaves nothing behind, so each command starts from tick 0, and `tick` initialises the session before it plays. To play several ticks in a row, leave it off: the first command starts a background dev server, and `pnpm run sdk dev stop` stops it at the end of the job.
82
+
83
+ ### 5. Do the same from Node
84
+
85
+ ```typescript
86
+ import { execFile } from 'node:child_process';
87
+
88
+ execFile(
89
+ 'pnpm',
90
+ ['run', '--silent', 'sdk', 'build', '--validate-only', '--json'],
91
+ (error, stdout) => {
92
+ const envelope = JSON.parse(stdout);
93
+ if (envelope.ok) return;
94
+ for (const diagnostic of envelope.diagnostics) {
95
+ const where = diagnostic.pointer ?? `${diagnostic.line}:${diagnostic.column}`;
96
+ console.log(`${diagnostic.code} ${diagnostic.file} ${where}: ${diagnostic.message}`);
97
+ }
98
+ process.exitCode = error?.code ?? 1;
99
+ }
100
+ );
101
+ ```
102
+
103
+ ## Verify
104
+
105
+ - Misspell a key in a scenario's `settings` and run step 1: the exit code is 3 and a `SETTINGS_UNKNOWN_KEY` diagnostic points at the key
106
+ - Fix it and run step 1 again: the exit code is 0
107
+
108
+ ## Troubleshooting
109
+
110
+ - **`JSON.parse` fails on the first line**: pnpm printed its banner on stdout. Add `--silent` to `pnpm run`
111
+ - **A diagnostic has no `file`**: the check could not tie the finding to one file. Its `resource` and `message` still say what it is about
112
+ - **`LOGIC_ERROR` has no `line`**: the throw came from somewhere other than your bundle's own frames, such as a timeout. The `resource` says which logic it was
113
+
114
+ ## Related
115
+
116
+ - [CLI reference: exit codes, JSON output and diagnostics](../cli-reference.md#exit-codes)
117
+ - [Publish a package to the Serious Gaming platform](publish-a-package.md)
@@ -0,0 +1,66 @@
1
+ # Create a capability
2
+
3
+ Define a named slice of the game's state that the engine and screens read and write.
4
+
5
+ **Keywords:** create capability, new capability, capability variables, data structure, capability schema, variablesSchema, scope
6
+
7
+ ## Prerequisites
8
+
9
+ - A package with `seriousgaming.sdk.json` in your working directory
10
+
11
+ ## Steps
12
+
13
+ 1. Create the capability. Its identifier is its directory name, and every variable declares a `type` and a `default`:
14
+
15
+ ```bash
16
+ pnpm run sdk new capability '{
17
+ "definition": {
18
+ "identifier": "pricing",
19
+ "variablesSchema": {
20
+ "type": "object",
21
+ "properties": {
22
+ "BasePrice": { "type": "number", "default": 100, "minimum": 0, "description": "Price before adjustments" },
23
+ "CurrentPrice": { "type": "number", "default": 100, "minimum": 0, "description": "Price after adjustments" }
24
+ },
25
+ "required": ["BasePrice", "CurrentPrice"],
26
+ "additionalProperties": false
27
+ }
28
+ }
29
+ }'
30
+ ```
31
+
32
+ This writes `capabilities/pricing/capability.sdk.json`. For nested objects, arrays, and keyed collections, see [Variable types](../guides/capabilities.md#variable-types).
33
+
34
+ 2. Optional: give it a `scope` other than the default `team`, by adding `"scope"` to the `definition` above: `shared` for one value across all teams, `team-readable` for a per-team value other teams may watch, or `private` for engine bookkeeping no screen receives. See [Scope](../guides/capabilities.md#scope).
35
+
36
+ 3. Declare it on each resource that uses it, in `reads` or `writes`. For the engine:
37
+
38
+ ```bash
39
+ pnpm run sdk update engine --patch '[
40
+ { "op": "add", "path": "/definition/writes/-", "value": "pricing" }
41
+ ]'
42
+ ```
43
+
44
+ The command regenerates that resource's `types.ts`, so `context.pricing` is typed.
45
+
46
+ ## Verify
47
+
48
+ ```bash
49
+ pnpm run sdk init
50
+ pnpm run sdk view
51
+ ```
52
+
53
+ The team's context holds `pricing` with its defaults.
54
+
55
+ ## Troubleshooting
56
+
57
+ | Problem | Solution |
58
+ | ------------------------------------ | --------------------------------------------------------------------------- |
59
+ | A resource cannot see the capability | Declare it in that resource's `reads` or `writes`; it receives nothing else |
60
+ | The build rejects a default | The default does not match the variable's `type` |
61
+
62
+ ## Related
63
+
64
+ - [Capabilities guide](../guides/capabilities.md): the state model, scope, lifetime, and variable types
65
+ - [Create an engine](create-engine.md)
66
+ - [Design state for concurrent writes](design-state-for-concurrent-writes.md)
@@ -0,0 +1,127 @@
1
+ # Create an endpoint
2
+
3
+ **Keywords:** create endpoint, new endpoint, server handler, callable handler, scaffold endpoint
4
+
5
+ ## Prerequisites
6
+
7
+ - A package with a valid `seriousgaming.sdk.json`
8
+ - A UI that will call the endpoint (or plan to add one)
9
+ - Optional: capabilities the endpoint will read
10
+
11
+ ## Steps
12
+
13
+ 1. Scaffold the endpoint:
14
+
15
+ ```bash
16
+ pnpm run sdk new endpoint
17
+ ```
18
+
19
+ Interactive mode prompts for a short identifier and the request and response properties. Alternatively pass a JSON definition:
20
+
21
+ ```bash
22
+ pnpm run sdk new endpoint '{
23
+ "definition": {
24
+ "identifier": "summarise",
25
+ "requestSchema": {
26
+ "type": "object",
27
+ "properties": { "text": { "type": "string" } },
28
+ "required": ["text"],
29
+ "additionalProperties": false
30
+ },
31
+ "responseSchema": {
32
+ "type": "object",
33
+ "properties": { "summary": { "type": "string" } },
34
+ "required": ["summary"],
35
+ "additionalProperties": false
36
+ },
37
+ "uses": ["generateText"]
38
+ },
39
+ "logic": { "directory": "logic", "entrypoint": "logic.ts" },
40
+ "rootDirectory": "."
41
+ }'
42
+ ```
43
+
44
+ 2. Fill in the handler at `endpoints/<identifier>/logic/logic.ts`:
45
+
46
+ ```typescript
47
+ import { getText } from '@seriousgaming/sdk';
48
+
49
+ import type { Handler } from '../types';
50
+
51
+ export const Handle: Handler = async ({ request, callbacks, caller }) => {
52
+ const response = await callbacks.generateText({
53
+ prompt: `Summarise this text in one sentence: ${request.text}`,
54
+ });
55
+ return { summary: getText(response) };
56
+ };
57
+ ```
58
+
59
+ The signature is `({ request, context, callbacks, caller, team, meta }) => Promise<Response>`, where `team` is the team a team-scoped call runs for. A session-scoped handler has no `team`. An endpoint that declares no `writes` cannot change state: return only what fits the declared `responseSchema`.
60
+
61
+ To change state, declare the capabilities you may write and return the change alongside the response. `scope`, `writes`, and `visibility` can go straight into the `definition` you pass to `new endpoint`, which then scaffolds a handler of the writing shape:
62
+
63
+ ```json
64
+ { "scope": "team", "writes": ["price"] }
65
+ ```
66
+
67
+ ```typescript
68
+ export const Handle: Handler = async ({ request, context }) => ({
69
+ response: { previous: context.price.Price, current: request.price },
70
+ capabilities: { price: { ...context.price, Price: request.price } },
71
+ });
72
+ ```
73
+
74
+ The generated `Handler` type switches shape when `writes` is present, so the compiler tells you which one you owe, and a writing endpoint that returns a bare value is refused at run time. Writes are held to the declaration, and are not put to the game's validators: see [Endpoints (concept)](../guides/endpoints.md#endpoints-and-validators) for why.
75
+
76
+ 3. Set the scope if the endpoint is not called against a single team:
77
+
78
+ ```json
79
+ { "scope": "session" }
80
+ ```
81
+
82
+ `scope` defaults to `'team'`, which gives the handler that team's board and a `team` naming it: `team.teamId`, `team.index` and `team.name`. Use `team`, not `caller`, to find the team's own row in anything keyed by team: `caller` says who is calling, and a facilitator's agent or the CLI calls on a team's behalf without being on it, so its `caller.teamId` is absent. `caller.teamId` is set only for a participant or an observer, and names their own team. A session UI, a setup screen, or a scenario editor has no team of its own, so calling a team-scoped endpoint from one fails the build.
83
+
84
+ 4. Declare the endpoint on every UI that should be allowed to call it:
85
+
86
+ ```json
87
+ {
88
+ "definition": {
89
+ "endpoints": ["summarise"]
90
+ }
91
+ }
92
+ ```
93
+
94
+ UIs that don't list the endpoint cannot call it. Add the identifier to each consuming UI's `endpoints` array.
95
+
96
+ 5. Use the endpoint from the UI. The generated `types.ts` makes `callbacks.endpoints` strongly typed:
97
+
98
+ ```typescript
99
+ const { summary } = await callbacks.endpoints['summarise']({
100
+ text: 'lots of text here',
101
+ });
102
+ ```
103
+
104
+ ## Verify
105
+
106
+ - `pnpm run sdk endpoint list` shows the endpoint with its scope and whether it writes
107
+ - `pnpm run sdk endpoint call <identifier> --team 1 --request '{...}'` runs it without a UI
108
+ - `pnpm run sdk generate` (or any `new`/`update` command) produces `endpoints/<identifier>/types.ts` with `Request`, `Response`, `Context`, and `Handler` exports (plus `Writes`, where the endpoint declares any)
109
+ - Calling an undeclared endpoint from a UI is a TypeScript error
110
+ - The dev server rejects calls when the UI did not declare the endpoint or when the request shape does not match `requestSchema`
111
+
112
+ ## Troubleshooting
113
+
114
+ - _'Endpoint X not found in configuration'_: re-run `pnpm run sdk generate`; the endpoint's bundle must sit at `endpoints/<identifier>/endpoint.sdk.json`
115
+ - _'Endpoint "X" rejected the request: \<path\> must have required property ...'_: the iframe sent a payload that does not match `requestSchema`. Check that the UI's call matches the declared request shape
116
+ - _'"ui" does not list endpoint "X" in its endpoints'_: add the endpoint identifier to the UI's `endpoints` array
117
+ - _'Resource "X" attempted to call callback "Y" but did not declare it in its bundle's "uses" array'_: add the `ApiUse` value to the endpoint bundle's `uses` array
118
+ - _'which is team-scoped, but this surface has no team of its own'_: a session UI, setup screen, or scenario editor is calling it; set `"scope": "session"` on the endpoint
119
+ - _'wrote X, which it does not declare in writes'_: the handler returned a capability outside its `writes`; add it, or stop returning it
120
+ - _'The game moved on while the endpoint was running'_: a tick committed mid-call, so the patch was computed against a board that no longer exists. Make the handler faster, or move the work into the engine
121
+
122
+ ## Related
123
+
124
+ - [Make an endpoint callable from outside the game](make-an-endpoint-external.md)
125
+ - [Call an endpoint from a UI](call-endpoint-from-ui.md)
126
+ - [Declare API uses](declare-api-uses.md)
127
+ - [Endpoints (concept)](../guides/endpoints.md)