@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,86 @@
1
+ # Recipes
2
+
3
+ Each recipe is one task, start to finish: prerequisites, numbered steps, a way to check it worked, and fixes for what usually goes wrong. For the full reference on a resource type, see the [guides](../guides/index.md).
4
+
5
+ ## Getting started
6
+
7
+ - [Start a game from an empty package](start-from-an-empty-package.md): build a game from nothing, with a capability, an engine, and a game UI
8
+ - [Create an engine](create-engine.md): an engine that reads one capability and writes another
9
+ - [Create a capability](create-capability.md): a new slice of the game's state
10
+ - [Create settings](create-settings.md): a read-only parameter logic and screens read, which scenarios and facilitators can change before play
11
+ - [Shape your game](create-game.md): set ticks, teams, and locales
12
+
13
+ ## Scenarios
14
+
15
+ - [Create a scenario](create-scenario.md): a variant of the game with its own settings and assets
16
+ - [Share assets across scenarios](share-assets-across-scenarios.md): reference one file from several scenarios with a `shared:` reference
17
+
18
+ ## Facilitation
19
+
20
+ - [Create a facilitator guide](create-guide.md): markdown facilitation content shown on the facilitator's page
21
+ - [Create a presentation](create-presentation.md): the facilitator's slide deck, live with the session's results and exportable to PDF and PowerPoint
22
+
23
+ ## User interfaces
24
+
25
+ - [Create a user interface](create-user-interface.md): a game UI that shows the game's state
26
+ - [Design state for concurrent writes](design-state-for-concurrent-writes.md): shape capabilities and `setData` calls so participants writing at once never overwrite each other
27
+ - [Share logic between an engine and a UI](share-logic-between-engine-and-ui.md): one calculation in one file, so a live preview cannot drift from the engine's result
28
+ - [Upload and display files](upload-and-display-files.md): let participants upload files and show them in results
29
+ - [Create a session UI](create-session-ui.md): a facilitator screen that shows every team and controls the session
30
+ - [Create a waiting room UI](create-waiting-room-ui.md): replace the host's built-in holding screen with your own
31
+ - [Create a setup screen](create-setup.md): a short form at session creation that sets this session's settings
32
+ - [Create a scenario editor](create-scenario-editor.md): a screen for building new scenarios for a released game
33
+ - [Adapt a UI for observers](adapt-ui-for-observers.md): tell a facilitator observing a team from a participant with `useUserInterfaceViewerRole`
34
+ - [Remember a viewer's choice](remember-a-viewers-choice.md): keep a tab, a collapsed panel or a sort order across a reload with `useViewerStorage`, keyed by session, surface and viewer
35
+ - [Adapt a game for walkthroughs](adapt-a-game-for-walkthroughs.md): use `runKind` to fill in what a solo demonstration lacks and keep outward effects out of it
36
+
37
+ ## Multi-team games
38
+
39
+ - [Observe other teams' state live](observe-other-teams-state.md): show one team's pending decisions on other teams' screens with team-readable capabilities
40
+ - [Negotiate across teams](negotiate-across-teams.md): let two teams propose, agree, and settle an exchange at tick time
41
+ - [Show all teams' participants in a UI](show-all-teams-participants.md): a directory of who is on each team, with `getAllTeamsParticipants`
42
+ - [Review a past day in a UI](review-a-past-day.md): scrub back through a team's tick history with `getPastState`
43
+
44
+ The full reference is the [Multi-team games](../guides/multi-team-games.md) guide.
45
+
46
+ ## Progression
47
+
48
+ - [Use requestTick in a UI](use-request-tick.md): advance the game from a button in your UI
49
+ - [Use requestPlay in a setup screen](use-request-play.md): leave the configuration step from a button in your setup screen
50
+ - [Create a tick controller](create-tick-controller.md): automatic tick timing driven by the game's logic
51
+ - [End a game when a condition is met](end-game-on-condition.md): stop play when a goal is reached rather than after a fixed number of ticks
52
+
53
+ ## Lifecycle hooks
54
+
55
+ - [Create a hook](create-hook.md): react to a lifecycle signal, such as a participant joining a team
56
+ - [Test a multi-role game in the dev server](test-a-multi-role-game.md): add, name, and open several participants per team to play through a role-based interaction
57
+
58
+ ## Validation
59
+
60
+ - [Create a validator](create-validator.md): reject UI mutations that break the game's rules
61
+
62
+ ## Runtime callbacks
63
+
64
+ - [Declare the runtime callbacks a resource uses](declare-api-uses.md): tell the SDK which callbacks, such as `generateText` and `extractText`, a resource may call
65
+
66
+ ## Endpoints
67
+
68
+ - [Create an endpoint](create-endpoint.md): a server-side handler UIs call by identifier
69
+ - [Call an endpoint from a UI](call-endpoint-from-ui.md): declare an endpoint and call it with a typed callback
70
+ - [Make an endpoint callable from outside the game](make-an-endpoint-external.md): publish an endpoint to a facilitator's AI agent or a platform API
71
+ - [Act on the session from an endpoint](act-on-the-session-from-an-endpoint.md): close the waiting room, advance a tick and more, in one call
72
+ - [Take a file in an endpoint](take-a-file-in-an-endpoint.md): accept an image or PDF from a UI, a script or an AI agent
73
+
74
+ ## Deployment and distribution
75
+
76
+ - [Distribute a built package](distribute-package.md): bundle `dist/` into an uploadable zip archive
77
+ - [Publish a package to the Serious Gaming platform](publish-a-package.md): build, bundle, and upload in one command
78
+ - [Check a package from a script](check-a-package-from-a-script.md): validate and tick from CI or an AI agent, branching on exit codes and reading each diagnostic's file and pointer
79
+
80
+ ## Testing and verification
81
+
82
+ - [Drive a game from the CLI](../cli-simulation-testing.md): configure, tick, patch, and read a session from the terminal
83
+ - [Test a user interface](test-user-interface.md): write and run React component tests
84
+ - [Test UIs end to end with Playwright](e2e-testing.md): browser tests against the dev server
85
+ - [Test a whole game in-process](test-with-engine.md): whole games with `createTestSession`, for rules, flow, hooks, validators and completion
86
+ - [Fix duplicate package errors in a UI](fix-duplicate-packages.md): resolve 'Invalid hook call' errors caused by two copies of React
@@ -0,0 +1,101 @@
1
+ # Make an endpoint callable from outside the game
2
+
3
+ **Keywords:** external endpoint, visibility, agent callable endpoint, AI agent, facilitator agent, expose endpoint to AI, agent tool, MCP endpoint, platform API, integration
4
+
5
+ Publish one of your endpoints to callers outside the game, such as a facilitator's AI agent or a platform API, so it can be invoked by asking or by another system rather than by clicking.
6
+
7
+ ## Prerequisites
8
+
9
+ - An endpoint that already works (see [Create an endpoint](create-endpoint.md))
10
+ - A clear idea of which request from outside the game it answers
11
+
12
+ ## Steps
13
+
14
+ 1. Set `visibility` to `external` and add a description in the endpoint's bundle, `endpoints/setprice/endpoint.sdk.json`:
15
+
16
+ ```json
17
+ {
18
+ "definition": {
19
+ "displayName": "Set the unit price",
20
+ "description": "Sets a team's unit price, in pounds per unit. Use when a facilitator asks to change what a team charges, for example to unblock a team that has priced itself out of the market. Must be between 0 and 50.",
21
+ "scope": "team",
22
+ "visibility": "external",
23
+ "writes": ["price"]
24
+ }
25
+ }
26
+ ```
27
+
28
+ The description is required once `visibility` is `external`, and the build fails without one. It is what an outside caller, an AI agent most of all, reads to decide whether to call this endpoint rather than another, so say **when to reach for it**, not just what it does.
29
+
30
+ An endpoint is `internal` when `visibility` is absent, and a caller outside the game is refused it. Which outside callers may use an external endpoint, and how they sign in, is up to the platform.
31
+
32
+ 2. Set the scope to match what the call acts on:
33
+
34
+ - `"scope": "team"` for anything about one team, such as setting its decision. The platform resolves which team the facilitator meant and passes it to your handler on `team`, with its id, index and display name
35
+ - `"scope": "session"` for anything session-wide
36
+
37
+ Do not add a team to your `requestSchema`. A team the model types is a team the model can invent.
38
+
39
+ 3. Put the bounds in the request schema, not only in the description:
40
+
41
+ ```json
42
+ {
43
+ "requestSchema": {
44
+ "type": "object",
45
+ "properties": {
46
+ "price": { "type": "number", "minimum": 0, "maximum": 50 }
47
+ },
48
+ "required": ["price"],
49
+ "additionalProperties": false
50
+ }
51
+ }
52
+ ```
53
+
54
+ The runtime refuses a request outside this before your handler runs. The description stops the model making the call; the schema stops the call landing.
55
+
56
+ An external endpoint's request must be an object at the top, because an outside caller passes named arguments. One that takes no input declares `{ "type": "object", "properties": {}, "additionalProperties": false }`.
57
+
58
+ 4. Check the request in the handler for anything the schema cannot express:
59
+
60
+ ```typescript
61
+ export const Handle: Handler = async ({ request, context }) => {
62
+ if (!Number.isFinite(request.price)) {
63
+ throw new Error('A price must be a finite number');
64
+ }
65
+ return {
66
+ response: { previous: context.price.Price, current: request.price },
67
+ capabilities: { price: { ...context.price, Price: request.price } },
68
+ };
69
+ };
70
+ ```
71
+
72
+ An endpoint's write is not put to your validators, whoever called it, so the handler is where a rule about the request itself belongs.
73
+
74
+ 5. Read it back as an outside caller will receive it:
75
+
76
+ ```bash
77
+ pnpm run sdk endpoint list --external
78
+ ```
79
+
80
+ ## Verify
81
+
82
+ - `pnpm run sdk endpoint list --external` shows the endpoint, its scope, whether it writes, and its description
83
+ - `pnpm run sdk endpoint call setprice --external --team 1 --request '{"price": 3.53}'` returns the expected response, called the way an outside caller calls it
84
+ - `pnpm run sdk view` shows the change on the right team, and no others
85
+ - A request outside the schema is refused, and leaves the board untouched
86
+
87
+ ## Troubleshooting
88
+
89
+ - _'is external, so it must carry a description'_: Add `description` to the endpoint's definition
90
+ - _'is external, so its requestSchema must declare `"type": "object"`'_ (`EXTERNAL_REQUEST_NOT_OBJECT`): Wrap the request in an object with a named property for each value, such as `{ "price": 3.53 }` rather than `3.53`
91
+ - _'is internal, so a caller outside the game cannot call it'_: Set `"visibility": "external"` on the endpoint
92
+ - _'`agentCallable` has been replaced by `visibility`'_: Replace `"agentCallable": true` with `"visibility": "external"`
93
+ - _'which is team-scoped, but this surface has no team of its own'_: A session UI, setup screen, or scenario editor also lists this endpoint; give it `"scope": "session"` or stop listing it there
94
+ - _Two endpoints get confused for each other_: Run `endpoint list --external` and read the descriptions together. If they do not say when to pick one over the other, the agent has nothing to go on
95
+ - _No user interface requests it_: That warning is suppressed for external endpoints, whose caller no bundle names. If you see it, `visibility` is not `external`
96
+
97
+ ## Related
98
+
99
+ - [Structuring a game for AI facilitation](../guides/ai-facilitation.md)
100
+ - [Create an endpoint](create-endpoint.md)
101
+ - [Endpoints (concept)](../guides/endpoints.md)
@@ -0,0 +1,164 @@
1
+ # Negotiate across teams
2
+
3
+ Build a game where two teams propose, agree, and settle an exchange with each other, using a team-readable ledger and authoritative settlement at tick time.
4
+
5
+ **Keywords:** negotiation, trade, exchange, barter, cross-team transaction, two teams agree, atomic settlement, proposal, offer, accept, counterparty, ledger, transfer resources between teams
6
+
7
+ ## What you'll build
8
+
9
+ A game UI writes only its own team's state, so no UI can move a resource from one team to another: a UI is untrusted, and a mutual exchange needs one authority. Every negotiation game therefore has the same shape: **teams publish intent, the engine settles it.** Each team publishes proposals and acceptances in a `team-readable` capability, and an aggregate engine pairs them and moves both sides at tick time. For who may write each capability scope, see [Choosing between team-readable and shared](../guides/multi-team-games.md#choosing-between-team-readable-and-shared).
10
+
11
+ ## Prerequisites
12
+
13
+ - A shared-state game (`teams.mode: 'shared-state'`): see [Multi-team games](../guides/multi-team-games.md)
14
+ - Familiarity with [Observe other teams' state](observe-other-teams-state.md)
15
+
16
+ ## Steps
17
+
18
+ ### 1. Model proposals as a team-readable ledger
19
+
20
+ Each team writes only its own copy; counterparties read across. Key by proposal id, never an array (see [Design state for concurrent writes](design-state-for-concurrent-writes.md)):
21
+
22
+ ```bash
23
+ pnpm run sdk new capability '{
24
+ "definition": {
25
+ "identifier": "trades",
26
+ "scope": "team-readable",
27
+ "variablesSchema": {
28
+ "type": "object",
29
+ "properties": {
30
+ "Proposed": {
31
+ "type": "object",
32
+ "description": "Offers this team has made, keyed by proposal id",
33
+ "default": {},
34
+ "additionalProperties": {
35
+ "type": "object",
36
+ "properties": {
37
+ "toTeamId": { "type": "string" },
38
+ "give": { "type": "number" },
39
+ "take": { "type": "number" }
40
+ },
41
+ "required": ["toTeamId", "give", "take"],
42
+ "additionalProperties": false
43
+ }
44
+ },
45
+ "Accepted": {
46
+ "type": "object",
47
+ "description": "Proposal ids from other teams that this team has accepted",
48
+ "default": {},
49
+ "additionalProperties": { "type": "boolean" }
50
+ }
51
+ },
52
+ "required": ["Proposed", "Accepted"],
53
+ "additionalProperties": false
54
+ }
55
+ }
56
+ }'
57
+ ```
58
+
59
+ Two one-sided declarations, each written by its own team, are what stands in for a two-party handshake. Agreement is the **conjunction**: a proposal by A that appears in B's `Accepted`.
60
+
61
+ ### 2. Propose and accept from the UI
62
+
63
+ Each side writes only its own capability, so both calls are ordinary `setData`:
64
+
65
+ ```tsx
66
+ const ownTeam = useUserInterfaceOwnTeam();
67
+ const { setData } = useUserInterfaceData<Context, Settings>();
68
+ const counterparties = useUserInterfaceCrossTeamData<{ trades: Trades }>();
69
+
70
+ // Team A proposes. The id must be stable and collision-free across teams.
71
+ const propose = (toTeamId: string, give: number, take: number) => {
72
+ const id = `${ownTeam?.teamId}-${Date.now()}`;
73
+ void setData({ trades: { Proposed: { [id]: { toTeamId, give, take } } } });
74
+ };
75
+
76
+ // Team B accepts by id. It never touches team A's state.
77
+ const accept = (proposalId: string) => {
78
+ void setData({ trades: { Accepted: { [proposalId]: true } } });
79
+ };
80
+ ```
81
+
82
+ `setData` deep-merges, so writing one key under `Proposed` leaves siblings alone and two participants on the same team do not clobber each other.
83
+
84
+ ### 3. Settle authoritatively in an aggregate engine
85
+
86
+ This is the only place the exchange actually happens. The engine (with `trades` and a `holdings` capability in its `reads` and `writes`) sees every team at once, so it can pair proposals with acceptances and move both sides atomically:
87
+
88
+ ```typescript
89
+ // engine/logic.ts
90
+ import { settle } from '../shared/settlement';
91
+
92
+ export const RunAggregate: RunAggregateFunction<TeamContext, SharedContext, Settings> = async ({
93
+ teams,
94
+ }) => {
95
+ const agreed = settle(
96
+ teams.map((t) => ({
97
+ teamId: t.teamId,
98
+ trades: t.context.trades,
99
+ holdings: t.context.holdings,
100
+ }))
101
+ );
102
+
103
+ return {
104
+ shared: { capabilities: {} },
105
+ teams: teams.map((t) => ({
106
+ capabilities: {
107
+ ...t.context,
108
+ holdings: agreed.holdingsByTeam[t.teamId],
109
+ trades: { Proposed: {}, Accepted: {} },
110
+ },
111
+ })),
112
+ };
113
+ };
114
+ ```
115
+
116
+ Rules worth stating explicitly in your settlement function:
117
+
118
+ - **Only settle a mutually-agreed pair.** A proposal without a matching acceptance is not a trade
119
+ - **Reject what cannot be paid.** Check both sides' holdings before transferring; a trade that would overdraw either party must be dropped, not applied
120
+ - **Make ordering deterministic.** When two agreed trades compete for the same resource, sort by proposal id (or another stable key) before applying, so the same inputs always produce the same result
121
+
122
+ ### 4. Preview the outcome without duplicating the rules
123
+
124
+ Participants want to see the effect before the tick. Put the settlement calculation in a shared file and import it from both sides, so the preview cannot drift from the scored result:
125
+
126
+ ```tsx
127
+ import { projectHoldings } from '../../shared/settlement';
128
+
129
+ const preview = projectHoldings(ownHoldings, pendingTradesForMe);
130
+ ```
131
+
132
+ See [Share logic between an engine and a UI](share-logic-between-engine-and-ui.md) for the mechanics and the two constraints on shared files.
133
+
134
+ ### 5. Review past trades
135
+
136
+ Clearing `Proposed` and `Accepted` in step 3 costs nothing in review: a tick's snapshot is the board the tick was submitted with, so `getPastState` still shows what each team proposed and accepted (see [Review a past day in a UI](review-a-past-day.md)).
137
+
138
+ ## Verify
139
+
140
+ - Two teams can propose and accept, and neither write is rejected
141
+ - A proposal with no matching acceptance does not settle
142
+ - A trade neither side can pay for does not settle, and leaves both holdings untouched
143
+ - After the tick, both teams' holdings moved by the same amount in opposite directions, and totals across the session are conserved
144
+ - The UI's preview matches the post-tick holdings for the same inputs
145
+ - `getPastState` on the completed tick shows what each team proposed and accepted
146
+
147
+ ## Troubleshooting
148
+
149
+ | Problem | Solution |
150
+ | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
151
+ | A UI write to another team's capability is rejected | Expected: teams publish intent about themselves, and only an engine settles. See [What you'll build](#what-youll-build) |
152
+ | Setting `scope: 'shared'` on the ledger fails the build | A game UI cannot write a shared capability. The ledger must be `team-readable` |
153
+ | `useUserInterfaceCrossTeamData` returns an empty array | The UI must list the capability in `crossTeamReads`, and the game must be `teams.mode: 'shared-state'` |
154
+ | A trade settles twice | Clear `Proposed` and `Accepted` in the same tick that settles them |
155
+ | Both teams' totals drift | You are applying one side of a pair. Transfer as a single operation over both parties, and assert conservation in an [engine test](../guides/engine-testing.md) |
156
+ | The preview disagrees with the result | The two are not running the same code. See step 4 |
157
+
158
+ ## Related
159
+
160
+ - [Share logic between an engine and a UI](share-logic-between-engine-and-ui.md)
161
+ - [Observe other teams' state](observe-other-teams-state.md)
162
+ - [Design state for concurrent writes](design-state-for-concurrent-writes.md)
163
+ - [Review a past day](review-a-past-day.md)
164
+ - [Multi-team games](../guides/multi-team-games.md)
@@ -0,0 +1,119 @@
1
+ # Observe other teams' state live
2
+
3
+ **Keywords:** cross-team, crossTeamReads, team-readable, observe other teams, live decisions, pending decisions, shared pool, useUserInterfaceCrossTeamData, getPastState, CrossTeamContext, capability scope, useUserInterfaceOwnTeam, own team, which team am I
4
+
5
+ Let one team's user interface watch another team's pending decisions live, without waiting for a tick. Typical use: a shared resource pool gauge that settles while teams negotiate.
6
+
7
+ ## Prerequisites
8
+
9
+ - A multi-team game with `teams.mode: 'shared-state'` in `seriousgaming.sdk.json`
10
+ - A capability holding the decisions teams should be able to see
11
+
12
+ ## Steps
13
+
14
+ 1. Set the capability's scope to `team-readable` in `capability.sdk.json`:
15
+
16
+ ```json
17
+ {
18
+ "definition": {
19
+ "scope": "team-readable",
20
+ "variablesSchema": {
21
+ "type": "object",
22
+ "properties": {
23
+ "DonateToPool": { "type": "number", "default": 0 }
24
+ },
25
+ "required": ["DonateToPool"],
26
+ "additionalProperties": false
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ 2. Declare the cross-team read on the observing user interface in `ui.sdk.json`:
33
+
34
+ ```json
35
+ {
36
+ "definition": {
37
+ "reads": ["decisions"],
38
+ "crossTeamReads": ["decisions"],
39
+ "writes": ["decisions"]
40
+ }
41
+ }
42
+ ```
43
+
44
+ 3. Read every team's live value in the UI with `useUserInterfaceCrossTeamData`:
45
+
46
+ ```typescript
47
+ import { useUserInterfaceCrossTeamData } from '@seriousgaming/sdk/ui';
48
+
49
+ // Generated beside Context, and narrower: it carries exactly the
50
+ // capabilities this surface listed in `crossTeamReads`.
51
+ import type { CrossTeamContext } from '../types';
52
+
53
+ function PoolGauge() {
54
+ const teams = useUserInterfaceCrossTeamData<CrossTeamContext>();
55
+ const pool = teams.reduce((sum, team) => sum + team.context.decisions.DonateToPool, 0);
56
+ return (
57
+ <ul>
58
+ {teams.map((team) => (
59
+ <li key={team.teamId}>
60
+ {team.name}: {team.context.decisions.DonateToPool}
61
+ </li>
62
+ ))}
63
+ <li>Pool total: {pool}</li>
64
+ </ul>
65
+ );
66
+ }
67
+ ```
68
+
69
+ The array contains one entry per team, **including your own**. Identify yours with `useUserInterfaceOwnTeam()`, which returns the `TeamInfo` of the team whose surface you are rendering:
70
+
71
+ ```typescript
72
+ import { useUserInterfaceCrossTeamData, useUserInterfaceOwnTeam } from '@seriousgaming/sdk/ui';
73
+
74
+ const ownTeam = useUserInterfaceOwnTeam();
75
+ const teams = useUserInterfaceCrossTeamData<CrossTeamContext>();
76
+ const others = teams.filter((team) => team.teamId !== ownTeam?.teamId);
77
+ ```
78
+
79
+ **Pass `CrossTeamContext`, not `Context`.** `Context` is every capability the surface reads or writes; `CrossTeamContext` is only the ones it declared `crossTeam`. Reading another team's value of a capability left out of `crossTeamReads` returns your own team's value and nobody else's, which looks exactly like an early session where nobody has written yet. Typed against `CrossTeamContext`, that read is a compile error instead.
80
+
81
+ Do not use `useUserInterfaceParticipant()?.teamId` for this. It is `null` when a facilitator observes the surface, so the split silently collapses and every team reads as 'theirs' in the observer view. `useUserInterfaceOwnTeam()` is populated for observers too. `useUserInterfaceTeams()` is a different hook: it lists every team in the session and does not mark which is yours.
82
+
83
+ 4. Regenerate types and rebuild:
84
+
85
+ ```bash
86
+ pnpm run sdk generate
87
+ pnpm run sdk build
88
+ ```
89
+
90
+ ## Notes
91
+
92
+ - Observed values are pending state: they can change again before the tick consumes them
93
+ - Other teams' values are read-only; each team still writes only its own copy
94
+ - Keep private data private by splitting capabilities: only the capability marked `team-readable` is observable
95
+ - A cross-team read is the most expensive thing a screen can declare: one entry per team, delivered to every participant, every time anyone writes. That is free in a workshop and heavy in a large cohort. If the screen shows a summary rather than each team's own value (a ranking, a market price, an average, a count of who has submitted), compute it in `RunAggregate` into a `scope: shared` capability instead, which is one value whatever the team count. See [Choosing between team-readable and shared](../guides/multi-team-games.md#choosing-between-team-readable-and-shared)
96
+ - `useUserInterfaceCrossTeamData` is live only. To review other teams' values at a committed past tick, ask its historical counterpart for every team: `getPastState({ fromTick: tick, teams: 'all' })` (see [Review a past day](review-a-past-day.md)). Entries come back in the same shape the hook returns, plus the tick they were captured at, so one renderer serves both. Mind the timing: a past value is the board that tick was _submitted with_, so it carries each team's decisions for the tick as well as the previous tick's outcomes (see [Reviewing past ticks](../guides/ui.md#reviewing-past-ticks)). If the cross-team fact you want is a single settled outcome rather than each team's raw copy, computing it into a `scope: shared` capability at tick time is often simpler: the shared store is included in every team's own board, so it becomes reviewable without any cross-team declaration at all
97
+
98
+ ## Verify
99
+
100
+ - Start the dev server with at least two teams: `pnpm run sdk dev --teams 2`
101
+ - Open team 1 and team 2 side by side, change the decision on team 1, and confirm team 2's view updates within about a second, without a tick
102
+
103
+ ## Troubleshooting
104
+
105
+ - **Build fails with 'a crossTeamReads entry requires scope: team-readable'**: the capability is still default-scoped or shared; set `"scope": "team-readable"` on the capability definition
106
+ - **Build fails with 'lists X in crossTeamReads but does not read it'**: add the same identifier to `reads`. `reads` is what says the capability arrives at all; `crossTeamReads` only says every team's value of it does
107
+ - **Build fails with 'interaction mode is not shared-state'**: team-readable capabilities only work in shared-state games; set `"teams": { "mode": "shared-state" }` at the top level of `seriousgaming.sdk.json`
108
+ - **`crossTeamReads` is rejected as an unknown property**: only the game UI and the waiting room UI carry the field. Engines, endpoints, validators, tick controllers, and session UIs receive cross-team data through an aggregate engine instead
109
+ - **The hook returns an empty array**: check the UI lists the capability in `crossTeamReads`. The hook takes no capability identifier: each entry's `context` carries exactly the capabilities the bundle declared that way, so there is nothing to misspell
110
+ - **`CrossTeamContext` is `Record<string, never>`**: the surface lists nothing in `crossTeamReads`, so there is nothing to observe. Add the capability to `crossTeamReads` and regenerate
111
+ - **A property 'x' does not exist on type 'CrossTeamContext' error**: `x` is readable but is not in `crossTeamReads`, so only your own team's copy would ever arrive. Add the flag if the surface should see every team's, or read it from `data` if it should not
112
+
113
+ ## Related
114
+
115
+ - [Negotiate across teams](negotiate-across-teams.md): when teams need to transact, not just observe
116
+ - [Share logic between an engine and a UI](share-logic-between-engine-and-ui.md): keep a live preview in step with the settled result
117
+ - [Multi-team games](../guides/multi-team-games.md)
118
+ - [Create a capability](create-capability.md)
119
+ - [Create a user interface](create-user-interface.md)
@@ -0,0 +1,139 @@
1
+ # Publish a package to the Serious Gaming platform
2
+
3
+ Build, bundle, and upload a package to the Serious Gaming platform in a single command.
4
+
5
+ **Keywords:** publish, upload, deploy, release, publisher, api key, publishing access, permission, ship
6
+
7
+ ## What you'll build
8
+
9
+ A repeatable pipeline that takes your working tree from source to a hosted package version. The default flow:
10
+
11
+ 1. `seriousgaming-sdk build` produces `dist/`
12
+ 2. `seriousgaming-sdk package bundle` (auto-runs if the zip is missing) produces `dist/<identifier>-<version>.zip`
13
+ 3. `seriousgaming-sdk package publish` POSTs the zip to `/api/v0/admin/publishers/<slug>/packages` and prints the resulting `PackageVersionResponse`
14
+
15
+ Designed so an AI agent can drive the whole sequence from a single prompt.
16
+
17
+ ## Prerequisites
18
+
19
+ - A built package (`seriousgaming-sdk build` has run at least once)
20
+ - A valid semver string in `package.json` (`version`)
21
+ - A platform account with **publishing access**, and an API key issued from it. Business Simulations grants publishing access: ask your contact there to enable it on your account
22
+ - The slug of the publisher you are uploading under
23
+ - Node 24 or newer, the version the SDK supports
24
+
25
+ ## Steps
26
+
27
+ ### 1. Issue an API key
28
+
29
+ 1. Open the platform and go to your organisation's settings page, then the API keys section
30
+ 2. Create a key and give it a descriptive name (like 'CI publish' or '<your machine> local')
31
+ 3. Copy the key to your clipboard. It will not be shown again
32
+
33
+ Keys take the form `sg_<lookupId>_<secret>`. A key acts as the account that created it, so create it from the account Business Simulations gave publishing access; the upload endpoint refuses a key whose account does not have it.
34
+
35
+ Never paste the key into a command line directly. Put it in a `.env` file (and `.gitignore` that file), export it from your shell profile, or store it in the [global settings file](../guides/global-settings.md) so it stays out of shell history. If you lose the key, revoke it on the same page and create a new one.
36
+
37
+ ### 2. Configure the API key and publisher
38
+
39
+ Add to `.env` in your package's working directory:
40
+
41
+ ```bash
42
+ SERIOUSGAMING_API_KEY=sg_your_lookup_id_your_secret
43
+ SERIOUSGAMING_PUBLISHER=your-publisher-slug
44
+ ```
45
+
46
+ The publisher slug scopes the upload. The CLI resolves it from (highest priority first) the `--publisher` flag, the `SERIOUSGAMING_PUBLISHER` environment variable, then `platform.defaultPublisher` in your global settings. If none resolves, the command fails rather than guessing. The first upload of a package binds it to that publisher, and later versions must use the same one.
47
+
48
+ If you publish under one publisher most of the time, set it once in the global settings file so every project picks it up:
49
+
50
+ ```bash
51
+ # Replace the slug with your own; `config set` overwrites any value already there
52
+ pnpm run sdk config set platform.defaultPublisher your-publisher-slug
53
+ ```
54
+
55
+ For a non-production target (dev, staging) also set:
56
+
57
+ ```bash
58
+ SERIOUSGAMING_API_HOST=https://staging.example.com
59
+ ```
60
+
61
+ All values can also be set with shell `export`, or overridden per-invocation with `--api-key`, `--publisher`, and `--host`.
62
+
63
+ ### 3. Bump the version
64
+
65
+ Production runtimes reject re-uploads of the same identifier and version, so bump before each publish:
66
+
67
+ ```bash
68
+ pnpm run sdk package version patch
69
+ ```
70
+
71
+ ### 4. Build and publish
72
+
73
+ ```bash
74
+ pnpm run sdk build
75
+ pnpm run sdk package publish
76
+ ```
77
+
78
+ The publish command auto-runs `package bundle` when the zip is missing, so a fresh build can ship in one step. To skip auto-bundling pass `--no-auto-bundle` and run `package bundle` yourself.
79
+
80
+ ### 5. Capture the result in CI
81
+
82
+ ```bash
83
+ pnpm run --silent sdk package publish --json > publish.json
84
+ ```
85
+
86
+ On a 200 response the envelope's `data` is the server's `PackageVersionResponse`:
87
+
88
+ ```json
89
+ {
90
+ "ok": true,
91
+ "command": "package publish",
92
+ "data": {
93
+ "handle": "pv_abc123",
94
+ "packageHandle": "pkg_xyz456",
95
+ "packageIdentifier": "com.example.coffeeshop",
96
+ "packageName": "Coffee Shop",
97
+ "version": "1.2.0",
98
+ "createdAt": "2026-05-15T12:00:00Z",
99
+ "games": [{ "identifier": "com.example.coffeeshop.game" }]
100
+ },
101
+ "warnings": []
102
+ }
103
+ ```
104
+
105
+ On any non-2xx response the CLI exits non-zero, and the envelope is `{ "ok": false, "error": { "code", "message" }, ... }`, so a capture script has one shape to parse and a code to branch on: `PLATFORM_UNAUTHORISED` (exit 5) for a bad key, `VERSION_EXISTS` (exit 3) for a version already uploaded, `PLATFORM_REJECTED` (exit 3) for a package the platform refused. See [Exit codes](../cli-reference.md#exit-codes).
106
+
107
+ A complete CI sequence:
108
+
109
+ ```bash
110
+ # In CI, after lint/test/typecheck have passed
111
+ pnpm run --silent sdk package version patch --json | tee version.json
112
+ pnpm run sdk build
113
+ pnpm run --silent sdk package publish --json | tee publish.json
114
+ ```
115
+
116
+ `version.json` carries `previous`, `next`, and the files touched in its `data`. `publish.json` carries the hosted version's handle for any downstream step (notification, tagging, audit log).
117
+
118
+ ## Verify
119
+
120
+ - The CLI exits 0
121
+ - `publish.json` contains a `handle` starting with `pv_`
122
+ - The hosted dashboard lists the new version under your package
123
+
124
+ ## Troubleshooting
125
+
126
+ - **'No API key configured'**: set `SERIOUSGAMING_API_KEY` in your environment or pass `--api-key`
127
+ - **'No publisher configured'**: set `SERIOUSGAMING_PUBLISHER`, pass `--publisher <slug>`, or run `pnpm run sdk config set platform.defaultPublisher <slug>`
128
+ - **'Invalid API key'** (401): the token is malformed or has been revoked. Issue a new one
129
+ - **'The account that created this API key does not have publishing access'** (403): the key is valid, but its account cannot publish. Ask Business Simulations to grant publishing access to that account, or use a key from an account that has it
130
+ - **An identifier error mentioning a publisher** (400): the package is already bound to a different publisher. Upload later versions under the same publisher it was first published with
131
+ - **'Version X.Y.Z of <identifier> already exists'** (409): bump the version (`pnpm run sdk package version patch`) and retry. The CLI deliberately does not auto-bump, so an agent can decide whether to bump or abort
132
+ - **'Bundle not found at dist/...'**: run `pnpm run sdk build` first, or drop `--no-auto-bundle` and let the command bundle for you
133
+ - **Network errors**: the original message is surfaced verbatim. Check connectivity and the `--host` URL
134
+
135
+ ## Related
136
+
137
+ - [CLI reference: package publish](../cli-reference.md#publish-a-package-to-the-serious-gaming-platform)
138
+ - [Distribute a built package](distribute-package.md): the underlying bundle step
139
+ - [Set the package version](../cli-reference.md#set-the-package-version): bumping before publish