@tinkerlandai/indiana-sdk 0.8.0 → 0.8.3

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 (53) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +67 -20
  3. package/README.zh-CN.md +66 -19
  4. package/dist/ai-usage-BV-FlgWr.d.ts +247 -0
  5. package/dist/{ai-usage-Bigjb8MS.d.ts → api-B3FIE41b.d.ts} +554 -423
  6. package/dist/chunk-2QPETWDX.js +1 -0
  7. package/dist/chunk-ANOOBDW5.js +350 -0
  8. package/dist/chunk-BNU6IG3F.js +1 -0
  9. package/dist/chunk-GRLMT5PR.js +1 -0
  10. package/dist/chunk-LMR2TM3X.js +1 -0
  11. package/dist/chunk-OBYJWIRB.js +1 -0
  12. package/dist/{chunk-G2R56FKU.js → chunk-SHSNIJJM.js} +1 -1
  13. package/dist/{chunk-OVMNCZYC.js → chunk-SOQO2RDZ.js} +1 -1
  14. package/dist/chunk-VVV3JNS3.js +1 -0
  15. package/dist/{chunk-YAFMBXIE.js → chunk-X747SY26.js} +1 -1
  16. package/dist/chunk-Y32NPIYY.js +1 -0
  17. package/dist/chunk-ZT66RIFU.js +10 -0
  18. package/dist/{configuration-sync-VFbY1pCN.d.ts → configuration-sync-DNvKXHk-.d.ts} +25 -25
  19. package/dist/{content-K2HCZVC2.js → content-J3FKZTGT.js} +1 -1
  20. package/dist/{errors-BpCmukJr.d.ts → errors-C8lRYZ0n.d.ts} +49 -2
  21. package/dist/{funnel-runtime-start-DKcTCRrd.d.ts → funnel-local-client-wbz-4gFx.d.ts} +701 -6055
  22. package/dist/funnel-runtime-host-C5P5JTV3.js +1 -0
  23. package/dist/index.d.ts +1831 -1901
  24. package/dist/index.js +1 -1
  25. package/dist/native-shell.js +1 -1
  26. package/dist/openrouter.d.ts +4 -3
  27. package/dist/openrouter.js +1 -1
  28. package/dist/page.d.ts +7 -7
  29. package/dist/page.js +1 -1
  30. package/dist/paywall-3GIRKSHN.js +1 -0
  31. package/dist/popup.d.ts +2 -2
  32. package/dist/popup.js +1 -1
  33. package/dist/react-native.d.ts +97 -4
  34. package/dist/react-native.js +1 -1
  35. package/dist/screen-renderer-5FPYVSPW.js +1 -0
  36. package/dist/server.d.ts +4648 -8
  37. package/dist/server.js +1 -1
  38. package/dist/src-BMJCIMIF.js +1 -0
  39. package/dist/visual.js +1 -1
  40. package/package.json +3 -3
  41. package/dist/chunk-2A6UYUFA.js +0 -1
  42. package/dist/chunk-3BQFJANQ.js +0 -1
  43. package/dist/chunk-3ENVRJE6.js +0 -350
  44. package/dist/chunk-A3N4ZZTM.js +0 -7
  45. package/dist/chunk-AE4CUTUE.js +0 -1
  46. package/dist/chunk-CXYOZSOF.js +0 -1
  47. package/dist/funnel-runtime-host-HNZ47TZW.js +0 -1
  48. package/dist/paywall-PD62B2AY.js +0 -1
  49. package/dist/src-4MRSESVS.js +0 -1
  50. package/dist/{chunk-6X42DWOM.js → chunk-DSPLGTQI.js} +0 -0
  51. package/dist/{chunk-S5TVMQRC.js → chunk-EXCPE2XH.js} +0 -0
  52. package/dist/{customer-page-BrCZxcaQ.d.ts → customer-page-WUQVK_tp.d.ts} +4 -4
  53. package/dist/{screen-BazUOjwM.d.ts → screen-BpAejkn0.d.ts} +267 -267
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  Release facts and artifact evidence: [SDK release ledger](../../docs/sdk-releases.md).
4
4
 
5
+ ## 0.8.3 — 2026-10-26
6
+
7
+ - Refresh sanitized custom HTML fallback content when Product availability changes.
8
+ - Preserve the selected Product across empty availability refreshes, keeping purchase disabled while that Product is unavailable.
9
+ - Synchronize the shared Web and native renderers with the current Product selection rules.
10
+ - Align Server SDK request helpers with the committed messaging contract.
11
+
12
+ ## 0.8.1 — 2026-10-25
13
+
14
+ - Organize SDK implementations by capability while preserving public entry points and interfaces.
15
+ - Consolidate Funnel observed state, pending-command and purchase recovery, and shared request validation without changing credential or execution authority.
16
+ - Share Paywall binding and purchase-observation rules across hosts; keep rendering independent of authenticated clients.
17
+ - Keep the current mutation schema in durable command storage and remove an invalid comparison against the unsupported `evaluate` operation.
18
+
5
19
  ## 0.8.0 — 2026-10-24
6
20
 
7
21
  - Resolve independent Pages, Popups and Banners and render them through the explicit `mountContent` host entry.
package/README.md CHANGED
@@ -1,16 +1,18 @@
1
1
  # @tinkerlandai/indiana-sdk
2
2
 
3
+ These examples use SDK 0.8.1 with `https://api.dev.tinker.cash` and test App credentials (protocol 0.8). Check [environment state](../../docs/development-status.md) before switching. The SDK default remains the production API, so set `baseUrl` explicitly for these examples.
4
+
3
5
  [简体中文](./README.zh-CN.md)
4
6
 
5
- The Indiana business SDK covers four core domains: **Paywall, Funnel, GrowthBook and commercial capabilities**, plus shared identity/session, permission/scope, configuration, transport, event and recovery foundations. The browser/native client and merchant-server entries share contracts and core behavior while retaining separate credentials and allowed operations. Indianapolis is the backend runtime they call; it is not the merchant's server SDK.
7
+ The Indiana business SDK groups its capabilities as **Identity, Commerce, GrowthBook, Analytics, Delivery (Offering, Paywall, Page, Popup and Banner), and Funnel**, over shared protocol, transport, configuration, rendering and recovery foundations. These responsibilities follow the existing L0–L4 architecture; language packages and entry points implement different subsets. The browser/native client and merchant-server entries share contracts and core behavior while retaining separate credentials and allowed operations. Indianapolis is the backend runtime they call; it is not the merchant's server SDK.
6
8
 
