@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.
- package/LICENSE +93 -0
- package/README.md +36 -2
- package/dist/bundling/logic-exports.d.ts +34 -0
- package/dist/bundling/node-builtins.d.ts +21 -0
- package/dist/bundling/transpiler.d.ts +7 -0
- package/dist/cli.js +27368 -0
- package/dist/client/dev-server.d.ts +59 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/stub-services.d.ts +32 -0
- package/dist/client/test-session.d.ts +195 -0
- package/dist/client.js +16884 -0
- package/dist/core/asset-reference-translator.d.ts +46 -0
- package/dist/core/clock.d.ts +57 -0
- package/dist/core/faults.d.ts +65 -0
- package/dist/core/game-settings.d.ts +65 -0
- package/dist/core/history.d.ts +89 -0
- package/dist/core/listen-with-fallback.d.ts +30 -0
- package/dist/core/mutation-queue.d.ts +61 -0
- package/dist/core/package-scenario.d.ts +89 -0
- package/dist/core/participant-registry.d.ts +109 -0
- package/dist/core/pending-changes.d.ts +14 -0
- package/dist/core/protocol.d.ts +83 -0
- package/dist/core/scenario-drafts.d.ts +32 -0
- package/dist/core/session-endpoints.d.ts +108 -0
- package/dist/core/session-gates.d.ts +75 -0
- package/dist/core/session-host.d.ts +94 -0
- package/dist/core/session-refusals.d.ts +56 -0
- package/dist/core/session-rewind.d.ts +32 -0
- package/dist/core/session-roster.d.ts +128 -0
- package/dist/core/session-surfaces.d.ts +32 -0
- package/dist/core/session-ticks.d.ts +18 -0
- package/dist/core/session-timers.d.ts +84 -0
- package/dist/core/session-writes.d.ts +40 -0
- package/dist/core/session.d.ts +385 -0
- package/dist/core/settings-locks.d.ts +22 -0
- package/dist/core/team-names.d.ts +52 -0
- package/dist/core/team-state-store.d.ts +209 -0
- package/dist/core/tick-timer.d.ts +75 -0
- package/dist/dev/blob-links.d.ts +10 -0
- package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
- package/dist/dev/console/index.html +12 -0
- package/dist/dev/console-path.d.ts +19 -0
- package/dist/dev/lockfile.d.ts +124 -0
- package/dist/dev/package-watcher.d.ts +21 -0
- package/dist/dev/routes.d.ts +67 -0
- package/dist/dev/shell.d.ts +260 -0
- package/dist/dev/surfaces.d.ts +41 -0
- package/dist/dev/tools-identity.d.ts +23 -0
- package/dist/dev/view/batch.d.ts +172 -0
- package/dist/dev/view/commands.d.ts +75 -0
- package/dist/dev/view/route.d.ts +58 -0
- package/dist/dev/view/view.d.ts +78 -0
- package/dist/dev/view/viewer.d.ts +32 -0
- package/dist/discovery/config.d.ts +9 -0
- package/dist/discovery/manifest-paths.d.ts +21 -0
- package/dist/discovery/removed-fields.d.ts +90 -0
- package/dist/docs/api/deck.md +386 -0
- package/dist/docs/api/index.md +67 -0
- package/dist/docs/api/sdk-commands.md +286 -0
- package/dist/docs/api/sdk-e2e.md +433 -0
- package/dist/docs/api/sdk-projection.md +207 -0
- package/dist/docs/api/sdk-testing.md +214 -0
- package/dist/docs/api/sdk-tools-client.md +560 -0
- package/dist/docs/api/sdk-ui-testing.md +231 -0
- package/dist/docs/api/sdk-ui.md +1269 -0
- package/dist/docs/api/sdk.md +2140 -0
- package/dist/docs/changelog-0.x.md +1156 -0
- package/dist/docs/changelog.md +236 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +154 -0
- package/dist/docs/cli-reference.md +2488 -0
- package/dist/docs/cli-simulation-testing.md +222 -0
- package/dist/docs/concepts/index.md +46 -0
- package/dist/docs/concepts/terminology.md +322 -0
- package/dist/docs/environment-variables.md +102 -0
- package/dist/docs/getting-started.md +145 -0
- package/dist/docs/guides/ai-facilitation.md +220 -0
- package/dist/docs/guides/capabilities.md +460 -0
- package/dist/docs/guides/endpoints.md +335 -0
- package/dist/docs/guides/engine-testing.md +477 -0
- package/dist/docs/guides/engine.md +527 -0
- package/dist/docs/guides/file-handling.md +405 -0
- package/dist/docs/guides/games.md +176 -0
- package/dist/docs/guides/global-settings.md +94 -0
- package/dist/docs/guides/guide.md +123 -0
- package/dist/docs/guides/hooks.md +183 -0
- package/dist/docs/guides/index.md +51 -0
- package/dist/docs/guides/internationalization.md +288 -0
- package/dist/docs/guides/llm-integration.md +635 -0
- package/dist/docs/guides/multi-team-games.md +586 -0
- package/dist/docs/guides/patterns.md +391 -0
- package/dist/docs/guides/presentation.md +196 -0
- package/dist/docs/guides/production-constraints.md +130 -0
- package/dist/docs/guides/scenario-editor.md +143 -0
- package/dist/docs/guides/scenarios.md +515 -0
- package/dist/docs/guides/session.md +972 -0
- package/dist/docs/guides/settings.md +458 -0
- package/dist/docs/guides/setup.md +193 -0
- package/dist/docs/guides/speech-generation.md +233 -0
- package/dist/docs/guides/starter-game.md +164 -0
- package/dist/docs/guides/testing.md +575 -0
- package/dist/docs/guides/tick-controller.md +569 -0
- package/dist/docs/guides/type-safety.md +516 -0
- package/dist/docs/guides/ui.md +1389 -0
- package/dist/docs/guides/upgrading.md +636 -0
- package/dist/docs/guides/validators.md +217 -0
- package/dist/docs/guides/waiting-room.md +244 -0
- package/dist/docs/index.md +41 -0
- package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
- package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
- package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
- package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
- package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
- package/dist/docs/recipes/create-capability.md +66 -0
- package/dist/docs/recipes/create-endpoint.md +127 -0
- package/dist/docs/recipes/create-engine.md +111 -0
- package/dist/docs/recipes/create-game.md +70 -0
- package/dist/docs/recipes/create-guide.md +75 -0
- package/dist/docs/recipes/create-hook.md +80 -0
- package/dist/docs/recipes/create-presentation.md +142 -0
- package/dist/docs/recipes/create-scenario-editor.md +102 -0
- package/dist/docs/recipes/create-scenario.md +98 -0
- package/dist/docs/recipes/create-session-ui.md +116 -0
- package/dist/docs/recipes/create-settings.md +88 -0
- package/dist/docs/recipes/create-setup.md +115 -0
- package/dist/docs/recipes/create-tick-controller.md +65 -0
- package/dist/docs/recipes/create-user-interface.md +96 -0
- package/dist/docs/recipes/create-validator.md +67 -0
- package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
- package/dist/docs/recipes/declare-api-uses.md +67 -0
- package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
- package/dist/docs/recipes/distribute-package.md +80 -0
- package/dist/docs/recipes/e2e-testing.md +116 -0
- package/dist/docs/recipes/end-game-on-condition.md +154 -0
- package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
- package/dist/docs/recipes/index.md +86 -0
- package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
- package/dist/docs/recipes/negotiate-across-teams.md +164 -0
- package/dist/docs/recipes/observe-other-teams-state.md +119 -0
- package/dist/docs/recipes/publish-a-package.md +139 -0
- package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
- package/dist/docs/recipes/review-a-past-day.md +73 -0
- package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
- package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
- package/dist/docs/recipes/show-all-teams-participants.md +109 -0
- package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
- package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
- package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
- package/dist/docs/recipes/test-user-interface.md +95 -0
- package/dist/docs/recipes/test-with-engine.md +115 -0
- package/dist/docs/recipes/upload-and-display-files.md +148 -0
- package/dist/docs/recipes/use-request-play.md +65 -0
- package/dist/docs/recipes/use-request-tick.md +75 -0
- package/dist/docs/troubleshooting.md +816 -0
- package/dist/execution/blob-inputs.d.ts +57 -0
- package/dist/execution/bundle-reload.d.ts +42 -0
- package/dist/execution/config-resolver.d.ts +127 -0
- package/dist/execution/configuration-builder.d.ts +66 -0
- package/dist/execution/context-composition.d.ts +161 -0
- package/dist/execution/endpoint-actions.d.ts +9 -0
- package/dist/execution/endpoint-payloads.d.ts +32 -0
- package/dist/execution/endpoint-runner.d.ts +129 -0
- package/dist/execution/engine-state.d.ts +40 -0
- package/dist/execution/executor-callbacks.d.ts +56 -0
- package/dist/execution/executor.d.ts +436 -0
- package/dist/execution/gates.d.ts +45 -0
- package/dist/execution/hook-chain.d.ts +25 -0
- package/dist/execution/logic-calls.d.ts +190 -0
- package/dist/execution/logic-failure.d.ts +84 -0
- package/dist/execution/logic-loader.d.ts +54 -0
- package/dist/execution/logic-result.d.ts +76 -0
- package/dist/execution/tick-controller-call.d.ts +32 -0
- package/dist/execution/types.d.ts +48 -0
- package/dist/generation/ts-type.d.ts +39 -0
- package/dist/guide/locale.d.ts +12 -0
- package/dist/guide/video-registry.d.ts +42 -0
- package/dist/help-examples.d.ts +125 -0
- package/dist/host/adapter.d.ts +67 -0
- package/dist/host/host-channel.d.ts +222 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/protocol.d.ts +783 -0
- package/dist/host/surfaces.d.ts +42 -0
- package/dist/host/theme.d.ts +55 -0
- package/dist/host.js +583 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/schema-builder.d.ts +29 -0
- package/dist/prompts/types.d.ts +108 -0
- package/dist/prompts/validators.d.ts +32 -0
- package/dist/runtime/capability-scope.d.ts +6 -0
- package/dist/runtime/context-merge.d.ts +29 -0
- package/dist/runtime/context-projection.d.ts +41 -0
- package/dist/runtime/guarded-callbacks.d.ts +15 -0
- package/dist/runtime/index.d.ts +27 -0
- package/dist/runtime/manifest-version.d.ts +55 -0
- package/dist/runtime/tick-validation.d.ts +25 -0
- package/dist/runtime.js +346 -0
- package/dist/schema.json +1546 -0
- package/dist/server/vite-plugin.d.ts +46 -0
- package/dist/services/tts/client.d.ts +51 -0
- package/dist/services/tts/elevenlabs.d.ts +24 -0
- package/dist/services/tts/types.d.ts +44 -0
- package/dist/storage/asset-resolver.d.ts +49 -0
- package/dist/storage/blob-store.d.ts +41 -0
- package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
- package/dist/templates/endpoint/logic.ts.liquid +25 -0
- package/dist/templates/engine/init.json.liquid +7 -0
- package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
- package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
- package/dist/templates/engine/logic.test.ts.liquid +61 -0
- package/dist/templates/engine/logic.ts.liquid +32 -0
- package/dist/templates/engine/run.json.liquid +7 -0
- package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
- package/dist/templates/guide/02-preparation.md.liquid +18 -0
- package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
- package/dist/templates/hook/logic.test.ts.liquid +46 -0
- package/dist/templates/hook/logic.ts.liquid +12 -0
- package/dist/templates/package/CLAUDE.md.template +338 -0
- package/dist/templates/package/DESIGN.md.liquid +144 -0
- package/dist/templates/package/README.md.liquid +143 -0
- package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
- package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
- package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
- package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
- package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
- package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
- package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
- package/dist/templates/package/claude/settings.json.template +16 -0
- package/dist/templates/package/gitignore.template +15 -0
- package/dist/templates/package/global-setup.ts.template +21 -0
- package/dist/templates/package/globals.d.ts.template +2 -0
- package/dist/templates/package/oxfmtrc.json.template +14 -0
- package/dist/templates/package/oxlintrc.json.template +238 -0
- package/dist/templates/package/package.json.liquid +65 -0
- package/dist/templates/package/playwright.config.ts.liquid +19 -0
- package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
- package/dist/templates/package/test-setup.ts.liquid +7 -0
- package/dist/templates/package/tsconfig.json.liquid +15 -0
- package/dist/templates/package/vite.config.ts.liquid +19 -0
- package/dist/templates/package/vitest.config.ts.liquid +25 -0
- package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
- package/dist/templates/presentation/index.html.liquid +20 -0
- package/dist/templates/presentation/src/App.tsx.liquid +49 -0
- package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
- package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
- package/dist/templates/presentation/src/main.tsx.liquid +14 -0
- package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
- package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
- package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
- package/dist/templates/session/src/App.test.tsx.liquid +60 -0
- package/dist/templates/session/src/App.tsx.liquid +128 -0
- package/dist/templates/session/src/i18n.ts.liquid +53 -0
- package/dist/templates/session/src/main.tsx.liquid +48 -0
- package/dist/templates/session/src/theme.ts.liquid +46 -0
- package/dist/templates/settings-editor/index.html.liquid +12 -0
- package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
- package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
- package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
- package/dist/templates/setup/src/App.tsx.liquid +102 -0
- package/dist/templates/setup/src/i18n.ts.liquid +47 -0
- package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
- package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
- package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
- package/dist/templates/starter-game/engine/defaults.ts +81 -0
- package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
- package/dist/templates/starter-game/engine/logic.test.ts +221 -0
- package/dist/templates/starter-game/engine/logic.ts +149 -0
- package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
- package/dist/templates/starter-game/engine/types.ts +139 -0
- package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
- package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
- package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
- package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
- package/dist/templates/starter-game/guide/guide.test.ts +108 -0
- package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
- package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
- package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
- package/dist/templates/starter-game/package.json +68 -0
- package/dist/templates/starter-game/playwright.config.ts +19 -0
- package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
- package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
- package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
- package/dist/templates/starter-game/session/defaults.ts +80 -0
- package/dist/templates/starter-game/session/index.html +12 -0
- package/dist/templates/starter-game/session/session.sdk.json +11 -0
- package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
- package/dist/templates/starter-game/session/src/App.tsx +309 -0
- package/dist/templates/starter-game/session/src/Section.tsx +37 -0
- package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
- package/dist/templates/starter-game/session/src/format.ts +8 -0
- package/dist/templates/starter-game/session/src/i18n.ts +51 -0
- package/dist/templates/starter-game/session/src/main.tsx +48 -0
- package/dist/templates/starter-game/session/src/theme.ts +45 -0
- package/dist/templates/starter-game/session/types.ts +135 -0
- package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
- package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
- package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
- package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
- package/dist/templates/starter-game/test-setup.ts +7 -0
- package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
- package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
- package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
- package/dist/templates/starter-game/tsconfig.json +19 -0
- package/dist/templates/starter-game/ui/defaults.ts +60 -0
- package/dist/templates/starter-game/ui/index.html +12 -0
- package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
- package/dist/templates/starter-game/ui/src/App.tsx +175 -0
- package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
- package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
- package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
- package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
- package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
- package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
- package/dist/templates/starter-game/ui/src/format.ts +12 -0
- package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
- package/dist/templates/starter-game/ui/src/main.tsx +48 -0
- package/dist/templates/starter-game/ui/src/menu.ts +18 -0
- package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
- package/dist/templates/starter-game/ui/src/theme.ts +45 -0
- package/dist/templates/starter-game/ui/src/writes.ts +43 -0
- package/dist/templates/starter-game/ui/types.ts +137 -0
- package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
- package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
- package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
- package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
- package/dist/templates/starter-game/vite.config.ts +19 -0
- package/dist/templates/starter-game/vitest.config.ts +15 -0
- package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
- package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
- package/dist/templates/starter-game/waiting_room/index.html +12 -0
- package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
- package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
- package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
- package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
- package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
- package/dist/templates/starter-game/waiting_room/types.ts +59 -0
- package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
- package/dist/templates/tick-controller/logic.ts.liquid +8 -0
- package/dist/templates/ui/index.html.liquid +12 -0
- package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
- package/dist/templates/ui/src/App.tsx.liquid +103 -0
- package/dist/templates/ui/src/i18n.ts.liquid +47 -0
- package/dist/templates/ui/src/main.tsx.liquid +48 -0
- package/dist/templates/ui/src/theme.ts.liquid +46 -0
- package/dist/templates/validator/logic.test.ts.liquid +42 -0
- package/dist/templates/validator/logic.ts.liquid +21 -0
- package/dist/templates/waiting-room/index.html.liquid +12 -0
- package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
- package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
- package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
- package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
- package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
- package/dist/templating/resource-metadata.d.ts +131 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/atomic-write.d.ts +40 -0
- package/dist/utils/cli-error.d.ts +64 -0
- package/dist/utils/document-parser-client.d.ts +20 -0
- package/dist/utils/env-loader.d.ts +40 -0
- package/dist/utils/error-codes.d.ts +335 -0
- package/dist/utils/errors.d.ts +19 -0
- package/dist/utils/global-settings.d.ts +65 -0
- package/dist/utils/llm-client.d.ts +34 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/output.d.ts +109 -0
- package/dist/utils/platform.d.ts +14 -0
- package/dist/utils/prompt.d.ts +36 -0
- package/dist/utils/real-path.d.ts +6 -0
- package/dist/utils/schema-properties.d.ts +35 -0
- package/dist/validation/ajv.d.ts +17 -0
- package/dist/validation/facilitator-guide-validator.d.ts +27 -0
- package/dist/validation/findings.d.ts +40 -0
- package/dist/validation/game-constraints.d.ts +10 -0
- package/dist/validation/game-validator.d.ts +34 -0
- package/dist/validation/package-validation.d.ts +41 -0
- package/dist/validation/package-validator.d.ts +19 -0
- package/dist/validation/resource-validator.d.ts +79 -0
- package/dist/validation/schema-defaults-validator.d.ts +29 -0
- package/dist/validation/schema-validation.d.ts +115 -0
- package/dist/validation/semver.d.ts +9 -0
- package/package.json +122 -3
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Serious Gaming SDK are recorded here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and the SDK adheres to [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## How to read this file
|
|
9
|
+
|
|
10
|
+
1. Find your currently installed SDK version in `package.json` (or run
|
|
11
|
+
`cat node_modules/@seriousgaming/sdk/package.json | grep '"version"'`, or
|
|
12
|
+
`node_modules/@businesssimulations/seriousgaming-sdk/package.json` before 1.0).
|
|
13
|
+
2. Read every entry **above** that version: those are the changes since you
|
|
14
|
+
last updated.
|
|
15
|
+
3. If any entry is under **Breaking Changes**, address it before rebuilding.
|
|
16
|
+
Coming from 0.6, follow the [upgrading guide](guides/upgrading.md), which
|
|
17
|
+
turns every breaking change below into an ordered step.
|
|
18
|
+
|
|
19
|
+
Entries are grouped into: **Breaking Changes**, **Added**, **Changed**,
|
|
20
|
+
**Deprecated**, **Removed**, **Fixed** and **Security**. Releases before 1.0
|
|
21
|
+
are in the [0.x changelog](changelog-0.x.md).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## [Unreleased]
|
|
26
|
+
|
|
27
|
+
### Breaking Changes
|
|
28
|
+
|
|
29
|
+
#### Packages
|
|
30
|
+
|
|
31
|
+
- **The packages are renamed and move to the public npm registry.** `@businesssimulations/seriousgaming-sdk` is `@seriousgaming/sdk`, `@businesssimulations/seriousgaming-deck` is `@seriousgaming/deck`, and `@businesssimulations/seriousgaming-sdk-tools` is `@seriousgaming/sdk-tools`, each with the same subpaths. They install from npmjs.com with no token. The `seriousgaming-sdk` command keeps its name. `@seriousgaming/sdk-tools` declares `@seriousgaming/sdk` as a peer dependency at the same version. Scaffolded files carry the new names: `package.json.liquid`, `vite.config.ts.liquid`, `vitest.config.ts.liquid`, `oxlintrc.json.template`, `global-setup.ts.template`, `README.md.liquid`, `CLAUDE.md.template` and the `workflow-create-presentation.md`, `workflow-create-user-interface.md`, `workflow-test-engine-or-ui.md` and `workflow-update-sdk-version.md` rules. See [step 1 of the upgrading guide](guides/upgrading.md#step-1-rename-the-packages)
|
|
32
|
+
- **Runtime plumbing leaves the SDK.** The host library moves from the SDK's `./host` to `@seriousgaming/sdk-tools/host`, with the host-side wire types `./ui` used to export, and the runtime helpers (`createGuardedCallbacks`, `deepMergeContext`, `projectContext`, `validateTickControllerResult`, `MANIFEST_VERSION` and others) move from the SDK root to `@seriousgaming/sdk-tools/runtime`. `isApiUseDeclared`, `isDeliverableToSurfaces`, `withheldCapabilityIds`, `resolveManifestVersion`, `removedSharedFlagError` and `tickControllerResultSchema` are removed, and `sdk-tools/client` no longer re-exports `ExecutionHistory`, `JsonPatchOperation`, `LLMGenerateOptions` or `GameContext`. No game needs them; a test harness that merged with `deepMergeContext` uses `mergingSetData` from `./ui/testing`. See [step 9](guides/upgrading.md#step-9-meet-what-the-build-and-runtime-now-enforce)
|
|
33
|
+
|
|
34
|
+
#### Settings
|
|
35
|
+
|
|
36
|
+
- **Settings resources replace `configurationSchema`.** A game's parameters live in `settings/<name>/settings.sdk.json`, holding `{ "definition": { "settingsSchema": {...} } }`, and a package may hold any number. Every property declares a default, and a top-level `required` is refused. The engine, UIs, the tick controller, hooks, validators and endpoints list the settings they read in `"settings": [...]` and receive exactly those, keyed by identifier, as `settings` (where logic received `config`, and `useUserInterfaceData()` and `useSessionUserInterfaceData()` returned `config`). Settings are fixed when play starts. The build refuses a `configurationSchema` on any bundle, an empty one included. Generated `types.ts` exports `Settings` and `defaults.ts` `defaultSettings`, in place of `Config` and `defaultConfig`, and `ModuleFunctionArgs` is `LogicFunctionArgs`. The `CLAUDE.md.template`, `DESIGN.md.liquid`, `workflow-create-engine.md`, `workflow-create-user-interface.md` and `workflow-test-engine-or-ui.md` guidance and every logic and UI scaffold are updated. See [step 2 of the upgrading guide](guides/upgrading.md#step-2-move-configuration-into-settings-resources)
|
|
37
|
+
- **The game-level settings are the built-in `game` settings, and `ticks` is `maxTicks`.** Rename `ticks` in `seriousgaming.sdk.json` to `maxTicks`, and move a scenario's `ticks`, `teams` and `waitingRoom` into its `settings.game` (`ticks` again as `maxTicks`); the build refuses the old fields, naming what replaced each. `teams.min`, `teams.max` and the `perTeam` counts must be whole numbers. A presentation reads `game.maxTicks` (was `game.ticks`). A scenario's `teams.defaultNames` replaces the game's list whole, rather than falling back to the game's name past its end. A settings resource named `game` is refused with `SETTINGS_RESERVED`. In the dev server, an edit to the manifest's `maxTicks` or team bounds waits for the next initialisation, as every settings edit does. The `CLAUDE.md.template`, `DESIGN.md.liquid` and `README.md.liquid` scaffolds say `maxTicks`. See [step 2g](guides/upgrading.md#2g-rename-ticks-to-maxticks-and-move-a-scenarios-game-fields-into-settingsgame)
|
|
38
|
+
- **A single-team game locks `teams.max`.** A game is single-team only when it declares `teams.max: 1` and locks it with `gameSettingsConstraints: { "teams": { "max": { "readOnly": true } } }`. A `teams.max` of 1 left unlocked is treated as possibly multi-team, as an omitted one is, so an independent game whose engine writes a shared capability fails the build until it adds the lock, and its message says so. The rule that a scenario or setup could not widen a `teams.max` of 1 is gone: the lock is what does that now. See [step 2g](guides/upgrading.md#2g-rename-ticks-to-maxticks-and-move-a-scenarios-game-fields-into-settingsgame)
|
|
39
|
+
- **The game-level `configuration` block is removed, and a scenario's `configuration` is `settings`.** A game sets its own values as its settings' defaults, and the build refuses both old fields. Scenario overrides, `sdk configure` overrides and `configures` are keyed by settings identifier. `sdk update package` no longer accepts a `configuration` block
|
|
40
|
+
- **Setup and the scenario editor are slots.** `configuration-user-interfaces/` and `configurationSurfaces` are removed, and the build refuses both: a quick-config UI moves to `setup/setup.sdk.json`, a scenario builder to `scenario_editor/scenario_editor.sdk.json`. Both use `SettingsEditorWrapper` and `useSettingsEditor()` (`{ settings, updateSettings, configures }`), replacing the `useConfigurationUserInterface*` hooks and types, and `renderSettingsEditor` replaces `renderConfigurationUserInterface`. `sdk new setup` and `sdk new scenario_editor` take no name. The configuration step exists exactly when a game has `setup`, and the host refuses `requestPlay` from the scenario editor. `CLAUDE.md.template`, `DESIGN.md.liquid`, `README.md.liquid`, `workflow-create-endpoint.md` and `workflow-test-engine-or-ui.md` are updated. See [step 3](guides/upgrading.md#step-3-move-configuration-uis-into-the-setup-and-scenario-editor-slots)
|
|
41
|
+
|
|
42
|
+
#### Logic
|
|
43
|
+
|
|
44
|
+
- **The engine, hooks and writing endpoints return what they write under `capabilities`.** `Init`, `Run` and a hook's `Run` return `{ capabilities: { ...context, sales } }`, aggregate functions `{ shared: { capabilities }, teams: [{ capabilities }] }`, and a writing endpoint `{ response, capabilities }` rather than `{ response, context }`. A result in the old shape fails the call with a message saying how to wrap it. An engine split into steps types each step as `StepFunction`. `sdk build` emits `manifestVersion: 3`. The `engine`, `hook` and `endpoint` logic templates and their tests, `CLAUDE.md.template` and the `workflow-create-engine.md`, `workflow-create-endpoint.md` and `workflow-test-engine-or-ui.md` rules are updated. See [step 4](guides/upgrading.md#step-4-return-what-logic-writes-under-capabilities)
|
|
45
|
+
- **Shared state has its own channel.** `InitAggregate` and `RunAggregate` receive the shared store once, as `shared.context`, and each `teams[i].context` carries that team's own capabilities only; a shared capability returned in a `teams` entry fails the init or tick. Type aggregates as `RunAggregateFunction<TeamContext, SharedContext, Settings>`. A session UI's `SessionData` is `{ shared, teams }`, and its generated `types.ts` adds `TeamContext`, `SharedWrites` and `SessionData`, with `SharedContext` now covering shared reads too. `createMockSharedState` joins the UI testing helpers. The aggregate engine and session UI templates, `DESIGN.md` and the starter game are updated. See [step 5](guides/upgrading.md#step-5-read-shared-state-from-its-own-channel)
|
|
46
|
+
- **One name for each thing.** `meta.currentTick` is `meta.tick`; every count of completed ticks (`useUserInterfaceData().currentTick`, session and presentation team `tick`, endpoint `session.tick`, CLI JSON `currentTick`) is `completedTicks`; one `TimerState` replaces five timer types, with `enabled`, `paused`, `intervalMs` and `pausedMs`; a tick controller returns `enabled`, `intervalMs` and `complete` and always receives `teams` as `{ teamId, index, context }`; `teamName` is `name`; `Participant` replaces `SessionParticipant`, `UserInterfaceTeamParticipant` and `UserInterfaceParticipant`; `TeamInfo` replaces `EndpointTeam`; and the player-surface hooks are `useUserInterfaceViewerRole`, `useUserInterfaceOwnTeam`, `useUserInterfaceOwnTeamJoinLink` and `useUserInterfaceCrossTeamData`. See [step 6](guides/upgrading.md#step-6-one-name-for-each-thing)
|
|
47
|
+
- **Completion is `complete` and a record's time is `recordedAt`.** `isComplete` is `complete` on an endpoint's `session` and team statuses, past-state entries and the state projection, and a past-state entry's `timestamp` is `recordedAt`. See [step 6d](guides/upgrading.md#6d-completion-record-times-blob-retention-and-the-manifest-types)
|
|
48
|
+
- **The settings-editor blob retention is `settings`.** `writeBlob(data, { retention: 'config' })` is refused; pass `retention: 'settings'`, which is also what a setup or scenario editor upload is stored with
|
|
49
|
+
- **The manifest types take manifest names.** `SDKConfiguration`, the authored `seriousgaming.sdk.json`, is `PackageManifest`; `Configuration`, the built `seriousgaming.json`, is `ResolvedPackageManifest`; and `LogicConfiguration` is `LogicLayout`
|
|
50
|
+
- **Timer durations are milliseconds, which the compiler cannot catch.** `intervalTick(intervalMs)`, a tick controller's `intervalMs`, a settings `intervalMs` default and a session UI's `extendTick(byMs, teamId?)` take milliseconds where they took seconds: `intervalTick(60)` is refused as below the 30-second minimum, and `extendTick(120)` extends by 120 ms. See [step 6c](guides/upgrading.md#6c-timers-in-milliseconds)
|
|
51
|
+
- **A validator names what it gates.** Rename `Run` to `UpdateData`; the build refuses a validator exporting `Run`, or neither gate. The `validator/logic.ts.liquid` scaffold and its test export `UpdateData`. See [step 8](guides/upgrading.md#step-8-rename-each-validators-run-to-updatedata)
|
|
52
|
+
- **Logic may not import Node built-ins.** `sdk build`, `dev` and `tick` refuse one with `LOGIC_NODE_BUILTIN`, and the dev server no longer gives logic `require`, matching production
|
|
53
|
+
- **Logic argument types require `meta.now` and `participants`**, so a test that builds arguments by hand passes both, or uses `logicArgs` (see Added). A test double typed as a generated `Callbacks` must supply every declared service, and `LogicCallbacks` takes `ApiUse` values. See [step 10](guides/upgrading.md#step-10-update-the-tests)
|
|
54
|
+
|
|
55
|
+
#### Endpoints
|
|
56
|
+
|
|
57
|
+
- **`agentCallable` is replaced by `visibility`.** Replace `"agentCallable": true` with `"visibility": "external"`, and delete `"agentCallable": false`; the build refuses the old field. `sdk endpoint list --agent` is `--external`, and `caller.userInterfaceIdentifier` is optional. See [step 7](guides/upgrading.md#step-7-update-endpoints)
|
|
58
|
+
- **An external endpoint's request is an object.** The build refuses an endpoint with `"visibility": "external"` whose `requestSchema` is not `"type": "object"` at the top (`EXTERNAL_REQUEST_NOT_OBJECT`), since an outside caller, such as an AI agent, passes named arguments. Internal endpoints are unaffected. See [step 7a](guides/upgrading.md#7a-replace-agentcallable-with-visibility)
|
|
59
|
+
- **Handlers receive `settings`, `session` and, team-scoped, `team`.** The generated `Handler` type requires them, so a unit test building arguments by hand passes them, or uses `logicArgs.endpoint`. A session-scoped handler's type has no `team`
|
|
60
|
+
- **`caller.teamId` is the caller's own team**, absent for a facilitator or a caller outside the game, where it held the endpoint's target team. Read the target from `team`. See [step 7d](guides/upgrading.md#7d-read-the-target-team-from-team)
|
|
61
|
+
- **The scenario editor calls only endpoints that need no session.** The build refuses a `scenario_editor` listing an endpoint that declares `reads`, `writes` or `actions` (`EDITOR_ENDPOINT_STATEFUL`), and the dev server answers a call from the editor with no session: the endpoint's `settings` resolved from the scenario being edited and the editor's unsaved changes, an empty `context`, no participants, a `session` of nothing played, and a `caller` of the new role `'author'`, not `'facilitator'`, since whoever creates a scenario may not be a facilitator, with no `sessionId`, which is now optional on `EndpointCaller`. The `workflow-create-endpoint.md` rule says so. See [step 7e](guides/upgrading.md#7e-give-the-scenario-editor-endpoints-that-need-no-session)
|
|
62
|
+
- **Endpoint failures are typed.** A surface's endpoint call rejects with `EndpointError` (`kind`, `outcome`, `applied`, `failedStep`), `callEndpoint:response` carries those fields in place of `error`, and `Session.query` and `createTestSession().send` answer `{ type: 'endpointFailure', failure }` instead of throwing
|
|
63
|
+
|
|
64
|
+
#### Surfaces
|
|
65
|
+
|
|
66
|
+
- **A player surface writes only what it declares.** A game UI or waiting room write (or `deletes` path) naming a capability outside its `writes`, or any key that is not a capability, is refused as `denied`
|
|
67
|
+
- **A session UI's team write changes only what it declares.** A write through `setData(teamId, update)` that changes a capability outside the session UI's `writes`, or names a key that is not a capability, is refused as `denied`, where 0.6 applied it. A capability carried back unchanged is still accepted, so a surface that returns a team's whole context with one field edited keeps working. Add the capability to the session UI's `writes`
|
|
68
|
+
- **`uses` on user interfaces is removed**, and the build refuses it. It never had an effect; a surface reaches a service through an endpoint that declares it
|
|
69
|
+
- **Demo mode is removed.** `demo` in the manifest and scenarios, `GameDemo` and `sdk dev --demo` are gone, and the build refuses the field. Use a walkthrough run to show a game
|
|
70
|
+
|
|
71
|
+
#### CLI
|
|
72
|
+
|
|
73
|
+
- **The CLI's results are a contract.** With `--json`, every command writes one envelope: `{ "ok": true, "command", "data", "diagnostics", "warnings" }` on success and `{ "ok": false, "command", "error": { "code", "message", "hint", "details" }, "diagnostics", "warnings" }` on failure. `new`, `update` and `delete` used to print `{ "success", ... }`; `build --validate-only --json` puts its summary in `data` and each finding in `diagnostics`; `package bundle`, `package version`, `package publish`, `configure`, `blob upload`, `endpoint list`, `endpoint audit` and `dev status` put their result in `data`; and a failing `endpoint call` reports in `error.details`. See [step 11](guides/upgrading.md#step-11-update-scripts-and-agents-that-drive-the-cli)
|
|
74
|
+
- **Exit codes say what kind of failure it was.** 1 is a bug in the CLI, 2 a usage error, 3 an invalid package, 4 a refusal by the game (including a failed tick) and 5 the environment, where every failure used to exit 1
|
|
75
|
+
- **`delete --force` is `delete --yes`**, which also answers an interactive `update`'s confirmation
|
|
76
|
+
- **`blob upload --config` is `blob upload --settings`**, and its `retention` reads `settings`. `tick --json` and `view` report `complete` in place of `isComplete`, and `view --tick N` entries carry `recordedAt` in place of `timestamp`
|
|
77
|
+
- **The CLI never waits on a prompt without a terminal.** Without a terminal on both stdin and stdout, with `--json` or `SERIOUSGAMING_JSON`, or with `CI` set, a command that would ask fails with exit 2 and `PROMPT_REQUIRED`. Stdin is read to its end rather than for 100ms
|
|
78
|
+
- **`new package` fails when `pnpm install` fails** in the new package (exit 5, `INSTALL_FAILED`). The files are written; run `pnpm install` to finish
|
|
79
|
+
- **`session.needsConfiguration` is `session.inSetup`** on session state (`createTestSession().state()`, `@seriousgaming/sdk/e2e`, the dev server's `/api/session`) and in `sdk reconfigure --json`, matching the spec's name for the setup state
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
|
|
83
|
+
#### Logic and testing
|
|
84
|
+
|
|
85
|
+
- **`logicArgs` in `@seriousgaming/sdk/testing`.** `logicArgs.run({ context, settings: defaultSettings })` builds the arguments a logic function takes, filling `meta` (a fixed `seed` and `now`), the roster and offline stub callbacks around what the test names, typed from the function it is passed to. There are builders for `init`, `run`, `aggregate`, `hook`, `endpoint`, `tickController`, `validator` and `requestTick`, and the stubs are exported as `createStubCallbacks()`. See [Testing](guides/testing.md#testing-the-run-function)
|
|
86
|
+
- **Typed test sessions.** `createTestSession<Context, SharedContext>(options)` types what a test reads back and writes, and adds `tick({ team })`, `requestTick(team)`, `setData(team, patch, { deletes, as })`, `setSharedData(patch)`, `team(index)` and `shared`, which holds the shared store under `context` as a session surface's `sessionData.shared` does. `createStubServices`, `stubGenerateText` and `TestServices` are exported from `sdk-tools/client`. See [Engine testing](guides/engine-testing.md#injecting-decisions)
|
|
87
|
+
- **`meta.now` on every logic call**, the operation's time in milliseconds, the same for every team in one tick and controlled by the session clock in tests, and **a read-only `participants` roster**: per-team logic sees its team; aggregates, the tick controller and session-scoped endpoints see everyone
|
|
88
|
+
- **Logic can delete keys.** `Init`, `Run`, a hook's `Run`, each level of an aggregate result and a writing endpoint may return `deletes` beside `capabilities`, with the same paths and rules as a UI's `setData` deletes. See [Removing a key](guides/engine.md#removing-a-key)
|
|
89
|
+
- **The engine can keep private memory between ticks.** `Init`, `Run` and the aggregate functions may return `state` beside `capabilities` and receive it back on the next call, typed with the new last type parameter of the engine function types. No other reader sees it, undo and reset rewind it, and it must be JSON-serialisable. New types `EngineResult` and `EngineFunctionArgs`; `createTestSession`'s `state()` reports it, and the `engine/logic.ts.liquid` scaffold mentions it. See [Keeping memory between ticks](guides/engine.md#keeping-memory-between-ticks)
|
|
90
|
+
- **The engine can end the game.** A tick's `Run` result, or a `RunAggregate` result's `shared` or `teams` entries, may carry `complete: true`, working exactly as a tick controller's completion. New types `RunResult` and `RunAggregateTickResult`. See [Game completion](guides/tick-controller.md#game-completion)
|
|
91
|
+
- **`Init` is optional.** An engine without one starts each team from its capabilities' defaults
|
|
92
|
+
- **`StepFunction` and `LogicResult` types**, for an engine that splits `Init` or `Run` into steps and for the `{ capabilities }` result shape
|
|
93
|
+
- **A service a resource declares is always there.** A declared `generateText`, `extractText`, `generateSpeech` or `writeBlob` is typed as present on the resource's generated `Callbacks`, so the `?.` and `!` guards can go, and a runtime that cannot provide a declared service refuses the package at load
|
|
94
|
+
- **`RequestTick` validators** refuse a participant's or observer's request for the next tick, receiving `{ context, requestedTick, actor, settings, meta }`; the facilitator's own ticks and timers are never gated. Generated validator types gain `RequestTickFunction`. See [Gating tick requests](guides/validators.md#gating-tick-requests)
|
|
95
|
+
|
|
96
|
+
#### Settings, capabilities and scenarios
|
|
97
|
+
|
|
98
|
+
- **A game can lock a setting.** `readOnly: true` on any property of a `settingsSchema` keeps the game's own value: the build refuses a scenario that sets it (`SETTINGS_LOCKED`, pointing at the value), and a setup screen or scenario editor that changes it is refused as `denied`, naming the setting. Locking an object locks everything in it. The dev console's Save leaves locked values out of the scenario it writes. See [Lock a setting](guides/settings.md#lock-a-setting)
|
|
99
|
+
- **A game can narrow the built-in `game` settings.** `gameSettingsConstraints` in `seriousgaming.sdk.json` takes `minimum`, `maximum`, `enum` and `readOnly` for each game setting, for example four to ten rounds or a fixed team ceiling. The build refuses a constraint that widens the SDK's schema (`GAME_CONSTRAINT_INVALID`) and a game whose own values break its constraints (`GAME_CONSTRAINT_VIOLATED`), and merges the rest into the `game` settings schema it emits. A bound rules out `null`, so the generated type of a bounded `maxTicks` is `number`. See [Narrow or lock the game settings](guides/settings.md#narrow-or-lock-the-game-settings)
|
|
100
|
+
- **A scenario editor's draft is held to the settings schemas.** An edit outside a setting's range is refused as `denied`, as a setup write already was, and the dev console's Save refuses a scenario whose settings break a schema
|
|
101
|
+
- **`sdk new settings`, `sdk update settings` and `sdk delete settings`.** `new` takes a JSON definition or prompts for each property, and refuses one without a default; `update` renames a settings resource and re-keys every reference; `delete` removes it and every reference. The interactive `new` commands ask which settings a resource reads. See [Create settings](recipes/create-settings.md)
|
|
102
|
+
- **A setup screen can set the number of rounds.** The tick limit, the team bounds, the participant guidance and the default team names are the built-in `game` settings, so a setup UI or scenario editor that lists `game` in `configures` edits them with `updateSettings`, a scenario overrides them under `settings.game`, `sdk configure` sets them under `game`, and any resource can declare `settings: ["game"]` to read them. Generated `types.ts` types them, and `GameSettings` is exported from the SDK. Only `waitingRoom.openOnStart` stays out of setup's reach, and play cannot start while the team count is outside the bounds setup chose. The factory example's setup and scenario editor set the number of rounds. See [The game settings](guides/settings.md#the-game-settings)
|
|
103
|
+
- **Every resource reads settings directly**, so scenarios and setup reach endpoints, hooks and validators, and a surface no longer needs the engine to copy a value into a capability. The starter game reads its menu from a `menu` settings resource. See [Reading settings rather than copying them](guides/settings.md#reading-settings-rather-than-copying-them)
|
|
104
|
+
- **Capabilities that undo and reset leave alone.** `"lifetime": "session"` keeps a value exactly as it was across an undo, a per-team reset and a session reset, for facilitator state such as a pacing gate or an announcement. The build refuses one in the engine's `reads` or `writes`. See [Lifetime](guides/capabilities.md#lifetime)
|
|
105
|
+
- **`tsType` on a capability schema property** names the TypeScript type the generated `types.ts` uses, so reads of a loose object need no cast. See [A TypeScript type of your own](guides/capabilities.md#a-typescript-type-of-your-own)
|
|
106
|
+
- **`scenarioFormat` in `seriousgaming.sdk.json`**, an integer from 1. Bump it when a release would make a saved custom scenario wrong, and a runtime refuses scenarios saved under another format. See [Scenario format: when to bump it](guides/scenarios.md#scenario-format-when-to-bump-it)
|
|
107
|
+
- **`requires` in `seriousgaming.sdk.json`**: an optional list of the runtime features a game depends on. A runtime without one refuses the package at load and names it, and `sdk build` does the same for a feature the installed SDK does not support. No optional features exist yet
|
|
108
|
+
|
|
109
|
+
#### Endpoints
|
|
110
|
+
|
|
111
|
+
- **External endpoints.** `"visibility": "external"` publishes an endpoint to any caller outside the game (a facilitator's AI agent, a platform API or another integration), whose handler sees `caller.role` as `'external'`. `sdk endpoint call --external` makes a call the way an outside caller does
|
|
112
|
+
- **Endpoints know their team and where the session stands.** A team-scoped handler receives `team: { teamId, index, name }`, and every handler `session`: the ticks completed, the tick limit, the waiting room, completion, and each team's standing. Declare `roster: true` to list each team's participants too
|
|
113
|
+
- **Endpoints can act on the session.** An endpoint that declares `actions` may return `actions: [{ type: 'setWaitingRoom', open: false }, { type: 'tick' }]` beside its response, carried out after its write in one step: tick, the timer controls, the waiting room, renaming and adding teams, moving participants and the locale. Export `Plan` and `Respond` in place of `Handle` to answer after the actions land
|
|
114
|
+
- **A session-scoped endpoint can write individual teams**, returning `teams: { 'team-1': { ... } }`: every team's patch lands, or none does
|
|
115
|
+
- **Endpoints can take files.** Mark a request property with `"blob": { "accept": [...], "maxBytes": ... }` and the handler receives a checked `blob:` reference, whether the caller uploaded first or sent the bytes inline as a `data:` URI
|
|
116
|
+
- **`EndpointError`** and the `EffectStep`, `EndpointFailure`, `EndpointFailureOutcome` and `EndpointCallResult` types
|
|
117
|
+
- **The CLI and dev server do what an outside caller does.** `sdk endpoint call` gains `--principal kind:id[:name]`, `--attach field=path` and `--save-blobs dir`, and prints a failure's kind, message and landed steps; `sdk endpoint audit` lists every endpoint call that changed the session; `sdk endpoint list` shows each endpoint's actions. The dev server's `listEndpoints`, `principal` and `getEndpointAudit` serve an outside caller, and an external response carries a URL for every `blob:` reference
|
|
118
|
+
- **The build fails on an endpoint that exports neither `Handle` nor `Plan`**, or whose schema does not compile, where both used to fail only at run time
|
|
119
|
+
|
|
120
|
+
#### User interfaces
|
|
121
|
+
|
|
122
|
+
- **A game can ship a presentation.** A UI bundle in the `presentation/` slot becomes the facilitator's slide deck: read-only, shown during a session with every team's results or with no session at all. Every runtime offers it. Build it with the new `@seriousgaming/deck` package, published in lockstep with the SDK, which gives slides, click-to-reveal steps, transitions, building blocks in the theme's colours, and export to PDF, editable PowerPoint and PNG. `sdk new presentation` scaffolds a starter deck, the dev console shows it at `/presentation`, new packages depend on the deck, and the scaffold gains a `workflow-create-presentation.md` rule and a 'Presentations' section in `CLAUDE.md.template`. See [Building presentations](guides/presentation.md)
|
|
123
|
+
- **UI hooks for assets and past state.** `useAssetUrl(ref)` returns an asset's URL, or `undefined` until the host has sent them; `useAsset(ref, { as, locale? })` fetches and reads an asset, trying the locale's copy first; `usePastState(query)` wraps `getPastState` and drops overtaken answers. See [Reviewing past ticks](guides/ui.md#reviewing-past-ticks)
|
|
124
|
+
- **`useViewerStorage(key, initial)`** on every surface: browser storage namespaced by session, viewer and surface, falling back to memory where storage is blocked. The handshake carries an opaque `sessionKey` for it, and the `render*` test helpers take a `sessionKey` option
|
|
125
|
+
- **The wrappers set `lang`, `dir` and `color-scheme` on the document**, in step with the locale and the theme. The scaffolded `main.tsx` files no longer set `lang` and `dir` themselves
|
|
126
|
+
- **`contrastText` on every theme colour**, meeting WCAG AA against it (`ThemeColorRole`, `ThemeContrastText`). The `theme.ts` templates, `CLAUDE.md.template`, `workflow-create-user-interface.md`, `workflow-create-engine.md` and `workflow-test-engine-or-ui.md` are updated
|
|
127
|
+
- **`failure.retry()` on `useMutationReporter`** after a `conflict`, for a person-initiated 'Try again'
|
|
128
|
+
- **Typed session writes and test fixtures.** A session UI's generated `types.ts` adds `TeamWrites`, the team-scope capabilities in its `writes`, and `useSessionUserInterfaceData<SessionData, Settings, SharedWrites, TeamWrites>()` types the per-team `setData(teamId, update)` with it, so a team write naming a shared capability fails to compile. `createMockTeamState<TeamContext>`, `createMockSharedState<SharedContext>` and `renderSessionUserInterface<SessionData>` type a test's fixtures the same way. Every parameter defaults to today's untyped form, so existing code compiles unchanged. See [Session UIs](guides/testing.md#session-uis)
|
|
129
|
+
- **A session UI can delete keys.** Both forms of a session surface's `setData` take `{ deletes }` last, as a player surface's does: `setData(teamId, {}, { deletes })` removes from one team and `setData({}, { deletes })` from the shared store. Each path must fall under a capability the surface writes, of the form's scope, or the write is refused as `denied`. `mergingSetData` in `@seriousgaming/sdk/ui/testing` takes the same options and applies them after the merge, as the host does. See [Removing a key](guides/session.md#removing-a-key)
|
|
130
|
+
- **A session UI can delete an empty team** with `callbacks.deleteTeam(teamId)` in an independent session, once its participants have been moved out. The index is left as a gap, and undo never brings the team back. The dev console has a Delete team button. See [Deleting a team](guides/session.md#deleting-a-team)
|
|
131
|
+
- **`pauseTimer` and `resumeTimer` are awaitable**, resolving once the host has applied the change and rejecting with a `MutationError` when it refuses
|
|
132
|
+
- **Every past-state entry carries `index` and `name`**, and the mode-colour theme types (`UserInterfaceModeColors` and siblings) are exported
|
|
133
|
+
|
|
134
|
+
#### CLI and dev server
|
|
135
|
+
|
|
136
|
+
- **Stable error codes and `--json` on every command.** Each failure carries an `UPPER_SNAKE` code and an exit code from its category, with a `hint` where there is something to do, and `SERIOUSGAMING_JSON=1` turns `--json` on for a whole job. See [CLI error codes](cli-error-codes.md), and [Check a package from a script](recipes/check-a-package-from-a-script.md) for driving the CLI from CI or an agent
|
|
137
|
+
- **Diagnostics that say where.** A failed `build`, or any command that loads the package, lists every finding at once in `diagnostics`, each with a code, the `file`, an RFC 6901 `pointer`, the `resource` and a `hint` such as `Did you mean 'BaseDemand'?`. A logic error carries `LOGIC_ERROR` with the TypeScript file, line and column and the tick and team it threw for
|
|
138
|
+
- **`SETTINGS_STALE` (exit 4)** for a command that would advance or change play while the session no longer matches the package, and `stale` in `sdk view`'s data
|
|
139
|
+
- **A dev server for tests and recordings, beside your own.** `sdk dev --isolated` keeps no lock file, ignores saved configuration and keeps uploads in a temporary directory; `--port auto` takes free ports; `SERIOUSGAMING_DEV_URL` points session commands at a server; and `startDevServer(options)` from `@seriousgaming/sdk-tools/client` starts one in a test runner. See [Run a second server beside your own](cli-reference.md#run-a-second-server-beside-your-own)
|
|
140
|
+
- **The dev server picks up more without a restart**: edits to `seriousgaming.sdk.json`, a surface added while it runs, and a capability added while it runs, seeded from its defaults. `sdk dev --reinit-on-change` restarts the game at tick 0 on every edit that applies only at initialisation
|
|
141
|
+
- **`SERIOUSGAMING_DEV_STATE_DIR`** keeps the dev server's lock file outside the package
|
|
142
|
+
- **For runtimes: the manifest schema and the logic-result checks.** `@seriousgaming/sdk-tools/schema.json` is the JSON Schema of this release's manifest version, its `ResolvedPackageManifest` the built `seriousgaming.json`, and `@seriousgaming/sdk-tools/runtime` adds `assertJsonSerialisable`, `readEngineResult`, `readAggregateResult` and `readTickControllerResult`. A game needs none of them
|
|
143
|
+
- **The SDK has a README** saying which subpaths carry semver guarantees (the root, `./ui` and `./ui/testing`)
|
|
144
|
+
|
|
145
|
+
### Changed
|
|
146
|
+
|
|
147
|
+
- **The session scaffold uses the generated types throughout.** `sdk new session` writes an `App.tsx` that passes `SharedWrites` and `TeamWrites` to `useSessionUserInterfaceData`, so both forms of `setData` are typed, and an `App.test.tsx` that renders through `renderSessionUserInterface<SessionData>()` with fixtures from `createMockTeamState<TeamContext>()` and `createMockSharedState<SharedContext>()` in place of a hand-built provider tree, so a shared capability on a team fixture fails to compile. The guidance names the type argument too: merge the changes to `CLAUDE.md.template`, `claude/rules/workflow-create-user-interface.md` and `claude/rules/workflow-test-engine-or-ui.md`, and the session templates `session/src/App.tsx.liquid` and `session/src/App.test.tsx.liquid`, into a game you scaffolded earlier
|
|
148
|
+
- **Docs restructured: one guide per resource kind, steps-only recipes, and generated CLI, API and error-code reference.** Each concept page is folded into its guide, recipes are cut to their steps and link to the guide, the CLI reference, the API reference (`api/`) and [CLI error codes](cli-error-codes.md) are generated from the code, the upgrading guide covers 0.6 to 1.0 only, and releases before 1.0 move to [changelog-0.x.md](changelog-0.x.md). The scaffold's `CLAUDE.md.template` and the `workflow-test-engine-or-ui.md`, `workflow-create-user-interface.md` and `workflow-create-presentation.md` rules now link to the shipped guides for settings resolution, the UI test harness and presentation habits rather than restating them; merge them from `dist/templates/package/`. The same pages are published as a versioned docs site, one directory per release plus `latest/`, with an `llms.txt` index and an `llms-full.txt` holding the whole documentation as one file for agents
|
|
149
|
+
- **The published packages declare Node 24 or later** in `engines`, and link to the docs site at https://sdk.seriousgaming.com/ rather than to the source repository. The scaffold's `workflow-update-sdk-version.md` rule names all three packages it bumps; merge it from `dist/templates/package/`
|
|
150
|
+
- **The packages carry licences.** `@seriousgaming/sdk` and `@seriousgaming/deck` are Apache-2.0. `@seriousgaming/sdk-tools` is under the Serious Gaming SDK Tools Licence in its `LICENSE` file: use it to build, test and publish games for the Serious Gaming platform; files it generates or copies into your project are yours under Apache-2.0. All three were `UNLICENSED`
|
|
151
|
+
- **A settings edit no longer applies mid-session.** At tick 0 the dev server re-initialises on the edit; once a tick is played it keeps the settings the session started with, the console offers a Re-initialise button, and `sdk tick`, `sdk undo`, a context patch and a writing endpoint call are refused with `SETTINGS_STALE` until `sdk init`. The same applies to `teams`, `waitingRoom.openOnStart` and `identifier`, adding or removing `setup`, and removing a capability or changing its `scope`. See [What reloads](cli-reference.md#what-reloads)
|
|
152
|
+
- **The dev server listens on `localhost` only.** It used to accept connections from any machine on the network. To test on a phone or tablet, start it with `pnpm run sdk dev --lan`. See [Start the dev server](cli-reference.md#start-the-dev-server)
|
|
153
|
+
- **Test sessions are offline by default.** `createTestSession` stubs `generateText`, `generateSpeech` and `extractText` with deterministic answers; pass `services: 'platform'` to reach the real services. A hand-built theme object needs `contrastText`
|
|
154
|
+
- **Surfaces never retry a `conflict` themselves**; a runtime with optimistic concurrency retries a losing write before reporting it
|
|
155
|
+
- **A tick whose logic throws is `tick-failed`**, where the dev server reported it as `error`. A surface's `requestTick` rejects with `kind: 'tick-failed'`, and `sdk tick` fails with `TICK_FAILED`
|
|
156
|
+
- **Every runtime offers walkthrough runs**, so `setWalkthroughLink` always carries a link and `useSessionUserInterfaceWalkthroughLink()` is `null` only until the host sends it. `callbacks.resetWalkthrough()` is refused as `denied` while the session has no walkthrough, and for a named team
|
|
157
|
+
- **`sdk team remove` refuses a team with participants, and any team in a shared-state session.** Move participants out with `sdk participant move` first. A team added later at the freed index is a new team
|
|
158
|
+
- **A tick controller's `intervalMs` is optional when its timer is off**, and a game with neither `maxTicks` nor a tick controller builds with a warning, since its engine can end it with `complete: true`
|
|
159
|
+
- **A returned capability merges one level deep.** Each variable an engine, hook or endpoint returns replaces its old value whole, objects included, so `PendingJunction: {}` empties it. The dev server and `createTestSession` already did this; it is now the rule for every runtime. See [How a result folds into the context](guides/engine.md#how-a-result-folds-into-the-context)
|
|
160
|
+
- **A saved custom scenario is validated against the package each time it is used**, and refused with the offending keys named rather than opened on defaults
|
|
161
|
+
- **A CLI command notices a dev server started by other tools.** After an upgrade or rebuild, a command replaces a server it started in the background, and fails with `SESSION_STALE` (exit 5) for one you started with `sdk dev`
|
|
162
|
+
- **Every session command repeats what the dev server could not apply live** (`SETTINGS_STALE`, `RELOAD_FAILED`, `CAPABILITY_SEEDED`) on stderr and in `warnings`, and `GET /api/session` reports them
|
|
163
|
+
- **The dev server runs the checks `sdk build` runs**, at start and after every edit. A package `build` would refuse still runs, so you can fix it while you watch, but each error is printed on stderr, shown in a banner on the console, and listed in `GET /api/session` `warnings` with its diagnostic code (for example `REFERENCE_UNKNOWN`) and `build: true`, so every session command repeats it. See [Where the session lives](cli-reference.md#where-the-session-lives)
|
|
164
|
+
- **A schema error the SDK cannot explain points at the upgrading guide**, and a refused shared write from a session UI says why
|
|
165
|
+
- **The `@seriousgaming/sdk/e2e` helpers find the dev server through `SERIOUSGAMING_DEV_URL`** and take `{ url }` beside `{ port }`
|
|
166
|
+
- **The UI library no longer logs every host ping to the browser console**
|
|
167
|
+
- **The scaffolded `CLAUDE.md` is half the length and leads with what not to do.** It opens with 'Non-negotiables' and an index of `.claude/rules/`, points at the docs shipped inside `@seriousgaming/sdk-tools` (`sdk docs view`), teaches the CLI's result contract (envelope, exit codes, `--yes`, `diagnostics`), no longer runs `init` after `sdk configure`, and bumps `@seriousgaming/deck` with the SDK. `CLAUDE.md.template`, `workflow-create-engine.md`, `workflow-create-endpoint.md` and `workflow-create-user-interface.md` cover declared writes, typed endpoint failures, retries and the Node rule. `workflow-create-endpoint.md`, `workflow-create-engine.md`, `workflow-update-sdk-version.md`, `DESIGN.md.liquid`, `README.md.liquid` and `oxlintrc.json.template` catch up with manifest version 3. `new package --empty` writes `CLAUDE.md` and `.claude/` too. Merge them from `dist/templates/package/`
|
|
168
|
+
- **A missing platform API key is an environment failure.** A tick or endpoint call whose logic called `generateText` with no key configured fails with `LLM_NOT_CONFIGURED` (exit 5), where it was `TICK_FAILED` or `HANDLER_ERROR` (exit 4), with a one-line message and a hint saying how to set the key, in place of the multi-line banner
|
|
169
|
+
- **`sdk init` reports the scenario it started on** as `scenario` in its output, `null` when there is none
|
|
170
|
+
- **`new`, `update` and `delete` report paths relative to the package root** in `filesCreated`, `filesUpdated`, `filesDeleted` and `directoriesRenamed`, where they were absolute
|
|
171
|
+
- **A manifest failure names `seriousgaming.sdk.json`, not its absolute path**, and an unsupported `requires` carries a `FEATURE_UNSUPPORTED` diagnostic pointing at the entry
|
|
172
|
+
- **The scaffold's `pnpm run lint` fails on a warning** (`oxlint --max-warnings=0`)
|
|
173
|
+
- **The starter game declares its scenario format and describes its settings for scenario authors.** `new package` writes `scenarioFormat: 1` into `seriousgaming.sdk.json`, leaves `customScenarios` out so a new game opts into facilitator-saved scenarios deliberately, and every settings property carries a `title`, a `description` with its units, a range and, for `CurrencySymbol`, an `enum`. Whole-number settings are typed `integer`, so a scenario that sets one to a fraction is refused. The generated `types.ts` names `Menu` and `UsualDemand` and narrows `CurrencySymbol` to its three symbols
|
|
174
|
+
- **The scaffold's lint refuses `any` and non-null assertions (`!`)** outside tests, `testing/` and each UI's `src/main.tsx`, and its `.gitignore` ignores Playwright's `test-results/` and `playwright-report/`. Merge `oxlintrc.json.template` and `gitignore.template` from `dist/templates/package/`
|
|
175
|
+
- **The scaffold's dependencies are refreshed**: `@playwright/test` 1.64, `@types/node` 24.19, `@vitejs/plugin-react` 6.1.2, `jsdom` 30.1, `oxlint` 1.87, `react-i18next` 17.0.16, `vite` 8.3.4 and `vitest` 5.0.3. Merge them from `package.json.liquid`, then run `pnpm exec playwright install chromium`
|
|
176
|
+
- **The scaffold's `playwright.config.ts` sets `forbidOnly` on CI**, so a stray `test.only` fails the run rather than skipping every other test. Merge it from `playwright.config.ts.liquid`
|
|
177
|
+
- **The runtime specification settles readings implementers had to choose**: an endpoint's effects take their own `meta.now`; how a cohort tick states the boards it decided on is the runtime's choice; work outside play state may follow an endpoint's effects; a session UI's unchanged capability is compared deeply after the merge; a clamped tick-controller result is reported to operators at every evaluation; and settings the host enforces use the values frozen at play start
|
|
178
|
+
- **The conformance suite reports more of a production host fairly.** Checks that call an endpoint from outside the game require the new `external-callers` driver capability and checks that hold a session at setup require `setup-state`, so a host without either reports them unsupported. REQ-014 and REQ-020 check the outcome rather than a message or the host page's own traffic, each built fixture variant has its own package identifier, a new check holds a participant's `getPastState` to its own surface's rights whatever surface it claims, and the moved-participant check waits for the remount instead of racing it
|
|
179
|
+
- **Three doc pages have new names**: `guides/game-configuration.md` is [`guides/settings.md`](guides/settings.md), `guides/examples.md` is [`guides/patterns.md`](guides/patterns.md), and `recipes/share-logic-between-module-and-ui.md` is [`recipes/share-logic-between-engine-and-ui.md`](recipes/share-logic-between-engine-and-ui.md). The docs no longer point at example packages you cannot see, the publishing and LLM guides say an account needs access Business Simulations grants (as the 403 messages from `package publish` and `generateText` now do, and the scaffold's `workflow-publish-package.md`), and `llms.txt` and `llms-full.txt` on the docs site link by absolute URL
|
|
180
|
+
|
|
181
|
+
### Removed
|
|
182
|
+
|
|
183
|
+
- **`sdk docs ask` and `sdk docs search`**, with the `@xenova/transformers` dependency behind them (about 230MB less to install) and `HF_HOME` and `HUGGINGFACE_HUB_CACHE`. Read the shipped docs with `sdk docs view <path>`, search `node_modules/@seriousgaming/sdk-tools/dist/docs`, or copy them out with `sdk docs export`. Both commands stay as hidden pointers that say so and fail with `USAGE_REMOVED_COMMAND` (exit 2), so agent guidance that still names them finds its way. Delete the `sharp` and `protobufjs` overrides from your `pnpm-workspace.yaml`
|
|
184
|
+
- **`GameRunner` and `MultiTeamGameRunner`**, deprecated in 0.6.0, with their option and result types. Use `createTestSession`; see [Moving from GameRunner](guides/engine-testing.md#moving-from-gamerunner)
|
|
185
|
+
|
|
186
|
+
### Fixed
|
|
187
|
+
|
|
188
|
+
- **A write to what the tick controller reads no longer restarts the countdown.** The dev server re-armed the timer every time the controller was re-run after a write, even when it gave the same answer, so a team writing all round pushed its own automatic tick back indefinitely, a facilitator's `extendTick` was undone, and a paused timer lost the time it had left. A re-run that returns the same answer now leaves the countdown alone; only a different answer starts a new window
|
|
189
|
+
- **`sdk build` checks settings defaults against their schema.** A settings default that broke its own schema, such as a string on a number property or an object default missing a key its `required` lists, passed the build and reached logic as the value every session played with. Defaults in capabilities, endpoints and UIs were already checked; settings resources were missed. The build now refuses one, naming the settings resource and the path, and lists settings resources in its report
|
|
190
|
+
- **Team ids are documented as opaque.** The `TeamRunMeta.teamId` and `PastStateQuery.teams` docs said a team id is always `team-${index + 1}`. It is an opaque, stable id: take it from `SessionData` or `setTeamList` and never build one from an index, since a runtime may assign other ids. A runtime never assigns `'own'` or `'all'`, so a `getPastState` query cannot mistake a team for either
|
|
191
|
+
- **A setup UI or scenario editor that spreads an object-valued setting is no longer refused.** A `required` inside an object property was enforced on every override, so `updateSettings({ engine: { ...settings.engine, Execs: { ...settings.engine.Execs, AA: true } } })` was denied for the keys the change left out, and a scenario overriding one key of such an object failed `sdk build`. Overrides are now partial at every depth, as they already were at the top, and a value of the wrong type or an undeclared key at any depth is still refused. An item of an array an override supplies is still held to its `required`, since an array replaces the one beneath whole
|
|
192
|
+
- **The scenario editor no longer changes the session running beside it.** The dev server stored the editor's edits in its session's settings, so editing a scenario reconfigured the session, opening the editor restarted the session on that scenario, and an endpoint the editor called read the setup step's edits as the draft. The editor now edits a draft of its scenario that belongs to no session: its `setSettings` and its endpoints read the draft, **Save** writes it into the scenario, and a session sees the values once it is started on that scenario
|
|
193
|
+
- **A setup UI or scenario editor changes only the settings it lists in `configures`.** The dev server accepted an `updateSettings` naming any settings resource, the built-in `game` settings included, so a settings editor that did not configure `game` could still change the number of rounds. A payload naming a settings resource outside `configures` is now refused as `denied`, naming it, and none of it is applied. List `game` in `configures` to edit the game settings
|
|
194
|
+
- **Saving a scenario keeps every field it does not edit.** The dev server's **Save** rebuilt the scenario's `scenario.sdk.json` from scratch, so saving over one dropped every locale but English and every description, renamed it to its identifier, and dropped `default` and every other field it did not write. It now replaces the settings alone, the number of rounds and team settings under `settings.game` among them. **Add scenario** from a copy now carries the source's locales, descriptions and other fields under the new scenario's name, and is never the default
|
|
195
|
+
- **An engine or hook that changes a capability outside its `writes` fails, aggregate engines included.** The dev server checked only the capabilities a resource declared in `reads`, and only after a per-team `Init` or `Run` or a hook, so `InitAggregate` and `RunAggregate` could change a read-only capability under `shared` or in a `teams` entry, and any of them could write a capability it never declared. A changed capability that is not in `writes` now fails the call, naming it, and nothing of the result is applied. A capability returned unchanged, as the `{ ...context }` spread returns it, still passes. Add a capability to the resource's `writes` if it is meant to write it
|
|
196
|
+
- **A per-team `Init` that changes a shared capability fails, as one from `Run` does.** The dev server checked only `Run`, so a team's `Init` could write a value every team reads. It now fails with _'modified shared capability "market" in Init. Shared capabilities may only be written in InitAggregate'_, and a team added to a shared-state game whose `Init` fails leaves the session as it was
|
|
197
|
+
- **An aggregate engine in an independent game initialises each team on its own.** The dev server handed `InitAggregate` every team at once; it now calls it once per team with a one-element `teams`, as it already called `RunAggregate`
|
|
198
|
+
- **`participantJoin` fires once per participant when play starts.** A game with a setup step ran the hook twice for each participant seated at Play, so a join counter or role assignment ran double. A reset still replays it once
|
|
199
|
+
- **A session reset keeps the settings the setup step chose.** The dev server went back to the settings last saved by `sdk configure`, so a reset session ran on different values from the ones it started with. Going back to the setup step keeps them too, so the form opens on what was chosen
|
|
200
|
+
- **A tick controller result with an unknown key, or a non-boolean `enabled` or `complete`, fails the evaluation.** The dev server ignored the extra key and treated `complete: 'no'` as true, ending the game. It now reports the failure, naming the key, and stops the timer, as for a controller that throws
|
|
201
|
+
- **A surface that replaces its own document after connecting is torn down.** The dev server bounded only a frame's first load, so a UI that navigated itself to another page stayed on screen in the session's chrome. `HostChannel` in `@seriousgaming/sdk-tools/host` now bounds every load of a frame whose element offers a `load` event, and falls back to the platform view when no handshake follows
|
|
202
|
+
- **`-j, --json` and `-w, --working-dir` work before the command**, so `sdk --json view` no longer fails with `USAGE_BAD_FLAG`
|
|
203
|
+
- **A bad number to `--teams`, `--team` or `fault --times` is a usage error (exit 2)**, where `--teams abc` reached the session and failed with `SESSION_ERROR`, and `--team 2abc` was read as team 2
|
|
204
|
+
- **`--scenario` naming a scenario the package does not hold fails with `RESOURCE_NOT_FOUND` (exit 2)**, listing the scenarios it has, on `init`, `tick` and `dev`
|
|
205
|
+
- **`sdk update package` leaves `package.json` alone.** Every update set its `name` to the package identifier and its `description` to the game's name, and reformatted the file, so an npm name such as `@acme/game` was lost. `update package` now writes `seriousgaming.sdk.json` only; `package.json` owns its name, description and version. An update that changes nothing no longer rewrites `seriousgaming.sdk.json` either, so it keeps its formatting
|
|
206
|
+
- **Logic stuck in a loop no longer wedges the command line.** A synchronous loop that never returns blocks the dev server, and its time limit with it. A command that finds the server taking connections but not answering now fails with `DEV_SERVER_UNRESPONSIVE` (exit 5) rather than hanging, and `sdk dev stop` kills a server that does not exit within five seconds and clears its lock. `sdk dev status` reports such a server with `responding: false`, and `sdk dev status` and `sdk dev stop` now honour `-w`
|
|
207
|
+
- **A `title` on a setting, a variable or an endpoint field no longer becomes a type name in the generated `types.ts`.** A title of 'Context' broke the file with a duplicate identifier and one of 'Settings' renamed the settings type `Settings1`. Titles on values are now ignored by type generation and kept everywhere else; a title on an object still names its type. Run `pnpm run generate` to pick it up
|
|
208
|
+
- **A built `seriousgaming.json` now passes the SDK's own manifest schema.** Each user interface's `ui` block recorded a `directory` the schema does not allow, so a platform checking uploads against the schema would refuse every package. The block now carries only `entrypoint`; rebuild to pick it up
|
|
209
|
+
- **`sdk fault --reason` now reaches the outcome for every fault.** Only `llm-failure` used it; `conflict`, `denied`, `tick-failure` and `endpoint-timeout` now carry your `--reason` as the outcome's message, and `hook-throw` adds it to the reported failure. Without `--reason`, the message is unchanged
|
|
210
|
+
- **`sdk fault llm-failure` now fails LLM calls.** It was accepted and reported armed but never fired. While armed, each call logic makes to `generateText`, `extractText` or `generateSpeech` fails as a platform failure would, carrying the `--reason` you gave, and uses up one of its `--times`
|
|
211
|
+
- **A resource copied into a running package is picked up**, and an edit made just as `sdk dev` starts is no longer missed on macOS
|
|
212
|
+
- **An uploaded file's name survives a space**, and uploads from setup and the scenario editor are kept across a reset
|
|
213
|
+
- **The dev server now clears a game's uploads on a reset, as the platform does.** A session reset deletes `game`-retention blobs, including what `writeBlob` and `generateSpeech` stored. `sdk init`, the console's Re-initialise and Reconfigure, and selecting a scenario also delete setup and scenario-editor uploads. The automatic re-initialise after a package edit keeps those, and a per-team reset and the walkthrough clear nothing
|
|
214
|
+
- **`pnpm test:e2e` in a scaffolded package no longer hangs after its last test.** The scaffold starts an isolated server with `startDevServer` in `testing/e2e/global-setup.ts`. To adopt it, copy `global-setup.ts.template`, replace `playwright.config.ts` and drop `{ port }` from your helper calls
|
|
215
|
+
- **A shared package of yours can import the SDK's UI entry.** The scaffold's `vite.config.ts` and `vitest.config.ts` dedupe `@seriousgaming/sdk` and `@seriousgaming/deck`, so a second copy no longer leaves every hook reading empty data
|
|
216
|
+
- **The scaffold's `.claude/settings.json` pre-approves only read and check commands**, never `dev`, watch-mode tests or `package publish`. Replace the file to adopt it
|
|
217
|
+
- **`renderUserInterface`'s `setData` resolves a `MutationCommit`** as the real one does, and its default `uploadBlob` and `readAsset` resolve empty values. CLI messages and the generated `engine/types.ts` header say 'engine' where they said 'module'
|
|
218
|
+
- **`sdk dev` no longer reports itself as already running after a restore** when a lock's pid had been reused
|
|
219
|
+
- **Saving a scenario keeps every settings resource**, not only those named `engine`, `ui` or `session`
|
|
220
|
+
- **A waiting room that declares cross-team reads receives cross-team data**, so `useUserInterfaceCrossTeamData()` is no longer empty there
|
|
221
|
+
- **A session UI's `pauseTimer`, `resumeTimer` and `extendTick` honour their `teamId`**, and a message naming a team the session does not hold is refused rather than acting on another team
|
|
222
|
+
- **Interactive `sdk update engine` works**, and `sdk new settings`, `sdk update settings` and `sdk new endpoint` ask for settings and properties rather than 'configuration fields'
|
|
223
|
+
- **An endpoint's `extendTick` action may pull the deadline in** with a negative `byMs`, as a session UI's can
|
|
224
|
+
- **A `blob:` reference whose id is not a stored blob's is refused as not found wherever it is resolved**
|
|
225
|
+
- **`sdk new` and `sdk delete` tell you to run `pnpm run generate` and `pnpm run build`, not npm**, and the dependency comment in the UI, waiting room, setup and scenario editor templates says `pnpm add`
|
|
226
|
+
- **The dev server's `POST /api/blobs` takes an upload with no `Content-Type`**, and refuses an empty one with a 400 that says so
|
|
227
|
+
- **A team-scoped endpoint called by the CLI or `createTestSession` receives its team**, and a call naming a team id and index that disagree, or no team in a session of several, is refused
|
|
228
|
+
- **An endpoint called by `sdk endpoint call` no longer sees `caller.userInterfaceIdentifier: 'cli'`**; the field is absent. The dev server refuses a past-state read or endpoint call naming a surface the package does not declare
|
|
229
|
+
- **Resetting the walkthrough through the command channel follows the session's current scenario, locale and settings**
|
|
230
|
+
- **pnpm's `--` before a command's flags works in a game.** Given `pnpm run sdk tick` with a `--` ahead of `--no-server`, the CLI read every flag after it as a stray argument and failed with 'too many arguments'. It now drops a `--` that stands before the command's own flags, and the docs no longer show one
|
|
231
|
+
- **`getPastState` from a game UI honours `teams: 'all'` and a team id**, returning another team's rows narrowed to the UI's `crossTeamReads`. It always answered with the caller's own team
|
|
232
|
+
- **A participant moved to another team in the dev server sees their new team's board**, rather than keeping the old team's until the surface showed as disconnected
|
|
233
|
+
|
|
234
|
+
### Security
|
|
235
|
+
|
|
236
|
+
- **`pnpm audit` is clean.** Six advisories are cleared, one of them critical (`proxy-addr`), with `source-map-js`, `fast-uri` and `sharp`. `image-size`, reached through `@seriousgaming/deck`'s PowerPoint export, is held to `>=2.0.3` by a new override (GHSA-5p2g-fcmc-qvqq, GHSA-w3rx-r6r6-pgpr). A scaffolded package carries the same override in its `pnpm-workspace.yaml`: copy that block into an existing package's file, then run `pnpm update -r` and `pnpm audit`
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# The CLI and the dev server
|
|
2
|
+
|
|
3
|
+
## One session, two ways in
|
|
4
|
+
|
|
5
|
+
The dev server owns the session. It holds the teams, the contexts, the roster
|
|
6
|
+
and the tick clock in memory, and it is the only thing that runs your game's
|
|
7
|
+
logic. Everything else (the browser console, the CLI, an agent driving a build)
|
|
8
|
+
is a client of it.
|
|
9
|
+
|
|
10
|
+
That is what makes the two views agree. `sdk tick` advances the session a
|
|
11
|
+
browser is watching, and the browser repaints; a tick taken in the browser is
|
|
12
|
+
what the next `sdk view` reports. There is one session and one answer.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm run sdk dev # start it, and leave it running
|
|
16
|
+
pnpm run sdk tick # advance the session it is running
|
|
17
|
+
pnpm run sdk view # read that same session
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The dev server is faithful to the platform on how a game behaves, and
|
|
21
|
+
deliberately leaves out what the platform needs to be a product: accounts,
|
|
22
|
+
TLS, session PINs, and state that survives a restart. Check those on the
|
|
23
|
+
platform.
|
|
24
|
+
|
|
25
|
+
## Surfaces and their URLs
|
|
26
|
+
|
|
27
|
+
Every surface has its own URL, and nothing redirects, so a URL can be typed,
|
|
28
|
+
bookmarked or scripted without asking the server what the link is.
|
|
29
|
+
|
|
30
|
+
| URL | What it shows |
|
|
31
|
+
| ---------------------- | -------------------------------------------------------------------- |
|
|
32
|
+
| `/` | The console: your game's session UI, with the dev drawer |
|
|
33
|
+
| `/team/2/player/1` | The first participant of the second team |
|
|
34
|
+
| `/team/2/observe` | That team's game UI with no participant identity |
|
|
35
|
+
| `/team/2/waiting-room` | That team's waiting room UI |
|
|
36
|
+
| `/team/2/join` | The link a facilitator hands out: it asks which participant you are |
|
|
37
|
+
| `/setup` | The setup screen, for a game with a `setup` slot |
|
|
38
|
+
| `/guide` | The facilitator guide |
|
|
39
|
+
| `/print` | One printable sheet per team |
|
|
40
|
+
| `/presentation` | The facilitator's deck (`?preview` shows it with no session) |
|
|
41
|
+
| `/walkthrough` | The [walkthrough run](./cli-reference.md#walkthrough-runs)'s game UI |
|
|
42
|
+
|
|
43
|
+
`/team/<n>/join` is the one URL that resolves rather than names, because a link
|
|
44
|
+
a facilitator hands out cannot know who will open it. A team holding one
|
|
45
|
+
participant opens straight into their surface. The scenario editor has no URL of
|
|
46
|
+
its own to type: it is always opened on one scenario, from the dev drawer's
|
|
47
|
+
**Scenario** tab.
|
|
48
|
+
|
|
49
|
+
Add `?bare` to any URL to remove the dev chrome entirely. That is what a
|
|
50
|
+
participant sees, what you screenshot, and what a browser test should drive, so
|
|
51
|
+
a change to the console cannot fail a test about your game.
|
|
52
|
+
|
|
53
|
+
While a game with a `setup` slot is at its configuration step, every surface
|
|
54
|
+
except the guide, the print sheets, the presentation and the scenario editor
|
|
55
|
+
shows the setup screen instead, because there is no session to play yet. Once
|
|
56
|
+
play starts, `/setup` says the step has closed, and **Reconfigure** (in the
|
|
57
|
+
drawer, on `/setup`, or `sdk reconfigure`) goes back to it. See
|
|
58
|
+
[Building a setup screen](./guides/setup.md).
|
|
59
|
+
|
|
60
|
+
## The dev drawer
|
|
61
|
+
|
|
62
|
+
Press the backtick key (`` ` ``) on any surface to open the dev drawer, and
|
|
63
|
+
again, Escape, or its close button to shut it. It is closed by default, so the
|
|
64
|
+
page looks as it will in production. The handle is a floating button whose dot
|
|
65
|
+
shows whether the server is reachable, busy, or has reported an error, and you
|
|
66
|
+
can move it to any corner (remembered per browser).
|
|
67
|
+
|
|
68
|
+
| Tab | What is in it |
|
|
69
|
+
| -------- | ----------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| Session | Tick, undo, reset, the waiting room, adding a team, Reconfigure, the timer, each team's row, and the locale |
|
|
71
|
+
| Scenario | The scenarios the package declares, which one is active, and adding, editing or deleting one |
|
|
72
|
+
| Surfaces | Every surface you can open, with a link to each |
|
|
73
|
+
| State | The whole state projection, live |
|
|
74
|
+
| Faults | Arm a failure the dev server cannot otherwise produce |
|
|
75
|
+
| Errors | What the session has reported, newest first (shown once there is one) |
|
|
76
|
+
|
|
77
|
+
Everything the drawer does, the CLI does too, because both send the same
|
|
78
|
+
commands.
|
|
79
|
+
|
|
80
|
+
## How a command finds the session
|
|
81
|
+
|
|
82
|
+
`.seriousgaming-sdk/session.json` is a **lock file**, not state:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"pid": 48213,
|
|
87
|
+
"port": 3000,
|
|
88
|
+
"startedAt": "2026-09-13T09:14:22.104Z",
|
|
89
|
+
"toolsVersion": "1.0.0",
|
|
90
|
+
"toolsPath": "/work/game/node_modules/@seriousgaming/sdk-tools/dist/cli.js",
|
|
91
|
+
"hostname": "studio-mac",
|
|
92
|
+
"instanceId": "4f1c0a5e-6a43-4c2e-9d0b-1f8f3c2b7e61",
|
|
93
|
+
"background": true
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Written once at startup, removed at shutdown, never touched in between, and
|
|
98
|
+
never watched. A command reads it, checks the server is still the one it names,
|
|
99
|
+
connects, sends and prints the answer. A file left behind by a killed server, or
|
|
100
|
+
restored with a snapshot of the project, reads as 'nothing running' rather than
|
|
101
|
+
as an address, and is removed. A server started by other tools than the
|
|
102
|
+
command's is replaced when a command started it, and refused with
|
|
103
|
+
`SESSION_STALE` when you did. See
|
|
104
|
+
[Where the session lives](./cli-reference.md#where-the-session-lives).
|
|
105
|
+
|
|
106
|
+
`SERIOUSGAMING_DEV_URL` sends commands to a server by its address instead, such
|
|
107
|
+
as one started with `sdk dev --isolated` beside your own, and
|
|
108
|
+
`SERIOUSGAMING_DEV_STATE_DIR` keeps the lock outside the project.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pnpm run sdk dev status # is one running, and where
|
|
112
|
+
pnpm run sdk dev stop # stop it
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Nothing running?** A session command starts one in the background and carries
|
|
116
|
+
on, and says so, naming the URL. An agent runs `sdk tick` and it simply works,
|
|
117
|
+
and when somebody wants to look, the URL is already in the lock file.
|
|
118
|
+
|
|
119
|
+
The server it starts outlives the command. That is the point, and it is also
|
|
120
|
+
the thing to know before a test fixture calls a session command: whatever runs
|
|
121
|
+
first starts the session, and in Vitest a `describe` body runs at collection
|
|
122
|
+
time, before any `beforeAll`.
|
|
123
|
+
|
|
124
|
+
A server started this way has no flag to take a port from, so it reads
|
|
125
|
+
`SERIOUSGAMING_DEV_PORT` (from the environment or `.env`), and falls back to 3000. Either way it steps up to the first free port from there.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
SERIOUSGAMING_DEV_PORT=3310 pnpm run sdk tick
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**`--no-server`** is the escape hatch: one command, one process, a fresh session
|
|
132
|
+
each time, nothing left behind. That is what you want for a single command in
|
|
133
|
+
CI, and when you need a clean run without disturbing a server you have open.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
pnpm run sdk tick --no-server
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A fresh session each time is the whole of it, so a **sequence** of commands
|
|
140
|
+
cannot be built out of it: `tick --no-server` twice reports `completedTicks: 1`
|
|
141
|
+
both times, because the second one is a different session. A multi-tick
|
|
142
|
+
playthrough, the commonest shape of engine test there is, belongs in
|
|
143
|
+
[`createTestSession`](./guides/engine-testing.md), which runs a whole run in one
|
|
144
|
+
process with no server and no port.
|
|
145
|
+
|
|
146
|
+
## Driving a session
|
|
147
|
+
|
|
148
|
+
Every session command sends exactly what the console sends, so anything a
|
|
149
|
+
facilitator can do from the browser, an agent can do from a terminal: start and
|
|
150
|
+
tick the session, read and patch it, manage teams, participants and the waiting
|
|
151
|
+
room, switch scenario or locale, and arm faults. Each prints JSON on stdout and
|
|
152
|
+
exits 4 when the session refuses, with the reason the session gave and a code
|
|
153
|
+
naming the refusal (see [Exit codes](./cli-reference.md#exit-codes)). The
|
|
154
|
+
[CLI reference](./cli-reference.md#session-commands) lists them all.
|
|
155
|
+
|
|
156
|
+
### Reading the whole session
|
|
157
|
+
|
|
158
|
+
`sdk view --full` prints the state projection: one shape carrying the session,
|
|
159
|
+
every team's context and roster, the shared store, the resolved `settings`,
|
|
160
|
+
the timer and any armed faults. The dev server serves the same thing at
|
|
161
|
+
`GET /api/state`, and the console renders from it, so what you read is what the
|
|
162
|
+
browser is showing.
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pnpm run --silent sdk view --full | jq '.teams[] | {team: .index, completedTicks, context}'
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Arming a fault
|
|
169
|
+
|
|
170
|
+
The dev server serialises every write against every tick, so it never produces a
|
|
171
|
+
`conflict` outcome, which is the one a production runtime produces most, and
|
|
172
|
+
the 'Try again' path your UI was told to offer. Arming one makes that path reachable
|
|
173
|
+
in development, deliberately and reproducibly:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
pnpm run sdk fault conflict --reason 'testing the Try again path'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The next UI mutation settles as a conflict, once, and then the fault disarms
|
|
180
|
+
itself. `--times N` fires it more than once; `--disarm` puts it away.
|
|
181
|
+
|
|
182
|
+
The [CLI reference](./cli-reference.md#fault-injection) lists every fault you
|
|
183
|
+
can arm.
|
|
184
|
+
|
|
185
|
+
## Working alongside an agent
|
|
186
|
+
|
|
187
|
+
The arrangement that makes an agent useful here: you keep the browser open on
|
|
188
|
+
the game, and the agent drives the session from the terminal. It reads state
|
|
189
|
+
with `sdk view --full`, decides, writes with `sdk patch`, advances with
|
|
190
|
+
`sdk tick`, and you watch it happen.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Read where the game is
|
|
194
|
+
pnpm run --silent sdk view --full > session-state.json
|
|
195
|
+
|
|
196
|
+
# Decide, then write and advance
|
|
197
|
+
pnpm run sdk patch '[{"op":"add","path":"/plan/Make/lemonade","value":45}]'
|
|
198
|
+
pnpm run sdk tick
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The starter game opens with one team, so neither command names one. In a
|
|
202
|
+
game with several, add `--team N` to both: `patch` requires it, and `tick`
|
|
203
|
+
then advances only that team when the game is independent.
|
|
204
|
+
|
|
205
|
+
Nothing is written to disk by any of that, and nothing needs reconciling: both
|
|
206
|
+
of you are looking at one session.
|
|
207
|
+
|
|
208
|
+
## What does stay on disk
|
|
209
|
+
|
|
210
|
+
The session overrides, because they are authored content you edit by hand:
|
|
211
|
+
`.seriousgaming-sdk/configuration.json`, written by `sdk configure` and read
|
|
212
|
+
when a session starts. A session already running fixed its settings
|
|
213
|
+
when play started, so `sdk configure` starts it again on the new values, at tick 0,
|
|
214
|
+
keeping its team count, scenario and locale.
|
|
215
|
+
|
|
216
|
+
Session state does not persist, in any form. The session is the process: stop
|
|
217
|
+
the server and it is gone, start it again and you have a new one at tick 0.
|