@genex-ai/cli-demo 1.36.5 → 1.36.6-dev.862
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/dist/{blender-mcp-QVEGHE5L.js → blender-mcp-WSURPZET.js} +2 -2
- package/dist/{blender-serve-66OHC4ML.js → blender-serve-EH2MOW3U.js} +1 -1
- package/dist/{chunk-4BBTPRJW.js → chunk-EABIVAGT.js} +2 -2
- package/dist/{chunk-QI7FIYBY.js → chunk-K4EHXOZK.js} +1 -1
- package/dist/index.js +1301 -576
- package/package.json +1 -1
- package/templates/controllers/character/aim-cue.ts +2 -1
- package/templates/controllers/character/follow-camera.ts +4 -3
- package/templates/skills/genex-ai-video/SKILL.md +8 -3
- package/templates/skills/genex-game-director/SKILL.md +2 -1
- package/templates/skills/genex-getting-started/SKILL.md +2 -2
- package/templates/skills/genex-llm-in-games/SKILL.md +219 -73
- package/templates/skills/genex-llm-in-games/references/pricing.md +126 -85
- package/templates/skills/genex-monetization/SKILL.md +26 -0
- package/templates/skills/genex-threejs-camera-direction/SKILL.md +3 -3
- package/templates/skills/genex-threejs-character-controller/references/wiring.md +2 -2
- package/templates/skills/genex-threejs-embed-auth/SKILL.md +158 -36
- package/templates/skills/genex-threejs-embed-auth/references/shells.md +207 -0
- package/templates/skills/genex-threejs-game-ui/SKILL.md +22 -14
- package/templates/skills/genex-threejs-multiplayer/SKILL.md +46 -8
- package/templates/skills/genex-threejs-touch-controls/SKILL.md +63 -1
- package/templates/skills/genex-tool-llm/SKILL.md +11 -9
- package/templates/skills/genex-updates/SKILL.md +1 -1
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
# Calibrate, then declare
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
An in-game call is paid from the player's Genex credits and **billed as used**:
|
|
4
|
+
its real provider cost plus the platform fee. The game declares no price. It
|
|
5
|
+
declares a per-call **ceiling** — `maxCredits` on `generate()`,
|
|
6
|
+
`perCallMaxCredits` on a standing budget — and no call is ever billed past it.
|
|
7
|
+
|
|
8
|
+
The ceiling is still a number with two jobs, and both punish a guess:
|
|
9
|
+
|
|
10
|
+
- **It bounds the cost.** A budget's worst case, shown to the player beside
|
|
11
|
+
your own estimate, is built from `perCallMaxCredits`. A ceiling far above what
|
|
12
|
+
a call really costs asks the player to approve money the game will never
|
|
13
|
+
spend, and makes the sheet look like a withdrawal.
|
|
14
|
+
- **It sizes the answer.** Each call's room to answer is funded from its
|
|
15
|
+
ceiling, so a ceiling below what the answer needs cuts it off — and a cut-off
|
|
16
|
+
attempt is still billed for the work it did.
|
|
17
|
+
|
|
18
|
+
There is one honest way to pick it: run the real prompt on your own credits,
|
|
19
|
+
read what it really cost, and declare the numbers the benchmark prints.
|
|
20
|
+
|
|
21
|
+
Never derive them from a model vendor's published rates. Three layers sit
|
|
22
|
+
between that rate and what the player is billed — the provider's cost for YOUR
|
|
23
|
+
prompt, the platform fee, and the headroom a ceiling needs — and only the
|
|
24
|
+
benchmark sees the first. The other two come from the server; a number worked
|
|
25
|
+
out from the vendor's page drops at least one of them.
|
|
14
26
|
|
|
15
27
|
## 1. Freeze the prompt first
|
|
16
28
|
|
|
@@ -22,7 +34,7 @@ that the real game then cannot fund.
|
|
|
22
34
|
If the feature returns structured data, write the schema to a file now
|
|
23
35
|
(`./answer.schema.json`) and benchmark with it. Schema'd JSON and free text do
|
|
24
36
|
not cost the same, and shipping without a schema is the commonest way a call is
|
|
25
|
-
|
|
37
|
+
billed for an unusable result.
|
|
26
38
|
|
|
27
39
|
## 2. Run the benchmark
|
|
28
40
|
|
|
@@ -33,89 +45,105 @@ npx genex llm bench "<the frozen prompt, one real example filled in>" \
|
|
|
33
45
|
--model <id from the line above> \
|
|
34
46
|
--schema ./answer.schema.json \
|
|
35
47
|
--samples 3 \
|
|
36
|
-
--max-
|
|
48
|
+
--max-credits <n> --user-approved
|
|
37
49
|
```
|
|
38
50
|
|
|
39
|
-
- It spends **your**
|
|
40
|
-
credential. Nothing here runs in a shipped build.
|
|
41
|
-
|
|
51
|
+
- It spends **your** credits, on the development lane, through the CLI's own
|
|
52
|
+
credential. Nothing here runs in a shipped build. The build's asset allowance
|
|
53
|
+
does not count it, which is why it has its own gate.
|
|
54
|
+
- `--max-credits <n> --user-approved` is a hard gate, refused before any network
|
|
42
55
|
call. That is the same shape as every other spend approval in the CLI: you
|
|
43
|
-
state the ceiling for this run, out loud, once.
|
|
56
|
+
state the ceiling for this run, out loud, once. Your spendable balance has to
|
|
57
|
+
cover one attempt at it, or the run is refused before it starts.
|
|
58
|
+
`--max-coins` is the old name of the same flag and still works.
|
|
44
59
|
- `--samples 3` is the floor. The same prompt costs different amounts on
|
|
45
60
|
different runs, because the model's own output length varies.
|
|
46
61
|
- `npx genex llm price` re-prints the last run's recommendation without spending
|
|
47
|
-
anything again.
|
|
62
|
+
anything again. A file from before credits is reprinted as history, with a
|
|
63
|
+
request to re-run.
|
|
48
64
|
|
|
49
65
|
## 3. Read the output
|
|
50
66
|
|
|
51
|
-
Each sample prints
|
|
52
|
-
max of
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
dispatch. Re-running the bench into the same refusal spends nothing and
|
|
67
|
+
Each sample prints its real cost and the whole credits it was charged. The
|
|
68
|
+
aggregate prints p50, p95 and max of both **over the samples that succeeded and
|
|
69
|
+
settled**. A sample that failed is printed with its code and stays out of the
|
|
70
|
+
numbers; a sample the provider refused at its door (`provider_http_<status>`)
|
|
71
|
+
ran no inference, cost nothing, and prints the provider's own message under its
|
|
72
|
+
row — on a 401 or 403 that is the stand's provider configuration refusing the
|
|
73
|
+
model, which is the operator's to fix. A sample refused as `generation_limit`
|
|
74
|
+
never started and ends the run: the account's three ad-hoc calls are open or
|
|
75
|
+
recently stopped with a pending bill. `npx genex llm status` lists them with
|
|
76
|
+
what each holds; `npx genex llm cancel <id>` stops an active one; a stopped one
|
|
77
|
+
frees on its own once its bill resolves, and stops holding a slot ten minutes
|
|
78
|
+
after dispatch. Re-running the bench into the same refusal spends nothing and
|
|
64
79
|
learns nothing.
|
|
65
80
|
|
|
66
81
|
A sample the model had to stop writing (`provider_token_limit`) was cut off at
|
|
67
|
-
your `--max-
|
|
68
|
-
unknown — so the run recommends no
|
|
69
|
-
`--max-
|
|
70
|
-
answer is longer than one call there may produce; no
|
|
82
|
+
your `--max-credits`: it was billed, it is not a sample, and its real length is
|
|
83
|
+
unknown — so the run recommends no ceiling and asks you to re-run with a higher
|
|
84
|
+
`--max-credits`. When the stand itself cannot hold the answer, the run says the
|
|
85
|
+
answer is longer than one call there may produce; no ceiling fixes that, a
|
|
71
86
|
shorter answer does.
|
|
72
87
|
|
|
73
88
|
- **p50** is what a typical call costs. It is the number to reason about when
|
|
74
|
-
you ask "can the game afford this loop?"
|
|
75
|
-
minute you are about to disclose.
|
|
89
|
+
you ask "can the game afford this loop?".
|
|
76
90
|
- **p95** is what a bad-but-normal call costs: a long answer, a model that
|
|
77
|
-
reasons its way around. It is the
|
|
78
|
-
|
|
79
|
-
mid-session.
|
|
91
|
+
reasons its way around. It is what the **ceiling** has to cover, because a
|
|
92
|
+
ceiling below it cuts the unlucky calls off.
|
|
80
93
|
- **max** is diagnostic. When max sits far above p95, the prompt has an
|
|
81
94
|
unbounded branch in it — usually an unconstrained list or a missing schema.
|
|
82
95
|
Fix the prompt rather than declaring a bigger number.
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
the
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
Then the run prints what to declare:
|
|
98
|
+
|
|
99
|
+
- **`Declare maxCredits`** — the p95 real cost with the platform fee and the
|
|
100
|
+
**server's own recommended headroom** applied, in whole credits. It also
|
|
101
|
+
covers the answer's **length**: it is never below the smallest ceiling that
|
|
102
|
+
leaves room for the benchmarked answer, and when the length is what set it
|
|
103
|
+
the run says so in one sentence. A ceiling built from cost alone can cut the
|
|
104
|
+
answer off in the game while the bench — run under a larger `--max-credits`
|
|
105
|
+
— never saw it.
|
|
106
|
+
- **`Grant perCallMaxCredits`** — the same over the worst sample, never below
|
|
107
|
+
the line above it. It is emphatically NOT the benchmark's `max` of charged
|
|
108
|
+
credits: that number carries neither the headroom nor the answer's length,
|
|
109
|
+
so a ceiling set from it lands under the call ceiling and the budget's calls
|
|
110
|
+
are refused (`grant_price_unreasonable`).
|
|
111
|
+
- **`Per-call estimate: about … credits per call`** — the measured average,
|
|
112
|
+
fee included, no headroom: a fraction of a credit for a short call. It is the
|
|
113
|
+
honest figure for the disclosure.
|
|
114
|
+
- **`Grant perCallEstimateCredits`** — that average rounded up to a whole
|
|
115
|
+
credit.
|
|
116
|
+
|
|
117
|
+
Declare each verbatim:
|
|
93
118
|
|
|
94
119
|
```ts
|
|
95
|
-
const
|
|
96
|
-
const
|
|
120
|
+
const NPC_CALL_MAX = <Declare maxCredits>; // from `npx genex llm bench`, <date>
|
|
121
|
+
const NPC_GRANT_MAX = <Grant perCallMaxCredits>; // same run — budgets only
|
|
122
|
+
const NPC_CALL_ESTIMATE = <Grant perCallEstimateCredits>; // same run — budgets only
|
|
123
|
+
const NPC_CALL_AVERAGE = <the "about … credits per call" figure>; // same run
|
|
97
124
|
```
|
|
98
125
|
|
|
99
|
-
Write the benchmark date and the model id beside
|
|
100
|
-
person knows what the
|
|
101
|
-
|
|
126
|
+
Write the benchmark date and the model id beside them in `DESIGN.md`, so the
|
|
127
|
+
next person knows what the numbers describe. Do not add a margin of your own on
|
|
128
|
+
top, and do not round them down to look cheaper.
|
|
102
129
|
|
|
103
|
-
|
|
104
|
-
`perCallMaxCoins`. Declare that one verbatim too, and do not work it out by
|
|
105
|
-
hand. It is emphatically NOT the benchmark's `max`: p95 is nearest-rank, so at
|
|
106
|
-
the sample counts a benchmark actually takes, p95 and max are the same figure —
|
|
107
|
-
a ceiling set from `max` therefore lands *below* the recommended price, and
|
|
108
|
-
`requestSpendGrant()` refuses that pair before the request ever leaves the page.
|
|
109
|
-
|
|
110
|
-
The invariant, which the SDK and the server both enforce:
|
|
130
|
+
The invariants, which the SDK and the server both enforce:
|
|
111
131
|
|
|
112
132
|
```
|
|
113
|
-
|
|
133
|
+
perCallEstimateCredits ≤ perCallMaxCredits ≤ the limit the player approves
|
|
134
|
+
maxCredits on each call ≤ perCallMaxCredits
|
|
114
135
|
```
|
|
115
136
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
137
|
+
A ceiling is not a second price: a call is billed the real cost of the work it
|
|
138
|
+
did plus the fee (a one-time call rounded up to a whole credit), and never past
|
|
139
|
+
its ceiling.
|
|
140
|
+
|
|
141
|
+
**One-time calls round up; budgets do not.** A one-time `generate()` is captured
|
|
142
|
+
rounded UP to a whole credit when it settles, so a call that really costs a
|
|
143
|
+
fraction of a credit still costs the player one whole credit each time. Under a
|
|
144
|
+
budget each call adds its exact fraction and the player's credits move one
|
|
145
|
+
whole credit at a time. That is one more reason a repeated call belongs under a
|
|
146
|
+
budget.
|
|
119
147
|
|
|
120
148
|
## 4. Turn the game's loop into the disclosure
|
|
121
149
|
|
|
@@ -129,27 +157,39 @@ from the loop you wrote, counted honestly:
|
|
|
129
157
|
3. **Multiply, and pick the period that makes the number legible.** Five NPCs at
|
|
130
158
|
one call per thirty seconds is ten calls a minute, so `periodLabel: 'minute'`
|
|
131
159
|
and `estimatedCallsPerPeriod: 10`.
|
|
132
|
-
4. **
|
|
133
|
-
`
|
|
134
|
-
|
|
160
|
+
4. **Credits per period is calls × the measured average, rounded up** —
|
|
161
|
+
`estimatedCreditsPerPeriod: Math.ceil(10 * NPC_CALL_AVERAGE)`. Not the
|
|
162
|
+
ceiling: a call is billed what it uses, so the honest estimate is what calls
|
|
163
|
+
really average.
|
|
135
164
|
|
|
136
165
|
```ts
|
|
166
|
+
perCallMaxCredits: NPC_GRANT_MAX,
|
|
167
|
+
perCallEstimateCredits: NPC_CALL_ESTIMATE,
|
|
137
168
|
disclosure: {
|
|
138
169
|
periodLabel: 'minute',
|
|
139
170
|
estimatedCallsPerPeriod: 10,
|
|
140
|
-
|
|
171
|
+
estimatedCreditsPerPeriod: Math.ceil(10 * NPC_CALL_AVERAGE),
|
|
141
172
|
}
|
|
142
173
|
```
|
|
143
174
|
|
|
144
175
|
The player sees this attributed to your game — "the game estimates about …" —
|
|
145
|
-
beside the platform's own worst case computed from `
|
|
176
|
+
beside the platform's own worst case computed from `perCallMaxCredits`. The two
|
|
146
177
|
being far apart is normal; the estimate being far below what the game really
|
|
147
178
|
does is what breaks trust and burns the grant mid-session.
|
|
148
179
|
|
|
149
|
-
**Batching changes this arithmetic more than any
|
|
180
|
+
**Batching changes this arithmetic more than any tuning can.** Five NPCs
|
|
150
181
|
answered by one call that returns five decisions is two calls a minute, not ten,
|
|
151
|
-
and one
|
|
152
|
-
|
|
182
|
+
and one benchmark of the batched prompt replaces five of the unbatched one. Do
|
|
183
|
+
that before you reach for a cheaper model.
|
|
184
|
+
|
|
185
|
+
## Old code
|
|
186
|
+
|
|
187
|
+
`estimateCoins`, `perCallMaxCoins`, `perCallEstimateCoins` and
|
|
188
|
+
`disclosure.estimatedCoinsPerPeriod` are the names from before credits. The
|
|
189
|
+
server still accepts them and reads them as the same numbers in credits, but
|
|
190
|
+
they are deprecated: rename them to `maxCredits`, `perCallMaxCredits`,
|
|
191
|
+
`perCallEstimateCredits` and `estimatedCreditsPerPeriod` when you touch the
|
|
192
|
+
code, and re-benchmark — a number measured as a coin price is not a ceiling.
|
|
153
193
|
|
|
154
194
|
## Worked example
|
|
155
195
|
|
|
@@ -160,20 +200,21 @@ speech). The prompt carries the NPC's memory and the last two player lines.
|
|
|
160
200
|
1. Freeze the prompt with a full memory and a long player line. Write
|
|
161
201
|
`./answer.schema.json` with the two fields.
|
|
162
202
|
2. `npx genex llm bench "<that prompt>" --schema ./answer.schema.json
|
|
163
|
-
--samples 3 --max-
|
|
164
|
-
3. Read: p50 `<p50>`, p95 `<p95>`, max `<max>`, and the
|
|
165
|
-
|
|
166
|
-
`
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
203
|
+
--samples 3 --max-credits <ceiling> --user-approved`.
|
|
204
|
+
3. Read: p50 `<p50>`, p95 `<p95>`, max `<max>`, and the printed lines —
|
|
205
|
+
`Declare maxCredits: <call ceiling>`, `Grant perCallMaxCredits: <grant ceiling>`,
|
|
206
|
+
`Per-call estimate: about <average> credits per call` and
|
|
207
|
+
`Grant perCallEstimateCredits: <estimate>`.
|
|
208
|
+
4. Declare `NPC_CALL_MAX`, `NPC_GRANT_MAX`, `NPC_CALL_ESTIMATE` and
|
|
209
|
+
`NPC_CALL_AVERAGE`, each verbatim from the line that printed it.
|
|
210
|
+
5. Unbatched, the loop is ten calls a minute, so the disclosure is `10` calls
|
|
211
|
+
and `Math.ceil(10 * NPC_CALL_AVERAGE)` credits per minute.
|
|
171
212
|
6. Batch the five NPCs into one call — benchmark the batched prompt separately,
|
|
172
213
|
because it is a different prompt — and the disclosure becomes two calls a
|
|
173
|
-
minute at the batched
|
|
214
|
+
minute at the batched average.
|
|
174
215
|
7. Re-run steps 1–4 whenever the prompt, the schema or the model changes. A
|
|
175
|
-
prompt edit
|
|
216
|
+
prompt edit changes the ceiling; treat it like one.
|
|
176
217
|
|
|
177
218
|
Every `<placeholder>` above is read off your own benchmark run. None of these
|
|
178
219
|
figures is a platform constant, and none of them should be copied from another
|
|
179
|
-
game — a different prompt has a different
|
|
220
|
+
game — a different prompt has a different cost.
|
|
@@ -234,6 +234,22 @@ price sheet, cannot skin it, and cannot complete a purchase itself. That is
|
|
|
234
234
|
deliberate: it is what lets a player trust a purchase in a game they have never
|
|
235
235
|
played before.
|
|
236
236
|
|
|
237
|
+
Where that surface is depends on the shell the game is in
|
|
238
|
+
(`$genex-threejs-embed-auth`, `references/shells.md`), and two of them change
|
|
239
|
+
what the game does:
|
|
240
|
+
|
|
241
|
+
- **On the game's Genex page the sheet is drawn by the page, above the game.**
|
|
242
|
+
To show it, the page leaves fullscreen first, and the SDK lets go of the
|
|
243
|
+
pointer lock before it asks (`@genex-ai/embed-sdk` 0.32.0+; a build on an
|
|
244
|
+
older SDK keeps the cursor captured until the player presses Escape). Treat
|
|
245
|
+
a purchase like a pause: re-enter fullscreen and re-lock the pointer from
|
|
246
|
+
the player's next Resume click, never by yourself.
|
|
247
|
+
- **The Genex app has no purchase surface yet.** There `buy()` resolves
|
|
248
|
+
`failed` without opening anything. That is the app, not an error: when
|
|
249
|
+
`getShell()` from `@genex-ai/embed-sdk` is `"app"`, show the shop as
|
|
250
|
+
browse-only or leave the buy buttons out, and never show a failure message
|
|
251
|
+
for it.
|
|
252
|
+
|
|
237
253
|
### Delivering
|
|
238
254
|
|
|
239
255
|
```ts
|
|
@@ -295,6 +311,8 @@ your own earnings. Use the test grant instead.
|
|
|
295
311
|
- [ ] Every item price is on the 50/100/200/500/1000 grid
|
|
296
312
|
- [ ] Every price renders `priceDisplayUsdCents` beside the coin figure
|
|
297
313
|
- [ ] `buy()` is called synchronously inside a click/tap handler
|
|
314
|
+
- [ ] In the Genex app (`getShell() === "app"`) the buy buttons are hidden or
|
|
315
|
+
the shop is browse-only — never a failure message
|
|
298
316
|
- [ ] `canceled` is silent; only real failures show a message
|
|
299
317
|
- [ ] `deliverPending()` runs on every boot, before the player can act
|
|
300
318
|
- [ ] `consumeEntitlement()` is awaited BEFORE the effect is applied
|
|
@@ -338,3 +356,11 @@ environment. Nothing to fix in the game; say so and build the rest.
|
|
|
338
356
|
**Purchases do nothing in local testing** — local test mode has no wallet and no
|
|
339
357
|
server. `buy()` returns `failed` immediately by design. Test purchases on a
|
|
340
358
|
preview or published build.
|
|
359
|
+
|
|
360
|
+
**`buy()` returns `failed` inside the Genex app** — the app has no purchase
|
|
361
|
+
surface yet, so nothing opens. Not a bug to chase: hide the buy buttons there
|
|
362
|
+
(`getShell() === "app"`).
|
|
363
|
+
|
|
364
|
+
**The game dropped out of fullscreen when the shop's confirmation opened** — by
|
|
365
|
+
design on the game's Genex page: the confirmation is the page's, and the page
|
|
366
|
+
has to be visible to show it. Re-enter from the player's next click.
|
|
@@ -114,9 +114,9 @@ grants the permission.
|
|
|
114
114
|
"click to resume" button.
|
|
115
115
|
5. Always-visible state: reticle while locked; real cursor + "click to
|
|
116
116
|
aim/resume" cue while unlocked. The cue is also the validation hook.
|
|
117
|
-
6. If the lock request is rejected (a
|
|
118
|
-
|
|
119
|
-
"Drag to look" instead. Never a dead game.
|
|
117
|
+
6. If the lock request is rejected (a sandboxed embed without the
|
|
118
|
+
`allow-pointer-lock` sandbox token), the mode falls back to drag-orbit —
|
|
119
|
+
show "Drag to look" instead. Never a dead game.
|
|
120
120
|
7. Gate firing/actions on `followCam.aimState === "locked"` — the click that
|
|
121
121
|
acquires the lock (and any drag in the fallback) must not also shoot.
|
|
122
122
|
8. Do NOT exit the lock between rounds, respawns, or cutscenes — only for real
|
|
@@ -178,8 +178,8 @@ Want a custom HUD? Read `onAimChange: ({ state }) => …` yourself instead of th
|
|
|
178
178
|
eye-height target fed to `moveTo`, `avatar.visible = false`, and
|
|
179
179
|
`controller.setLockForward(true)`.
|
|
180
180
|
- **Touch / rejected lock:** touch never locks (the mode reports `"off"`); a
|
|
181
|
-
|
|
182
|
-
drag-orbit stays as the fallback — never a dead camera.
|
|
181
|
+
sandboxed embed without the `allow-pointer-lock` sandbox token goes
|
|
182
|
+
`"unavailable"` and drag-orbit stays as the fallback — never a dead camera.
|
|
183
183
|
|
|
184
184
|
The bucket rule (mandatory / recommended / never) and the full lock lifecycle
|
|
185
185
|
contract live in `$genex-threejs-camera-direction`.
|