@genex-ai/cli-demo 1.35.0 → 1.35.2-dev.749

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.
@@ -7,10 +7,10 @@ import {
7
7
  isRenderMode,
8
8
  sceneSummary,
9
9
  sheetOf
10
- } from "./chunk-5WBSWMH5.js";
10
+ } from "./chunk-FYHYZYBC.js";
11
11
  import {
12
12
  getCliVersion
13
- } from "./chunk-QI7FIYBY.js";
13
+ } from "./chunk-K4EHXOZK.js";
14
14
 
15
15
  // src/commands/blender-mcp.ts
16
16
  import fs from "fs/promises";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  c,
3
3
  getTemplatesDir
4
- } from "./chunk-QI7FIYBY.js";
4
+ } from "./chunk-K4EHXOZK.js";
5
5
 
6
6
  // src/lib/blender-serve.ts
7
7
  import { spawn } from "child_process";
@@ -10,7 +10,7 @@ import {
10
10
  getGenexEnvPath,
11
11
  normalizeApiOrigin,
12
12
  originKey
13
- } from "./chunk-QI7FIYBY.js";
13
+ } from "./chunk-K4EHXOZK.js";
14
14
 
15
15
  // src/lib/terms.ts
16
16
  import readline from "readline";
@@ -38,7 +38,7 @@ import fs from "fs";
38
38
  import os from "os";
39
39
  import path from "path";
40
40
  import { fileURLToPath } from "url";
41
- var RAW_CHANNEL = "latest";
41
+ var RAW_CHANNEL = "dev";
42
42
  var CLI_CHANNEL = RAW_CHANNEL === "dev" ? "dev" : "latest";
43
43
  var STANDS = {
44
44
  prod: { api: "https://api.genex.games", dashboard: "https://genex.games" },
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  writeSecretFile,
43
43
  writeUserToken,
44
44
  writeWorkspace
45
- } from "./chunk-5WBSWMH5.js";
45
+ } from "./chunk-FYHYZYBC.js";
46
46
  import {
47
47
  CLI_CHANNEL,
48
48
  DEFAULT_API_URL,
@@ -63,7 +63,7 @@ import {
63
63
  normalizeApiOrigin,
64
64
  originKey,
65
65
  resolveAgentTargets
66
- } from "./chunk-QI7FIYBY.js";
66
+ } from "./chunk-K4EHXOZK.js";
67
67
 
68
68
  // src/instrument.ts
69
69
  import * as Sentry from "@sentry/node";
@@ -22772,7 +22772,7 @@ async function runBlender(opts) {
22772
22772
  return 1;
22773
22773
  }