7
9
  **IAM source scanning, generation, validation and upload belong to the independent build package `@tinkerlandai/indiana-iam`.** This business SDK retains only the runtime release binding used by GB visual changes. See [SDK boundaries](../../docs/sdk-architecture.md) and the [IAM package](../iam/README.md).
8
10
 
9
11
  ```sh
10
- npm install --save-exact @tinkerlandai/indiana-sdk@0.8.0
12
+ npm install --save-exact @tinkerlandai/indiana-sdk@0.8.1
11
13
  ```
12
14
 
13
- SDK 0.8.0 requires HTTP protocol 0.8. Install a version that matches your runtime; publishing a package does not deploy the server. Publication and accepted environment scope are recorded in the [release ledger](../../docs/sdk-releases.md) and [development status](../../docs/development-status.md).
15
+ SDK 0.8.1 requires HTTP protocol 0.8. Install a version that matches your runtime; publishing a package does not deploy the server. Publication and accepted environment scope are recorded in the [release ledger](../../docs/sdk-releases.md) and [development status](../../docs/development-status.md).
14
16
 
15
17
  The data/session layer handles public JSON, integrity checks and authenticated operations. The presentation host composes that layer with the packaged screen renderer; the renderer receives supplied data and emits action intents. Client display entry points load presentation lazily. The `/server` entry has no screen presentation dependency and uses a scoped Server Key only on your backend. A local SDK guard never replaces runtime authorization or verified payment completion. Console preview supplies simulated data/session results to the same presentation host.
16
18
 
@@ -30,7 +32,7 @@ The SDK sends current protocol and SDK version headers and validates the protoco
30
32
  | ---------------------------------------- | ---------------------------------------------------------------------------------- |
31
33
  | `@tinkerlandai/indiana-sdk` | Browser Customer, Funnel, Feature, and standalone Paywall clients |
32
34
  | `@tinkerlandai/indiana-sdk/server` | Trusted Node.js backend, Customer Sessions, business writes, and Funnel decisions |
33
- | `@tinkerlandai/indiana-sdk/react-native` | Portable Customer core and authenticated-backend usage relay; no DOM presentation |
35
+ | `@tinkerlandai/indiana-sdk/react-native` | Portable Customer core and authenticated-backend usage relay; no DOM presentation |
34
36
  | `@tinkerlandai/indiana-sdk/popup` | Standalone screen rendering, sanitization, and previews |
35
37
  | `@tinkerlandai/indiana-sdk/openrouter` | Optional OpenRouter usage integration; requires its peer dependency |
36
38
  | `@tinkerlandai/indiana-sdk/native-shell` | Static Funnel shell assets for native WebViews |
@@ -39,21 +41,23 @@ The SDK sends current protocol and SDK version headers and validates the protoco
39
41
 
40
42
  The same package supports JavaScript and TypeScript and includes type declarations. Internal `@indiana/contracts` code is bundled; consumers do not install private workspace packages. Swift and Kotlin use their own package ecosystems and are released separately.
41
43
 
