@everfur/sdk 0.1.0
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/CHANGELOG.md +74 -0
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/animations/package.json +8 -0
- package/chat/package.json +8 -0
- package/client/package.json +8 -0
- package/core/package.json +8 -0
- package/dist/CameraPort-Cv31Pz7g.d.cts +29 -0
- package/dist/CameraPort-Cv31Pz7g.d.ts +29 -0
- package/dist/ChatController-CKdBvPj2.d.ts +146 -0
- package/dist/ChatController-CpUMvvZf.d.cts +146 -0
- package/dist/EverfurResult-D92-uL82.d.cts +240 -0
- package/dist/EverfurResult-D92-uL82.d.ts +240 -0
- package/dist/FilePort-BabWrv7I.d.cts +22 -0
- package/dist/FilePort-BabWrv7I.d.ts +22 -0
- package/dist/PhotoController-BItt5M7u.d.cts +43 -0
- package/dist/PhotoController-D8zMTdcW.d.ts +43 -0
- package/dist/TelemetryPort-BDNr00hu.d.cts +12 -0
- package/dist/TelemetryPort-BDNr00hu.d.ts +12 -0
- package/dist/animations/index.cjs +1997 -0
- package/dist/animations/index.d.cts +517 -0
- package/dist/animations/index.d.ts +517 -0
- package/dist/animations/index.js +1972 -0
- package/dist/cacheEpoch-DknKn3S0.d.cts +36 -0
- package/dist/cacheEpoch-DknKn3S0.d.ts +36 -0
- package/dist/chat/index.cjs +1170 -0
- package/dist/chat/index.d.cts +59 -0
- package/dist/chat/index.d.ts +59 -0
- package/dist/chat/index.js +1167 -0
- package/dist/client/index.cjs +2317 -0
- package/dist/client/index.d.cts +65 -0
- package/dist/client/index.d.ts +65 -0
- package/dist/client/index.js +2217 -0
- package/dist/config-BSjBdxrZ.d.cts +501 -0
- package/dist/config-CiJ0PVBB.d.ts +501 -0
- package/dist/core/index.cjs +3175 -0
- package/dist/core/index.d.cts +70 -0
- package/dist/core/index.d.ts +70 -0
- package/dist/core/index.js +3163 -0
- package/dist/identity-Brl-lDd6.d.cts +91 -0
- package/dist/identity-DK9zORrG.d.ts +91 -0
- package/dist/ids-CJ1S6adf.d.cts +46 -0
- package/dist/ids-CJ1S6adf.d.ts +46 -0
- package/dist/index.cjs +4488 -0
- package/dist/index.d.cts +156 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.js +4464 -0
- package/dist/petsRepository-BEGb97M9.d.cts +326 -0
- package/dist/petsRepository-Bu18r2kK.d.ts +326 -0
- package/dist/photo/index.cjs +2189 -0
- package/dist/photo/index.d.cts +43 -0
- package/dist/photo/index.d.ts +43 -0
- package/dist/photo/index.js +2186 -0
- package/dist/projection-CeIUsbSk.d.cts +8 -0
- package/dist/projection-CeIUsbSk.d.ts +8 -0
- package/dist/records/index.cjs +2840 -0
- package/dist/records/index.d.cts +224 -0
- package/dist/records/index.d.ts +224 -0
- package/dist/records/index.js +2834 -0
- package/dist/requestFunnel-DuUH-kAe.d.cts +28 -0
- package/dist/requestFunnel-dio5OmR9.d.ts +28 -0
- package/dist/resolve-Dq_4_agU.d.cts +86 -0
- package/dist/resolve-Dq_4_agU.d.ts +86 -0
- package/dist/runtime-BgQnA594.d.cts +349 -0
- package/dist/runtime-CBA-LvdM.d.ts +349 -0
- package/dist/server/index.cjs +533 -0
- package/dist/server/index.d.cts +48 -0
- package/dist/server/index.d.ts +48 -0
- package/dist/server/index.js +530 -0
- package/dist/testing/index.cjs +825 -0
- package/dist/testing/index.d.cts +113 -0
- package/dist/testing/index.d.ts +113 -0
- package/dist/testing/index.js +822 -0
- package/dist/testing/rn/index.cjs +449 -0
- package/dist/testing/rn/index.d.cts +49 -0
- package/dist/testing/rn/index.d.ts +49 -0
- package/dist/testing/rn/index.js +444 -0
- package/dist/video/index.cjs +1941 -0
- package/dist/video/index.d.cts +66 -0
- package/dist/video/index.d.ts +66 -0
- package/dist/video/index.js +1938 -0
- package/package.json +311 -0
- package/photo/package.json +8 -0
- package/records/package.json +8 -0
- package/server/device-blocked.cjs +15 -0
- package/server/package.json +9 -0
- package/testing/package.json +8 -0
- package/testing/rn/package.json +8 -0
- package/video/package.json +8 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@everfur/sdk`.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions are semver.
|
|
6
|
+
|
|
7
|
+
This is the first release. The line is deliberately pre-1.0: the API is stable enough to build against and
|
|
8
|
+
is guarded by api-extractor golden reports across all eleven subpaths, but 1.0.0 is a promise about breaking
|
|
9
|
+
changes that has not been earned by any production integration yet.
|
|
10
|
+
|
|
11
|
+
## [0.1.0] - 2026-08-23
|
|
12
|
+
|
|
13
|
+
### Fixed — correctness
|
|
14
|
+
|
|
15
|
+
- **Anonymous mode could never send a message.** The SDK never sent `x-everfur-session-id`, and the server
|
|
16
|
+
derives the anonymous identity from it, minting a fresh one per request. A conversation was created as one
|
|
17
|
+
user and the message sent as another, so every send settled `conversationNotFound`. One stable v4 id is
|
|
18
|
+
now minted per runtime and sent on both the unary and streaming paths.
|
|
19
|
+
- **Sign-out did not sign out.** `identity.clear()` only dropped the token cache, so a capability holding a
|
|
20
|
+
pre-logout `AuthContext` re-entered the partner's `getToken` and minted a fresh token for the signed-out
|
|
21
|
+
user. It is now terminal. The anonymous branch no longer carries the signed-out `userRef`, and the session
|
|
22
|
+
id rotates on logout.
|
|
23
|
+
- **A misconfigured provider was a white screen.** `EverfurProvider` threw synchronously from its own render,
|
|
24
|
+
above every error boundary a partner can install. Config now settles into a disabled runtime: children
|
|
25
|
+
render, every capability shows its off-state at `disabled.level === 'sdk_config'`, and the transport
|
|
26
|
+
refuses.
|
|
27
|
+
- **The runtime is rebuilt when the config identity changes or it has been disposed.** An asynchronously
|
|
28
|
+
supplied `publishableKey` never took effect, and React StrictMode's double-mount permanently killed it.
|
|
29
|
+
- **Records polled every 4 seconds forever.** `MAX_POLLS` never bound, because the counter lived inside an
|
|
30
|
+
effect keyed on an object rebuilt each tick. Measured 300 polls against a cap of 30.
|
|
31
|
+
- **Chat message bounds are measured in code points**, as the server measures them; UTF-16 units counted
|
|
32
|
+
every emoji twice and refused messages at half the real limit.
|
|
33
|
+
- Neither `POST /conversations` nor the SSE send is auto-replayed: the widget lane does not read
|
|
34
|
+
`Idempotency-Key`, so a retry was a second row, not a dedup.
|
|
35
|
+
|
|
36
|
+
### Fixed — security
|
|
37
|
+
|
|
38
|
+
- **`@everfur/sdk/server` is no longer resolvable from a React Native bundle.** The subpath merely omitted a
|
|
39
|
+
`react-native` export condition, which is not a block; it now maps to `null`. This is the module carrying
|
|
40
|
+
`sk_partner_`.
|
|
41
|
+
- `mintPartnerSession` validates its base URL instead of concatenating it, so a cleartext
|
|
42
|
+
`EVERFUR_API_BASE_URL` can no longer put the partner secret key on the wire.
|
|
43
|
+
- Config failure messages echo a redacted origin, never the raw base URL, which could carry userinfo.
|
|
44
|
+
- A secret key pasted into `publishableKey` is refused.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- `registerPet` on the runtime and the handle, plus `createPetsRepository` on `@everfur/sdk/client`. Records
|
|
49
|
+
and consent are unreachable for any pet the partner has not registered, and there was previously no way to
|
|
50
|
+
register one from the SDK.
|
|
51
|
+
- The clinical disclaimer is mounted on every pet-health surface, not only chat.
|
|
52
|
+
- `npm run docs:check` type-checks every documented snippet.
|
|
53
|
+
- `EverfurRequestBase` and `MultipartBody` are exported, so a `TransportPort` wrapper can extend the request
|
|
54
|
+
shape without depending on the union (see the `EverfurRequest` note below).
|
|
55
|
+
- `EverfurConfig.errorPolicy`'s `shouldRetry` and `maxAttempts` are now actually consulted. They were typed,
|
|
56
|
+
exported and documented while reaching no request in the SDK. Both narrow only: neither can make a spent
|
|
57
|
+
daily quota retryable nor resurrect a terminal code, and an SDK per-call cap still outranks them.
|
|
58
|
+
|
|
59
|
+
### Changed — potentially breaking
|
|
60
|
+
|
|
61
|
+
- **`DisabledLevel` gained an `sdk_config` member.** A consumer with an exhaustive `switch` over that union
|
|
62
|
+
will no longer compile. This is the one deliberate widening in this set.
|
|
63
|
+
- **`AuthContext.sessionId` is now required.** Building the context without one silently reintroduced the
|
|
64
|
+
anonymous-identity defect, so the type now catches it.
|
|
65
|
+
- **`EverfurRequest` is now a union, not an interface.** It was
|
|
66
|
+
`EverfurRequestBase & ({ body?: unknown; multipart?: undefined } | { body?: undefined; multipart: MultipartBody })`,
|
|
67
|
+
so carrying both a JSON body and a multipart body is unrepresentable rather than merely discouraged: the
|
|
68
|
+
combination produces a `content-type` without the platform-generated boundary and the server cannot parse
|
|
69
|
+
the request. Two ordinary patterns on the `TransportPort` seam stop compiling, both with a one-line fix:
|
|
70
|
+
- `interface X extends EverfurRequest` fails with TS2312 (an interface cannot extend a union). Extend the
|
|
71
|
+
newly exported `EverfurRequestBase` instead.
|
|
72
|
+
- `inner.request({ ...req, body: next })` inside a transport wrapper fails, because TypeScript cannot tell
|
|
73
|
+
which arm `req` is. Narrow first: `if (req.multipart === undefined) inner.request({ ...req, body: next })`.
|
|
74
|
+
- `RecordsRepository` dropped the `idem` parameters its only implementation ignored.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Everfur Health, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# @everfur/sdk (React Native)
|
|
2
|
+
|
|
3
|
+
Drop Everfur's AI vet assistant and pet-health features (records, gait/video, photo checkup, pet-state
|
|
4
|
+
media) into a React Native app. What each of your customers and users can see is decided **server-side** by
|
|
5
|
+
Everfur's entitlement control plane and rendered by the SDK, so you gate features without shipping an app
|
|
6
|
+
update.
|
|
7
|
+
|
|
8
|
+
- Hexagonal, framework-free core with a thin RN layer; every surface settles (no thrown domain errors).
|
|
9
|
+
- Server-authoritative entitlements: a feature you are not entitled to renders a deliberate off-state, never
|
|
10
|
+
a blank screen, and cannot be force-enabled client-side.
|
|
11
|
+
- The app never holds a secret. See [The security model](#the-security-model).
|
|
12
|
+
|
|
13
|
+
> `0.1.0`, MIT licensed, public on npm. Requires React Native `>=0.74`, and Node `>=18` on the backend
|
|
14
|
+
> that mints sessions.
|
|
15
|
+
|
|
16
|
+
**Getting keys:** Everfur keys are provisioned by the Everfur team during partner onboarding; there is
|
|
17
|
+
no self-serve signup yet. Contact [support@everfur.com](mailto:support@everfur.com) to get set up. The
|
|
18
|
+
publishable key is designed to ship inside your app; the secret key is not.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm i @everfur/sdk
|
|
24
|
+
# Always-required peers:
|
|
25
|
+
npm i react react-native react-native-safe-area-context
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Peer dependencies by capability (install only what you use):
|
|
29
|
+
|
|
30
|
+
| Capability | Extra peer deps | Notes |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| chat, entitlements | none | works with react + react-native alone |
|
|
33
|
+
| records | `expo-file-system`, `expo-image-manipulator` | document upload + client-side image prep |
|
|
34
|
+
| video (gait), photo (checkup) | `react-native-vision-camera` | **use `4.x` on RN 0.74**; `5.x` requires the new architecture |
|
|
35
|
+
|
|
36
|
+
The camera and file-system peers are optional: if you never mount `./video`, `./photo`, or `./records`,
|
|
37
|
+
you do not need them. `react-native-vision-camera` also needs its config plugin in `app.json` and a camera
|
|
38
|
+
usage description.
|
|
39
|
+
|
|
40
|
+
### Bundler resolution
|
|
41
|
+
|
|
42
|
+
No Metro configuration is required. Every subpath (`@everfur/sdk/chat`, `/records`, `/video`, `/photo`,
|
|
43
|
+
`/animations`, `/client`, `/core`, `/testing`, `/testing/rn`) resolves on a stock install. Modern bundlers
|
|
44
|
+
read the package `exports` map; Metro 0.80.x, which ships with Expo 51, defaults to
|
|
45
|
+
`unstable_enablePackageExports: false` and never reads it, so the package also ships classic root-level
|
|
46
|
+
resolution shims for the same subpaths. You do not need to turn package exports on, and turning them on does
|
|
47
|
+
not change what you get.
|
|
48
|
+
|
|
49
|
+
`@everfur/sdk/server` is the one exception, deliberately: it is your **backend** module and carries the
|
|
50
|
+
`sk_partner_` session minter. If it is ever pulled into an app bundle it throws on import rather than
|
|
51
|
+
shipping the minter to a device. Import it from your server only.
|
|
52
|
+
|
|
53
|
+
## Quickstart
|
|
54
|
+
|
|
55
|
+
Wrap your tree once, high, then drop features in behind a gate. This is the exact shape the reference app
|
|
56
|
+
uses:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { EverfurProvider, EverfurChat, petRef, userRef } from '@everfur/sdk';
|
|
60
|
+
|
|
61
|
+
export default function App() {
|
|
62
|
+
return (
|
|
63
|
+
<EverfurProvider
|
|
64
|
+
config={{
|
|
65
|
+
publishableKey: process.env.EXPO_PUBLIC_EVERFUR_PUBLISHABLE_KEY!, // pk_live_... safe to ship
|
|
66
|
+
apiBaseUrl: 'https://api.everfur.com/api/v1',
|
|
67
|
+
}}
|
|
68
|
+
// Omit `user` entirely for anonymous mode (publishable key alone, general answers).
|
|
69
|
+
user={{
|
|
70
|
+
userRef: userRef('your-stable-user-id'),
|
|
71
|
+
getToken: () => fetch('/everfur-session').then((r) => r.text()),
|
|
72
|
+
}}
|
|
73
|
+
activePet={petRef('pet-ref-123')}
|
|
74
|
+
>
|
|
75
|
+
{/* Every prebuilt widget self-gates. CapabilityGate is for YOUR surrounding UI - a tab, a nav
|
|
76
|
+
entry - not for wrapping a widget that already gates itself. */}
|
|
77
|
+
<EverfurChat petRef={petRef('pet-ref-123')} />
|
|
78
|
+
</EverfurProvider>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`getToken` calls **your** backend for a short-lived session token (never a secret in the app):
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
async function getToken(): Promise<string> {
|
|
87
|
+
const res = await fetch('https://your-backend.example.com/everfur/session', {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: { authorization: `Bearer ${yourAppsUserToken}` },
|
|
90
|
+
});
|
|
91
|
+
const { sessionToken } = await res.json();
|
|
92
|
+
return sessionToken;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The SDK calls `getToken` lazily and re-calls it once after a `401`, so a fresh token is always used without
|
|
97
|
+
you managing expiry. Records lives on its own subpath:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { EverfurRecords } from '@everfur/sdk/records';
|
|
101
|
+
import { petRef } from '@everfur/sdk';
|
|
102
|
+
|
|
103
|
+
<EverfurRecords petRef={petRef('pet-ref-123')} />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## The security model
|
|
107
|
+
|
|
108
|
+
The one rule: **the app never holds a secret.** Two credentials, kept apart.
|
|
109
|
+
|
|
110
|
+
| Credential | Where it lives | What it does |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| Publishable key `pk_live_...` | In the app (`EXPO_PUBLIC_...`). Safe to ship. | Names your tenant. Cannot mint a session or grant a feature by itself. |
|
|
113
|
+
| Partner secret key `sk_partner_...` | **Only** on your server. Never in the app, its bundle, env, git, or logs. | Mints session tokens. Revocable and rotatable without an app release. |
|
|
114
|
+
| Session token (bearer) | App memory, short-lived. | Authenticates one end user; re-minted via your backend on a 401. |
|
|
115
|
+
|
|
116
|
+
Your backend exchanges the two provisioning credentials for a short-lived session by calling
|
|
117
|
+
`POST /widget/v1/sessions` with `x-everfur-partner-key: pk_live_...` + `x-everfur-partner-secret-key:
|
|
118
|
+
sk_partner_...`. The SDK ships a **server-only** helper (`mintPartnerSession`, imported from
|
|
119
|
+
`@everfur/sdk/server`) so you do not hand-roll that request. That helper is deliberately absent from every
|
|
120
|
+
React-Native-resolvable bundle: a device can never import it, so the secret has no path onto the phone.
|
|
121
|
+
|
|
122
|
+
**Do:** ship `pk_live_` in the app; keep `sk_partner_` in a server secret manager; mint sessions on your
|
|
123
|
+
backend; serve everything over HTTPS.
|
|
124
|
+
**Never:** put `sk_partner_` in the app; mint sessions on the device; try to force-enable a feature
|
|
125
|
+
client-side (the server re-checks and returns `403`).
|
|
126
|
+
|
|
127
|
+
## How feature access works
|
|
128
|
+
|
|
129
|
+
Access is decided server-side and delivered to the SDK (`GET /widget/v1/entitlements`, fail-closed). Each
|
|
130
|
+
surface sits behind `CapabilityGate`; a capability you are not entitled to renders a deliberate off-state,
|
|
131
|
+
and it cannot be force-enabled client-side because the API re-checks on every request. A coarse feature
|
|
132
|
+
(`chat`, `records`, `video`, ...) is enabled only when every dotted sub-feature resource it requires is
|
|
133
|
+
granted (for example `records` needs both `records.upload.create` and `records.record.read`). You set these
|
|
134
|
+
per customer and per user in Everfur's admin control plane; the app renders the verdict.
|
|
135
|
+
|
|
136
|
+
Every SDK surface renders all four states (loading, empty, error, populated); a denied surface renders its
|
|
137
|
+
off-state, never a blank region.
|
|
138
|
+
|
|
139
|
+
## Public entry points
|
|
140
|
+
|
|
141
|
+
| Import | Contents |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `@everfur/sdk` | `EverfurProvider`, `CapabilityGate`, `EverfurChat`, `useEverfurChat`, `CapabilityName`, error/result types, branded id helpers (`petRef`, `userRef`, `conversationId`) |
|
|
144
|
+
| `@everfur/sdk/records` | `EverfurRecords`, records hooks + types |
|
|
145
|
+
| `@everfur/sdk/video` | gait / live-scan surface |
|
|
146
|
+
| `@everfur/sdk/photo` | photo checkup surface |
|
|
147
|
+
| `@everfur/sdk/animations` | server-driven pet-state media surface |
|
|
148
|
+
| `@everfur/sdk/server` | **server-only** `mintPartnerSession` (no React Native resolution; never bundled on-device) |
|
|
149
|
+
| `@everfur/sdk/testing`, `@everfur/sdk/testing/rn` | test seams (`MockTransport`, ...) for your own tests |
|
|
150
|
+
|
|
151
|
+
`@everfur/sdk/client` and `@everfur/sdk/core` expose the framework-free layers for advanced integrators;
|
|
152
|
+
most apps only need the root entry plus the capability subpaths.
|
|
153
|
+
|
|
154
|
+
## Reference app
|
|
155
|
+
|
|
156
|
+
A full, runnable integration (a fictional "Pawtrail" app plus a minimal mint backend that holds the secret)
|
|
157
|
+
lives at [`examples/minimal-integration.tsx`](examples/minimal-integration.tsx) in this repository — it is
|
|
158
|
+
type-checked by `npm run docs:check`, so it cannot rot. It shows anonymous mode, the two-credential session
|
|
159
|
+
mint, pet registration and sign-out. (A separate `everfur-integration-example-rn` app exists internally but
|
|
160
|
+
is not published anywhere a partner can reach, so do not rely on references to it.) It gates every
|
|
161
|
+
capability, and is the copy-paste source for the snippets above.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "GENERATED by scripts/subpath-shims.mjs from the package.json \"exports\" map. Do not edit by hand; run `npm run build`.",
|
|
3
|
+
"react-native": "../dist/animations/index.js",
|
|
4
|
+
"module": "../dist/animations/index.js",
|
|
5
|
+
"main": "../dist/animations/index.cjs",
|
|
6
|
+
"types": "../dist/animations/index.d.ts",
|
|
7
|
+
"sideEffects": false
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "GENERATED by scripts/subpath-shims.mjs from the package.json \"exports\" map. Do not edit by hand; run `npm run build`.",
|
|
3
|
+
"react-native": "../dist/chat/index.js",
|
|
4
|
+
"module": "../dist/chat/index.js",
|
|
5
|
+
"main": "../dist/chat/index.cjs",
|
|
6
|
+
"types": "../dist/chat/index.d.ts",
|
|
7
|
+
"sideEffects": false
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "GENERATED by scripts/subpath-shims.mjs from the package.json \"exports\" map. Do not edit by hand; run `npm run build`.",
|
|
3
|
+
"react-native": "../dist/client/index.js",
|
|
4
|
+
"module": "../dist/client/index.js",
|
|
5
|
+
"main": "../dist/client/index.cjs",
|
|
6
|
+
"types": "../dist/client/index.d.ts",
|
|
7
|
+
"sideEffects": false
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "GENERATED by scripts/subpath-shims.mjs from the package.json \"exports\" map. Do not edit by hand; run `npm run build`.",
|
|
3
|
+
"react-native": "../dist/core/index.js",
|
|
4
|
+
"module": "../dist/core/index.js",
|
|
5
|
+
"main": "../dist/core/index.cjs",
|
|
6
|
+
"types": "../dist/core/index.d.ts",
|
|
7
|
+
"sideEffects": false
|
|
8
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Opaque, host-defined capture request. Kept minimal at the spine; video/photo specs extend it. */
|
|
2
|
+
interface CaptureOptions {
|
|
3
|
+
/** 'photo' | 'video'; the coarse capture mode a capability requests. */
|
|
4
|
+
readonly mode: 'photo' | 'video';
|
|
5
|
+
/** Optional soft ceiling for a video capture, in seconds. */
|
|
6
|
+
readonly maxDurationSeconds?: number;
|
|
7
|
+
}
|
|
8
|
+
/** Opaque handle to captured media the FilePort can upload. Server-owned shape is defined by video/photo. */
|
|
9
|
+
interface MediaSource {
|
|
10
|
+
/** Local file URI produced by the capture. */
|
|
11
|
+
readonly uri: string;
|
|
12
|
+
/** MIME type, e.g. 'image/jpeg' | 'video/mp4'. */
|
|
13
|
+
readonly mimeType: string;
|
|
14
|
+
readonly widthPx?: number;
|
|
15
|
+
readonly heightPx?: number;
|
|
16
|
+
readonly durationSeconds?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Camera capture. Absent (or an unavailable native peer) => `isAvailable:false`; the SDK never throws for a
|
|
20
|
+
* missing capability, it renders the OffState.
|
|
21
|
+
*/
|
|
22
|
+
interface CameraPort {
|
|
23
|
+
readonly isAvailable: boolean;
|
|
24
|
+
capture(opts: CaptureOptions): Promise<MediaSource>;
|
|
25
|
+
}
|
|
26
|
+
/** The safe absent default: capture is unavailable and never throws until explicitly awaited. */
|
|
27
|
+
declare const unavailableCamera: CameraPort;
|
|
28
|
+
|
|
29
|
+
export { type CameraPort as C, type MediaSource as M, type CaptureOptions as a, unavailableCamera as u };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Opaque, host-defined capture request. Kept minimal at the spine; video/photo specs extend it. */
|
|
2
|
+
interface CaptureOptions {
|
|
3
|
+
/** 'photo' | 'video'; the coarse capture mode a capability requests. */
|
|
4
|
+
readonly mode: 'photo' | 'video';
|
|
5
|
+
/** Optional soft ceiling for a video capture, in seconds. */
|
|
6
|
+
readonly maxDurationSeconds?: number;
|
|
7
|
+
}
|
|
8
|
+
/** Opaque handle to captured media the FilePort can upload. Server-owned shape is defined by video/photo. */
|
|
9
|
+
interface MediaSource {
|
|
10
|
+
/** Local file URI produced by the capture. */
|
|
11
|
+
readonly uri: string;
|
|
12
|
+
/** MIME type, e.g. 'image/jpeg' | 'video/mp4'. */
|
|
13
|
+
readonly mimeType: string;
|
|
14
|
+
readonly widthPx?: number;
|
|
15
|
+
readonly heightPx?: number;
|
|
16
|
+
readonly durationSeconds?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Camera capture. Absent (or an unavailable native peer) => `isAvailable:false`; the SDK never throws for a
|
|
20
|
+
* missing capability, it renders the OffState.
|
|
21
|
+
*/
|
|
22
|
+
interface CameraPort {
|
|
23
|
+
readonly isAvailable: boolean;
|
|
24
|
+
capture(opts: CaptureOptions): Promise<MediaSource>;
|
|
25
|
+
}
|
|
26
|
+
/** The safe absent default: capture is unavailable and never throws until explicitly awaited. */
|
|
27
|
+
declare const unavailableCamera: CameraPort;
|
|
28
|
+
|
|
29
|
+
export { type CameraPort as C, type MediaSource as M, type CaptureOptions as a, unavailableCamera as u };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { R as RequestFunnel } from './requestFunnel-dio5OmR9.js';
|
|
2
|
+
import { A as AuthContext } from './identity-DK9zORrG.js';
|
|
3
|
+
import { a as EverfurErrorCode, f as EverfurError, E as EverfurResult } from './EverfurResult-D92-uL82.js';
|
|
4
|
+
import { b as ResponseId, a as ConversationId, P as PetRef } from './ids-CJ1S6adf.js';
|
|
5
|
+
import { F as Frame } from './config-CiJ0PVBB.js';
|
|
6
|
+
import { T as TelemetryPort } from './TelemetryPort-BDNr00hu.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Recovery policy. A host may narrow retry ceilings (e.g. a metered plan) but can never widen the SDK's
|
|
10
|
+
* settle-don't-throw contract. `attempt` is 0-based (the first try is attempt 0).
|
|
11
|
+
*/
|
|
12
|
+
interface ErrorPolicyPort {
|
|
13
|
+
/** Whether to retry `code` on this attempt. Default: `isAutoRetryable(code) && attempt < maxAttempts(code)`. */
|
|
14
|
+
shouldRetry(code: EverfurErrorCode, attempt: number): boolean;
|
|
15
|
+
/** Total attempts (the initial try plus retries) before the terminal error surfaces. Default 4. */
|
|
16
|
+
maxAttempts(code: EverfurErrorCode): number;
|
|
17
|
+
/** Observation hook for a handled, product-expected failure. Never rendered, never thrown. */
|
|
18
|
+
onExpectedFailure?(error: EverfurError): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The shipped default policy (applied when EverfurConfig omits `errorPolicy`). Pure and total.
|
|
22
|
+
* `authExpired` is deliberately NOT auto-retried here: it is a token refresh + single replay handled by the
|
|
23
|
+
* controller / authedSend, not a backoff retry (SPEC-00 §3.2 / §4.3).
|
|
24
|
+
*/
|
|
25
|
+
declare const defaultErrorPolicy: ErrorPolicyPort;
|
|
26
|
+
|
|
27
|
+
/** Injectable timers so the watchdog + deadlines are testable with a fake clock (no direct setTimeout). */
|
|
28
|
+
interface Timers {
|
|
29
|
+
setTimeout(handler: () => void, ms: number): unknown;
|
|
30
|
+
clearTimeout(handle: unknown): void;
|
|
31
|
+
now(): number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ChatStatus = 'idle' | 'streaming' | 'complete' | 'error';
|
|
35
|
+
/** Opaque citation record (DoneFrame.citations shape). Rendered by the UI layer, never trusted for logic. */
|
|
36
|
+
type Citation = Readonly<Record<string, unknown>>;
|
|
37
|
+
interface ChatMessage {
|
|
38
|
+
/** Stable list key: a client id for a user turn, the responseId for a finalized assistant turn. */
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly role: 'user' | 'assistant';
|
|
41
|
+
readonly text: string;
|
|
42
|
+
readonly citations?: readonly Citation[];
|
|
43
|
+
readonly responseId?: ResponseId;
|
|
44
|
+
/** Optimistic user bubble; removed if the turn fails BEFORE the first delta. */
|
|
45
|
+
readonly pending?: boolean;
|
|
46
|
+
}
|
|
47
|
+
interface ChatSnapshot {
|
|
48
|
+
readonly status: ChatStatus;
|
|
49
|
+
/** [] => Empty state; length > 0 => Populated. */
|
|
50
|
+
readonly messages: readonly ChatMessage[];
|
|
51
|
+
/** nulled on a 404 before recreate. */
|
|
52
|
+
readonly conversationId: ConversationId | null;
|
|
53
|
+
/** present iff status === 'error'; NEVER thrown. */
|
|
54
|
+
readonly error: EverfurError | null;
|
|
55
|
+
readonly isStreaming: boolean;
|
|
56
|
+
/** true during the initial conversation prime => Loading. */
|
|
57
|
+
readonly isBootstrapping: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Flattened, de-duplicated suggested-prompt strings from the prompts bootstrap (backend-owned copy; empty
|
|
60
|
+
* until the bootstrap resolves, empty when the backend returns none). The UI renders these as the tappable
|
|
61
|
+
* "Follow up questions" list; each string is a ready-to-send message.
|
|
62
|
+
*/
|
|
63
|
+
readonly suggestedPrompts: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
interface SendOptions {
|
|
66
|
+
readonly conversationId?: ConversationId;
|
|
67
|
+
readonly petRef?: PetRef;
|
|
68
|
+
readonly signal?: AbortSignal;
|
|
69
|
+
readonly idempotencyKey?: string;
|
|
70
|
+
}
|
|
71
|
+
interface ChatHandlers {
|
|
72
|
+
onFrame(frame: Frame): void;
|
|
73
|
+
onDone(responseId: ResponseId | null): void;
|
|
74
|
+
onError(error: EverfurError): void;
|
|
75
|
+
}
|
|
76
|
+
/** A suggested-prompt category returned by the prompts bootstrap endpoint. */
|
|
77
|
+
interface SuggestedPromptCategory {
|
|
78
|
+
readonly category: string;
|
|
79
|
+
readonly prompts: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
interface ChatControllerDeps {
|
|
82
|
+
/** Device auth context (bearer-XOR-pk precedence + lazy token). Carries the TransportPort. */
|
|
83
|
+
readonly auth: AuthContext;
|
|
84
|
+
readonly telemetry?: TelemetryPort;
|
|
85
|
+
readonly errorPolicy?: ErrorPolicyPort;
|
|
86
|
+
readonly timers?: Timers;
|
|
87
|
+
/** Injected RNG for deterministic backoff in tests. */
|
|
88
|
+
readonly rng?: () => number;
|
|
89
|
+
/** Injected id factory (client message ids) for deterministic tests. */
|
|
90
|
+
readonly newId?: () => string;
|
|
91
|
+
/** Injected idempotency-key factory for the TURN identity (see `buildSendRequest`). */
|
|
92
|
+
readonly makeIdempotencyKey?: () => string;
|
|
93
|
+
/** The ONE request funnel. Built from `auth` when absent; injected in tests for a deterministic clock. */
|
|
94
|
+
readonly funnel?: RequestFunnel;
|
|
95
|
+
readonly budget?: {
|
|
96
|
+
readonly idleMs?: number;
|
|
97
|
+
readonly overallMs?: number;
|
|
98
|
+
};
|
|
99
|
+
readonly initialConversationId?: ConversationId | null;
|
|
100
|
+
}
|
|
101
|
+
interface ChatController {
|
|
102
|
+
/** useSyncExternalStore subscribe. Returns an unsubscribe. */
|
|
103
|
+
subscribe(listener: () => void): () => void;
|
|
104
|
+
/** A stable frozen snapshot; identity changes only on real change. */
|
|
105
|
+
getSnapshot(): ChatSnapshot;
|
|
106
|
+
/** Stream a reply. The iterable ALWAYS completes cleanly; a failure is a terminal Frame, never a throw. */
|
|
107
|
+
send(text: string, opts?: SendOptions): AsyncIterable<Frame>;
|
|
108
|
+
/** Imperative escape hatch; returns a teardown, also torn down on dispose(). */
|
|
109
|
+
send(text: string, opts: SendOptions, handlers: ChatHandlers): () => void;
|
|
110
|
+
/** Re-send the last turn without a duplicate user bubble, reusing its idempotency key. */
|
|
111
|
+
retryLast(): AsyncIterable<Frame>;
|
|
112
|
+
/** Abort in-flight; not an error. Returns to pre-send (idle) and drops a never-answered optimistic bubble. */
|
|
113
|
+
stop(): void;
|
|
114
|
+
/** Idempotent. Aborts in-flight, bumps the epoch, drops listeners + handler subscriptions. */
|
|
115
|
+
dispose(): void;
|
|
116
|
+
/** Re-scope on user/pet switch: aborts in-flight and bumps the epoch so a late write drops. */
|
|
117
|
+
rescope(): void;
|
|
118
|
+
/** Bootstrap helper (§8): create a conversation. Unary, authed, settles. */
|
|
119
|
+
createConversation(opts?: {
|
|
120
|
+
readonly petRef?: PetRef;
|
|
121
|
+
}): Promise<EverfurResult<ConversationId>>;
|
|
122
|
+
/** Bootstrap helper (§8): fetch the session's suggested prompts, optionally personalized to a pet. Settles; never throws. */
|
|
123
|
+
getPrompts(opts?: {
|
|
124
|
+
readonly petRef?: PetRef;
|
|
125
|
+
}): Promise<EverfurResult<readonly SuggestedPromptCategory[]>>;
|
|
126
|
+
/** Adopt (or clear) the active conversation id, e.g. after bootstrap primes one. */
|
|
127
|
+
adoptConversation(id: ConversationId | null): void;
|
|
128
|
+
/**
|
|
129
|
+
* Adopt the suggested-prompt categories fetched by the bootstrap (§8): flattened + de-duplicated onto the
|
|
130
|
+
* snapshot's `suggestedPrompts` so the prebuilt UI can render the tappable follow-up list. Idempotent; a
|
|
131
|
+
* no-op when the flattened set is empty and none are shown.
|
|
132
|
+
*/
|
|
133
|
+
adoptPrompts(categories: readonly SuggestedPromptCategory[]): void;
|
|
134
|
+
}
|
|
135
|
+
declare function createChatController(deps: ChatControllerDeps): ChatController;
|
|
136
|
+
/**
|
|
137
|
+
* The server's own limits on a chat message, mirrored here so a violation is caught before any UI is
|
|
138
|
+
* rendered for it. `SendWidgetMessageRequest.message` on EFBackend main declares
|
|
139
|
+
* `min_length=1, max_length=4000`; the server remains authoritative and still enforces both.
|
|
140
|
+
*
|
|
141
|
+
* These are NOT in the generated contract projection, so they are pinned here with their source rather
|
|
142
|
+
* than derived. If the server relaxes either bound, this is the one place to update.
|
|
143
|
+
*/
|
|
144
|
+
declare const MESSAGE_MAX_CHARS = 4000;
|
|
145
|
+
|
|
146
|
+
export { type ChatController as C, type ErrorPolicyPort as E, MESSAGE_MAX_CHARS as M, type SuggestedPromptCategory as S, type Timers as T, type ChatControllerDeps as a, type ChatHandlers as b, type ChatMessage as c, type ChatSnapshot as d, type ChatStatus as e, type Citation as f, type SendOptions as g, createChatController as h, defaultErrorPolicy as i };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { R as RequestFunnel } from './requestFunnel-DuUH-kAe.cjs';
|
|
2
|
+
import { A as AuthContext } from './identity-Brl-lDd6.cjs';
|
|
3
|
+
import { a as EverfurErrorCode, f as EverfurError, E as EverfurResult } from './EverfurResult-D92-uL82.cjs';
|
|
4
|
+
import { b as ResponseId, a as ConversationId, P as PetRef } from './ids-CJ1S6adf.cjs';
|
|
5
|
+
import { F as Frame } from './config-BSjBdxrZ.cjs';
|
|
6
|
+
import { T as TelemetryPort } from './TelemetryPort-BDNr00hu.cjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Recovery policy. A host may narrow retry ceilings (e.g. a metered plan) but can never widen the SDK's
|
|
10
|
+
* settle-don't-throw contract. `attempt` is 0-based (the first try is attempt 0).
|
|
11
|
+
*/
|
|
12
|
+
interface ErrorPolicyPort {
|
|
13
|
+
/** Whether to retry `code` on this attempt. Default: `isAutoRetryable(code) && attempt < maxAttempts(code)`. */
|
|
14
|
+
shouldRetry(code: EverfurErrorCode, attempt: number): boolean;
|
|
15
|
+
/** Total attempts (the initial try plus retries) before the terminal error surfaces. Default 4. */
|
|
16
|
+
maxAttempts(code: EverfurErrorCode): number;
|
|
17
|
+
/** Observation hook for a handled, product-expected failure. Never rendered, never thrown. */
|
|
18
|
+
onExpectedFailure?(error: EverfurError): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The shipped default policy (applied when EverfurConfig omits `errorPolicy`). Pure and total.
|
|
22
|
+
* `authExpired` is deliberately NOT auto-retried here: it is a token refresh + single replay handled by the
|
|
23
|
+
* controller / authedSend, not a backoff retry (SPEC-00 §3.2 / §4.3).
|
|
24
|
+
*/
|
|
25
|
+
declare const defaultErrorPolicy: ErrorPolicyPort;
|
|
26
|
+
|
|
27
|
+
/** Injectable timers so the watchdog + deadlines are testable with a fake clock (no direct setTimeout). */
|
|
28
|
+
interface Timers {
|
|
29
|
+
setTimeout(handler: () => void, ms: number): unknown;
|
|
30
|
+
clearTimeout(handle: unknown): void;
|
|
31
|
+
now(): number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ChatStatus = 'idle' | 'streaming' | 'complete' | 'error';
|
|
35
|
+
/** Opaque citation record (DoneFrame.citations shape). Rendered by the UI layer, never trusted for logic. */
|
|
36
|
+
type Citation = Readonly<Record<string, unknown>>;
|
|
37
|
+
interface ChatMessage {
|
|
38
|
+
/** Stable list key: a client id for a user turn, the responseId for a finalized assistant turn. */
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly role: 'user' | 'assistant';
|
|
41
|
+
readonly text: string;
|
|
42
|
+
readonly citations?: readonly Citation[];
|
|
43
|
+
readonly responseId?: ResponseId;
|
|
44
|
+
/** Optimistic user bubble; removed if the turn fails BEFORE the first delta. */
|
|
45
|
+
readonly pending?: boolean;
|
|
46
|
+
}
|
|
47
|
+
interface ChatSnapshot {
|
|
48
|
+
readonly status: ChatStatus;
|
|
49
|
+
/** [] => Empty state; length > 0 => Populated. */
|
|
50
|
+
readonly messages: readonly ChatMessage[];
|
|
51
|
+
/** nulled on a 404 before recreate. */
|
|
52
|
+
readonly conversationId: ConversationId | null;
|
|
53
|
+
/** present iff status === 'error'; NEVER thrown. */
|
|
54
|
+
readonly error: EverfurError | null;
|
|
55
|
+
readonly isStreaming: boolean;
|
|
56
|
+
/** true during the initial conversation prime => Loading. */
|
|
57
|
+
readonly isBootstrapping: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Flattened, de-duplicated suggested-prompt strings from the prompts bootstrap (backend-owned copy; empty
|
|
60
|
+
* until the bootstrap resolves, empty when the backend returns none). The UI renders these as the tappable
|
|
61
|
+
* "Follow up questions" list; each string is a ready-to-send message.
|
|
62
|
+
*/
|
|
63
|
+
readonly suggestedPrompts: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
interface SendOptions {
|
|
66
|
+
readonly conversationId?: ConversationId;
|
|
67
|
+
readonly petRef?: PetRef;
|
|
68
|
+
readonly signal?: AbortSignal;
|
|
69
|
+
readonly idempotencyKey?: string;
|
|
70
|
+
}
|
|
71
|
+
interface ChatHandlers {
|
|
72
|
+
onFrame(frame: Frame): void;
|
|
73
|
+
onDone(responseId: ResponseId | null): void;
|
|
74
|
+
onError(error: EverfurError): void;
|
|
75
|
+
}
|
|
76
|
+
/** A suggested-prompt category returned by the prompts bootstrap endpoint. */
|
|
77
|
+
interface SuggestedPromptCategory {
|
|
78
|
+
readonly category: string;
|
|
79
|
+
readonly prompts: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
interface ChatControllerDeps {
|
|
82
|
+
/** Device auth context (bearer-XOR-pk precedence + lazy token). Carries the TransportPort. */
|
|
83
|
+
readonly auth: AuthContext;
|
|
84
|
+
readonly telemetry?: TelemetryPort;
|
|
85
|
+
readonly errorPolicy?: ErrorPolicyPort;
|
|
86
|
+
readonly timers?: Timers;
|
|
87
|
+
/** Injected RNG for deterministic backoff in tests. */
|
|
88
|
+
readonly rng?: () => number;
|
|
89
|
+
/** Injected id factory (client message ids) for deterministic tests. */
|
|
90
|
+
readonly newId?: () => string;
|
|
91
|
+
/** Injected idempotency-key factory for the TURN identity (see `buildSendRequest`). */
|
|
92
|
+
readonly makeIdempotencyKey?: () => string;
|
|
93
|
+
/** The ONE request funnel. Built from `auth` when absent; injected in tests for a deterministic clock. */
|
|
94
|
+
readonly funnel?: RequestFunnel;
|
|
95
|
+
readonly budget?: {
|
|
96
|
+
readonly idleMs?: number;
|
|
97
|
+
readonly overallMs?: number;
|
|
98
|
+
};
|
|
99
|
+
readonly initialConversationId?: ConversationId | null;
|
|
100
|
+
}
|
|
101
|
+
interface ChatController {
|
|
102
|
+
/** useSyncExternalStore subscribe. Returns an unsubscribe. */
|
|
103
|
+
subscribe(listener: () => void): () => void;
|
|
104
|
+
/** A stable frozen snapshot; identity changes only on real change. */
|
|
105
|
+
getSnapshot(): ChatSnapshot;
|
|
106
|
+
/** Stream a reply. The iterable ALWAYS completes cleanly; a failure is a terminal Frame, never a throw. */
|
|
107
|
+
send(text: string, opts?: SendOptions): AsyncIterable<Frame>;
|
|
108
|
+
/** Imperative escape hatch; returns a teardown, also torn down on dispose(). */
|
|
109
|
+
send(text: string, opts: SendOptions, handlers: ChatHandlers): () => void;
|
|
110
|
+
/** Re-send the last turn without a duplicate user bubble, reusing its idempotency key. */
|
|
111
|
+
retryLast(): AsyncIterable<Frame>;
|
|
112
|
+
/** Abort in-flight; not an error. Returns to pre-send (idle) and drops a never-answered optimistic bubble. */
|
|
113
|
+
stop(): void;
|
|
114
|
+
/** Idempotent. Aborts in-flight, bumps the epoch, drops listeners + handler subscriptions. */
|
|
115
|
+
dispose(): void;
|
|
116
|
+
/** Re-scope on user/pet switch: aborts in-flight and bumps the epoch so a late write drops. */
|
|
117
|
+
rescope(): void;
|
|
118
|
+
/** Bootstrap helper (§8): create a conversation. Unary, authed, settles. */
|
|
119
|
+
createConversation(opts?: {
|
|
120
|
+
readonly petRef?: PetRef;
|
|
121
|
+
}): Promise<EverfurResult<ConversationId>>;
|
|
122
|
+
/** Bootstrap helper (§8): fetch the session's suggested prompts, optionally personalized to a pet. Settles; never throws. */
|
|
123
|
+
getPrompts(opts?: {
|
|
124
|
+
readonly petRef?: PetRef;
|
|
125
|
+
}): Promise<EverfurResult<readonly SuggestedPromptCategory[]>>;
|
|
126
|
+
/** Adopt (or clear) the active conversation id, e.g. after bootstrap primes one. */
|
|
127
|
+
adoptConversation(id: ConversationId | null): void;
|
|
128
|
+
/**
|
|
129
|
+
* Adopt the suggested-prompt categories fetched by the bootstrap (§8): flattened + de-duplicated onto the
|
|
130
|
+
* snapshot's `suggestedPrompts` so the prebuilt UI can render the tappable follow-up list. Idempotent; a
|
|
131
|
+
* no-op when the flattened set is empty and none are shown.
|
|
132
|
+
*/
|
|
133
|
+
adoptPrompts(categories: readonly SuggestedPromptCategory[]): void;
|
|
134
|
+
}
|
|
135
|
+
declare function createChatController(deps: ChatControllerDeps): ChatController;
|
|
136
|
+
/**
|
|
137
|
+
* The server's own limits on a chat message, mirrored here so a violation is caught before any UI is
|
|
138
|
+
* rendered for it. `SendWidgetMessageRequest.message` on EFBackend main declares
|
|
139
|
+
* `min_length=1, max_length=4000`; the server remains authoritative and still enforces both.
|
|
140
|
+
*
|
|
141
|
+
* These are NOT in the generated contract projection, so they are pinned here with their source rather
|
|
142
|
+
* than derived. If the server relaxes either bound, this is the one place to update.
|
|
143
|
+
*/
|
|
144
|
+
declare const MESSAGE_MAX_CHARS = 4000;
|
|
145
|
+
|
|
146
|
+
export { type ChatController as C, type ErrorPolicyPort as E, MESSAGE_MAX_CHARS as M, type SuggestedPromptCategory as S, type Timers as T, type ChatControllerDeps as a, type ChatHandlers as b, type ChatMessage as c, type ChatSnapshot as d, type ChatStatus as e, type Citation as f, type SendOptions as g, createChatController as h, defaultErrorPolicy as i };
|