22774
22774
  if (sub === "serve") {
22775
- const { serveLocalBlender } = await import("./blender-serve-66OHC4ML.js");
22775
+ const { serveLocalBlender } = await import("./blender-serve-EH2MOW3U.js");
22776
22776
  const port = Number(process.env.GENEX_BLENDER_PORT ?? 8088);
22777
22777
  log.step(`Starting a local Blender service on port ${port}`);
22778
22778
  log.plain(
@@ -22781,7 +22781,7 @@ async function runBlender(opts) {
22781
22781
  return serveLocalBlender({ port, log });
22782
22782
  }
22783
22783
  if (sub === "mcp") {
22784
- const { runBlenderMcp } = await import("./blender-mcp-6TEAE65U.js");
22784
+ const { runBlenderMcp } = await import("./blender-mcp-HKT7N5BQ.js");
22785
22785
  return runBlenderMcp();
22786
22786
  }
22787
22787
  if (sub === "seat") {
@@ -23564,8 +23564,9 @@ async function reportModels(apiUrl, token, opts, log, toolsOnly = false) {
23564
23564
  );
23565
23565
  for (const m of shown) {
23566
23566
  const plan = m.personalPlan ? ` \xB7 personal plan: ${m.personalPlan}` : "";
23567
+ const kind = m.kind === "typesafe" ? " \xB7 judge (classifier): judge() under a budget, not generate() or bench" : "";
23567
23568
  log.plain(
23568
- ` ${c.cyan(m.id)} ${m.label} \u2014 ${usdPerMillion(m.inputUsdPerMillion)} in / ${usdPerMillion(m.outputUsdPerMillion)} out per million tokens${plan}`
23569
+ ` ${c.cyan(m.id)} ${m.label} \u2014 ${usdPerMillion(m.inputUsdPerMillion)} in / ${usdPerMillion(m.outputUsdPerMillion)} out per million tokens${plan}${kind}`
23569
23570
  );
23570
23571
  }
23571
23572
  if (hiddenCount > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "1.35.0",
3
+ "version": "1.35.2-dev.749",
4
4
  "description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -161,7 +161,7 @@ downloads the game too, binary assets and all:
161
161
 
162
162
  ```bash
163
163
  mkdir my-game && cd my-game
164
- npx @genex-ai/cli-demo@latest link <slug> # slug = the name in the play URL
164
+ npx @genex-ai/cli-demo@dev link <slug> # slug = the name in the play URL
165
165
  npm install
166
166
  ```
167
167
 
@@ -225,7 +225,7 @@ Safe to run any time — genex-owned skills are refreshed to the latest version,
225
225
  and your own files are never touched:
226
226
 
227
227
  ```bash
228
- npx @genex-ai/cli-demo@latest init
228
+ npx @genex-ai/cli-demo@dev init
229
229
  ```
230
230
 
231
231
  Use `--force` only if you intentionally want your own existing files overwritten
@@ -25,6 +25,23 @@ Two modes. Picking the wrong one is the most expensive mistake on this lane:
25
25
  A popup per NPC turn is not a feature, it is an interruption. More than a call
26
26
  or two per session means a grant.
27
27
 
28
+ **Deciding, not writing? Use the judge.** When the game needs to KNOW something
29
+ — is this NPC lying, which of five moods is the player in, how threatening is
30
+ the scene on a scale — and not to produce words, ask the grant's classifier with
31
+ `judge()` instead of asking `generate()` for a label. It answers typed questions
32
+ (`choice`, `score`, `noul`) with calibrated probabilities in about a third of a
33
+ second, writes no prose and cannot drift out of your options. It is grant-only
34
+ and billed as used: each call is a small fraction of a coin and accrues on the
35
+ budget, so a check every turn is affordable where a generate every turn is not.
36
+ `generate()` for words, `judge()` for decisions; a feature often wants both —
37
+ the judge picks the branch, generate writes the line.
38
+
39
+ **Words the player watches appear? Stream them.** Dialogue, narration, an NPC's
40
+ reply in a speech bubble: under a grant, `generateStream()` is the same call as
41
+ `generate({ grantId })`, but the text arrives while the model writes it instead
42
+ of all at once after it. Keep `generate()` for answers the game only uses whole
43
+ (a quest object, a list of decisions) — nobody watches those being written.
44
+
28
45
  ## Step 0 — is the lane live on this stand?
29
46
 
30
47
  ```bash
@@ -67,9 +84,24 @@ identity must be resolved first — `$genex-threejs-embed-auth`.
67
84
  (`billingStatus`, `reservedCoins`, `chargedCoins`, their display-USD twins).
68
85
  - `requestSpendGrant({ models, perCallMaxCoins, perCallEstimateCoins,
69
86
  disclosure: { periodLabel, estimatedCallsPerPeriod, estimatedCoinsPerPeriod },
70
- maxConcurrent?, maxCallsPerMinute?, allowExternal?, idempotencyKey? })` →
87
+ maxConcurrent?, maxCallsPerMinute?, allowExternal?, judge?: { enabled,
88
+ estimatedCallsPerPeriod }, idempotencyKey? })` →
71
89
  `{ status, grantId, … the limits the player approved }`; status is `active` |
72
90
  `canceled` | `expired` | `failed` | `pending`.
91
+ - `judge({ grantId, state, questions, idempotencyKey?, timeoutMs? })` →
92
+ `{ status, generationId, answers, error, billing }` — the grant's classifier,
93
+ answered in the same response. `questions` is 1–64 named `choice`
94
+ (`{ instructions, criteria: { option: description } }`), `score`
95
+ (`{ instructions, criteria: [levels, lowest first] }`) or `noul`
96
+ (`{ instructions }`); each answer has its question's type, and `noul` is a
97
+ probability — pick your own threshold. Needs a budget requested with `judge`.
98
+ - `generateStream({ …the generate() options, grantId, onDelta?(text, soFar),
99
+ onPartialField?(name, partialText), signal? })` → the same result as
100
+ `generate()`. Grant-only. `onDelta` hands over each piece and all the text so
101
+ far; for `outputFormat: 'json'`, `onPartialField` reports the schema's FIRST
102
+ top-level string property as far as it is written — put the spoken line first
103
+ in the schema. `signal` stops the reading, never the call (it still finishes
104
+ and is charged).
73
105
  - `getSpendGrant(grantId)` — live state and counters; the ONE source for an
74
106
  in-game budget readout.
75
107
  - `stopSpendGrant(grantId)` — the game's own stop door. Prospective: no further
@@ -188,6 +220,36 @@ arithmetic into `DESIGN.md` beside the feature. The player sees your estimate
188
220
  attributed to the game, beside the platform's own worst case; an estimate that
189
221
  is transparently low is a grant that dies mid-session.
190
222
 
223
+ **Using the judge too?** Add `judge: { enabled: true, estimatedCallsPerPeriod }`
224
+ to the same request — the checks you really expect per `periodLabel`, from the
225
+ same loop arithmetic. The sheet tells the player the game also uses a fast
226
+ classifier billed as used, with the platform's own worst case beside your count.
227
+ A judge-only feature passes `models: []`. A budget with the judge is coin-funded
228
+ only, and judge calls share the budget's `maxConcurrent` and
229
+ `maxCallsPerMinute`: batch questions into one call (up to 64) rather than one
230
+ call per question.
231
+
232
+ **Streaming a reply under the budget:**
233
+
234
+ ```ts
235
+ const res = await generateStream({
236
+ grantId, modelId, outputFormat: 'json', schema: LINE_SCHEMA, // `line` is its first property
237
+ prompt: npcPrompt(npc, playerLine), estimateCoins: NPC_CALL_PRICE,
238
+ idempotencyKey: `npc:${npc.id}:${turnId}`,
239
+ onPartialField: (_name, soFar) => npc.bubble.show(soFar), // provisional
240
+ });
241
+ applyGeneration(res); // the answer is THIS
242
+ ```
243
+
244
+ The pieces are provisional: a call can still fail after text has shown (the
245
+ model broke off, the JSON missed the schema), so the bubble shows them and
246
+ `applyGeneration` decides what the game keeps — the same one writer as always.
247
+ Price, benchmark and receipt are `generate()`'s: a started stream is charged its
248
+ declared price even when the player walks away mid-sentence. A stream holds one
249
+ of the budget's `maxConcurrent` slots until it has settled. Sometimes the reply
250
+ arrives whole — a busy stand, a retried call, a budget on the player's own plan —
251
+ and `onDelta` then gets the whole text once; the game needs no second path.
252
+
191
253
  **Then keep the burn low, because you wrote the loop:** batch those five NPCs
192
254
  into ONE call returning five decisions, cache a decision until the situation
193
255
  that caused it changes, pick the cheapest model that passes your own check, and
@@ -285,6 +347,8 @@ These are source contracts, not a claim that every stand runs this lane —
285
347
  - [ ] A bench refused as `generation_limit` was answered with `npx genex llm status`, never a retry
286
348
  - [ ] `generate()` / `requestSpendGrant()` is the first statement of a click handler
287
349
  - [ ] A repeated-call feature uses a grant; a one-off uses `generate()`
350
+ - [ ] A decision (a label, a yes/no, a level) is a `judge()` question under the grant, not a `generate()` asked for a word
351
+ - [ ] Dialogue the player watches appear uses `generateStream()` under the grant; the resolved result, not the pieces, goes to `applyGeneration()`
288
352
  - [ ] Disclosure numbers derive from the real loop and are written in `DESIGN.md`
289
353
  - [ ] Calls are batched and cached; nothing fires on an invisible timer
290
354
  - [ ] Every grant-ending code has in-fiction copy and a playable fallback
@@ -316,6 +380,19 @@ cause rather than showing it for both.
316
380
  **`grant_price_unreasonable`** — the declared per-call price is far above what
317
381
  that prompt can cost on that model. Re-benchmark and declare what it prints.
318
382
 
383
+ **`judge_not_enabled`** — the budget was approved without the judge. Request a
384
+ new one with `judge` from the next deliberate click; never auto-renew.
385
+ **`judge_requires_grant`** — the judge's model was sent to `generate()` with no
386
+ budget; the judge is grant-only and is called with `judge()`.
387
+ **`judge_period_limit`** — the checks reached the ceiling the player approved for
388
+ the period (your own `estimatedCallsPerPeriod` at the largest size): wait, and
389
+ if it keeps happening your declared rate is too low — fix the loop or the
390
+ estimate, never retry in a tight loop.
391
+
392
+ **`stream_requires_grant`** — `generateStream()` was called without a
393
+ `grantId`. Streaming is grant-only: a one-time call waits on the player's
394
+ approval popup, so use `generate()` for it.
395
+
319
396
  **`generation_limit`** — three ad-hoc calls are already in flight for this
320
397
  account, or recently stopped with their bill still pending; a pending call
321
398
  stops counting ten minutes after it was dispatched. From the bench: run
@@ -38,7 +38,7 @@ update, so update immediately.)
38
38
  Run exactly the command the nudge printed, from the game project root:
39
39
 
40
40
  ```bash
41
- npm i -D @genex-ai/cli-demo@latest # the genex CLI (a dev dependency)
41
+ npm i -D @genex-ai/cli-demo@dev # the genex CLI (a dev dependency)
42
42
  npm i @genex-ai/embed-sdk@latest # identity/saves SDK (ships inside the game)
43
43
  npm i @genex-ai/multiplayer@latest # multiplayer SDK (only if the game uses it)
44
44
  ```