44
+ For an independent **Page, Popup or Banner resource**, import `createIndiana` and `mountContent` from the root entry, resolve it with `indiana.resolveContent(...)`, then mount the result. `/page` runs GB URL redirect experiments on an existing merchant page; `/visual` applies GB visual changes to that page. `/popup` exposes lower-level screen rendering and preview primitives. Its name does not select or resolve a Popup resource. The public import paths stay unchanged; see [entry ownership](../../docs/sdk-architecture.md#public-entry-points-and-capability-ownership).
45
+
42
46
  The separate [Python server SDK](../python/README.md) provides synchronous and asynchronous Customer, holdings, entitlement-check and usage clients against the same current HTTP protocol. It is distributed from `https://tinker.cash/sdk/python` (not PyPI) and is not part of this npm package.
43
47
 
44
- The [Go server SDK](../go/README.md) provides the same initial capabilities with typed responses, Context cancellation and goroutine-safe client reuse. It is installed with `go get tinker.cash/sdk/go` through the default Go module proxy and is versioned separately from this npm package.
48
+ The [Go server SDK](../go/README.md) provides the same initial capabilities with typed responses, Context cancellation and goroutine-safe client reuse. It is installed with `go get tinker.cash/sdk/go` through the default Go module proxy and shares the release version with the other business SDKs.
45
49
 
46
50
  ## API origin and test services
47
51
 
48
52
  The canonical repository policy, including production/test domain roles, package tags and test-host configuration, is [SDK API entry and development testing](../../docs/sdk-endpoints-and-testing.md).
49
53
 
50
- Omitting `baseUrl` selects `https://api.tinker.cash`. This applies to Customer/Server, Funnel, Feature, Paywall, page and visual clients. `createConfigurationSync` also defaults its optional `origin` to that address. Swift `IndianaConfiguration(scope: ...)` and Kotlin `IndianaConfiguration(scope = ...)` source uses the same default; native packages are released separately. Explicit `baseURL` / `baseUrl` still overrides it. Funnel and Paywall shells and renderers are packaged locally; their executable code is not downloaded from that API origin.
54
+ Omitting `baseUrl` selects `https://api.tinker.cash`. This applies to Customer/Server, Funnel, Feature, Paywall, page and visual clients. `createConfigurationSync` also defaults its optional `origin` to that address. Swift `IndianaConfiguration(scope: ...)` and Kotlin `IndianaConfiguration(scope = ...)` uses the same default and shared release version. Explicit `baseURL` / `baseUrl` still overrides it. Funnel and Paywall shells and renderers are packaged locally; their executable code is not downloaded from that API origin.
51
55
 
52
56
  Both `beta` and `latest` versions published with this default use the production API address. Tags select package versions, not deployments. Our test apps must explicitly set `baseUrl: "https://api.dev.tinker.cash"` and configuration-sync `origin` to the same address. `dev.tinker.cash` is the test main site/Admin, not the Go API entry. The API hosts forward through Vercel to their respective Go Indianapolis services; routing and business readiness have separate acceptance.
53
57
 
54
58
  For our own testing, always require a test-service address instead of relying on the production default:
55
59
 
56
- The test API origin is `https://api.dev.tinker.cash`; `dev.tinker.cash` is the test site/Admin entry. Set `INDIANA_TEST_BASE_URL=https://api.dev.tinker.cash` and use the native Cloud SQL test App/Key. The legacy active dev Admin has its original database binding, so its old keys do not automatically authenticate against Go. Foundation acceptance covers 21 operations; Funnel, public configuration and provider execution remain closed. Future `beta` and `latest` SDK releases share the production default; the test host supplies the override.
60
+ Set `INDIANA_TEST_BASE_URL=https://api.dev.tinker.cash` and use the native Cloud SQL test App/Key. A production Key or a retained Supabase Key cannot authenticate against that test environment.
57
61
 
58
62
  ```ts
59
63
  const testBaseUrl = process.env.INDIANA_TEST_BASE_URL;
@@ -74,7 +78,7 @@ const configuration = createConfigurationSync({
74
78
 
75
79
  Use the same explicit address for every client in a test run, including Funnel start/controllers and page/visual clients. Environment variables are read by the host, not automatically by the SDK. Sandbox/Live selects business scope inside the chosen service; it does not select a deployment or database. Never retry against another service automatically after an error or unknown command result.
76
80
 
77
- Offline tests inject `fetch` and assert the default address, overrides and failure behavior without reaching the public API. Integration tests consume a fixed local SDK tarball against an explicitly selected, protocol-compatible test runtime and retain its version/hash and request IDs. The published-0.6 test API acceptance covers the 21-operation native foundation, including idempotent business results and permission refusals; broader capabilities, browsers and production require separate acceptance. See the [maintainer testing procedure](../../docs/sdk-selfserve.md#maintainer-api-origin-testing).
81
+ For maintainer checks, use the [SDK testing guide](../../docs/sdk-selfserve.md#maintainer-api-origin-testing). SDK contract fixtures and actual GOP business tests have separate owners.
78
82
 
79
83
  ## Create a Customer Session from a trusted backend
80
84
 
@@ -111,7 +115,7 @@ const { holdings } = await indiana.readHoldings();
111
115
  indiana.reset();
112
116
  ```
113
117
 
114
- Indiana maintains an active Customer session through ordinary SDK requests, using the same token and session row. The validity window is 24 hours with a 30-day absolute limit; there is no merchant renewal timer or additional refresh token. Revocation and issuer expiry still apply, and an expired authorization needs a new verified login. This requires the matching session-lifecycle migration, installed on Indiana-test and accepted on dev with the published SDK 0.5.0; production is unchanged by this task. See [lifecycle and evidence](../../docs/customer-credential-storage-proposal.md#automatic-session-lifecycle-2026-10-21).
118
+ Indiana maintains an active Customer session through ordinary SDK requests, using the same token and session row. The validity window is 24 hours with a 30-day absolute limit; there is no merchant renewal timer or additional refresh token. Revocation and issuer expiry still apply, and an expired authorization needs a new verified login. See [Customer credentials](../../docs/customer-credentials.md).
115
119
 
116
120
  Customer updates are partial patches and do not use a Customer revision. When a Guest signs in to an existing Customer, the response reports `switched: true`. Resources and history keep their original owner; create a new Session/Funnel for the target Customer. Guest financial facts are not merged.
117
121
 
@@ -182,7 +186,7 @@ Sessions retain Sticky assignments in memory. Supply `stickyBucketAssignmentDocs
182
186
 
183
187
  Evaluation is untracked by default. `{ track: true }` invokes configured `onExperimentViewed`/`onFeatureUsage` callbacks once per native assignment/value in that session after successful evaluation and Sticky persistence. Connect callbacks to the existing event tracker only when the result is actually used. Callback errors reach `onError`; event delivery/retries remain the tracker's responsibility, and evaluation does not manufacture durable exposure or usage receipts.
184
188
 
185
- Mixed graphs, resource/Bundle outputs and expired configurations fail explicitly. There is no automatic remote execution. `decide()` retains its server-side idempotency, committed receipts and financial checks; local output never grants a Coupon, purchase, entitlement or campaign frequency promise. This source feature requires the matching HTTP implementation and has not been published by this change.
189
+ Mixed graphs, resource/Bundle outputs and expired configurations fail explicitly. There is no automatic remote execution. `decide()` retains its server-side idempotency, committed receipts and financial checks; local output never grants a Coupon, purchase, entitlement or campaign frequency promise.
186
190
 
187
191
  ### Standalone Feature delivery
188
192
 
@@ -209,7 +213,7 @@ unsubscribe();
209
213
  flags.destroy();
210
214
  ```
211
215
 
212
- Since protocol 0.4, the selected environment's integration settings control behavior events, experiment exposures and Feature usage independently. New environments enable only exposure collection; existing environments retain all three. `flush()` includes a `skipped` count for intentionally disabled collection. The database rechecks every batch, including direct SDK/server and Funnel ingestion. Running experiment/Bandit dependencies prevent incompatible collection changes. These controls do not delete history or affect commercial usage accounting.
216
+ The selected environment's integration settings control behavior events, experiment exposures and Feature usage independently. New environments enable only exposure collection; existing environments retain all three. `flush()` includes a `skipped` count for intentionally disabled collection. The database rechecks every batch, including direct SDK/server and Funnel ingestion. Running experiment/Bandit dependencies prevent incompatible collection changes. These controls do not delete history or affect commercial usage accounting.
213
217
 
214
218
  Flags default to local evaluation; Owner/Admin can switch each flag to server evaluation. Server-evaluated flags deliver only keys and results, using the caller's fallback until the first result arrives. Attribute changes immediately discard old results. List hashes are not a secrecy boundary. Local Funnels cannot depend on server-evaluated flags; mode switches also check active historical Runs. `identify` / `reset` release the previous identity's Feature instances and event queue.
215
219
 
@@ -228,7 +232,7 @@ This beta does not certify complete commercial or production operation. Remainin
228
232
  - Complete delivery for `claimRewardProduct`.
229
233
  - Real Apple/Google store acceptance. Independent purchase/restore contracts and partial services do not certify store acceptance. `createNativeReceiptClient().recover()` uses `subscriptions/native/restore`.
230
234
 
231
- Live commercial execution remains subject to explicit server gates. Other Product/Price, Entity, subscription-addition, and quantity combinations depend on the matching server's actual capabilities.
235
+ Implemented commercial operations use the original credential, permission, scope and configured-provider checks in both Sandbox and Live. An unsupported operation returns 501; a provider configuration or business precondition is not a migration allowlist.
232
236
 
233
237
  ## AI usage
234
238
 
@@ -240,7 +244,7 @@ Publishing to npm does not deploy Web services, apply database migrations, or re
240
244
 
241
245
  See the [SDK release ledger](https://github.com/tinkerlandai/Indiana/blob/main/docs/sdk-releases.md) for versions and immutable publication evidence, and the [developer documentation](https://tinker.cash/docs?lang=en) for integration guides.
242
246
 
243
- ## Shared public configuration (Indiana-test candidate)
247
+ ## Shared public configuration
244
248
 
245
249
  Use one manager per trusted origin and exact App/environment for Features, canonical Funnel files and enabled page experiments. Use a matching runtime and an explicitly reviewed public configuration publication.
246
250
 
@@ -278,7 +282,7 @@ Optional browser persistence defaults to localStorage and contains only public b
278
282
 
279
283
  ## Local Funnel execution
280
284
 
281
- Local and Remote share the authoritative Indiana Run, command/action receipts, business services and Queue recovery. Only pure Condition, Feature/Experiment and Feature-based selections change calculation host. Local uses the existing public GB evaluator; Remote calculates inside Indiana and never requires public configuration. The independent pure `evaluateFunnelNode` and advisory `.local()` helpers still perform no business effects.
285
+ Local and Remote share the authoritative Indiana Run, command/action receipts, business services and Queue recovery. A Local host predicts pure Condition/Feature/Experiment paths for display, then sends a `commit_segment` advance signal. GOP recomputes the accepted path from the Run inputs and approved public GB configuration. Remote evaluates on GOP without client configuration. Independent `evaluateFunnelNode` and advisory `.local()` helpers have no business effects.
282
286
 
283
287
  ```ts
284
288
  import {
@@ -309,21 +313,21 @@ started.controller.reset();
309
313
 
310
314
  Merchant backends use the same adapter from `/server`, with their original Server Key as `credential` and the Indiana Customer UUID as `customerId`. Each Run request proves the original live issuing Key in a server-only header; it is never saved in a pending command or exposed to the renderer. The ordinary `createFunnelRunController` accepts `serverKey` only on a merchant backend. Customer clients continue proving their original Customer identity with `identify(customerToken)`; they cannot override it.
311
315
 
312
- The SDK submits each calculation against the exact Run/Version, node, visit/revision, receipt and evaluation-policy revision. Its fixed request ID and frozen result survive a lost reply. Concurrent commands use the existing server fences. A Local result only selects a published exit or declared value; it cannot supply an arbitrary business invocation. Customer Coupon claims still require `offers:resolve` and explicit `selfClaimEnabled`; merchant grants retain `offers:grant`. The existing independent Coupon issuer owns eligibility, inventory, possession and expiry. Standalone `claimCoupon` and `grantOffer` remain available without a Funnel.
316
+ A Flow `commit_segment` carries only its retained request ID, parked node/visit/revision, policy revision and configuration generation. Caller steps, results and boundaries are not accepted. The SDK prediction yields to the server response; response-loss recovery preserves the original command identity. Customer Coupon claims still require `offers:resolve` and explicit `selfClaimEnabled`; merchant grants retain `offers:grant`. The independent Coupon issuer owns eligibility, inventory, possession and expiry.
313
317
 
314
- After a result is accepted, actions, duration/event waits, retries and recovery use the same backend driver, `indiana-funnel-advancement` Queue and Cron repair as Remote. A disconnected host does not cancel an accepted Coupon obligation. A Local condition whose result has not been submitted parks at its receipt and releases the worker; it cannot continue until a Local host returns. The bounded SDK loop settles up to 32 calculation boundaries per call; longer chains continue on a subsequent read. This source cut does not certify latency or sustained capacity.
318
+ After an advance signal is accepted, actions, waits, retries and recovery use the same GOP driver and Cloud Tasks / Cloud Scheduler as Remote. A disconnected host does not cancel accepted work. Before the required signal arrives, the Run remains parked. The bounded SDK loop settles up to 32 calculation boundaries per call; longer chains continue on a later read.
315
319
 
316
320
  ### Shared UI host
317
321
 
318
- Screens, live Customer/Coupon data, shown acknowledgements, Paywall and external-task handling use the same Run controller and UI host. Canonical node/command history uses the common recording limits. The optional `report_progress` upload contract and `funnel_client_events` storage are withdrawn from 0.6; recovery uses the existing authoritative Run, command and business receipts. There is no second host business state machine, `invoke` adapter, Local checkpoint owner, `browserFunnelStorage` or Local presentation controller.
322
+ Screens, live Customer/Coupon data, shown acknowledgements, Paywall and external-task handling use the same Run controller and UI host. Canonical node/command history uses the common recording limits. Recovery uses authoritative Run, command and business receipts, without an observation-upload API or a second storage authority. There is no second host business state machine, `invoke` adapter, Local checkpoint owner, `browserFunnelStorage` or Local presentation controller.
319
323
 
320
- Old host checkpoints must be reconciled or drained with their original business identities before switching clients. Do not turn an old execution into a fresh start/request ID. The SDK does not import old checkpoints. On existing test targets the retirement candidate moves the original observation relation into a private read-only archive, preserves every row and continues refusing scoped legacy execution IDs; already accepted managed Runs retain exact replay. It accepts no new observations and creates no new business model. The production packet adds no observation or archive table. A mode change does not rewrite a Run's original policy or accepted commands. New results against a stale policy fail closed; close/reconcile that original Run before starting another intent.
324
+ Recovery uses the original Run Token and command identities. Do not turn an unknown business outcome into a new start/request ID. The SDK does not import legacy checkpoints. A mode change never rewrites a Run's accepted policy or commands; resolve that original Run before starting another intent.
321
325
 
322
- The native renderer shell uses the same pure JavaScript calculation adapter and native Run-command bridge; credentials remain in the native session. A headless Swift/Kotlin session exposes the pending calculation and does not silently calculate it remotely. Wire schemas and the renderer shell are generated together. Installing the package does not migrate a database or deploy a runtime. Browser, provider, capacity and native-store acceptance remain separate. See the unification receipt.
326
+ The native renderer shell uses the same pure JavaScript calculation adapter and native Run-command bridge; credentials remain in the native session. A headless Swift/Kotlin session exposes the pending calculation and does not silently calculate it remotely. Wire schemas and the renderer shell are generated together. Installing the package does not migrate a database or deploy a runtime. Browser, provider, capacity and native-store acceptance remain separate.
323
327
 
324
328
  ## Funnel-scoped resource requests
325
329
 
326
- Formal Local Runs submit calculations to Indiana; the canonical executor supplies the original Run/node/visit/receipt when it issues a Coupon or performs commerce. An application consuming a pure Funnel result can bind its Customer client explicitly:
330
+ Formal Local Runs submit advance signals to Indiana; the canonical executor supplies the original Run/node/visit/receipt when it issues a Coupon or performs commerce. An application consuming a pure Funnel result can bind its Customer client explicitly:
327
331
 
328
332
  ```ts
329
333
  const resources = indiana.forFunnel({ funnelId, versionId });
@@ -331,3 +335,46 @@ await resources.claimCoupon(couponId, savedRequestId);
331
335
  ```
332
336
 
333
337
  The scoped client automatically carries that fixed Funnel context into Coupon claims, Offering/Paywall resolution and attach quotes. Indiana recomputes only the requested Funnel using trusted facts; caller-reported branches are not permission. Independent `indiana.claimCoupon(...)` remains available under Coupon-owned self-claim rules. Reuse the same request ID and input after an unknown response; accepted commands retain their original result after a later Funnel pause.
338
+
339
+ ## Standalone Pages, Popups and Banners
340
+
341
+ Publish a Banner with no required inputs and a declared `continue` action. Issue a Customer Token with `content:read`, then explicitly resolve and mount it. Use the matching `kind` for Page or Popup.
342
+
343
+ ```ts
344
+ import {
345
+ createIndiana,
346
+ mountContent,
347
+ type ContentActionIntent,
348
+ } from "@tinkerlandai/indiana-sdk";
349
+
350
+ export async function showBanner(
351
+ container: HTMLElement,
352
+ customerToken: string,
353
+ contentId: string,
354
+ onContinue: (intent: ContentActionIntent) => Promise<void>,
355
+ ) {
356
+ const indiana = createIndiana({
357
+ baseUrl: "https://api.dev.tinker.cash",
358
+ scope: { projectId: "<TEST_APP_UUID>", environment: "sandbox" },
359
+ });
360
+ indiana.identify(customerToken);
361
+ const { content } = await indiana.resolveContent({
362
+ kind: "banner",
363
+ selector: { type: "resource", id: contentId },
364
+ });
365
+ const host = await mountContent(container, {
366
+ content,
367
+ actions: { continue: onContinue },
368
+ onError: console.error,
369
+ });
370
+ return {
371
+ retryAction: host.retryAction,
372
+ destroy() {
373
+ host.destroy();
374
+ indiana.reset();
375
+ },
376
+ };
377
+ }
378
+ ```
379
+
380
+ Provide every declared action handler. `onContinue` uses `intent.requestId` as its backend idempotency key; `retryAction()` reuses that intent after a failure. Call `destroy()` on unmount, sign-out or account change. The host renders content and emits intents; your backend still authorizes and executes business actions.
package/README.zh-CN.md CHANGED
@@ -1,16 +1,18 @@
1
1
  [English](./README.md)
2
2
 
3
+ 本页使用 SDK 0.8.1、`https://api.dev.tinker.cash` 与测试 App 凭据(协议 0.8)。切换前核对[环境状态](../../docs/development-status.md)。SDK 默认仍指向生产 API,本页示例必须显式设置 `baseUrl`。
4
+
3
5
  # @tinkerlandai/indiana-sdk
4
6
 
5
- 业务 SDK 的核心范围是 **Paywall、Funnel、GrowthBook 和商业能力四个域,加身份/会话、权限/作用域、配置、传输、事件与恢复等共用基础能力**。客户端与商户服务端共用契约和核心行为,凭据及可执行操作分开。Indianapolis 是它们调用的 SDK 运行服务,不是商户安装的服务端 SDK。
7
+ 业务 SDK 按职责分为 **身份、商业、GrowthBook、Analytics、内容交付(Offering、Paywall、Page、Popup、Banner)和 Funnel 六组能力**,共用协议、传输、配置、渲染与恢复等基础设施。分类沿用系统的 L0–L4 归属,各语言包和入口支持的子集不同。客户端与商户服务端共用契约和核心行为,凭据及可执行操作分开。Indianapolis 是它们调用的 SDK 运行服务,不是商户安装的服务端 SDK。
6
8
 
7
9
  **IAM 的源码扫描、生成、校验和上传属于独立构建包 `@tinkerlandai/indiana-iam`。** 普通业务 SDK 只保留 GB visual 应用页面变更所需的运行时 release 绑定。完整定义见 [SDK 边界](../../docs/sdk-architecture.md) 与 [IAM 构建包](../iam/README.md)。
8
10
 
9
11
  ```sh
10
- npm install --save-exact @tinkerlandai/indiana-sdk@0.8.0
12
+ npm install --save-exact @tinkerlandai/indiana-sdk@0.8.1
11
13
  ```
12
14
 
13
- SDK 0.8.0 要求 HTTP 协议 0.8。安装版本须与运行服务匹配;发布包不会部署服务端。实际发布、标签与环境验收范围见 [SDK 发布总账](../../docs/sdk-releases.md) 和 [开发状态](../../docs/development-status.md)。
15
+ SDK 0.8.1 要求 HTTP 协议 0.8。安装版本须与运行服务匹配;发布包不会部署服务端。实际发布、标签与环境验收范围见 [SDK 发布总账](../../docs/sdk-releases.md) 和 [开发状态](../../docs/development-status.md)。
14
16
 
15
17
  数据/会话层负责公开 JSON、完整性校验和鉴权操作;展示宿主将会话接口接入打包的屏幕渲染器,渲染器消费已提供的数据并返回动作意图。客户端按需加载展示;`/server` 入口使用商户后端的受限 Server Key,不加载屏幕展示。SDK 本地检查不替代运行时授权或付款核实。后台 Preview 为同一个展示宿主提供模拟会话和数据。
16
18
 
@@ -20,13 +22,13 @@ React Native 使用独立包 `@tinkerlandai/indiana-react-native`,原 `@tinker
20
22
 
21
23
  ## Funnel Local / Remote 统一执行
22
24
 
23
- Local / Remote 只区分纯条件、Feature / 实验和 Feature 选择在哪里计算。正式流程都使用 Indiana 的同一个 Run、command/action 回执、业务服务、幂等和 Queue 恢复。Local SDK 计算结果并提交固定节点命令,后端领取并执行发券、等待、重试;Remote 在 Indiana 计算。Remote 不依赖公共 CDN 初始化。
25
+ Local / Remote 共用 Indiana Run、command/action 回执、业务服务、幂等和恢复。Local SDK 只预测纯条件、Feature/实验路径用于展示,再提交 `commit_segment` 推进信号;GOP 根据 Run 输入和批准的公共 GB 配置重算并接受路径。Remote 在 GOP 计算,不依赖客户端公共配置。
24
26
 
25
27
  `createLocalFunnelClient().start({ requestId, locale, parameters })` 返回 `runToken` 和同一个 `controller`,传给 `showFunnelRun` 即可。原 Server Key 的商户服务端另外指定原 Indiana Customer UUID;浏览器使用 Customer Token,不能改写 Customer。恢复必须使用安全保存的原 Run token,不能把旧 Local checkpoint 或未知发券结果转换为一个新请求身份。
26
28
 
27
- 条件结果尚未提交时,Run 停车并释放 Worker,仍需 Local 宿主返回;条件结果已受理后,后台业务操作可以通过原来的 Queue / Cron 脱离宿主恢复。Local SDK 不再直接调用发券服务来推进图,也没有第二套宿主业务执行器、定时器和展示状态。独立 Coupon API 保留自己的自领政策、权限、资格、库存、幂等和原有效期,不要求创建 Funnel。
29
+ 推进信号尚未提交时,Run 停车并释放 Worker,仍需 Local 宿主返回;信号受理后,后台业务操作通过 GOP 的 Cloud Tasks/Cloud Scheduler 恢复。Local SDK 不直接发券,也没有第二套业务执行器。独立 Coupon API 保留自己的自领政策、权限、资格、库存、幂等和有效期,不要求创建 Funnel。
28
30
 
29
- 步骤记录共用后端限制;历史 `report_progress` 仍是调用方观察,不能当成发券证明。原生渲染壳复用同一个 JS 纯求值器及原生 Run bridge;无界面 Swift/Kotlin 会明确返回待本地求值,不自动切换 Remote。详细使用和迁移边界见[英文说明](README.md#local-funnel-execution)及统一回执。安装本包不会自动迁移数据库或部署服务;浏览器、支付渠道、容量和原生商店验收分别记录。
31
+ Flow `commit_segment` 只包含固定 requestId、停车位置 nodeId/visit/revision、policyRevision 和 generation,不接受调用方步骤、计算结果或边界。SDK 的展示预测服从服务器受理结果;回包丢失使用原命令身份恢复。原生渲染壳复用同一纯求值器与 Run bridge;无界面 Swift/Kotlin 返回待本地推进状态,不自动改成 Remote。详见[Local 执行](README.md#local-funnel-execution)。
30
32
 
31
33
  ## 版本兼容与支持
32
34
 
@@ -40,32 +42,34 @@ SDK 发送当前协议及 SDK 版本请求头,并校验成功响应的协议
40
42
  | ---------------------------------------- | ----------------------------------------------------------- |
41
43
  | `@tinkerlandai/indiana-sdk` | 浏览器 Customer、Funnel、Feature 与独立 Paywall 客户端 |
42
44
  | `@tinkerlandai/indiana-sdk/server` | 可信 Node.js 后端、Customer Session、业务写入和 Funnel 决策 |
43
- | `@tinkerlandai/indiana-sdk/react-native` | Customer 核心和经认证业务后端转发的用量上报,不含 DOM 展示 |
45
+ | `@tinkerlandai/indiana-sdk/react-native` | Customer 核心和经认证业务后端转发的用量上报,不含 DOM 展示 |
44
46
  | `@tinkerlandai/indiana-sdk/popup` | 独立屏幕渲染、清洗和预览 |
45
47
  | `@tinkerlandai/indiana-sdk/openrouter` | 可选 OpenRouter 用量集成,需要安装对应 peer dependency |
46
48
  | `@tinkerlandai/indiana-sdk/native-shell` | 原生 WebView 的静态 Funnel shell 资源;不代表商店购买验收 |
47
49
  | `@tinkerlandai/indiana-sdk/page` | 匿名商户页面的网址实验;使用公开的 App environment ID |
48
50
  | `@tinkerlandai/indiana-sdk/visual` | 匿名商户页面的视觉实验及曝光采集;使用公开的环境 ID |
49
51
 
50
- JavaScript 与 TypeScript 共用本包,声明文件随包提供。内部 `@indiana/contracts` 在构建时内联,接入方无需安装私有包。Swift 和 Kotlin 使用各自的包管理生态,不在本次 npm 发布范围内。
52
+ JavaScript 与 TypeScript 共用本包,声明文件随包提供。内部 `@indiana/contracts` 在构建时内联,接入方无需安装私有包。Swift 和 Kotlin 使用各自的包管理生态与独立产物。
53
+
54
+ 展示独立的 **Page、Popup 或 Banner 资源**,从主入口导入 `createIndiana` 和 `mountContent`,先用 `indiana.resolveContent(...)` 解析,再挂载结果。`/page` 用于商户现有页面的 GB 网址跳转实验,`/visual` 用于该页面的 GB 视觉修改;`/popup` 提供底层屏幕渲染与预览工具,不负责选择或解析 Popup 资源。现有公开导入路径保持不变,详见[入口职责](../../docs/sdk-architecture.md#public-entry-points-and-capability-ownership)。
51
55
 
52
56
  独立的 [Python 服务端 SDK](../python/README.md) 使用同一当前 HTTP 协议,提供同步、异步 Customer、持仓/权益检查和用量上报。它从 `https://tinker.cash/sdk/python` 分发(不在 PyPI),不属于这个 npm 包。
53
57
 
54
- [Go 服务端 SDK](../go/README.md) 提供同样的首版能力,使用类型化响应、Context 取消和 goroutine 并发复用客户端。通过 Go 默认模块代理以 `go get tinker.cash/sdk/go` 安装,版本与 npm 包分开。
58
+ [Go 服务端 SDK](../go/README.md) 提供同样的首版能力,使用类型化响应、Context 取消和 goroutine 并发复用客户端。通过 Go 默认模块代理以 `go get tinker.cash/sdk/go` 安装,与其他业务 SDK 共用发行版本。
55
59
 
56
60
  ## API 默认地址与自测
57
61
 
58
62
  默认地址、四个域名、版本标签和测试配置的持续规范见 [SDK API 入口与开发测试规范](../../docs/sdk-endpoints-and-testing.md)。
59
63
 
60
- 省略 `baseUrl` 时默认请求 `https://api.tinker.cash`;Customer/Server、Funnel、Feature、Paywall、页面与视觉实验客户端共用此入口。公共配置同步的 `origin` 也可省略并使用同一默认值。Swift `IndianaConfiguration(scope: ...)`、Kotlin `IndianaConfiguration(scope = ...)` 源码同步默认地址,原生包另行发布;仍可显式覆盖 `baseURL`/`baseUrl`。Funnel/Paywall 的壳和渲染器随 SDK 本地打包,不从 API origin 下载可执行代码。
64
+ 省略 `baseUrl` 时默认请求 `https://api.tinker.cash`;Customer/Server、Funnel、Feature、Paywall、页面与视觉实验客户端共用此入口。公共配置同步的 `origin` 也可省略并使用同一默认值。Swift `IndianaConfiguration(scope: ...)`、Kotlin `IndianaConfiguration(scope = ...)` 采用同一默认地址与统一发行版本;仍可显式覆盖 `baseURL`/`baseUrl`。Funnel/Paywall 的壳和渲染器随 SDK 本地打包,不从 API origin 下载可执行代码。
61
65
 
62
- 包含此默认值的版本无论标记为 `beta` 或 `latest`,都使用生产 API 地址。标签选择包版本,不选择服务环境。已发布 npm 0.5.0 的示例仍需显式传入与其协议匹配的服务地址,不能将此默认值套用到旧包。两套 API 入口经 Vercel 转发到各自 Go Indianapolis,入口和业务就绪分别验收。
66
+ 包含此默认值的版本无论标记为 `beta` 或 `latest`,都使用生产 API 地址。标签选择包版本,不选择服务环境。两套 API 入口经 Vercel 转发到各自 Go Indianapolis,入口和业务就绪分别验收。
63
67
 
64
68
  我们自己测试时,宿主必须显式要求 `INDIANA_TEST_BASE_URL`,缺失就报错,不回落生产默认值。同一轮 Server、Customer、Funnel、页面/视觉客户端的 `baseUrl` 和配置同步的 `origin` 使用同一个测试地址;环境变量由宿主读取,SDK 不自动读取。`environment: "sandbox"` 只选择该服务内的业务环境,不选择部署或数据库。
65
69
 
66
- 测试 API 地址为 `https://api.dev.tinker.cash`,测试主站/Admin 使用 `dev.tinker.cash`。测试应用配置 `INDIANA_TEST_BASE_URL=https://api.dev.tinker.cash`,业务验收仍须等待测试服务就绪。默认地址保持生产 `https://api.tinker.cash`,由测试应用显式覆盖。
70
+ 测试 API 地址为 `https://api.dev.tinker.cash`,测试主站/Admin 使用 `dev.tinker.cash`。测试应用配置 `INDIANA_TEST_BASE_URL=https://api.dev.tinker.cash`,使用测试环境自己的 App/Key,并核验权限与集成配置。默认地址保持生产 `https://api.tinker.cash`,由测试应用显式覆盖。
67
71
 
68
- 离线测试注入 `fetch`,验证默认地址、覆盖、renderer 地址和网络失败不换服务;集成测试用固定本地 tarball 连接明确选定、协议匹配的测试 runtime,记录包哈希、服务版本和 requestId。公开 0.6.0 的原生 GCP 测试已覆盖 21 项基础能力、原请求重放与权限拒绝;完整能力、浏览器和生产另行验收,不自动切回 JS。当前旧 dev Admin 仍有原数据库绑定,不能将其生成的 Key 当作 Go 原生测试 Key。具体命令见[维护者测试流程](../../docs/sdk-selfserve.md#maintainer-api-origin-testing)。
72
+ 维护者验证入口见[测试流程](../../docs/sdk-selfserve.md#maintainer-api-origin-testing)。SDK 契约夹具与真实 GOP 业务测试分别负责,不以旧版本通过数描述当前能力。
69
73
 
70
74
  ## 从可信后端创建 Customer Session
71
75
 
@@ -102,7 +106,7 @@ const { holdings } = await indiana.readHoldings();
102
106
  indiana.reset();
103
107
  ```
104
108
 
105
- Indiana 在普通 SDK 请求中自动延续同一 Customer 会话和 Token,默认有效窗口 24 小时、最长 30 天;无需回商户后端的续期定时器,也不增加 Refresh Token。撤销、Key 到期继续生效,授权过期后才需要商户重新认证。此行为需要配套会话迁移;Indiana-test 已安装,并在 dev 使用已发布 SDK 0.5.0 实测,生产本次未改动。见[生命周期与证据](../../docs/customer-credential-storage-proposal.md#automatic-session-lifecycle-2026-10-21)。
109
+ Indiana 在普通 SDK 请求中延续同一 Customer 会话和 Token,默认有效窗口 24 小时、最长 30 天;无需商户续期定时器,也不增加 Refresh Token。撤销和 Key 到期继续生效,授权过期后需要重新认证。见[Customer 凭据](../../docs/customer-credentials.md)。
106
110
 
107
111
  Customer 使用原始业务字段;资料更新是部分 patch,没有 Customer revision。Guest 登录到已有 Customer 时返回 `switched: true`,资源和历史保留原归属,接入方应为目标 Customer 创建新 Session/Funnel;不合并旧 Guest 的财务事实。
108
112
 
@@ -217,7 +221,7 @@ Stripe 环境的 `autumn` 模式由 Indiana 执行;`record_only` 返回交给
217
221
  - `claimRewardProduct` 的完整交付。
218
222
  - Apple/Google 真实商店验收。当前独立购买/恢复的协议及部分服务实现不代表商店已验收;`createNativeReceiptClient().recover()` 使用 `subscriptions/native/restore`,不再发送旧 Funnel 原生恢复命令。
219
223
 
220
- Live 商业执行仍有显式服务端门禁。其他 Product/Price、Entity、订阅追加和数量组合,以匹配服务端的实际能力为准;不自动回退到旧实现。
224
+ 已实现商业能力在 Sandbox 与 Live 均沿用原凭据、权限、作用域和真实 Provider 配置检查。未实现操作返回 501;缺少配置或业务前置条件不等于迁移期白名单。
221
225
 
222
226
  ## AI 用量
223
227
 
@@ -229,11 +233,11 @@ npm 发布不部署 Web 服务、不应用数据库迁移,也不发布 Swift
229
233
 
230
234
  版本、冻结产物哈希和 registry 回读见 [发布总账](../../docs/sdk-releases.md);当前架构与实现边界见 [开发状态](../../docs/development-status.md)。
231
235
 
232
- ## 共享公共配置(Indiana-test 候选)
236
+ ## 共享公共配置
233
237
 
234
238
  源码新增 `createConfigurationSync({ origin, scope })`;`scope` 必须明确包含 `projectId`、`environment` 和 `environmentId`。将同一个实例作为 `configuration` 传给 `createIndiana`、Funnel start/Run controller 和 `createPageExperiments`,共用清单、文件缓存、并发请求和更新通知。`/server` 导出的 `createSynchronizedFunnelLocalEvaluator(configuration, funnelId)` 使用同一份规范 Funnel 文件与 GB 代次进行纯判断,保留仅可信服务端执行的边界。
235
239
 
236
- 此能力需要匹配的 Indiana-test 网关及已审核发布的配置,自 npm 0.6.0 起包含在公开包中。公共文件不需要 Customer 凭据;业务操作沿用现有凭据和服务端资格检查。完整调用示例见[英文说明](README.md#shared-public-configuration-indiana-test-candidate)。
240
+ 公共配置由匹配运行端发布。公共文件不需要 Customer 凭据;业务操作沿用原凭据和服务端资格检查。完整调用见[英文说明](README.md#shared-public-configuration)。
237
241
 
238
242
  初始化总等待不超过 5 秒;活跃消费者共用 12–15 秒轮询,并在回前台/联网后刷新。Local 控制状态自源站验证起最多有效 60 秒,旧 304 或失败不能续期。暂停/过期后停止新本地判断及客户屏幕;冷启动离线时,仅凭持久缓存不能恢复 active 权限。Indiana Run 的命令与观察直接读取服务端状态,独立于 CDN 控制有效期。
239
243
 
@@ -243,12 +247,55 @@ npm 发布不部署 Web 服务、不应用数据库迁移,也不发布 Swift
243
247
 
244
248
  `showFunnelRun(container, { client: started.controller })` 在 Local / Remote 都接收同一个服务器 Run controller。屏幕、Customer / Product / Coupon 数据、shown 回执、Paywall、外部任务和未知结果重试共用后端路径。宿主销毁只卸载 UI;停止一个 Run 需要显式提交 Close。Console 私有预览仍是无真实业务效果的模拟。
245
249
 
246
- 步骤回执和执行上限共用同一份 Contract 与服务器记录;0.6 撤出 `report_progress` 上传接口和 `funnel_client_events`;恢复使用原有 Run、命令及业务回执。测试候选将现有旧表移入私有只读归档,保留全部数据及旧执行 ID 防重;已接受 Run 仍按原回执恢复,不再接收新观察。生产包不新增观察或归档表。Local Funnel 不再用本地 checkpoint、`flow.signal`、`local.resolveScreen`、`local.openPaywall` 或直调 Coupon API 来持有流程业务义务。独立 `claimCoupon` / `grantOffer` 仍可单独使用,继续由 Coupon 校验资格、额度、权限和有效期。
250
+ 步骤回执和执行上限共用同一 Contract 与服务器记录;恢复使用原 Run、命令及业务回执。Local 不使用本地 checkpoint 或直接 Coupon 调用持有流程业务义务。独立 `claimCoupon`/`grantOffer` 继续由 Coupon 校验资格、额度、权限和有效期。
247
251
 
248
- 完整调用和旧执行身份迁移要求见[统一执行说明](README.md#local-funnel-execution)。本轮未部署、应用远端迁移或发布 SDK。
252
+ 完整调用和恢复要求见[统一执行说明](README.md#local-funnel-execution)。
249
253
 
250
254
  ## Funnel 资源上下文
251
255
 
252
256
  正式 Local Run 由服务端执行原业务动作,自动携带原 Run、node、visit 和 receipt。消费纯 Funnel 结果时,使用 `indiana.forFunnel({ funnelId, versionId })` 获取固定上下文的客户端;其领取、Offering/Paywall 解析和购买报价自动携带这一个 Funnel。服务端以可信事实复算指定流程,不扫描其他 Funnel。
253
257
 
254
258
  不带上下文的 `indiana.claimCoupon(...)` 仍按 Coupon 自身规则独立领取。未知结果使用原 requestId 和原输入重试;已接受命令在 Funnel 后续暂停时仍返回原结果。
259
+
260
+ ## 独立 Page、Popup 与 Banner
261
+
262
+ 先发布一个没有必填输入、声明 `continue` 动作的 Banner。Customer Token 需要 `content:read` 权限,再显式读取和挂载。Page/Popup 使用相应 `kind`。
263
+
264
+ ```ts
265
+ import {
266
+ createIndiana,
267
+ mountContent,
268
+ type ContentActionIntent,
269
+ } from "@tinkerlandai/indiana-sdk";
270
+
271
+ export async function showBanner(
272
+ container: HTMLElement,
273
+ customerToken: string,
274
+ contentId: string,
275
+ onContinue: (intent: ContentActionIntent) => Promise<void>,
276
+ ) {
277
+ const indiana = createIndiana({
278
+ baseUrl: "https://api.dev.tinker.cash",
279
+ scope: { projectId: "<TEST_APP_UUID>", environment: "sandbox" },
280
+ });
281
+ indiana.identify(customerToken);
282
+ const { content } = await indiana.resolveContent({
283
+ kind: "banner",
284
+ selector: { type: "resource", id: contentId },
285
+ });
286
+ const host = await mountContent(container, {
287
+ content,
288
+ actions: { continue: onContinue },
289
+ onError: console.error,
290
+ });
291
+ return {
292
+ retryAction: host.retryAction,
293
+ destroy() {
294
+ host.destroy();
295
+ indiana.reset();
296
+ },
297
+ };
298
+ }
299
+ ```
300
+
301
+ 每个声明动作都需要同名 handler。`onContinue` 用 `intent.requestId` 作为业务后端的幂等键;失败后 `retryAction()` 重用原意图。卸载、退出或换号时调用 `destroy()`。宿主只渲染和发出意图,业务后端仍负责鉴权与执行。