summer-engine 4.6.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +3 -2
  3. package/.codex-plugin/plugin.json +5 -4
  4. package/.cursor-plugin/plugin.json +4 -3
  5. package/.factory-plugin/plugin.json +4 -3
  6. package/CHANGELOG.md +37 -0
  7. package/README.md +3 -3
  8. package/commands/start.md +1 -1
  9. package/dist/cli/commands/api.d.ts +43 -0
  10. package/dist/cli/commands/api.js +191 -0
  11. package/dist/cli/commands/login.d.ts +2 -5
  12. package/dist/cli/commands/login.js +10 -81
  13. package/dist/cli/commands/token.d.ts +17 -0
  14. package/dist/cli/commands/token.js +25 -0
  15. package/dist/cli/index.js +4 -0
  16. package/dist/core/api-client.d.ts +5 -1
  17. package/dist/core/api-client.js +21 -0
  18. package/dist/core/capabilities/doctor.d.ts +1 -1
  19. package/dist/core/capabilities/doctor.js +1 -1
  20. package/dist/core/capabilities/game-task-plan.d.ts +2 -2
  21. package/dist/core/capabilities/navigation/open.d.ts +3 -0
  22. package/dist/core/capabilities/navigation/open.js +53 -0
  23. package/dist/core/capabilities/projects-local.js +2 -1
  24. package/dist/core/capabilities/runtime-control.d.ts +2 -2
  25. package/dist/core/capabilities/seeing/args.d.ts +4 -4
  26. package/dist/core/capabilities/tool-dispatch.js +37 -0
  27. package/dist/core/capabilities/ui-control.d.ts +2 -2
  28. package/dist/core/capabilities/view-set.d.ts +11 -0
  29. package/dist/core/capabilities/view-set.js +15 -0
  30. package/dist/core/capability-skew.d.ts +8 -0
  31. package/dist/core/capability-skew.js +18 -0
  32. package/dist/core/cli-session.d.ts +25 -0
  33. package/dist/core/cli-session.js +97 -0
  34. package/dist/core/engine-self-start.d.ts +20 -0
  35. package/dist/core/engine-self-start.js +108 -5
  36. package/dist/core/oauth.d.ts +15 -2
  37. package/dist/core/oauth.js +26 -3
  38. package/dist/core/ops/areas.d.ts +1 -1
  39. package/dist/core/ops/areas.js +5 -3
  40. package/dist/core/ops/engine-answer.js +5 -2
  41. package/dist/core/ops/engine.js +6 -0
  42. package/dist/core/ops/save-files.d.ts +2 -0
  43. package/dist/core/ops/save-files.js +13 -0
  44. package/dist/core/ops/studio-page.d.ts +22 -0
  45. package/dist/core/ops/studio-page.js +63 -0
  46. package/dist/core/sign-in-link.d.ts +38 -0
  47. package/dist/core/sign-in-link.js +106 -0
  48. package/dist/core/store.js +19 -1
  49. package/dist/core/summer-api.d.ts +114 -0
  50. package/dist/core/summer-api.js +281 -0
  51. package/dist/mcp/ops-surface.d.ts +3 -0
  52. package/dist/mcp/ops-surface.js +6 -0
  53. package/dist/mcp/server.js +16 -4
  54. package/dist/mcp/tool-results.d.ts +9 -0
  55. package/dist/mcp/tool-results.js +64 -0
  56. package/dist/mcp/tool-surface.js +7 -1
  57. package/dist/mcp/tools/api-tools.d.ts +50 -0
  58. package/dist/mcp/tools/api-tools.js +65 -0
  59. package/dist/mcp/tools/local-project-tools.d.ts +14 -0
  60. package/dist/mcp/tools/local-project-tools.js +13 -2
  61. package/dist/mcp/tools/navigation-tools.js +1 -0
  62. package/dist/mcp/tools/scene-tools.js +7 -0
  63. package/gemini-extension.json +3 -2
  64. package/library/references/mcp-tools-reference/mcp-tools-reference.md +15 -2
  65. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  66. package/library/skills/README.md +6 -5
  67. package/library/skills/design-npc/SKILL.md +1 -1
  68. package/library/skills/design-npc/resource.yaml +2 -1
  69. package/library/skills/navigate-summer/SKILL.md +23 -2
  70. package/library/skills/navigate-summer/resource.yaml +5 -3
  71. package/library/skills/summer-api/SKILL.md +198 -0
  72. package/library/skills/summer-api/resource.yaml +38 -0
  73. package/library/skills/summer-assets/SKILL.md +1 -1
  74. package/library/skills/summer-assets/resource.yaml +3 -2
  75. package/library/skills/summer-router/SKILL.md +251 -0
  76. package/library/skills/summer-router/resource.yaml +35 -0
  77. package/library/tools/api-batch/resource.yaml +82 -0
  78. package/library/tools/api-estimate/resource.yaml +52 -0
  79. package/library/tools/api-job/resource.yaml +58 -0
  80. package/library/tools/api-models/resource.yaml +57 -0
  81. package/library/tools/api-run/resource.yaml +70 -0
  82. package/library/tools/view-set/GUIDE.md +3 -0
  83. package/library/tools/view-set/resource.yaml +78 -0
  84. package/package.json +3 -3
  85. package/registry/generated/aliases.json +7 -0
  86. package/registry/generated/counts.json +3 -3
  87. package/registry/generated/gemini-extension.json +3 -2
  88. package/registry/generated/index.json +316 -51
  89. package/registry/generated/marketplace.claude.json +1 -1
  90. package/registry/generated/plugin.claude-directory.json +4 -3
  91. package/registry/generated/plugin.claude.json +3 -2
  92. package/registry/generated/plugin.codex.json +5 -4
  93. package/registry/generated/plugin.cursor.json +4 -3
  94. package/registry/generated/plugin.factory.json +4 -3
  95. package/registry/generated/server.json +2 -2
  96. package/registry/generated/skills-index.md +6 -5
  97. package/registry/generated/skills-registry.json +29 -15
  98. package/registry/operations/legacy-names.json +30 -0
  99. package/registry/operations/public-catalog.json +9169 -4926
  100. package/library/skills/ai-characters-in-games/SKILL.md +0 -235
  101. package/library/skills/ai-characters-in-games/resource.yaml +0 -30
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: summer-api
3
+ description: "Summer API game assets: models, estimate, one job or a batch, download the files."
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Codex, Windsurf, Gemini, OpenCode, Factory, Copilot]
6
+ category: _meta
7
+ user-invocable: false
8
+ allowed-tools: Bash Read Write summer_api_models summer_api_estimate summer_api_run summer_api_batch summer_api_job
9
+ ---
10
+
11
+ # Summer API: game assets for any agent
12
+
13
+ The Summer API makes game assets from curated models tested for games: 3D models (GLB), images, video, voice lines, dialogue, sound effects and music. Each finished file is saved to the person's Summer library and downloads into their project. Every model runs at the provider's list price, with no markup on assets.
14
+
15
+ Use this skill when the person asks for the Summer API, or wants asset files for any engine, app or pipeline from a script, a batch or a coding agent. Inside a running Summer Engine project, the asset skills (`prop-model`, `character-model`, `sound-effect`, ...) import the asset into the scene for you; use them there.
16
+
17
+ Two ways in, with the same calls and the same answers:
18
+
19
+ | You have | Use |
20
+ |---|---|
21
+ | A shell | `summer api ...` commands. If `summer --version` is missing or below 4.8, write `npx -y summer-engine@latest` in place of `summer`. |
22
+ | Summer's local MCP server | The tools `summer_api_models`, `summer_api_estimate`, `summer_api_run`, `summer_api_batch` and `summer_api_job` |
23
+
24
+ Your own code calls the same API over HTTP. The HTTP guide, the OpenAPI document and copy-paste code are on docs.summerengine.com/api (quickstart, jobs, errors).
25
+
26
+ ## 1. Sign in once
27
+
28
+ ```bash
29
+ summer login
30
+ ```
31
+
32
+ The browser opens the Summer sign-in page. The person signs in, picks a spend limit for this app and chooses **Allow**. Tell them the browser opens, and wait for them. The sign-in renews itself; do it once per computer. There are no API keys.
33
+
34
+ - `summer token` prints a one-hour access token from the same sign-in, for the person's own HTTP code. Keep tokens out of game code, builds and commits.
35
+ - Listing models and estimating need no sign-in. Running a job does.
36
+ - The person sees and changes each app's spend limit, or revokes it, in Settings > Connected apps on summerengine.com.
37
+
38
+ ## 2. Pick a model
39
+
40
+ ```bash
41
+ summer api models --capability 3d
42
+ summer api models 3d/default
43
+ ```
44
+
45
+ MCP: `summer_api_models({ capability: "3d" })`, `summer_api_models({ model: "3d/default" })`.
46
+
47
+ - Capabilities: `image`, `3d`, `video`, `voice`, `dialogue`, `sound`, `music`.
48
+ - `<capability>/default` (such as `3d/default`) runs the model Summer tests first for that job. A job answer names the model that ran in `model`, so you can run the same model again.
49
+ - Each model lists its `inputSchema` (the exact fields), `outputFormats`, typical time and `price`. Read prices from this list every time, never from memory.
50
+ - Voice and dialogue models list `voices` (`voiceId` and `name`). Use one of those ids.
51
+
52
+ ## 3. Price it, then ask
53
+
54
+ ```bash
55
+ summer api estimate 3d/default --input '{"kind": "text-to-3d", "prompt": "a stylized treasure chest, game prop"}'
56
+ ```
57
+
58
+ MCP: `summer_api_estimate({ model: "3d/default", input: { kind: "text-to-3d", prompt: "a stylized treasure chest, game prop" } })`.
59
+
60
+ The answer has `price` (US dollars as a decimal string), `atMost` (`true`: the most it can cost, the charge can be lower), a `breakdown`, and a `quoteId` that holds the price for 15 minutes. Estimates are free.
61
+
62
+ Before you spend: tell the person what you will make, how many, and the total. Wait for a yes. Jobs spend their paid Summer credits (plan credits and top-ups).
63
+
64
+ ## 4. Run one job
65
+
66
+ ```bash
67
+ summer api run 3d/default \
68
+ --input '{"kind": "text-to-3d", "prompt": "a stylized treasure chest, game prop"}' \
69
+ --quote <quoteId> --download assets/props
70
+ ```
71
+
72
+ MCP: `summer_api_run({ model: "3d/default", input: { kind: "text-to-3d", prompt: "a stylized treasure chest, game prop" }, quoteId: "<quoteId>", out: "assets/props" })`.
73
+
74
+ - `--download <dir>` (MCP `out`) follows the job to the end and saves every file as `<assetId>.<ext>`. `--wait` (MCP `wait: true`) follows it without saving.
75
+ - `--quote` (MCP `quoteId`): Summer refuses to charge more than the quote. If the price went up, the job does not start and nothing is charged (`conflict`): estimate again.
76
+ - Images, voice and sound usually finish inside the call. 3D and video take minutes: one MCP call follows a job for about a minute, then answers `next`. Call `summer_api_job({ jobId, wait: true })` until the job finishes. The CLI follows it to the end by itself.
77
+ - In Windows PowerShell or cmd, write the input to a file and pass `--input "@input.json"`: those shells break quotes inside `'{...}'`.
78
+ - Each run sends its own idempotency key, so a retried request never runs twice. To retry the same run by hand, pass the same `--idempotency-key` (MCP `idempotencyKey`).
79
+
80
+ ## 5. Run many: a batch
81
+
82
+ For a set of assets (props for a level, every UI icon, a sound pack), send one batch instead of many runs. One batch takes up to 500 jobs, all or none.
83
+
84
+ ```jsonl
85
+ {"model": "3d/default", "input": {"kind": "text-to-3d", "prompt": "a wooden barrel with iron bands, game prop"}}
86
+ {"model": "3d/default", "input": {"kind": "text-to-3d", "prompt": "a mossy stone well, game prop"}}
87
+ {"model": "image/default", "input": {"prompt": "a red health potion icon, flat fantasy style, centered", "removeBackground": true}}
88
+ {"model": "sound/default", "input": {"text": "glass potion bottle uncorked, short pop", "durationSeconds": 1}}
89
+ ```
90
+
91
+ ```bash
92
+ summer api run --batch jobs.jsonl --download assets
93
+ ```
94
+
95
+ MCP: `summer_api_batch({ jobs: [{ model: "3d/default", input: { kind: "text-to-3d", prompt: "a wooden barrel with iron bands, game prop" } }], out: "assets" })`. Read it again with `summer_api_batch({ batchId: "<id>", wait: true, out: "assets" })`.
96
+
97
+ - Every job is priced when the batch arrives. When the paid credits and the app's limit cover all of them, all are accepted at once and run in order; each holds its price (`estimatedPrice` on the job, the total on the batch). When they do not, nothing runs: the error's `details` say how many fit (`fits`) and how much more is needed (`shortUsd`).
98
+ - The batch answers its jobs in the order you sent them. Give each job a `title` where the model takes one (3D and image models do): it names the asset in the person's library.
99
+ - A batch takes no `quoteId`. Estimate each distinct request first (same model and input, same price), add them up, and give the person the total for the whole batch.
100
+ - Each line may carry its own `idempotencyKey`; the batch has one too (`--idempotency-key`). To pick up a batch after the terminal closed, run the same command with the same `--idempotency-key`: it answers the batch it already made and follows it. MCP: `summer_api_batch({ batchId, wait: true })`.
101
+
102
+ ## 6. Follow and read
103
+
104
+ ```bash
105
+ summer api jobs <jobId> --download assets # follow it and save its files
106
+ ```
107
+
108
+ MCP: `summer_api_job({ jobId: "<jobId>", out: "assets" })`.
109
+
110
+ A Summer API job runs to the end once it is accepted, queued or running: it cannot be cancelled. A job that fails costs nothing. So confirm the list with the person before you send it.
111
+
112
+ | `state` | Means | Do |
113
+ |---|---|---|
114
+ | `queued` | Waiting for one of the account's running slots; its price is held | Keep following; it starts in order |
115
+ | `running` | Working; `progress` 0 to 100 when known | Keep following |
116
+ | `done` | Finished; `result.assets` has the files | Download them |
117
+ | `failed` | It did not work; nothing is charged | Show `error.message`; change the input or the model |
118
+ | `cancelled`, `expired`, `stale`, `declined` | Stopped without a result | Start a new job if the person still wants it |
119
+
120
+ A finished job has `result.assets`: one entry per file, each with `assetId`, `fileUrl` and `thumbnailUrl`. The file type is the extension of `fileUrl` (the model's `outputFormats` lists them). Job ids may hold a colon (`gen:...`, `q:...`); send them as they are.
121
+
122
+ With `--json`, or when its output is piped, every command prints the API's own answer: `{"job": {...}}` for a run or a job (plus `"files"` with `--download`), `{"batch": {...}}` for a batch, the estimate's fields at the top level, and `{"ok": false, "error": {"code", "message", "details"}}` on an error. In a terminal it prints a short summary.
123
+
124
+ ## Limits: money only
125
+
126
+ - **No rate limits on paid usage.** Send as much work as the person agrees to pay for.
127
+ - **Running slots.** Each account runs 10 jobs at once at the start, and up to 40 as it buys more credits. Jobs past the slots answer `queued` (their id starts with `q:`) and start in order as slots free up. Queued work is not an error; do not resend it.
128
+ - **Two money limits**, both checked before any work starts: the person's paid balance and this app's own spend limit. Queued and running jobs hold their price against both until they are charged.
129
+ - **A failed job costs nothing.** A retry with the same idempotency key never charges twice.
130
+
131
+ ## Errors
132
+
133
+ Every error has a stable `code` and a `message` written for the person. Branch on the code; show the message.
134
+
135
+ | Code | Means | Do |
136
+ |---|---|---|
137
+ | `not_signed_in` | No sign-in, the app was revoked, or a `summer token` older than an hour | CLI and MCP: run `summer login`, then retry. Own HTTP code: get a new `summer token` |
138
+ | `no_credits` | The paid balance cannot cover the job or the batch | Show the message: it links where to add credits. Do not retry until the person tops up |
139
+ | `over_limit` | This app's own spend limit is reached | Show the message: the person raises the limit in Connected apps, or waits for it to reset |
140
+ | `invalid_input` | The input does not match the model, or the `quoteId` expired | Fix the field the message names (`param`); read the model's `inputSchema`. An expired quote: estimate again |
141
+ | `not_found` | No such model, job or batch | Model ids come from `summer api models` |
142
+ | `conflict` | The price went up since your quote | Estimate again; nothing was charged |
143
+ | `idempotency_conflict` | The key was used for a different request | Use a new key for a new job |
144
+ | `in_progress` | The first request with this key is still running | Send the same request again shortly |
145
+ | `outcome_unknown` | Summer could not tell if the work started | Send the same request with the same key; never start a new job |
146
+ | `unavailable` | A model or service is down for a moment | Retry with backoff, or pick another model for the same job |
147
+ | `unsupported` | The job cannot do that (a cancel) | Let the job finish |
148
+
149
+ `no_credits` and `over_limit` are answers about money, not failures: stop, show the person the message and its link, and continue when they say so.
150
+
151
+ ## Prompts that work, per asset type
152
+
153
+ Write prompts as plain descriptions of the thing itself. Put the style in every prompt of a set, word for word, so the assets match.
154
+
155
+ | Asset | Model | Input that works |
156
+ |---|---|---|
157
+ | Prop | `3d/default` | `{"kind": "text-to-3d", "prompt": "a wooden treasure chest with iron trim, stylized low-poly, game prop"}`. One object per job. Add `"targetPolycount": 3000` for a light mesh. |
158
+ | Character | `3d/default` | `{"kind": "text-to-3d", "assetIntent": "character", "poseMode": "a-pose", "rig": true, "animationNames": ["Idle", "Walk", "Run"], "prompt": "a dwarf blacksmith, full body, stylized"}`. Full body, one character, neutral pose. |
159
+ | 3D from concept art | `3d/default` | `{"kind": "image-to-3d", "imageUrl": "<public URL>"}`. A clean picture of one object on a plain background. |
160
+ | Concept art, key art | `image/default` | `{"prompt": "...", "aspectRatio": "16:9"}`. Name the subject, the setting, the light and the style. |
161
+ | Icon, sprite, item | `image/default` | `{"prompt": "a red health potion, centered, flat fantasy style", "removeBackground": true}`. One object, centered; a transparent PNG comes back. |
162
+ | Matching set | `image/default` | Send the first image's `fileUrl` as `referenceImageUrl` in the next jobs, and keep the same `seed` and style words. |
163
+ | Sound effect | `sound/default` | `{"text": "heavy wooden door creaks open, stone hall echo", "durationSeconds": 2}`. Source, action, material, space. `"loop": true` for ambience. |
164
+ | Voice line | `voice/default` | `{"text": "Halt! Who goes there?", "voiceId": "<from the model's voices>"}`. Punctuation sets the delivery. |
165
+ | Conversation | `dialogue/default` | `{"inputs": [{"text": "...", "voiceId": "..."}, {"text": "...", "voiceId": "..."}]}`. One entry per line, up to 40. |
166
+ | Music | `music/default` | `{"prompt": "calm medieval village theme, lute and flute, 90 bpm, loopable", "durationSeconds": 60}`. Genre, mood, instruments, tempo. |
167
+ | Video | `video/default` | `{"prompt": "slow camera push toward a castle at dawn", "duration": 5}`, or `imageUrl` to animate key art. One shot per job. |
168
+
169
+ A picture input (`imageUrl`, `referenceImageUrl`) is a public URL, such as the `fileUrl` of an earlier job. For a file on the person's computer, the docs' jobs page shows the upload (`/v1/files`).
170
+
171
+ ## Without Summer Engine
172
+
173
+ This whole skill runs without Summer Engine: the CLI, the MCP tools and the HTTP API need only the Summer sign-in. The files work in any engine or app. Stop at the files; skip any step that imports into a Summer project.
174
+
175
+ ## Use the files
176
+
177
+ - Save files inside the project (`--download assets/...`), and keep each `assetId`: in Summer Engine, an asset imports by its id.
178
+ - GLB files are Y up and not at real-world size. Scale them in the engine (a door about 2 m, a person about 1.7 m).
179
+ - Assets made through the API are private to the person's Summer library. A `fileUrl` downloads without a sign-in, so share it only where the file may be seen.
180
+
181
+ ## Pitfalls
182
+
183
+ | Pitfall | Do this instead |
184
+ |---|---|
185
+ | Typing a price from memory | Read `summer api models`, then `summer api estimate` |
186
+ | Spending before the person agreed | Estimate, say the total, wait for a yes |
187
+ | Many single runs for a set of assets | One batch: all or none, priced up front |
188
+ | Resending a job that answered `queued` | It is accepted and its price is held. Follow it. |
189
+ | Treating `no_credits` or `over_limit` as a bug to retry | Show the message and its link; wait for the person |
190
+ | A new idempotency key on a retry after a timeout | The same key: it returns the first job, never a second charge |
191
+ | A token or a sign-in in game code | Only the developer's tools sign in. A shipped game uses Summer Router (the `summer-router` skill). |
192
+ | Several objects in one 3D prompt | One object per job; place them in the engine |
193
+
194
+ ## Done means
195
+
196
+ - Every job you started is `done`, or you told the person which failed and why.
197
+ - The files are in the folder the person asked for, and you told them the paths and formats.
198
+ - You told the person what it cost (`price` on each job, or the batch's `estimatedPrice`).
@@ -0,0 +1,38 @@
1
+ id: skill/summer-api
2
+ kind: skill
3
+ version: 1.0.0
4
+ summary: "Summer API game assets: models, estimate, one job or a batch, download the files."
5
+ use_when:
6
+ - generating asset files (3D models, images, sounds, voice, music, video) from a script, a pipeline or a coding agent outside Summer Engine
7
+ - making a set of assets in one go (a level's props, every UI icon, a sound pack) as one batch
8
+ - choosing a generation model, pricing a job before it runs, or reading a job's state and files
9
+ - a generation job answered no_credits, over_limit, not_signed_in or queued
10
+ do_not_use_when:
11
+ - generating inside a running Summer Engine project, where the asset skills import into the scene (asset-strategy)
12
+ - AI that a shipped game calls while people play (summer-router)
13
+ facets:
14
+ lifecycle:
15
+ - build
16
+ domains:
17
+ - assets
18
+ - agent-workflow
19
+ modalities:
20
+ - 3d-models
21
+ - images
22
+ - audio
23
+ - video
24
+ related:
25
+ tools:
26
+ - tool/api-models
27
+ - tool/api-estimate
28
+ - tool/api-run
29
+ - tool/api-batch
30
+ - tool/api-job
31
+ skills:
32
+ - skill/summer-assets
33
+ - skill/summer-cli
34
+ - skill/asset-strategy
35
+ - skill/summer-router
36
+ source: official
37
+ license: MIT
38
+ status: preview
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: summer-assets
3
- description: "Make game assets with Summer, no Summer Engine: find, generate and download 3D models, images, audio and video for any engine or app."
3
+ description: "Game assets for any engine, no Summer Engine: find, generate and download them."
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Codex, Windsurf, Gemini, OpenCode, Factory, Copilot]
6
6
  category: _meta
@@ -1,7 +1,7 @@
1
1
  id: skill/summer-assets
2
2
  kind: skill
3
- version: 1.0.1
4
- summary: "Make game assets with Summer, no Summer Engine: find, generate and download 3D models, images, audio and video for any engine or app."
3
+ version: 1.0.2
4
+ summary: "Game assets for any engine, no Summer Engine: find, generate and download them."
5
5
  use_when:
6
6
  - starting a session that has the Summer assets profile (generation tools, no engine tools)
7
7
  - the person wants assets for their own engine or web app, not a game built in Summer Engine
@@ -26,6 +26,7 @@ related:
26
26
  - tool/get-asset-download-url
27
27
  skills:
28
28
  - skill/asset-strategy
29
+ - skill/summer-api
29
30
  - skill/sound-effect
30
31
  - skill/prop-model
31
32
  - skill/concept-art
@@ -0,0 +1,251 @@
1
+ ---
2
+ name: summer-router
3
+ description: "AI characters for games on Summer Games: Summer Router replies, tools, speech, images."
4
+ license: MIT
5
+ compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
+ category: ai-and-npcs
7
+ user-invocable: true
8
+ allowed-tools: Read Grep Edit Write summer_get_scene_tree summer_add_node summer_set_prop summer_connect_signal summer_save_scene summer_get_script_errors
9
+ paths: ["**/*.gd", "**/*.tscn"]
10
+ ---
11
+
12
+ # Summer Router: AI characters in your game
13
+
14
+ A shopkeeper who haggles, a guard who opens the gate when convinced, a narrator who says the player's name. With **Summer Router**, a game calls AI while people play. The game names a character and sends what the player said. Summer holds the instructions, the model, the voice, the caps and the provider keys, and meters each call from the creator's paid Summer credits. Players never pay, and nothing secret is in the build.
15
+
16
+ ## What works today (early preview)
17
+
18
+ | | Status |
19
+ |---|---|
20
+ | Where | Games **published on Summer Games**, played in the Summer app (desktop, phone) or on summer.games, by a signed-in player. Other platforms and storefronts answer `not_on_summer_games`; they are coming soon. |
21
+ | Engine | Summer Engine 0.8 or later: `Summer.client.router`, `Summer.authority.router` and the `SummerRouterCharacter` node |
22
+ | Character replies | Available: text replies that keep the conversation and call your game's tools (`reply`) |
23
+ | Speech | Available: a whole clip in the character's voice (`speak`) |
24
+ | Images | Available: one image from a prompt and an optional reference picture (`generate("image", ...)`) |
25
+ | Transcription | Coming soon: `transcribe` answers `unavailable`. Keep typed input. |
26
+ | Realtime voice | Coming soon: a `talk` session in `"voice"` mode talks in text turns and still speaks the replies |
27
+
28
+ In the editor and in Local Play, every call answers `unavailable`, so the game uses its offline lines. Test the character with the test conversation (step 2) and play the real thing on Summer Games.
29
+
30
+ ## 1. Plan: who calls
31
+
32
+ Ask the person one question if it is not clear: is the game single-player or multiplayer?
33
+
34
+ | Game | Who calls | Counts toward |
35
+ |---|---|---|
36
+ | Single-player | The player's game: `Summer.client.router` | The player's caps and the game's caps |
37
+ | Multiplayer | The game server (the authority): `Summer.authority.router`, for one player's `SummerSession` | The game's caps and call rate |
38
+
39
+ In a multiplayer game, the server owns the world. Anything a character does that changes the game (sell an item, give gold, open a shared door) runs on the server. A player's game may also talk to a character itself and tell the server what happened; then the server checks the exchange with `verify_exchange` before it acts (step 4).
40
+
41
+ ## 2. Make the character
42
+
43
+ A character lives on Summer, not in the build. Make it in Summer Studio (the game's **Grow > Summer Router** tab), or with the Summer MCP:
44
+
45
+ ```text
46
+ router.save_character({
47
+ gameId: "<from games.list>",
48
+ character: {
49
+ id: "shopkeeper",
50
+ name: "Mira the shopkeeper",
51
+ instructions: "You run the potion shop in Emberfall. Warm, a little greedy, never rude. You sell only what is in stock: {{stock}}. When the player agrees on a potion and its price, call sell_item. You never give items for free.",
52
+ first_message: "Welcome, traveller! Potions, tonics, a little luck in a bottle?",
53
+ variables: { stock: "health potion 25 gold, mana potion 30 gold" },
54
+ overridable: ["variables"],
55
+ tools: [{
56
+ name: "sell_item",
57
+ description: "Sell one item to the player once they agree on it and its price.",
58
+ parameters: { type: "object", properties: { item: { type: "string" }, price: { type: "integer" } }, required: ["item", "price"] }
59
+ }]
60
+ }
61
+ })
62
+ ```
63
+
64
+ - Over MCP, call it with `summer_call({ op: "router.save_character", input: {...} })`, or from a shell: `summer router save-character --input '<json>'`. Find the game id with `games.list`. On a game that is live, the person approves the change with one link.
65
+ - Test it before you write game code: `router.test_character({ gameId, characterId: "shopkeeper", messages: ["Do you have anything for my wounds?", "Fine, 25 gold for the health potion."], toolOutputs: { sell_item: { ok: true } }, expectTools: ["sell_item"] })`. Test turns count toward a developer cap of their own.
66
+ - `router.models` lists the text models, voices and the full character schema (`include: ["schema"]`). Pick `model` and `voice.id` from it, or leave them out for the game's defaults.
67
+ - The tool `name` in the character is the name your game handles. Tool arguments come from the model: check every one in game code.
68
+
69
+ The character, field by field:
70
+
71
+ | Field | What it does |
72
+ |---|---|
73
+ | `id` | What the game calls it by: lowercase letters, digits, `_` or `-`, up to 64 |
74
+ | `name`, `first_message`, `language` | Display name, its opening line, a language such as `en` or `pt-BR` |
75
+ | `instructions` | Who it is and how it talks, up to 20,000 characters. `{{name}}` placeholders must be declared in `variables` |
76
+ | `variables` | Declared placeholders and their defaults. A build may send new values only when `overridable` lists `"variables"` |
77
+ | `overridable` | Fields a build may change per call, such as `"variables"` or `"first_message"`. `context` needs no entry: a build may always send it |
78
+ | `model`, `temperature`, `max_output_tokens`, `reasoning` | The text model (from `router.models`) and how it answers; leave out for the game's default |
79
+ | `voice` | `{"id": "<from router.models voices>"}` for `speak` |
80
+ | `tools` | Up to 32: `name`, `description`, `parameters` (a JSON Schema object) |
81
+ | `limits` | This character's own daily caps: `player_daily_micro_usd`, `game_daily_micro_usd` (1,000,000 = 1 US dollar) |
82
+
83
+ ## 3. Single-player: call from the player's game
84
+
85
+ ```gdscript
86
+ var conversation_id := ""
87
+
88
+ func ask_shopkeeper(line: String) -> void:
89
+ var r: SummerRouterResult = await Summer.client.router.reply("shopkeeper", line, {
90
+ "conversation_id": conversation_id,
91
+ "variables": {"stock": Shop.stock_line()},
92
+ "context": "The player has %d gold." % Player.gold,
93
+ "tools": {"sell_item": func(args: Dictionary): return Shop.sell(str(args.get("item", "")), int(args.get("price", 0)))},
94
+ })
95
+ if r.ok:
96
+ conversation_id = r.conversation_id
97
+ $Bubble.show_line(r.text, r.exchange_id) # label it as AI; offer a report button
98
+ else:
99
+ $Bubble.show_line(OfflineLines.shopkeeper(line), "")
100
+ ```
101
+
102
+ - `tools` maps each tool name to a `Callable`. The SDK runs the handler, sends what it returns back to the character, and the character goes on, up to 8 rounds in one call. Return `{"error": "..."}` when the action fails (not enough gold, out of stock); the character then says so.
103
+ - `Shop.sell` checks the price against your own price list, the stock and the gold. The model's word is never the price.
104
+ - `conversation_id`: Summer keeps the history. Send the one the last result named. Without it, each call starts a new conversation.
105
+ - For a whole conversation in one node, add a `SummerRouterCharacter`, set **Character ID**, turn on **Show UI**, and give it `tools`. It shows a bar with the character's words and a text field.
106
+
107
+ Speech and images from the player's game:
108
+
109
+ ```gdscript
110
+ var line: SummerRouterResult = await Summer.client.router.speak("", {"exchange_id": r.exchange_id}) # voice that reply
111
+ if line.ok:
112
+ $Voice.stream = line.audio
113
+ $Voice.play()
114
+
115
+ var art: SummerRouterResult = await Summer.client.router.generate("image", {"prompt": "a shop sign shaped like a potion", "size": "1024x1024"})
116
+ if art.ok:
117
+ $Sign.texture = art.texture
118
+ ```
119
+
120
+ Voice only when the character has one (the `has_voice` of each entry in `status().characters`), and consider voicing on tap: speech costs more than text.
121
+
122
+ `await Summer.client.router.status()` asks Summer what this player can use now: `available` (and `reason`, an error tag, when it is not), `characters` (each `id`, `name`, `has_voice`, `client_tools`), `kinds` (the capabilities Summer serves) and `voice` (realtime voice, `false` during the early preview). `report(exchange_id, reason)` answers `report_id` and `status`; it takes one of this player's replies from the last 30 days.
123
+
124
+ ## 4. Multiplayer: call from the server
125
+
126
+ The player's game sends what the player typed to the server as a Command (the `multiplayer-state` skill sets up Commands and shared State). The server answers the Command at once, asks the character for that player's session, runs the tool calls itself, and publishes the line in shared State so every player sees it.
127
+
128
+ ```gdscript
129
+ # Server (authority). Call from your Command handler for {"c": "talk", "text": ...}.
130
+ const MAX_ROUNDS := 3
131
+
132
+ func _talk(request: SummerNetworkCommandRequest, text: String) -> void:
133
+ var session := request.get_session()
134
+ request.accept({}, Game.pack({"asked": true})) # the reply arrives in shared State
135
+ var conversation := "shop_" + session.session_id.md5_text()
136
+ var options := {"conversation_id": conversation, "variables": {"stock": Shop.stock_line()}}
137
+ var r: SummerRouterResult = await Summer.authority.router.reply(session, "shopkeeper", text.strip_edges().left(4000), options)
138
+ for _round in MAX_ROUNDS:
139
+ if not r.ok or r.tool_calls.is_empty():
140
+ break
141
+ var results := []
142
+ for call in r.tool_calls:
143
+ var output: Variant = {"error": "unknown tool"}
144
+ if call.name == "sell_item":
145
+ output = Shop.sell_to(session, str(call.args.get("item", ""))) # checks stock, price and wallet
146
+ results.append({"call_id": call.call_id, "output": output})
147
+ r = await Summer.authority.router.reply(session, "shopkeeper", "", {"conversation_id": conversation, "tool_results": results})
148
+ var line := r.text if r.ok else r.message # r.message is a plain line for players
149
+ if line.is_empty():
150
+ line = OfflineLines.shopkeeper(text)
151
+ shop_doc.lines.append({"by": session.player.display_name, "text": line})
152
+ shop_group.reset(Game.pack(shop_doc))
153
+ ```
154
+
155
+ - The server has `reply(session, character, text, options)`, `speak(session, text, {"character": id})` and `generate(session, "image", {...})`. Options are the client's without `tools`: the server reads `r.tool_calls` and answers them with `tool_results` on the next call, with the same `conversation_id`.
156
+ - `speak` and `generate` on the server return `data` and `mime_type`. Audio and images are too big for shared State: to voice a line, each player's game calls `Summer.client.router.speak(line, {"character": "shopkeeper"})` for the line it shows (it counts toward that player's caps).
157
+ - Server calls count toward the game's caps, not the player's. Keep your own pace per player on the server: one line in flight per player, a short gap between lines, and a length cap on the text, so one player cannot spend the whole game's daily cap.
158
+ - `Shop.sell_to` on the server is the only place an item or gold changes hands. Never apply an effect a client reports.
159
+
160
+ **A client talked, the server acts.** When the player's game called `Summer.client.router` itself and tells the server "the shopkeeper agreed to sell", the server checks the exchange first:
161
+
162
+ ```gdscript
163
+ var check: SummerRouterResult = await Summer.authority.router.verify_exchange(session, exchange_id)
164
+ if check.ok and check.tool_calls.any(func(call): return call.name == "sell_item"):
165
+ Shop.sell_to(session, str(check.tool_calls[0].args.get("item", "")))
166
+ # Anything else (another player's exchange, older than an hour, made up): do nothing.
167
+ ```
168
+
169
+ ## 5. Every refusal keeps the game running
170
+
171
+ Every call returns a `SummerRouterResult`. Read `ok` first. On a refusal, `error` is a stable tag, `fix` says what to do (for you), and `message` is a plain line you may show players. Show `message` or your own offline line, and go on. Treat a tag you do not know as `unavailable`.
172
+
173
+ | Tag | Means | Game does |
174
+ |---|---|---|
175
+ | `unavailable` | No Summer app, an old app, the editor or Local Play, or the service is down (`reason` says which) | Offline line |
176
+ | `not_signed_in` | The player is not signed in | Offline line |
177
+ | `not_on_summer_games` | The game is not running from Summer Games | Offline line; publish on Summer Games |
178
+ | `router_disabled` | The creator turned Summer Router off for the game | Offline lines until the game restarts |
179
+ | `credits_required` | The creator's paid credits ran out | Offline lines until the game restarts |
180
+ | `limit_reached` | A cap has no room: `scope` (such as `player_day`, `game_day`, `game_month`), `resets_at` (Unix seconds) | Offline lines until `resets_at` |
181
+ | `rate_limited` | Too many calls a minute (`retry_after` seconds) | Offline line; wait before the next call |
182
+ | `age_restricted` | This player may not talk to this character | Offline lines for this player |
183
+ | `moderated` | The safety filter blocked this turn | Offline line |
184
+ | `provider_unavailable` | The model is down for a moment | Offline line; the next call may work |
185
+ | `character_not_found` | No character with this id | Bug: fix the id, or save the character |
186
+ | `invalid_input` | The call breaks a limit (text 1 to 4000 characters, at most 32 variables), or sends `variables` the character does not let a build set | Bug: fix the call or the character |
187
+ | `tool_unhandled` | The character called a tool the game has no handler for | Add the handler to `tools` |
188
+
189
+ `retryable` is `true` when the same call can work later (`unavailable`, `limit_reached`, `provider_unavailable`, `rate_limited`). Never retry in a loop: a cap is the product working.
190
+
191
+ ## 6. Caps and money
192
+
193
+ Every game starts on, with caps on from day one:
194
+
195
+ | Cap | Default |
196
+ |---|---|
197
+ | Spend per player per day | 5 US dollars |
198
+ | Spend per game per day | 100 US dollars |
199
+ | Spend per game per month | 1,000 US dollars |
200
+ | Spend per account per month, all games | 2,000 US dollars |
201
+ | Calls a minute per player | 30 |
202
+ | Calls a minute per game | 1,200 |
203
+
204
+ - The creator changes them per game in Grow > Summer Router, or an agent asks with `router.set_settings` (the person approves with one link). `router.set_account_limit` changes the account cap. `router.usage` shows calls, units, how much of each cap is used and which calls a cap stopped.
205
+ - A character can carry its own daily caps per player and per game (`limits` in the character).
206
+ - Calls are paid from the creator's paid Summer credits (plan credits and top-ups), the same balance as the Summer API. A result never carries a price.
207
+
208
+ ## 7. Ship it safely
209
+
210
+ - No provider key, provider SDK, model id or character instructions in the project. The build holds character ids only.
211
+ - Every character keeps an offline line set that is good on its own. Most refusals happen in normal play.
212
+ - Label AI lines as AI in the game (a small tag on the bubble), and offer a report button: `Summer.client.router.report(exchange_id, "inappropriate")`.
213
+ - Never ask players for personal details, and keep characters off romance, self-harm and medical advice. Write it into the instructions.
214
+ - In a multiplayer game, a reply never changes shared state on a client. The server applies every effect and checks every tool argument.
215
+ - `Summer.client.router.is_available()` says, without a network call, whether this run can call Summer Router. `status()` asks Summer which characters and kinds it serves for this player.
216
+
217
+ ## Verify
218
+
219
+ - `grep` the project for provider names, model ids and anything key-shaped: none.
220
+ - In the editor every character answers with its offline line, and no error is shown.
221
+ - `router.test_character` with `expectTools` passes for each tool the game handles.
222
+ - Every tool handler refuses bad arguments (an unknown item, a wrong price, not enough gold).
223
+ - In multiplayer, the server is the only code that sells, gives or opens anything, and it checks a relayed exchange with `verify_exchange`.
224
+ - On Summer Games, signed in, the character answers, its tool works, and `router.usage` shows the calls.
225
+
226
+ ## Common mistakes
227
+
228
+ | Mistake | Do this instead |
229
+ |---|---|
230
+ | Calling a provider from the game "until Summer Router is on" | Summer Router only; offline lines meanwhile |
231
+ | Sending the character's instructions from the build | Save them on Summer; the build sends `variables` and `context` |
232
+ | Trusting the price or item a tool call names | Check them against your own list on the game's side |
233
+ | Retrying on `limit_reached` or `credits_required` | Offline lines until the cap resets or the creator acts |
234
+ | Waiting for transcription before shipping | Ship typed input now; transcription is coming soon |
235
+ | Testing only in the editor | The editor answers `unavailable`; use the test conversation, then Summer Games |
236
+
237
+ <!-- shell-commands: generated from the operation catalog by npm run generate:registry; do not edit by hand -->
238
+ ## From a shell
239
+
240
+ No Summer MCP tools in this session? Run each one as a command; when `summer --version` is missing or below 4.0, use `npx -y summer-engine@latest` in place of `summer`. Fields are flags in kebab-case (`--scene-path res://main.tscn`) or one `--input '<json>'`, and `summer ops describe <op>` prints the exact input. The `summer-cli` skill has the rest.
241
+
242
+ | In this skill | Command |
243
+ |---|---|
244
+ | `summer_add_node` | `summer engine add-node` |
245
+ | `summer_connect_signal` | `summer engine connect-signal` |
246
+ | `summer_get_scene_tree` | `summer engine get-scene-tree` |
247
+ | `summer_get_script_errors` | `summer engine get-script-errors` |
248
+ | `summer_save_scene` | `summer engine save-scene` |
249
+ | `summer_set_prop` | `summer engine set-prop` |
250
+ | `games.list` | `summer games list` |
251
+ <!-- /shell-commands -->
@@ -0,0 +1,35 @@
1
+ id: skill/summer-router
2
+ kind: skill
3
+ version: 1.0.0
4
+ summary: "AI characters for games on Summer Games: Summer Router replies, tools, speech, images."
5
+ use_when:
6
+ - a talking NPC (shopkeeper, guard, companion) players chat with, in a single-player or multiplayer game on Summer Games
7
+ - a game should speak a line in a character's voice, or make an image, while people play
8
+ - about to put a model name, a provider SDK or a provider key into a game project
9
+ - choosing between Summer.client.router and Summer.authority.router in a single-player or multiplayer game
10
+ - handling Summer Router refusals (limit_reached, credits_required, router_disabled, not_on_summer_games, unavailable) or changing its caps
11
+ do_not_use_when:
12
+ - scripted dialogue trees or enemy behaviour (design-npc)
13
+ - making asset files while building the game (summer-api)
14
+ facets:
15
+ lifecycle:
16
+ - build
17
+ domains:
18
+ - ai
19
+ - npc
20
+ - multiplayer
21
+ modalities:
22
+ - scripts
23
+ - scenes
24
+ compatibility:
25
+ engine: ">=4.6"
26
+ related:
27
+ skills:
28
+ - skill/design-npc
29
+ - skill/multiplayer-state
30
+ - skill/summer-api
31
+ source: official
32
+ license: MIT
33
+ status: preview
34
+ aliases:
35
+ - skill/ai-characters-in-games
@@ -0,0 +1,82 @@
1
+ id: tool/api-batch
2
+ kind: tool
3
+ version: 1.0.0
4
+ summary: Run up to 500 Summer API jobs at once, all or none, with the Summer sign-in; follow every job and save the files.
5
+ use_when:
6
+ - making many game assets through the Summer API in one go (a set of props, sounds or icons)
7
+ - following or downloading a batch that summer_api_batch started
8
+ do_not_use_when:
9
+ - one job (summer_api_run)
10
+ - the person has not agreed to spend credits
11
+ facets:
12
+ lifecycle:
13
+ - build
14
+ domains:
15
+ - generate
16
+ - assets
17
+ compatibility:
18
+ engine: ">=4.6"
19
+ source: official
20
+ license: MIT
21
+ status: preview
22
+ aliases:
23
+ - summer_api_batch
24
+ implementation:
25
+ module: src/mcp/tools/api-tools.ts
26
+ export: registerApiTools
27
+ surfaces:
28
+ mcp:
29
+ tool_name: summer_api_batch
30
+ remote: true
31
+ input_schema:
32
+ type: object
33
+ properties:
34
+ jobs:
35
+ type: array
36
+ minItems: 1
37
+ maxItems: 500
38
+ items:
39
+ type: object
40
+ properties:
41
+ model:
42
+ type: string
43
+ minLength: 1
44
+ maxLength: 120
45
+ description: A Summer API model id or alias from summer_api_models, e.g. 3d/default.
46
+ input:
47
+ type: object
48
+ additionalProperties: {}
49
+ description: The model's input, matching its inputSchema from summer_api_models.
50
+ idempotencyKey:
51
+ type: string
52
+ minLength: 1
53
+ maxLength: 200
54
+ description: "This job's own key; by default the batch key and the job's position."
55
+ required:
56
+ - model
57
+ additionalProperties: false
58
+ description: Up to 500 jobs, all or none. Leave out with batchId.
59
+ batchId:
60
+ type: string
61
+ minLength: 1
62
+ maxLength: 200
63
+ description: Read a batch summer_api_batch answered, instead of starting one.
64
+ idempotencyKey:
65
+ type: string
66
+ minLength: 1
67
+ maxLength: 200
68
+ description: Reuse to retry the same batch without queuing it twice.
69
+ wait:
70
+ type: boolean
71
+ description: Follow every job until it finishes (up to about a minute in this call).
72
+ out:
73
+ type: string
74
+ minLength: 1
75
+ description: "A folder on this computer: save the finished job's files there (implies wait)."
76
+ additionalProperties: false
77
+ authority:
78
+ filesystem: true
79
+ editor_mutation: false
80
+ network: true
81
+ credentials: true
82
+ publish: false