Files
Caprika 8fb94d28eb feat(cms): 统一客户端活动点位接入与验收修复 (#7986)
* feat(cms): add verified desktop touchpoint hosts and Test acceptance

* fix(cms): keep Test modal scrim above app chrome

* fix(cms): load host styles and support hover packages

* fix(cms): report mounted host compatibility for restoration

* fix(cms): parse Vela actions in shared manifests without executing them

* fix(cms): preserve authorized actions and correct modal and hover behavior (#8003)

* fix(cms): harden touchpoint host interactions

* fix(cms): preserve modal actions and verify effective close controls

* test(cms): cover hover exit and above-anchor placement

* fix(cms): hide client test controls by default

* fix(cms): persist automatic campaign impressions per account and device

* fix(web): remove host campaign close button

* fix(cms): suppress campaign modals during onboarding

* fix(cms): unify test and production campaign lifecycle (#8118)

* fix(cms): enable safe campaign navigation in test and production

* fix(cms): enforce server-authorized touchpoint lifetimes

* fix(cms): refresh production touchpoints when client language changes

* fix(cms): stabilize hover refresh and synchronize Test locale

* fix(cms): preserve campaigns on focus and remember Test dismissal

* fix(cms): discover Test deployments without reloading

* fix(cms): keep granted touchpoint display authority through transport failures

A refresh that times out or fails in transport is not a withdrawal, but the
shared lifecycle treated every failure the same way: `status = "error"` then
`revoke()`, which tore down a lease the server had already granted and was
still inside its own deadline. One slow poll therefore blanked a running
campaign until the next 30s tick, and a second failure could not restore it
because the value had been dropped.

`abandonAttempt` now names the invariant: cancel the attempt, keep the lease,
and let `armExpiry` retire it at the deadline the server actually granted. The
lease length is the bound, so no retry budget is needed — Test's sixty-second
contract survives exactly one missed poll, and Production survives as many as
its authorization covers.

Three cases still clear, and one of them is new:

  - the failure carries the server's own withdrawal. A 410 is authoritative
    even when its receipt body is unreadable, so `ProductionTouchpointLoadError`
    now marks that one case and the lifecycle honours it. Without this, a
    malformed 410 would have been ridden out as if it were transport noise.
  - nothing is mounted, or the lease was already fenced by `wake`. Page
    recovery has no evidence the activity is still live, so it stays withdrawn.
  - the lease has run out on its own.

This does not fix OPEND-3237, which is a new activity failing its FIRST load:
there is no prior lease to keep and the display stays empty. It fixes the
adjacent class where a mounted activity is erased by a transient refresh.

* fix(cms): keep a failed refresh recoverable inside its own poll cycle

Three changes to how one refresh cycle behaves when it does not go cleanly.
None of them fixes OPEND-3237, whose first-load failure is still unexplained;
they remove the ways a working campaign is lost to an ordinary hiccup.

Bounded retries. A failed attempt used to get its next chance from the fixed
30s tick. For the sixty-second lease the Test runtime is granted, that chance
lands exactly when the lease expires, so a single failure put display on the
edge of going blank with nothing able to recover it. Retries at 1s and 3s give
a fast failure — transport error, 5xx, DNS — several chances while the lease is
still comfortably alive.

A retry must finish inside the cycle that spawned it. `refresh` declines to
start while a request is in flight, so a chain that ran past the next tick
would not merely be late: it would swallow that tick entirely. Worst case was
15+1+15+3+15 = 49s against a 30s interval. Scheduling now requires room for the
retry AND its full budget, which is also what makes "retries cover fast
failures" true in the code rather than only in a comment — an attempt that
burned the whole budget leaves no room by construction and is never retried.
The retry budget resets per cycle, not per success: otherwise the first cycle
to exhaust its retries would leave every later cycle with none.

Request budget 10s -> 15s. One refresh fetches a context and every enabled
placement, so the budget covers a round, not a request. A real round of 11.5s
whose placements all succeeded was being abandoned by the ten-second budget. It
stays well under the 30s interval on purpose.

Forward `accept-encoding` through the touchpoint runtime proxy. Decisions carry
base64 content and run to megabytes, and the proxy built its upstream headers
from scratch, dropping the caller's preference — every refresh pulled the
payload uncompressed, measured at 383KB against 214KB for the same decision.
Because the body is piped through verbatim, upstream's `content-encoding` has
to be handed back with it, or the caller decodes gzip as JSON; both halves land
together. Verified end to end through daemon and web: 2,149,685 -> 1,606,678
bytes on the wire, byte-identical after decode.

An authoritative withdrawal is never retried: a 410 that took a campaign off
the screen must not put it back.

* fix(cms): present each Test campaign modal once per account and device

Test modal presentations now share the production touchpoint-displayed:v1
impression: recorded once visible, read before any new automatic opening,
and persisted across reload, remount, and client restart.

* fix(cms): give the Test control readout an explicit ladder weight
2026-09-17 05:58:48 +00:00

117 lines
3.7 KiB
JSON

{
"name": "@open-design/contracts",
"version": "0.22.1",
"private": true,
"type": "module",
"description": "Shared pure TypeScript contracts for the OpenDesign web/daemon boundary.",
"main": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
},
"./api/connectionTest": {
"types": "./dist/api/connectionTest.d.ts",
"default": "./dist/api/connectionTest.mjs"
},
"./api/amrWallet": {
"types": "./dist/api/amrWallet.d.ts",
"default": "./dist/api/amrWallet.mjs"
},
"./api/orbit": {
"types": "./dist/api/orbit.d.ts",
"default": "./dist/api/orbit.mjs"
},
"./api/finalize": {
"types": "./dist/api/finalize.d.ts",
"default": "./dist/api/finalize.mjs"
},
"./api/handoff": {
"types": "./dist/api/handoff.d.ts",
"default": "./dist/api/handoff.mjs"
},
"./api/providerModels": {
"types": "./dist/api/providerModels.d.ts",
"default": "./dist/api/providerModels.mjs"
},
"./api/reasoningExecution": {
"types": "./dist/api/reasoningExecution.d.ts",
"default": "./dist/api/reasoningExecution.mjs"
},
"./api/research": {
"types": "./dist/api/research.d.ts",
"default": "./dist/api/research.mjs"
},
"./api/touchpointTestRuntime": {
"types": "./dist/api/touchpointTestRuntime.d.ts",
"default": "./dist/api/touchpointTestRuntime.mjs"
},
"./runtime/deck-stage-fallback": {
"types": "./dist/runtime/deck-stage-fallback.d.ts",
"default": "./dist/runtime/deck-stage-fallback.mjs"
},
"./runtime/deck-protocol": {
"types": "./dist/runtime/deck-protocol.d.ts",
"default": "./dist/runtime/deck-protocol.mjs"
},
"./runtime/preview-observability": {
"types": "./dist/runtime/preview-observability.d.ts",
"default": "./dist/runtime/preview-observability.mjs"
},
"./runtime/preview-build-focus": {
"types": "./dist/runtime/preview-build-focus.d.ts",
"default": "./dist/runtime/preview-build-focus.mjs"
},
"./runtime/preview-runtime-state": {
"types": "./dist/runtime/preview-runtime-state.d.ts",
"default": "./dist/runtime/preview-runtime-state.mjs"
},
"./runtime/preview-guards": {
"types": "./dist/runtime/preview-guards.d.ts",
"default": "./dist/runtime/preview-guards.mjs"
},
"./design-systems/components-manifest": {
"types": "./dist/design-systems/components-manifest.d.ts",
"default": "./dist/design-systems/components-manifest.mjs"
},
"./design-systems/derived-token-outputs": {
"types": "./dist/design-systems/derived-token-outputs.d.ts",
"default": "./dist/design-systems/derived-token-outputs.mjs"
},
"./design-systems/token-schema": {
"types": "./dist/design-systems/token-schema.d.ts",
"default": "./dist/design-systems/token-schema.mjs"
},
"./critique": {
"types": "./dist/critique.d.ts",
"default": "./dist/critique.mjs"
},
"./analytics": {
"types": "./dist/analytics/index.d.ts",
"default": "./dist/analytics/index.mjs"
},
"./runtime/html-injection-points": {
"types": "./dist/runtime/html-injection-points.d.ts",
"default": "./dist/runtime/html-injection-points.mjs"
}
},
"scripts": {
"build": "node ./esbuild.config.mjs && tsc -p tsconfig.json --emitDeclarationOnly",
"test": "vitest run",
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.tests.json --noEmit"
},
"dependencies": {
"@open-design/release": "workspace:*",
"zod": "3.25.76"
},
"devDependencies": {
"esbuild": "0.28.0",
"typescript": "5.9.3",
"vitest": "4.1.6"
}
}