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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +38 -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 +27404 -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 +16920 -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 +218 -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 +241 -0
  69. package/dist/docs/cli-and-dev-server.md +217 -0
  70. package/dist/docs/cli-error-codes.md +156 -0
  71. package/dist/docs/cli-reference.md +2489 -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 +577 -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 +339 -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 +123 -3
@@ -0,0 +1,335 @@
1
+ # Endpoints
2
+
3
+ An endpoint is a named, server-side handler that a caller invokes by identifier: one of the game's own user interfaces, or a caller outside the game, such as a facilitator's AI agent or a platform API, when the endpoint allows it. It is how a caller runs code you wrote: you own the request and response shapes, the runtime validates both, and the handler works strictly within the bounds it declares.
4
+
5
+ ## The model
6
+
7
+ ### Why endpoints exist
8
+
9
+ A user interface runs in an iframe the runtime does not trust. Handing it `generateText`, `extractText` or `generateSpeech` directly would let a hijacked or buggy iframe send any prompt it liked, draining quota and producing unaudited LLM traffic. So a UI reaches those services only through an endpoint, which shrinks what can happen from 'whatever prompt the iframe can construct' to 'whatever prompts you have written'.
10
+
11
+ The caller passes structured parameters, and the handler builds the prompt and shapes the answer. Anything the caller sends outside `requestSchema` is refused before the handler runs, and anything the handler returns outside `responseSchema` is refused before it reaches the caller.
12
+
13
+ The same holds for state. An endpoint changes nothing it has not declared: it writes only the capabilities in its `writes`, and carries out only the session operations in its `actions`.
14
+
15
+ ### Anatomy
16
+
17
+ ```
18
+ endpoints/summarise_report/
19
+ ├── endpoint.sdk.json # Request and response schemas, reads, writes, uses
20
+ ├── logic/
21
+ │ ├── logic.ts # Exports Handle, or Plan and Respond
22
+ │ └── logic.test.ts
23
+ └── types.ts # Generated: Request, Response, Context, Handler
24
+ ```
25
+
26
+ The endpoint's identifier is its directory name and is never authored. `endpoint.sdk.json` declares:
27
+
28
+ | Field | Required | What it says |
29
+ | ---------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
30
+ | `requestSchema` | Yes | The request, as a full JSON Schema 7 object |
31
+ | `responseSchema` | Yes | The response, as a full JSON Schema 7 object |
32
+ | `displayName`, `description` | `description` when `external` | What the endpoint does, for author-time tools and, when external, for an outside caller to read |
33
+ | `scope` | No, defaults to `team` | Whether the call acts on one team or the session ([Scope](#scope)) |
34
+ | `reads` | No | The capabilities the handler sees |
35
+ | `writes` | No | The capabilities the handler may change ([Changing state](#changing-state)) |
36
+ | `uses` | No | The gated callbacks the handler may call ([Declare the APIs a resource uses](./llm-integration.md#declare-the-apis-a-resource-uses)) |
37
+ | `settings` | No | The [settings resources](./settings.md) the handler reads |
38
+ | `actions` | No | The session operations it may return ([Acting on the session](#acting-on-the-session)) |
39
+ | `roster` | No, defaults to `false` | Whether `session` lists each team's participants |
40
+ | `visibility` | No, defaults to `internal` | Who may call it ([Calling from outside the game](#calling-from-outside-the-game)) |
41
+
42
+ To scaffold one, follow [Create an endpoint](../recipes/create-endpoint.md).
43
+
44
+ ## Scope
45
+
46
+ `scope` says what a call acts on. A team-scoped endpoint receives that team's board and a `team` naming it (`teamId`, `index` and `name`), whoever calls it. A session-scoped one has neither, because there is no single team for them to mean anything against.
47
+
48
+ The surface calling an endpoint has to be able to supply what its scope needs. A session UI, setup screen or scenario editor has no team of its own, so one that calls a team-scoped endpoint fails the build, which tells you to declare the endpoint `"scope": "session"`.
49
+
50
+ The scenario editor has no session either, so an endpoint it lists may declare no `reads`, `writes` or `actions`, and the build fails with `EDITOR_ENDPOINT_STATEFUL` if it does. See [Calls from the scenario editor](#calls-from-the-scenario-editor).
51
+
52
+ Do not put a team in a request schema. A caller outside the game names the team it means through the platform, which hands it to your handler on `team`, so a team the caller typed can never be a team it invented.
53
+
54
+ ### What each scope may write
55
+
56
+ | Endpoint `scope` | May write | Must not write |
57
+ | -------------------- | ------------------------------------------------------------------------------------ | -------------- |
58
+ | `team` (the default) | `team`, `team-readable` and `private` capabilities, under `capabilities` | `shared` |
59
+ | `session` | `shared` capabilities under `capabilities`; any other scope, per team, under `teams` | Nothing |
60
+
61
+ The build fails when a team-scoped endpoint writes a shared capability, naming both. A team-scoped endpoint has one board, and a shared capability does not live on a board, so the write would have nowhere to go. To change one team, call a team-scoped endpoint against it.
62
+
63
+ A session-scoped endpoint's `capabilities` go to the shared store and reach every team at once, exactly as a facilitator's shared write does. Its own `writes` array is what authorises the write, which matters because a caller outside the game has no bundle to borrow permission from.
64
+
65
+ A session-scoped endpoint has no board of its own, so it names the boards it changes. Its per-team writes go under `teams`, keyed by team id, and every team changes or none does:
66
+
67
+ ```typescript
68
+ export const Handle: Handler = async ({ request, teams }) => ({
69
+ response: { granted: request.workers },
70
+ teams: Object.fromEntries(
71
+ (teams ?? []).map((team) => [
72
+ team.teamId,
73
+ { workers: { WorkerCount: team.context.workers.WorkerCount + request.workers } },
74
+ ])
75
+ ),
76
+ });
77
+ ```
78
+
79
+ 'Give every team two more workers' is then one call rather than one per team, each able to fail on its own.
80
+
81
+ ### What a session-scoped endpoint reads
82
+
83
+ With no board of its own, a session-scoped endpoint receives its declared capabilities split by their scope:
84
+
85
+ - A `shared` capability arrives on `context`, as the single value it is
86
+ - A `team`, `team-readable` or `private` capability arrives on `teams`, one entry per team in stable index order
87
+
88
+ ```typescript
89
+ export const Handle: Handler = async ({ context, teams }) => ({
90
+ average: context.market.AveragePrice, // shared: one value
91
+ behind: (teams ?? [])
92
+ .filter((team) => team.context.decisions.Price > context.market.AveragePrice)
93
+ .map((team) => team.name),
94
+ });
95
+ ```
96
+
97
+ Each entry carries `teamId`, `index`, `name` and that team's `context`. The generated `types.ts` splits to match, so `Context` holds only what genuinely has one value and `TeamContext` holds the rest.
98
+
99
+ **Read wide, answer narrow.** Assembling every team's board is the cheapest shape the SDK has for a cross-team question: it is pulled when the caller asks rather than pushed on every write, and it goes to one caller rather than to every participant's screen. The answer still crosses back to the caller, though, and at a hundred teams a raw dump of every board is both slow and useless to a model. Summarise in the handler, and declare the narrowest `reads` that answer the request.
100
+
101
+ ## Writing the handler
102
+
103
+ ```typescript
104
+ import type { Handler } from '../types';
105
+
106
+ export const Handle: Handler = async ({ request, context, settings, callbacks, caller, meta }) => {
107
+ return { summary: '...' };
108
+ };
109
+ ```
110
+
111
+ The handler receives:
112
+
113
+ - `request`: validated against `requestSchema` before the handler runs
114
+ - `context`: the capabilities listed in `reads` and `writes`, and nothing else. On a session-scoped endpoint, only the shared ones (see above)
115
+ - `team`: on a team-scoped endpoint, the team the call acts on. `teams`: on a session-scoped one, every team's declared values
116
+ - `settings`: exactly the settings resources listed in `settings`, keyed by identifier, read-only
117
+ - `callbacks`: the callbacks named in `uses`, plus the ungated `readAsset` and `getPastState`. An undeclared gated callback throws
118
+ - `caller`: who is calling, issued by the host and never by the iframe (see below)
119
+ - `meta`: the facts true of the whole run: `meta.locale` (the session locale), `meta.seed`, `meta.runKind` and `meta.now`, the instant the call started
120
+ - `participants`: a read-only copy of the roster the call covers, the addressed team's on a team-scoped endpoint and every participant on a session-scoped one
121
+ - `session`: where the session stands ([Acting on the session](#acting-on-the-session))
122
+
123
+ `caller` carries `role`, and, where they apply, `sessionId`, `teamId`, `playerId`, `userInterfaceIdentifier` and `principal`. `sessionId` is absent only on a call from the scenario editor, which has no session. `role` is `'participant'`, `'observer'`, `'facilitator'`, `'author'` or `'external'`: a session UI and a setup screen report `'facilitator'`, a scenario editor reports `'author'`, and a caller outside the game reports `'external'` with no `userInterfaceIdentifier`. An author is whoever is creating a scenario, who on the platform may not be a facilitator, so an endpoint that trusts a facilitator further than other callers should not extend that to an author. `principal` is the platform's account of which agent or API key made an external call, so an endpoint can answer one caller differently from another.
124
+
125
+ `caller.teamId` is the caller's own team, set for a participant or an observer and absent for a facilitator, an author or an outside caller. It is not the team the call acts on: read that from `team`. A facilitator calling a team-scoped endpoint has `team` and no `caller.teamId`; a participant has both, and they are equal.
126
+
127
+ ### Calls from the scenario editor
128
+
129
+ The scenario editor edits a scenario rather than a session, and runs whether or not one exists, so an endpoint it calls is answered with no session, even in a dev server running one beside it:
130
+
131
+ - `settings`: the settings the endpoint declares, as the scenario being edited resolves them, with the editor's unsaved changes over it. An assistant advising on a draft sees the draft, so declare `settings` rather than passing the values in the request
132
+ - `context`: empty, with no `team` or `teams`
133
+ - `participants`: empty
134
+ - `session`: the game's `mode` and the scenario's tick limit, with nothing played and no teams
135
+ - `caller`: `role: 'author'` and `userInterfaceIdentifier: 'scenario_editor'`, with no `sessionId`, `teamId` or `playerId`
136
+
137
+ The full argument and result types are in the API reference: [`EndpointBaseArgs`](../api/sdk.md#endpointbaseargs), [`EndpointCaller`](../api/sdk.md#endpointcaller) and [`EndpointSession`](../api/sdk.md#endpointsession).
138
+
139
+ ### Changing state
140
+
141
+ An endpoint that declares no `writes` cannot change state at all, and returns its response bare. One that declares `writes` returns its response and its change together, and its generated `Handler` type says so:
142
+
143
+ ```typescript
144
+ export const Handle: Handler = async ({ request, context }) => ({
145
+ response: { previous: context.price.Price, current: request.price },
146
+ capabilities: { price: { ...context.price, Price: request.price } },
147
+ });
148
+ ```
149
+
150
+ The two are separate because they are held to different contracts. `response` is validated against `responseSchema`. `capabilities` is held to `writes`, and naming a capability outside that set is a fault rather than a silent drop, because the alternative leaves the response describing a change the board never received. Any other top-level key is refused, and so is a writing endpoint that returns a bare value.
151
+
152
+ To remove a key, such as one entry of a map, name its path in `deletes` beside `capabilities`. It is removed from the same board once `capabilities` has merged, and is held to `writes` the same way: a path under a capability the endpoint only reads is an `invalid-response` fault that applies nothing. A session-scoped endpoint's `deletes` remove paths from shared capabilities; its per-team `teams` patches carry no deletes.
153
+
154
+ ```typescript
155
+ export const Handle: Handler = async ({ request, context }) => ({
156
+ response: { closed: request.orderId },
157
+ capabilities: { orders: context.orders },
158
+ deletes: [['orders', 'Open', request.orderId]],
159
+ });
160
+ ```
161
+
162
+ The change goes through the same queue as every other write, and lands on the board the call was made against. It is refused outright if a tick commits while the handler is still running, since it was computed against a board that no longer exists. That is a reason to keep a writing endpoint fast rather than to build a retry loop: a handler that calls a model can easily outlive the tick it was called in.
163
+
164
+ ### Reading past ticks
165
+
166
+ `callbacks.getPastState` reads any tick that has already closed, projected onto the capabilities the endpoint declared:
167
+
168
+ ```typescript
169
+ const rounds = await callbacks.getPastState<Context>({
170
+ fromTick: 0,
171
+ toTick: Number.MAX_SAFE_INTEGER,
172
+ capabilityIds: ['profit'],
173
+ });
174
+ ```
175
+
176
+ It is not gated by `uses`, because it reads state the endpoint already declared, one tick further back. A team-scoped endpoint reads its own team's ticks; a session-scoped one reads every team's.
177
+
178
+ A range that runs past the last completed tick comes back short rather than failing, so treat a short result as normal. Before the first tick there is nothing to read and the answer is empty.
179
+
180
+ The same callback reaches your engine, hooks and tick controller. That lets a game stop keeping a capability purely to carry last tick's figure forward: a value in context rides every push to every participant for the rest of the session, and the snapshot it was copied from is already recorded.
181
+
182
+ ## Acting on the session
183
+
184
+ An endpoint that declares `actions` can return session operations beside its response, and the runtime carries them out after its write:
185
+
186
+ ```json
187
+ "actions": ["setWaitingRoom", "tick"]
188
+ ```
189
+
190
+ ```typescript
191
+ export const Handle: Handler = async ({ session }) => {
192
+ if (session.complete) throw new Error('The game is complete');
193
+ return {
194
+ response: { fromRound: session.completedTicks },
195
+ actions: [
196
+ ...(session.waitingRoomOpen ? [{ type: 'setWaitingRoom' as const, open: false }] : []),
197
+ { type: 'tick' },
198
+ ],
199
+ };
200
+ };
201
+ ```
202
+
203
+ The actions are the facilitator's own operations, held to the same rules as when the session UI performs them: `tick`, `pauseTimer`, `resumeTimer`, `extendTick`, `setWaitingRoom`, `renameTeam`, `addTeam`, `moveParticipant` and `setLocale` (see [`EndpointAction`](../api/sdk.md#endpointaction) for each one's fields). A `teamId` left off means the call's own team on a team-scoped endpoint, and the whole session on a session-scoped one.
204
+
205
+ The runtime applies the write, the per-team writes and the actions as one step, so nothing else lands in between, and it checks everything it can before applying anything. A step that can only fail once applied, such as a tick the engine fails, stops the sequence there: what came before it stays applied, and the caller is told which step failed.
206
+
207
+ Anyone who can call the endpoint can trigger its actions: a participant's game UI as well as the session UI. That is how a game lets a participant end their own turn under its own rules. An endpoint that should act only for the facilitator checks `caller.role` and refuses the rest.
208
+
209
+ `session` tells the handler where things stand when it is called: the ticks completed (`completedTicks`), the tick limit (`maxTicks`), whether the waiting room is open, whether the game is complete, and each team's `completedTicks`, completion and timer (a [`TimerState`](../api/sdk.md#timerstate), or `null` without a tick controller). Declare `roster: true` to have each team's participants listed as well. That list grows with the cohort, which is why it is off by default.
210
+
211
+ ### Reporting what the actions did
212
+
213
+ `Handle` runs before any of its effects land, so its response cannot say what they did: an endpoint that ticks cannot name the round it started. When that matters, answer in two phases. Export `Plan` in place of `Handle`, returning the effects alone, and `Respond`, which writes the response once they have landed:
214
+
215
+ ```typescript
216
+ import type { Planner, Responder } from '../types';
217
+
218
+ export const Plan: Planner = async ({ session }) => ({
219
+ actions: [{ type: 'tick' }],
220
+ });
221
+
222
+ export const Respond: Responder = async ({ session, plan, outcomes }) => ({
223
+ round: session.completedTicks, // read after the tick
224
+ });
225
+ ```
226
+
227
+ `Handle` always returns the response, `Plan` never does, and the build refuses any other mix of the three. `Respond` receives the same arguments as `Plan`, read afresh, plus `plan` (what `Plan` returned) and `outcomes` (what each action came to, such as the tick a `tick` committed). It changes nothing. It runs outside the session's queue, so a slow one, such as one that calls a model, holds up nobody else's writes, and the state it sees is the state when it runs. `meta.now` is the same for both phases.
228
+
229
+ If `Respond` fails, the effects have still landed, and the caller is told so. See [Act on the session from an endpoint](../recipes/act-on-the-session-from-an-endpoint.md).
230
+
231
+ ## Files in and out
232
+
233
+ A request field can be a file. Mark a string property with `blob`, saying which types it takes and how large:
234
+
235
+ ```json
236
+ "document": {
237
+ "type": "string",
238
+ "description": "The research, as a PDF.",
239
+ "blob": { "accept": ["application/pdf"], "maxBytes": 5242880 }
240
+ }
241
+ ```
242
+
243
+ The handler always receives a `blob:` reference, typed `` `blob:${string}` ``, already checked against the declaration. It reads the file with `callbacks.readAsset`, passes it to `extractText`, or stores the reference in a capability for a screen to show with `getAssetUrl`.
244
+
245
+ The caller sends one of two things:
246
+
247
+ - **A `blob:` reference**, for a file it has already uploaded. A game UI gets one from `uploadBlob`; a script or an agent gets one from the platform's upload route (locally, `POST /api/blobs` or `pnpm run sdk blob upload`)
248
+ - **A `data:` URI**, such as `data:application/pdf;name=research.pdf;base64,...`, carrying the file inline in the one call. Good for small files: a model writing base64 spends its context on it, so past a few megabytes, upload first
249
+
250
+ The runtime refuses anything else before your handler runs: a type the field does not take, bytes that are not the type they claim, a file over the size, a reference to a blob that does not exist, and a URL. See [Take a file in an endpoint](../recipes/take-a-file-in-an-endpoint.md).
251
+
252
+ Going the other way, an endpoint that produces binary data (audio, an image, a generated document) declares `writeBlob` in `uses` and calls `callbacks.writeBlob(bytes, { name, mimeType })`, which returns a `blob:<id>` reference. It returns the reference in its JSON response, and the UI fetches the bytes with `getAssetUrl` or `readAsset`. A caller outside the game is given a URL to fetch it from. The call itself stays JSON only, so a new kind of binary output needs no protocol change.
253
+
254
+ ## Calling from a UI
255
+
256
+ A UI's bundle lists the endpoints it may call:
257
+
258
+ ```json
259
+ {
260
+ "definition": {
261
+ "endpoints": ["summarise_report"]
262
+ }
263
+ }
264
+ ```
265
+
266
+ The UI's generated `types.ts` produces a typed `Endpoints` interface and merges it into the UI's `Callbacks` type. Calling an undeclared endpoint is a TypeScript error, and the host refuses the call at run time as a second line of defence.
267
+
268
+ ```typescript
269
+ const { summary } = await callbacks.endpoints['summarise_report']({
270
+ fileBlobId,
271
+ });
272
+ ```
273
+
274
+ A UI has no `uses` of its own. It reaches `generateText` and the other gated services by calling an endpoint that declares them. See [Call an endpoint from a UI](../recipes/call-endpoint-from-ui.md).
275
+
276
+ ## Calling from outside the game
277
+
278
+ An endpoint is `internal` unless it says otherwise, and only the game's own user interfaces reach it. Setting `visibility` to `external` publishes it to callers outside the game as well: a facilitator's AI agent, a platform API, or another integration. A caller outside the game is refused an internal endpoint before its request is validated.
279
+
280
+ ```json
281
+ {
282
+ "definition": {
283
+ "description": "Sets a team's unit price, in pounds per unit. Use when a facilitator asks to change what a team charges.",
284
+ "visibility": "external"
285
+ }
286
+ }
287
+ ```
288
+
289
+ An external endpoint must carry a `description`, because it is what an outside caller, a model most of all, reads to decide whether to call it.
290
+
291
+ Its `requestSchema` must also be an object at the top, `"type": "object"`, since an outside caller passes its request as named arguments, as an agent passes a tool's input. The build fails with `EXTERNAL_REQUEST_NOT_OBJECT` on a bare string, number or array. An external endpoint that takes no input declares an empty object:
292
+
293
+ ```json
294
+ { "type": "object", "properties": {}, "additionalProperties": false }
295
+ ```
296
+
297
+ An internal endpoint's request may take any shape. Which outside callers may use it, and how they sign in, is up to the platform. An outside caller works against the endpoints of the session it addresses, for that session's life. `pnpm run sdk endpoint call --external` makes a call the way an outside caller does. See [Make an endpoint callable from outside the game](../recipes/make-an-endpoint-external.md) and, for writing descriptions a model can act on, [Structuring a game for AI facilitation](./ai-facilitation.md).
298
+
299
+ ## When a call fails
300
+
301
+ A failed call is never a bare message, because a caller has to tell a call that changed nothing from one whose writes landed before an action failed. A UI's call rejects with an [`EndpointError`](../api/sdk-ui.md#endpointerror), `pnpm run sdk endpoint call --json` prints the same thing, and a `createTestSession` call answers `{ type: 'endpointFailure', failure }`. Each carries:
302
+
303
+ - `outcome`: why, as `outcome.kind` and usually `outcome.message`. The kinds are those of a refused write (`denied`, `conflict`, `tick-failed` and the rest) plus three only an endpoint has: `invalid-request` (the request failed `requestSchema`, so the handler did not run), `handler-error` (the handler threw or overran its time limit), and `invalid-response` (what it returned broke its declaration)
304
+ - `applied`: the steps that committed before the failure, in order: `{ kind: 'capabilities' }`, `{ kind: 'teams', teamId }`, or `{ kind: 'action', index, type }`. Empty when nothing changed
305
+ - `failedStep`: the step that failed, when one failed once applied
306
+
307
+ Most failures happen before anything is applied: a refusal, a bad request, a handler that throws, an undeclared write or action, or a round starting while the handler ran. Only two leave `applied` non-empty: an action that fails once applied (a `tick` on a game that has just completed, say), and `Respond` failing after the effects landed. See [Call an endpoint from a UI](../recipes/call-endpoint-from-ui.md) for handling one.
308
+
309
+ ## Endpoints and validators
310
+
311
+ An endpoint's write, and its actions, are not put to the game's [validators](./validators.md), whoever called it, including a participant's own game UI.
312
+
313
+ This is deliberate rather than a gap. A validator exists so a game can refuse a change a participant submits **directly**, the one route where the game has no code in the path. An endpoint is the game's code in the path, and checks its own request before it writes.
314
+
315
+ Keeping the two apart buys something. A validator can refuse a change outright while a strict endpoint remains the one sanctioned way to make it:
316
+
317
+ - The validator refuses any direct write to `price` from a participant
318
+ - An endpoint sets `price`, having checked whatever conditions you decide
319
+
320
+ The rule lives in the validator, the one sanctioned exception lives in the endpoint, and no participant can reach the exception except through the code you wrote.
321
+
322
+ ## The language environment
323
+
324
+ Endpoints run in the same language environment as the engine: the ECMAScript intrinsics, plus the `console` shim and byte helpers the SDK compiles into the bundle (see [the language environment your logic runs in](./engine.md#the-language-environment-your-logic-runs-in)). `process`, `fs`, `fetch`, timers and the other host globals are absent, and the dev server evaluates logic in a bare V8 context, so reaching for one throws in dev exactly as it does in production. Importing a Node built-in (`node:fs`, `crypto`, `path` and the rest) is refused when the endpoint is built or loaded, with `LOGIC_NODE_BUILTIN` naming the file and the import.
325
+
326
+ ## Related
327
+
328
+ - [Create an endpoint](../recipes/create-endpoint.md)
329
+ - [Call an endpoint from a UI](../recipes/call-endpoint-from-ui.md)
330
+ - [Act on the session from an endpoint](../recipes/act-on-the-session-from-an-endpoint.md)
331
+ - [Take a file in an endpoint](../recipes/take-a-file-in-an-endpoint.md)
332
+ - [Make an endpoint callable from outside the game](../recipes/make-an-endpoint-external.md)
333
+ - [Declare API uses](../recipes/declare-api-uses.md)
334
+ - [Structuring a game for AI facilitation](./ai-facilitation.md)
335
+ - [Capabilities](./capabilities.md)