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