@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,102 @@
|
|
|
1
|
+
# Environment variables
|
|
2
|
+
|
|
3
|
+
A single reference for every environment variable the dev server and CLI read. Set these in a `.env` file in your project root, export them in your shell, or pass equivalents as CLI flags (flags take priority).
|
|
4
|
+
|
|
5
|
+
The dev server resolves config in this order, highest priority first: CLI flags → `process.env` → `.env` file → [global settings file](./guides/global-settings.md) → built-in defaults.
|
|
6
|
+
|
|
7
|
+
## LLM (`generateText`)
|
|
8
|
+
|
|
9
|
+
Used by the engine, hooks, tick controllers, and endpoints that declare `'generateText'` in `uses` (UIs reach it through an endpoint). Text generation runs through the Serious Gaming platform's LLM pipeline, so the only credential needed is a platform API key. The same variables configure `package publish`. See the [LLM integration guide](./guides/llm-integration.md).
|
|
10
|
+
|
|
11
|
+
| Variable | Required | Default | Purpose |
|
|
12
|
+
| ------------------------ | -------------- | ------------------------------------- | ------------------------------- |
|
|
13
|
+
| `SERIOUSGAMING_API_KEY` | If LLM is used | None | Serious Gaming platform API key |
|
|
14
|
+
| `SERIOUSGAMING_API_HOST` | No | `https://app.businesssimulations.com` | Platform origin to call |
|
|
15
|
+
|
|
16
|
+
Tier-to-model mapping is configured on the platform; there are no per-tier model variables. Equivalent CLI flags on `pnpm run sdk dev`: `--api-key`, `--host`.
|
|
17
|
+
|
|
18
|
+
## Text-to-speech (`generateSpeech`)
|
|
19
|
+
|
|
20
|
+
Used by the engine, hooks, tick controllers, and endpoints that declare `'generateSpeech'` in `uses` (UIs reach it through an endpoint). The dev server ships with an ElevenLabs adapter. See the [speech generation guide](./guides/speech-generation.md).
|
|
21
|
+
|
|
22
|
+
| Variable | Required | Default | Purpose |
|
|
23
|
+
| --------------------- | -------------- | --------------------------------- | ----------------------------------- |
|
|
24
|
+
| `ELEVENLABS_API_KEY` | If TTS is used | None | ElevenLabs API key |
|
|
25
|
+
| `TTS_MODEL_QUICK` | No | `eleven_flash_v2_5` | Model for the `quick` tier |
|
|
26
|
+
| `TTS_MODEL_STANDARD` | No | Falls back to quick | Model for the `standard` tier |
|
|
27
|
+
| `TTS_MODEL_EXPERT` | No | Falls back to standard | Model for the `expert` tier |
|
|
28
|
+
| `TTS_VOICE_NEUTRAL` | No | ElevenLabs default voice | Voice ID for the `neutral` preset |
|
|
29
|
+
| `TTS_VOICE_NARRATOR` | No | Falls back to `TTS_VOICE_NEUTRAL` | Voice ID for the `narrator` preset |
|
|
30
|
+
| `TTS_VOICE_ANNOUNCER` | No | Falls back to `TTS_VOICE_NEUTRAL` | Voice ID for the `announcer` preset |
|
|
31
|
+
| `TTS_VOICE_EXECUTIVE` | No | Falls back to `TTS_VOICE_NEUTRAL` | Voice ID for the `executive` preset |
|
|
32
|
+
| `TTS_VOICE_COACH` | No | Falls back to `TTS_VOICE_NEUTRAL` | Voice ID for the `coach` preset |
|
|
33
|
+
|
|
34
|
+
Any `TTS_VOICE_<NAME>` env var is exposed as the `<name>` preset (lowercased), so authors can define custom presets beyond the recommended five.
|
|
35
|
+
|
|
36
|
+
Browse voice IDs at <https://elevenlabs.io/voices>.
|
|
37
|
+
|
|
38
|
+
## Package publishing (`pnpm run sdk package publish`)
|
|
39
|
+
|
|
40
|
+
`package publish` uses `SERIOUSGAMING_API_KEY` and `SERIOUSGAMING_API_HOST` as above, plus the publisher to upload under. See [Publish a package](./recipes/publish-a-package.md).
|
|
41
|
+
|
|
42
|
+
| Variable | Required | Default | Purpose |
|
|
43
|
+
| ------------------------- | ---------------------- | ---------------------------------------------- | ------------------------------ |
|
|
44
|
+
| `SERIOUSGAMING_PUBLISHER` | Unless set another way | `platform.defaultPublisher` in global settings | Publisher slug to upload under |
|
|
45
|
+
|
|
46
|
+
Equivalent CLI flag: `--publisher`, which takes priority.
|
|
47
|
+
|
|
48
|
+
## Global settings file
|
|
49
|
+
|
|
50
|
+
| Variable | Required | Default | Purpose |
|
|
51
|
+
| -------------------------- | -------- | ---------------------------------- | -------------------------------------------------------- |
|
|
52
|
+
| `SERIOUSGAMING_SDK_CONFIG` | No | `~/.seriousgaming-sdk/config.json` | Absolute path to the global settings file to use instead |
|
|
53
|
+
|
|
54
|
+
A relative path is an error. See the [global settings guide](./guides/global-settings.md).
|
|
55
|
+
|
|
56
|
+
## Dev server
|
|
57
|
+
|
|
58
|
+
A session command starts a dev server when none is running, and has no `--port` of its own to pass it. This is how to tell that server where to start looking. See [The CLI and the dev server](./cli-and-dev-server.md).
|
|
59
|
+
|
|
60
|
+
| Variable | Required | Default | Purpose |
|
|
61
|
+
| ----------------------------- | -------- | ----------------------------------- | ------------------------------------------------------------------------------- |
|
|
62
|
+
| `SERIOUSGAMING_DEV_PORT` | No | `3000` | Where the dev server starts looking for a free port, or `auto` for any free one |
|
|
63
|
+
| `SERIOUSGAMING_DEV_URL` | No | Unset | The server every session command targets, in place of the one the lock names |
|
|
64
|
+
| `SERIOUSGAMING_DEV_STATE_DIR` | No | The package's `.seriousgaming-sdk/` | Where the lock file naming the running server is kept |
|
|
65
|
+
|
|
66
|
+
`SERIOUSGAMING_DEV_PORT` is a starting point, not a binding: the server steps up to the first free port from there, and prints the one it lands on. Equivalent CLI flag on `pnpm run sdk dev`: `--port`.
|
|
67
|
+
|
|
68
|
+
`SERIOUSGAMING_DEV_URL` is for a server no lock names, such as one started with `sdk dev --isolated` beside your own, for a test run or a recording. With it set, a command never starts a server: one that does not answer fails the command with `DEV_SERVER_UNREACHABLE`.
|
|
69
|
+
|
|
70
|
+
`SERIOUSGAMING_DEV_STATE_DIR` moves the lock out of the project, since it is runtime state rather than project content: a container whose project directory is restored from a snapshot otherwise brings back a lock naming a process from before it. Each package gets its own entry inside it, so one directory can serve several. Saved configuration and uploads stay in the package. See [Where the session lives](./cli-reference.md#where-the-session-lives).
|
|
71
|
+
|
|
72
|
+
## Output and prompts
|
|
73
|
+
|
|
74
|
+
| Variable | Required | Default | Purpose |
|
|
75
|
+
| -------------------- | -------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
76
|
+
| `SERIOUSGAMING_JSON` | No | Unset | `1` makes every command print one JSON document on stdout, as if `--json` had been passed |
|
|
77
|
+
| `CI` | No | Unset | Any value turns interactive prompts off: a command that would ask fails with exit code 2 |
|
|
78
|
+
|
|
79
|
+
See [Exit codes](./cli-reference.md#exit-codes) and [JSON output](./cli-reference.md#json-output). Prompts are also off whenever stdin or stdout is not a terminal, and under JSON output.
|
|
80
|
+
|
|
81
|
+
## Example `.env`
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
# Serious Gaming platform (LLM + package publish)
|
|
85
|
+
SERIOUSGAMING_API_KEY=sg_...
|
|
86
|
+
|
|
87
|
+
# Dev server
|
|
88
|
+
SERIOUSGAMING_DEV_PORT=3310
|
|
89
|
+
|
|
90
|
+
# Speech
|
|
91
|
+
ELEVENLABS_API_KEY=...
|
|
92
|
+
TTS_VOICE_NEUTRAL=21m00Tcm4TlvDq8ikWAM
|
|
93
|
+
TTS_VOICE_NARRATOR=ErXwobaYiN019PkySvjV
|
|
94
|
+
TTS_VOICE_ANNOUNCER=VR6AewLTigWG4xSOukaG
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Never commit `.env` to version control. The `.gitignore` scaffolded by `new package` already lists it; check a package scaffolded some other way does too.
|
|
98
|
+
|
|
99
|
+
## Verify
|
|
100
|
+
|
|
101
|
+
- `pnpm run sdk dev` boots without warnings
|
|
102
|
+
- Calling `callbacks.generateText` and `callbacks.generateSpeech` from a resource with the right `uses` declarations succeeds (no `LLMClientError` / `TtsClientError`)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
Scaffold the starter game, play it in the dev server, change one thing, and see the change. It takes about 15 minutes, and you finish with a working game you understand well enough to reshape.
|
|
4
|
+
|
|
5
|
+
**Keywords:** getting started, quick start, first game, starter game, lemonade stand, scaffold, new package, tutorial
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- Node.js 24 or later
|
|
10
|
+
- pnpm 12
|
|
11
|
+
- A text editor, and some familiarity with TypeScript and React
|
|
12
|
+
|
|
13
|
+
## 1. Scaffold the starter game
|
|
14
|
+
|
|
15
|
+
Nothing is installed yet, so run the CLI through `pnpm dlx`, in an empty directory. The packages are on the public npm registry, so there is no token or `.npmrc` to set up first:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir my-game
|
|
19
|
+
cd my-game
|
|
20
|
+
pnpm dlx @seriousgaming/sdk-tools new package
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The command asks for three things: the package identifier (reverse-domain, such as `com.example.mygame`), the game's name, and a version. It then writes the package, installs its dependencies, and generates each resource's `types.ts`. There is no separate install step. If the install fails, the package is still written and the command tells you so; run `pnpm install` yourself once the cause is fixed.
|
|
24
|
+
|
|
25
|
+
What you get is a complete game rather than an empty shell. A lemonade stand, for 10 days: each morning a team decides how many lemonades, iced teas, and cookies to make. Whatever they make they pay for, whatever is left at closing is poured away, and anyone who wanted something that ran out simply leaves.
|
|
26
|
+
|
|
27
|
+
The game is copied out whole rather than rendered from templates, and it is the same game the SDK's own test suites play, so what you are handed is known to work. Pass `--empty` if you want only the package files and nothing else; [Start a game from an empty package](./recipes/start-from-an-empty-package.md) picks up from there.
|
|
28
|
+
|
|
29
|
+
## 2. Take the tour
|
|
30
|
+
|
|
31
|
+
[A tour of the starter game](./guides/starter-game.md) walks through every directory and file you now have, says what each one does, and points at the guide that goes deeper. Skim it now, or keep it open beside the next steps.
|
|
32
|
+
|
|
33
|
+
## 3. Play it
|
|
34
|
+
|
|
35
|
+
Start the dev server:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm run sdk dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
It opens the facilitator console at `http://localhost:3000`, which is the game's session UI: the screen a facilitator runs the session from. Leave it running in its own terminal, and use a second terminal for everything below.
|
|
42
|
+
|
|
43
|
+
The starter game begins with its participants held in the waiting room, which is the right start for a live session. To play:
|
|
44
|
+
|
|
45
|
+
1. Open `http://localhost:3000/team/1/player/1` in a new tab. This is the game UI for the first participant on team 1, and it shows the waiting room
|
|
46
|
+
2. In the console tab, switch off **Waiting room**. The participant's tab moves to the stall
|
|
47
|
+
3. Open `http://localhost:3000/team/1/player/2` as well. Each participant is put in charge of one item on the menu, so the two tabs can change different numbers without overwriting each other
|
|
48
|
+
4. Set how many of each thing to make, then press **Open the stall**. The engine runs one tick, which this game calls a day, and **How yesterday went** shows what sold, what was poured away, and who was turned away
|
|
49
|
+
|
|
50
|
+
Press the backtick key on any page for the dev drawer: tick, undo, and reset controls, the state as JSON, and the list of surfaces. Every page takes `?bare` to hide the dev chrome.
|
|
51
|
+
|
|
52
|
+
## 4. Change a setting
|
|
53
|
+
|
|
54
|
+
A game's numbers live in settings rather than in code, so the first change needs no code at all. The menu's settings are declared in `settings/menu/settings.sdk.json`, and every resource that needs them lists `"menu"` in its own `settings` (the engine's is in `engine/engine.sdk.json`). One of them is how much the stall can make in a day:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
"Capacity": {
|
|
58
|
+
"type": "number",
|
|
59
|
+
"description": "How many items the stall can make in a day, across the whole menu",
|
|
60
|
+
"default": 120,
|
|
61
|
+
"minimum": 1
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Halve it for this session:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm run sdk configure '{"menu":{"Capacity":60}}'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The key is the settings identifier, `menu`, the directory name under `settings/`. `configure` validates the value against that schema and writes it to `.seriousgaming-sdk/configuration.json`, the session-override layer, which wins over the others (see [Overriding settings](./guides/settings.md#overriding-settings)). The game's validator and UI read it as `settings.menu.Capacity`. Settings are fixed when play starts, so `configure` puts the running session back at tick 0 on the new value. The stall now says it can make 60 things in a day, and a plan over 60 is refused by the game's validator.
|
|
72
|
+
|
|
73
|
+
To go back to the defaults, clear the overrides, which restarts the session on them:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pnpm run sdk configure '{}'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 5. Change the engine
|
|
80
|
+
|
|
81
|
+
The engine is `engine/logic.ts`: an `Init` that sets up each team's first morning, and a `Run` that plays one day. Inside `Run`, this is the line that makes the stall pay for everything it made, including what it poured away:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// You paid to make everything, including what you poured away. That is
|
|
85
|
+
// the whole of the lesson about making too much.
|
|
86
|
+
spent += made * entry.Cost;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Change `made` to `sold`, so the stall pays only for what it sells:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
spent += sold * entry.Cost;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Save the file and play another day. You do not need to restart anything or reset the session. The dev server watches the package, and the next tick runs the new code. What it does not do is re-run days already played, so the change shows from the next day onwards: **Spent** no longer counts what was poured away, so making too much stops costing anything.
|
|
96
|
+
|
|
97
|
+
Edits to a UI hot-reload in the browser, and edits to a `*.sdk.json` bundle or to `seriousgaming.sdk.json` are picked up without a restart too. A change to a setting, which reaches the game through `Init`, waits for you to re-initialise once a day has been played: the console says so and offers a button. The [CLI reference](./cli-reference.md#what-reloads) has the whole table.
|
|
98
|
+
|
|
99
|
+
## 6. Drive it from the CLI and run its tests
|
|
100
|
+
|
|
101
|
+
Everything the console does, the CLI does too, against the same session:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pnpm run sdk tick --patch '[{"op":"replace","path":"/plan/Make/lemonade","value":70}]'
|
|
105
|
+
pnpm run sdk view
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`tick --patch` writes a decision into the context, here 70 lemonades, and then runs a tick; `view` prints the session as JSON. Both reach the dev server you started in step 3, so the browser repaints as they run.
|
|
109
|
+
|
|
110
|
+
Session commands (`init`, `tick`, `view`, `patch`, `undo`, `reset`, and the rest) always talk to a dev server. If none is running, the command starts one in the background, says so on stderr, and leaves it running for the next command. Check on it or stop it with:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pnpm run sdk dev status
|
|
114
|
+
pnpm run sdk dev stop
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A background server holds the package's one session, so `sdk dev` refuses to start a second and gives you the address of the first instead. Add `--no-server` to a session command to run it in-process against a fresh session with nothing left behind. [Drive a game from the CLI](./cli-simulation-testing.md) covers the whole workflow.
|
|
118
|
+
|
|
119
|
+
Now run the tests:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pnpm test:run
|
|
123
|
+
pnpm test:engine
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`test:run` runs the unit tests beside each resource, and `test:engine` plays whole games in-process with `createTestSession`, with no server and no browser. With the change from step 5 in place, one test in each suite fails: `pours away what it made and did not sell, having paid for it` in the unit tests, and `pours away what nobody wanted, having already paid for it` in the whole-game tests. That is the suite guarding the rule you just changed. Put the line back, or update the tests if the new rule is the one you want. `pnpm test:e2e` runs the browser tests with Playwright. They start a dev server of their own, and a package holds one session, so stop yours first with `sdk dev stop` or Ctrl+C.
|
|
127
|
+
|
|
128
|
+
## Working with an AI coding agent
|
|
129
|
+
|
|
130
|
+
A scaffolded package includes a `CLAUDE.md` and `.claude/rules/` written for coding agents. They cover the workflows an agent needs (creating resources, testing, publishing) and tell the agent to leave `sdk dev` for you to start. The points worth knowing:
|
|
131
|
+
|
|
132
|
+
- Session commands print JSON, so an agent can read state with `view` and make decisions with `tick --patch`
|
|
133
|
+
- Use `pnpm run --silent` when parsing output, so pnpm's banner stays out of the JSON
|
|
134
|
+
- The dev server and the CLI share one session, so you can watch in the browser while an agent plays from the terminal
|
|
135
|
+
|
|
136
|
+
[The CLI and the dev server](./cli-and-dev-server.md) explains how the two fit together.
|
|
137
|
+
|
|
138
|
+
## Next steps
|
|
139
|
+
|
|
140
|
+
- [Design state for concurrent writes](./recipes/design-state-for-concurrent-writes.md): the rule the starter game is built around. Keep its shape when you replace its model
|
|
141
|
+
- [Terminology](./concepts/terminology.md): the words the rest of the docs use, each with one meaning
|
|
142
|
+
- [Core concepts](./concepts/index.md): why the SDK is shaped the way it is
|
|
143
|
+
- [Creating engines](./guides/engine.md) and [Building user interfaces](./guides/ui.md): the full reference for the two resources you will change most
|
|
144
|
+
- [Start a game from an empty package](./recipes/start-from-an-empty-package.md): build your own game from nothing, once you know what the starter game does
|
|
145
|
+
- [CLI reference](./cli-reference.md): every command and flag
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Structuring a game for AI facilitation
|
|
2
|
+
|
|
3
|
+
A facilitator running your game may have an AI agent beside them: something they can ask 'how is Team B doing', 'why has their profit fallen', or 'set their unit price to 3.53'. This guide covers what that agent can see, what it can do, and what you as an author need to write down so it behaves.
|
|
4
|
+
|
|
5
|
+
Most of it is not new machinery. An agent acting for a facilitator is close to a facilitator's own screen: it sees what a screen declares, and it acts through endpoints. What is new is that the words you write become load-bearing, because a model reads them to decide what to do.
|
|
6
|
+
|
|
7
|
+
## What the agent can see
|
|
8
|
+
|
|
9
|
+
A game's data lives in named capabilities. Every screen declares which ones it needs, and receives exactly those. The agent is handed data the same way, so nothing you have not declared reaches it.
|
|
10
|
+
|
|
11
|
+
Two consequences worth planning around.
|
|
12
|
+
|
|
13
|
+
**Mark engine internals private.** Simulations accumulate state that exists only to get from one tick to the next: a serialised engine, a smoothing accumulator, a seed, a ledger the next tick consumes. A capability with `scope: 'private'` reaches your logic exactly as before and reaches no screen at all. In `capabilities/engine_state/capability.sdk.json` (the directory names the capability):
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"definition": {
|
|
18
|
+
"scope": "private",
|
|
19
|
+
"variablesSchema": { "…": "…" }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This matters more for an agent than for a screen. A screen ignores a capability it does not render. A model reads everything it is given, and an opaque blob of serialised simulation state is the single most reliable way to make it say something confidently wrong.
|
|
25
|
+
|
|
26
|
+
**Keep capabilities a size a model can be handed.** A capability holding a thousand values is fine for a screen that renders a table from it and expensive for an agent asked a one-sentence question. Where a capability has grown that large, either split it or compute a summary into a smaller one. The same aggregate your session UI already renders is usually the right thing.
|
|
27
|
+
|
|
28
|
+
## Describing your data
|
|
29
|
+
|
|
30
|
+
This is the highest-value thing on the list and it is not code.
|
|
31
|
+
|
|
32
|
+
A model handed `Backlog: 153` knows nothing: not the unit, not whether high is good, not what counts as normal in your game. The schema where you define a capability has room for a description per variable, and filling it in is what turns numbers into something an agent can reason about.
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"Backlog": {
|
|
37
|
+
"type": "number",
|
|
38
|
+
"default": 0,
|
|
39
|
+
"description": "Unfulfilled orders carried into the next round, in units. Lower is better; above about 200 a team is losing customers."
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Three things to include, in roughly this order of value:
|
|
45
|
+
|
|
46
|
+
- The unit, always. 'Revenue' could be pounds, thousands of pounds, or units sold
|
|
47
|
+
- Which direction is good, where it is not obvious from the name
|
|
48
|
+
- A sense of scale, where you have one. 'Typically 50 to 400' costs you eight words and saves the agent guessing
|
|
49
|
+
|
|
50
|
+
You do not have to do this everywhere at once. Start with the capabilities a facilitator actually asks about, which are usually the ones already on your session UI.
|
|
51
|
+
|
|
52
|
+
Two things descriptions cannot fix. The agent has no baseline unless it can also see history, so comparisons come from past ticks rather than from prose. And a snapshot is a board, not a narrative: 'which stakeholder did they engage most' is answerable only if your engine wrote that down.
|
|
53
|
+
|
|
54
|
+
## Letting the agent do things
|
|
55
|
+
|
|
56
|
+
An agent performs game-specific actions through endpoints, the same server-side handlers your own screens call. An endpoint becomes available to an agent, and to any other caller outside the game such as a platform API, when its `visibility` is `external`:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"definition": {
|
|
61
|
+
"displayName": "Set the unit price",
|
|
62
|
+
"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.",
|
|
63
|
+
"scope": "team",
|
|
64
|
+
"visibility": "external",
|
|
65
|
+
"writes": ["price"],
|
|
66
|
+
"…": "…"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
An endpoint is `internal` by default, and a caller outside the game is refused it. An endpoint you wrote for your own screens does not become part of an agent's vocabulary until you opt it in, because publishing an operation is a different act from wiring a button to a handler. The agent's handler sees `caller.role` as `'external'`, so it can tell the call from one made by the facilitator's own screens.
|
|
72
|
+
|
|
73
|
+
### Writing a description a model can act on
|
|
74
|
+
|
|
75
|
+
Everywhere else in the SDK a description is author-time convenience. Here it is the text a model reads to decide whether to call your endpoint, so the build refuses an external endpoint without one.
|
|
76
|
+
|
|
77
|
+
What separates a good one from a bad one is not length:
|
|
78
|
+
|
|
79
|
+
- Say what it does **and when to reach for it**. 'Sets the unit price' tells a model what the name already told it; 'use when a facilitator asks to change what a team charges' tells it when to choose this over the other eleven
|
|
80
|
+
- State the bounds in prose as well as in the schema. The schema refuses a bad call; the description stops the model making it
|
|
81
|
+
- Make it distinguishable from its neighbours. Three endpoints that all say 'updates the team' will be picked between at random
|
|
82
|
+
|
|
83
|
+
Read yours side by side before you ship:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pnpm run sdk endpoint list --external
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
That is when 'updates the team' three times becomes obvious, and it is the one failure mode in this whole area that no test catches.
|
|
90
|
+
|
|
91
|
+
### Scope
|
|
92
|
+
|
|
93
|
+
An endpoint declares whether it acts on one team or on the session. 'Set Team B's price' is team-scoped; 'open the next tick for everyone' is session-scoped.
|
|
94
|
+
|
|
95
|
+
Do not put a team in your request schema. The platform resolves which team the facilitator meant and hands it to your handler on `team`, as `{ teamId, index, name }`, so a summary can say 'The Daily Grind placed 2nd' and a lookup can find that team's row by `team.teamId`. A team the model typed is a team the model can invent.
|
|
96
|
+
|
|
97
|
+
Scope decides what the handler can change, and the build holds you to it: see [What each scope may write](./endpoints.md#what-each-scope-may-write). A session-scoped endpoint is how an agent changes something for the whole room, through a shared capability, or for several teams in one call, through per-team patches under `teams` where every team changes or none does. Its own `writes` array is what authorises it: an agent has no screen to borrow permission from.
|
|
98
|
+
|
|
99
|
+
### Asking about the whole room
|
|
100
|
+
|
|
101
|
+
A session-scoped endpoint has no team of its own, so its reads arrive split:
|
|
102
|
+
|
|
103
|
+
- A `shared` capability comes in on `context`, as the single value it is
|
|
104
|
+
- A `team`, `team-readable` or `private` capability comes in on `teams`, one entry per team
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
export const Handle: Handler = async ({ context, teams }) => ({
|
|
108
|
+
average: context.market.AveragePrice,
|
|
109
|
+
behind: (teams ?? [])
|
|
110
|
+
.filter((team) => team.context.decisions.Price > context.market.AveragePrice)
|
|
111
|
+
.map((team) => team.name),
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This is what lets an agent answer 'which teams haven't submitted yet' or 'who is charging above the market' in one call rather than one call per team.
|
|
116
|
+
|
|
117
|
+
### Asking about the past
|
|
118
|
+
|
|
119
|
+
An endpoint can read any tick that has already closed, through `callbacks.getPastState`. It needs no declaration beyond the capabilities it already reads:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
export const Handle: Handler = async ({ request, callbacks }) => {
|
|
123
|
+
const rounds = await callbacks.getPastState<Context>({
|
|
124
|
+
fromTick: 0,
|
|
125
|
+
toTick: Number.MAX_SAFE_INTEGER,
|
|
126
|
+
capabilityIds: ['profit'],
|
|
127
|
+
});
|
|
128
|
+
return { trend: rounds.map((round) => round.context.profit.Profit) };
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This is what makes 'how has Team B been doing' answerable. An agent has no screen, so it cannot fetch the ticks itself and hand them over the way one of your own surfaces would.
|
|
133
|
+
|
|
134
|
+
Two things to know. A range running past the last completed tick comes back **short rather than failing**, so treat a short answer as normal. And the same read is available in your engine, which means a capability you were keeping purely to carry last tick's figure forward can go: that value rides every push to every participant for the rest of the session, and the snapshot it was copied from is already recorded.
|
|
135
|
+
|
|
136
|
+
**Read wide, answer narrow.** The whole point of doing this in an endpoint is that every team's board is assembled server-side and never crosses back to the model. A hundred teams' raw state is both slow to hand to a model and useless once it gets there; a hundred teams reduced to the four names that answer the question is neither. Summarise in the handler, and declare the narrowest `reads` that answer the request.
|
|
137
|
+
|
|
138
|
+
### What checks the request
|
|
139
|
+
|
|
140
|
+
Three different things ask three different questions, and you want all three:
|
|
141
|
+
|
|
142
|
+
- `requestSchema` asks whether the request is well formed. The runtime refuses a bad one before your handler runs
|
|
143
|
+
- Your handler asks whether the request makes sense given what it knows. This is the only place that can
|
|
144
|
+
- A validator asks whether the resulting state is legal, and **does not run for an endpoint's write**
|
|
145
|
+
|
|
146
|
+
That last point is deliberate and worth understanding, because it is the opposite of what people expect. Validators exist to gate what a participant submits directly, where your game has no code in the path. An endpoint is your code in the path.
|
|
147
|
+
|
|
148
|
+
It also buys you something. A validator can refuse a change outright while a strict endpoint is the one sanctioned way to make it:
|
|
149
|
+
|
|
150
|
+
- The validator refuses any direct write to `price` from a participant
|
|
151
|
+
- The endpoint sets `price`, having checked the facilitator's reasons
|
|
152
|
+
|
|
153
|
+
Now there is exactly one route to that value, and you wrote it.
|
|
154
|
+
|
|
155
|
+
### Running the session, not only the game
|
|
156
|
+
|
|
157
|
+
An agent managing a session needs to do what a facilitator does: close the waiting room, start the next round, give a team more time. An endpoint can return those as `actions`, and the runtime carries them out after its write, in one step. So 'start the next round' is one tool call that the game wrote the rules for, rather than three platform calls the agent has to sequence and get right.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
export const Handle: Handler = async ({ session }) => {
|
|
161
|
+
if (session.complete) throw new Error('The game is complete');
|
|
162
|
+
return {
|
|
163
|
+
response: { fromRound: session.completedTicks },
|
|
164
|
+
actions: [
|
|
165
|
+
...(session.waitingRoomOpen ? [{ type: 'setWaitingRoom' as const, open: false }] : []),
|
|
166
|
+
{ type: 'tick' },
|
|
167
|
+
],
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`session` is what lets the endpoint decide. It says which round it is, whether the waiting room is open, whether the game is over, and where each team stands, which are the first things an agent asks before it acts. Declare `roster: true` to have each team's participants listed too.
|
|
173
|
+
|
|
174
|
+
The response is computed before the actions run, so it cannot say what they did. When the agent needs to hear the outcome, such as the round now in play, answer in two phases: `Plan` returns the actions, and `Respond` writes the response once they have landed. See [Act on the session from an endpoint](../recipes/act-on-the-session-from-an-endpoint.md).
|
|
175
|
+
|
|
176
|
+
### Taking and returning files
|
|
177
|
+
|
|
178
|
+
An agent working with a facilitator often has a file in hand: a research pack, a team photo, a brief. Mark a request property as a file with `blob`, and the agent can send it inline as a `data:` URI, or upload it first and send the reference. Your handler always receives a checked `blob:` reference. See [Take a file in an endpoint](../recipes/take-a-file-in-an-endpoint.md).
|
|
179
|
+
|
|
180
|
+
Going the other way, an endpoint that writes a report or an export with `writeBlob` returns its reference, and the runtime gives a caller outside the game a URL to fetch it from. How the file reaches the model or the person, inline or as a link, is the platform's choice.
|
|
181
|
+
|
|
182
|
+
### Who did what
|
|
183
|
+
|
|
184
|
+
An agent's change looks like anyone else's once it has landed. A platform that knows which agent or API key is calling passes it as `caller.principal`, so an endpoint can answer one caller differently from another, and the runtime keeps an audit of every endpoint call that changed the session: which endpoint, who called it, what it wrote and what it did. Locally, `pnpm run sdk endpoint audit` reads it.
|
|
185
|
+
|
|
186
|
+
## Testing it without a model
|
|
187
|
+
|
|
188
|
+
You do not need an agent to develop against this.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# What an agent would be offered, with the descriptions it reads
|
|
192
|
+
pnpm run sdk endpoint list --external
|
|
193
|
+
|
|
194
|
+
# Run one as an outside caller, with no model in the loop
|
|
195
|
+
pnpm run sdk endpoint call setprice --external --team 1 --request '{"price": 3.53}'
|
|
196
|
+
|
|
197
|
+
# As a particular agent, sending a file, and keeping what comes back
|
|
198
|
+
pnpm run sdk endpoint call attachresearch --external --team 1 \
|
|
199
|
+
--principal agent:claude-7:Claude --attach document=./research.pdf --save-blobs ./out
|
|
200
|
+
|
|
201
|
+
# What every endpoint call changed, and who made it (needs a running dev server)
|
|
202
|
+
pnpm run sdk endpoint audit
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Exercise the failure paths too, because they are what a model will find: a value outside the schema, a missing property, a team that does not exist.
|
|
206
|
+
|
|
207
|
+
## What is not in the SDK
|
|
208
|
+
|
|
209
|
+
The agent itself, and the protocol it speaks, are the platform's. The SDK's job is to make sure the package a platform reads carries everything an agent needs: declared data with descriptions, internals marked private, and operations that say who may call them.
|
|
210
|
+
|
|
211
|
+
That means you can prepare a game for AI facilitation now, and it will be ready when the platform side arrives.
|
|
212
|
+
|
|
213
|
+
## Related
|
|
214
|
+
|
|
215
|
+
- [Endpoints](./endpoints.md)
|
|
216
|
+
- [Make an endpoint callable from outside the game](../recipes/make-an-endpoint-external.md)
|
|
217
|
+
- [Act on the session from an endpoint](../recipes/act-on-the-session-from-an-endpoint.md)
|
|
218
|
+
- [Take a file in an endpoint](../recipes/take-a-file-in-an-endpoint.md)
|
|
219
|
+
- [Capabilities](capabilities.md)
|
|
220
|
+
- [Validators](validators.md)
|