@genex-ai/embed-sdk 0.17.0 → 0.21.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/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # Genex embed SDK
2
+
3
+ `@genex-ai/embed-sdk` connects a game to player identity, saves, commerce and
4
+ player-funded generation. Initialize it with your game's slug, API URL and
5
+ trusted dashboard origins before using player APIs. See
6
+ [the play-identity contract](../../CLI_INTEGRATION.md#6-durable-game-state--leaderboards--play-identity)
7
+ for identity and storage.
8
+
9
+ ## Generate during play
10
+
11
+ Runtime generation requires a signed-in player in a production game and an
12
+ enabled Genex runtime service. Each player approves their own payment on Genex.
13
+ The game never handles an OpenRouter key or confirms a charge itself.
14
+
15
+ ```ts
16
+ import { generate, getGenerationModels } from '@genex-ai/embed-sdk';
17
+
18
+ // After initEmbed() and player sign-in, populate your model picker from Genex.
19
+ const { models } = await getGenerationModels();
20
+ const model = models[0];
21
+
22
+ generateButton.addEventListener('click', async () => {
23
+ if (!model) return;
24
+ const result = await generate({
25
+ modelId: model.id,
26
+ estimateCoins: 5, // Fixed price for a started attempt, chosen after benchmarking.
27
+ prompt: 'Invent a friendly creature. Return its name and color as JSON.',
28
+ outputFormat: 'json',
29
+ schema: {
30
+ type: 'object',
31
+ properties: { name: { type: 'string' }, color: { type: 'string' } },
32
+ required: ['name', 'color'],
33
+ additionalProperties: false,
34
+ },
35
+ allowExternal: true,
36
+ });
37
+ if (result.status === 'succeeded') {
38
+ // Validate the shape your game expects before rendering it as data.
39
+ showCreature(result.output);
40
+ }
41
+ });
42
+ ```
43
+
44
+ Call `generate()` directly inside the click handler: a standalone game must
45
+ reserve a confirmation popup before asynchronous work. Embedded games ask the
46
+ trusted Genex parent to show its modal. Native WebViews and local-test identity
47
+ return `native_unsupported` and `local_test_unsupported` until they have supported
48
+ confirmation surfaces.
49
+
50
+ `GenerateOptions` requires `estimateCoins` (an integer from 0 to 1,000,000), `modelId`, `prompt`, `outputFormat: 'text' | 'json'`,
51
+ optional bounded `schema`, `allowExternal` (default false), `idempotencyKey` and
52
+ `timeoutMs` (default ten minutes). Reuse an idempotency key only for the same
53
+ operation; store it if you need retries to survive a reload. Only models returned
54
+ by Genex are accepted. This first adapter generates text and JSON, including
55
+ creature descriptions, dialogue, rules and other structured game data.
56
+
57
+ SDK **0.21.0+** returns the execution `status`, optional `generationId`, and
58
+ `output` plus `source` on success. Cancellation is a normal `canceled` result,
59
+ but it does not imply that consumed work was free. `pending` with
60
+ `error: 'wait_timeout'` means the SDK stopped waiting; it does not cancel or charge
61
+ again. Persist `generationId` and resume with `waitForGeneration(id)`, or inspect
62
+ the current server state with `getGeneration(id)`. Results also carry the
63
+ server's `billingStatus: 'pending'|'final'`, `chargedCoins`,
64
+ `chargedDisplayUsdCents`, `reservedCoins` and `reservedDisplayUsdCents` when
65
+ available, including on failures. These USD amounts use the frozen coin value;
66
+ never derive them from an estimate or the current catalog. An execution can end
67
+ while billing remains pending. `waitForGeneration()` waits for execution;
68
+ continue reading `getGeneration(id)` for later billing settlement. Missing
69
+ receipt fields mean unavailable information, not zero cost.
70
+
71
+ The Genex modal shows the fixed attempt price, its USD equivalent, the game and
72
+ the frozen request before approval. New public quotes carry
73
+ `quote.billingPolicy: 'declared-v1'` and `kind: 'fixed'`, with
74
+ `maxCoins = priceCoins = estimateCoins`. Despite its name, `estimateCoins`
75
+ is the developer-declared **fixed price of a started attempt**: declaring 5 coins
76
+ charges 5 even when actual usage would cost 2. Failure, cancellation or reaching
77
+ the budget limit after work starts also charges the full price; a usable result
78
+ is not guaranteed. No model work means zero charge. Zero is accepted only for a
79
+ server-authorized free or personal-only option.
80
+
81
+ The provider hard budget is derived from that price after the frozen platform
82
+ tariff, then capped by operator limits. Output is clipped to remaining funding;
83
+ an input that cannot fit is refused before a call. Unresolved provider expense
84
+ keeps billing pending and the reservation held until verified. The trusted UI
85
+ must acknowledge the quote's exact `billingPolicy` at confirmation; an old UI
86
+ cannot approve new terms. Previously issued `consumed-v1` quotes keep actual-
87
+ usage billing and quotes without a policy keep their original promise.
88
+
89
+ With `allowExternal: true`, Genex can offer the configured personal plan matching
90
+ the selected model alongside coins: Claude for `anthropic/` models, ChatGPT for
91
+ `openai/` models, and neither for other models. This choice appears only in the
92
+ trusted Genex approval modal; never add a "Your Plan" model row in the game.
93
+ Each personal choice costs zero coin and its own plan and usage limits apply.
94
+ New quotes freeze the matching choices; connector metadata stays server-owned.
95
+ Old unapproved quotes are narrowed to the matching provider before confirmation;
96
+ approved personal requests retain their connector for recovery. Once chosen,
97
+ funding never switches providers or falls back to paid generation. Historical
98
+ personal-only offerings never gain free coin execution. Genex neither collects
99
+ nor hosts subscription credentials.
100
+
101
+ `source: 'external'` means user-supplied output, not proof that a particular model
102
+ generated it; `modelProvenance: 'unverified'` makes that explicit. Treat every generated output as data. Never execute returned code
103
+ or use a claimed model/source as authority to mint coins, rewards or items.
104
+
105
+ ## Registered generation workflows
106
+
107
+ SDK **0.21.0+** supports multi-step generation and usage receipts through an
108
+ operator-registered workflow. This requires the game's trusted server executor;
109
+ an ordinary creator API key cannot register a workflow or dispatch its steps.
110
+ `generate()` and `requestWorkflow()` both require the fixed `estimateCoins`
111
+ price and use the declared-attempt policy above.
112
+
113
+ ```ts
114
+ import { getWorkflowOfferings, requestWorkflow, getEmbedToken } from '@genex-ai/embed-sdk';
115
+
116
+ // Load from this game's registered workflow before enabling the model picker.
117
+ const catalog = await getWorkflowOfferings(configuredWorkflowId);
118
+ // Public offerings contain API/model rows, including sponsored free models.
119
+ // Personal plan funding is offered only by the trusted Genex approval modal.
120
+ // Keep the complete selected offering: availability, funding, model and prices.
121
+ renderModelPicker(catalog.offerings);
122
+
123
+ createButton.addEventListener('click', async () => {
124
+ // Save the selected offering, exact input and benchmarked fixed price together.
125
+ const { offeringId, input, estimateCoins, idempotencyKey } = pendingCreation;
126
+ const approval = await requestWorkflow({
127
+ workflowId: catalog.workflowId, offeringId, input, estimateCoins, allowExternal: true, idempotencyKey,
128
+ });
129
+ if (approval.generationId) saveGenerationId(approval.generationId);
130
+ if (approval.status !== 'authorized' || !approval.generationId) return;
131
+ // Application-specific endpoint: the game backend verifies and claims the
132
+ // approved Genex run, binds it to one durable job, and enqueues its executor.
133
+ await enqueueApprovedWorkflow({
134
+ generationId: approval.generationId, embedToken: getEmbedToken(), input,
135
+ });
136
+ });
137
+ ```
138
+
139
+ Call `requestWorkflow()` directly from the click, before an earlier `await`, so
140
+ the SDK can reserve the standalone popup. It returns **approval**, not the
141
+ finished creature: `{ status: 'authorized'|'canceled'|'expired'|'failed'|'pending',
142
+ generationId?, funding?, error? }`, plus available billing receipt fields.
143
+ Waiting for completion before the backend
144
+ claims and enqueues the run would deadlock. Authorized replay also covers an
145
+ already-completed run, allowing the backend to recover the same job or artifact.
146
+ Personal-provider instructions remain open after authorization.
147
+
148
+ `input` is `{ operation: 'create'|'refactor', description, bundleId, creatureId?,
149
+ revisionDigest? }`; refactors require both creature identity and its revision
150
+ digest. Persist this input, offering and idempotency key before submission; reuse
151
+ them only for the same operation after sign-in or reload. Persist the returned
152
+ generation ID. Read with `getGeneration(id)` and wait with
153
+ `waitForGeneration(id)` after enqueueing. A wait timeout does not cancel work.
154
+ Recovering local state never starts a fresh charge without another player click.
155
+ Read a fresh embed token when sending an approved run to the game backend; never
156
+ persist or log the token.
157
+
158
+ The catalog's `estimatedCoins` and `priceCoins` are suggested price aliases,
159
+ and its maximum describes operator limits. Choose the game's fixed attempt price
160
+ after development benchmarks, pass it as `estimateCoins`, and show coin plus USD.
161
+ The player's final approval always uses the server quote. Preserve the selected
162
+ model, bundle, funding, availability and operator-owned tariff; the game cannot
163
+ change provider prices, dispatch costs or settlement.
164
+
165
+ For new public workflow quotes, `maxCoins` is the same fixed amount as
166
+ `priceCoins`, not a promise to bill actual usage. The operator-owned multiplier
167
+ (2 or 3, default 3) determines how much model work that fixed price can fund.
168
+ The whole started attempt is charged once, including failed or canceled work;
169
+ no model work is zero and unknown expense keeps the hold pending. Only the
170
+ selected model is admitted. The trusted executor validates and delivers the
171
+ artifact; the browser cannot settle it.
172
+
173
+ Use `resumeWorkflow(generationId, { timeoutMs? })` directly from a click to
174
+ reopen a saved approval. It reads the original request without creating a quote
175
+ or requiring a new price, including pre-0.21 consumed and legacy approvals.
176
+ Use `requestWorkflow()` with required `estimateCoins` for new operations.
177
+
178
+ Legacy quotes without `billingPolicy` retain their original fixed-price and
179
+ refund terms. Never replace a saved quote's policy with the current catalog's
180
+ policy, infer a missing multiplier, or reprice an approved operation.
181
+
182
+ The designated `google/gemini-3.8-flash` and `z-ai/glm-5.3-flash` offerings
183
+ cost **0 coins**. Personal Claude/ChatGPT funding choices in the Genex approval
184
+ modal also cost **0 coins** and use the player's own account through Genex-owned
185
+ MCP connectors. Claude uses `/player/mcp`; ChatGPT uses `/player/chatgpt/mcp`.
186
+ ChatGPT connector availability depends on account and workspace policy. One
187
+ external request may be active per player across both providers. Follow the
188
+ trusted instructions to fetch and submit each exact stage and attempt; no
189
+ subscription credentials pass through the game and no paid fallback is allowed.
190
+ Submitted results remain user-supplied; the executor must validate them before
191
+ delivery. Both APIs restrict personal funding to the selected model's provider.
192
+
193
+ These workflow endpoints still require signed-in production play, including
194
+ zero-coin offerings. Airena's existing sponsored free guest path is separate;
195
+ do not weaken the workflow's player/session checks to reproduce it.
196
+
197
+ The [runtime API contract](../../CLI_INTEGRATION.md#10-player-funded-runtime-generation)
198
+ describes both APIs, executor authorization, recovery and connector endpoints.
199
+
200
+
201
+ ## Benchmark your own coin costs on the server
202
+
203
+ SDK **0.21.0+** provides `@genex-ai/embed-sdk/development`. Use it only in a
204
+ local Node script or trusted server with your own full creator bearer credential
205
+ and an owned `projectId`. It needs no production play token and cannot choose a
206
+ player wallet. Restricted API keys and personal-only offerings are refused.
207
+ Keep the credential out of game code, browser environment variables and logs;
208
+ the subpath is disabled for browser resolution and rejects browser execution.
209
+
210
+ `maxCoins` is an explicit positive maximum from your own wallet. Development
211
+ uses `consumed-v1`: actual model usage at the frozen normal tariff, including
212
+ failed usage, up to that maximum. Public-sponsored Gemini/GLM models also use
213
+ this normal paid tariff when benchmarking. Actual zero cost is zero coin;
214
+ unknown expense remains held. No personal or paid fallback occurs.
215
+
216
+ ```ts
217
+ import { developmentGenerate } from '@genex-ai/embed-sdk/development';
218
+
219
+ const receipt = await developmentGenerate({
220
+ apiUrl: process.env.GENEX_API_URL!,
221
+ creatorToken: process.env.GENEX_CREATOR_TOKEN!,
222
+ projectId: process.env.GENEX_PROJECT_ID!,
223
+ maxCoins: 5,
224
+ request: {
225
+ modelId: 'your-configured-model-id',
226
+ prompt: 'Invent a friendly creature name.',
227
+ outputFormat: 'text',
228
+ idempotencyKey: 'calibration-sample-001',
229
+ },
230
+ });
231
+ // A 5-coin maximum can return chargedCoins: 2. Use repeated samples to choose
232
+ // the public fixed estimateCoins price; public fixed 5 still charges 5.
233
+ if (receipt.billingStatus === 'final') useForCalibration(receipt.chargedCoins, receipt.usage);
234
+ ```
235
+
236
+ The full receipt includes execution status, output on success, charged/reserved
237
+ coins and USD, plus development-only `usage: { costUsdPicos, costUsd,
238
+ maxProviderUsdPicos, unknownProviderUsdPicos }`. Unknown cost is null rather
239
+ than zero; workflow cost aggregates known actual step costs and reports remaining
240
+ unknown exposure separately. No token counts are inferred.
241
+
242
+ For a registered workflow, call `developmentRequestWorkflow({ apiUrl,
243
+ creatorToken, projectId, workflowId, maxCoins, request: { offeringId, input,
244
+ idempotencyKey? } })`. It returns the accepted, claimed workflow immediately so
245
+ your trusted executor can enqueue it. After enqueueing, call
246
+ `waitForDevelopmentGeneration(config, id, timeoutMs?)`. Both generic and workflow
247
+ benchmarks are recoverable with `getDevelopmentGeneration(config, id)` and
248
+ `cancelDevelopmentGeneration(config, id)`. The wait ends at execution plus final
249
+ billing, or returns the latest server view at timeout; timeout does not cancel
250
+ or claim a refund. Save the operation ID and idempotency key for recovery.