@vinaverse/server 0.1.0-alpha.2 → 0.1.0-alpha.20

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.
@@ -15,11 +15,17 @@ interface paths {
15
15
  put?: never;
16
16
  /**
17
17
  * Create an agent session for one subject
18
- * @description A session belongs to a conversation when it names one (`conversation_key`, `conversation_id`, or both) or when its agent runs a guided flow; a flow session that names none starts a new conversation. `Session` then returns its `conversation_id`, and a conversation started without a key can later be resumed by that id. A conversation is scoped to the tenant, the environment, the `subject.id` and the `agent`; `subject.type` is not part of it, so a guest who signs in keeps the conversation.
18
+ * @description A session belongs to a conversation when it names one (`conversation_key`, `conversation_id`, or both) or when its agent runs a guided flow; a flow session that names none starts a new conversation. `Session` then returns its `conversation_id`, and a conversation started without a key can later be resumed by that id. A conversation is scoped to the tenant, the environment, the `subject.id` and the `agent`, and (v1-draft.20) the mode and business scope it started with; `subject.type` itself is not part of it, so a guest who signs in keeps the conversation.
19
19
  * **`business_setup`.** The `business_setup` capability is only for a business's own people: a `subject.type` of `professional`, `host` or `staff`. Asked for any other subject, it is `400 invalid_request` with the pointer `/tool_access/capabilities`.
20
20
  * **`booking_proposals`.** The `booking_proposals` capability is only for a `client`: not a guest, who has no account in the tenant's app to confirm with. Asked for any other subject, it is `400 invalid_request` with the pointer `/tool_access/capabilities`.
21
+ * **`business_bookings` and `payouts`.** Like `business_setup`, only for a business's own people: a `subject.type` of `professional`, `host` or `staff`. Asked for any other subject, it is `400 invalid_request` with the pointer `/tool_access/capabilities` (v1-draft.38).
22
+ * **`quotes`** (v1-draft.31). The `quotes` capability is never for a `guest`: a cart is priced for a signed-in person. Asked for a guest, it is `400 invalid_request` with the pointer `/tool_access/capabilities`.
23
+ * **Audience** (v1-draft.20). An agent talks only to the subject types of its template (admin-v1 `AgentProfile.audience`): the customer agent to a `client` or `guest`, a business agent to a `professional`, `host` or `staff`, a guided flow to anyone its tenant names. Any other `subject.type` is `400 invalid_request` with the pointer `/subject/type`. The tenant's backend sets the type by the side of its app the person is on, never by the account's roles: a professional booking for themselves is a `client`.
24
+ * **Mode and business scope** (v1-draft.20). `experience_mode` is the agent's side: `customer` for the customer agent, `business` for a business agent. Left out, it follows the agent's template; given, it must match it (`400 invalid_request`, `/experience_mode`). `business_scope_id` names the business the person acts for (a professional's business, a host's place): only with `business` (`400`, `/business_scope_id`), and the tenant's backend derives it from its own records, never from the app. A conversation is bound, when it starts, to the subject, the agent, the mode and the business scope. The mode stands for the side of the app, so a guest who signs in as a client keeps their conversation, while a professional never resumes a customer conversation (the audience rule keeps each side's types apart). Resuming with any of them different is `404 not_found` (step 1 below), never a switch: a person acting for business A never resumes business B's conversation.
21
25
  * **The checks, in this order:** 1. `conversation_id` is given, and is unknown, erased, or belongs to another tenant, environment, `agent` or
22
- * `subject.id`: `404 not_found`.
26
+ * `subject.id`, or (v1-draft.20) was started with another `experience_mode` or `business_scope_id`: `404
27
+ * not_found`. A `conversation_key` names a conversation within the same binding,
28
+ * so the same key under another binding names another conversation.
23
29
  * 2. Both are given, and the key names a different conversation, or none: `409 conversation_mismatch`. 3. The conversation expired: `410 conversation_expired`. With `conversation_key` alone, an expired
24
30
  * conversation instead lets a new one start under the key; the id never starts one silently.
25
31
  * 4. The conversation is completed (its flow was accepted): `409 conversation_completed`. This holds by key too,
@@ -69,6 +75,30 @@ interface paths {
69
75
  patch?: never;
70
76
  trace?: never;
71
77
  };
78
+ "/v1/conversations": {
79
+ parameters: {
80
+ query?: never;
81
+ header?: never;
82
+ path?: never;
83
+ cookie?: never;
84
+ };
85
+ /**
86
+ * A person's past conversations with one agent, newest activity first
87
+ * @description v1-draft.21 (VIN-246): the conversations a history screen lists, for one subject and one agent, on one side of the app (`experience_mode`, and for a business `business_scope_id`, as the conversation was bound when it started). Newest activity first; ties by `id`.
88
+ * **Access.** A service key only, never a client secret: the app asks the tenant's backend, which checks the person and calls this. The key needs `conversations:read` (a key without scopes has every scope) and sees its own tenant and environment only.
89
+ * **What is listed.** Only what the tenant's `transcript_retention` still keeps: a conversation past it, a deleted one (`deleteConversation`) or an erased subject's is never listed, nor one where nothing was said (a session opened and left). A subject Vinaverse holds nothing for gets an empty page, the same as one with no conversations, so the answer never says whether a subject exists.
90
+ * **The title** is deterministic and never the person's words: from the conversation's last search intent (`title_source: intent`, for example "Haircut · Los Angeles", in the subject's locale), else the date it started (`title_source: date`). There is no preview of the messages.
91
+ * **Paging.** `next_cursor` is opaque and signed. It is bound to the filters, the key's tenant and environment, and expires after 24 hours: a cursor changed, used with other filters or expired is `400 invalid_request` at `/cursor`, and the caller starts again without it. An outage is `503 unavailable` (retryable), never an empty page: the app does not take it as "no conversations".
92
+ */
93
+ get: operations["listConversations"];
94
+ put?: never;
95
+ post?: never;
96
+ delete?: never;
97
+ options?: never;
98
+ head?: never;
99
+ patch?: never;
100
+ trace?: never;
101
+ };
72
102
  "/v1/conversations/{conversation_id}": {
73
103
  parameters: {
74
104
  query?: never;
@@ -91,6 +121,42 @@ interface paths {
91
121
  get: operations["getConversation"];
92
122
  put?: never;
93
123
  post?: never;
124
+ /**
125
+ * Delete one of a person's conversations, everywhere
126
+ * @description v1-draft.21 (VIN-246): the person deletes a conversation from their history, through the tenant's backend. A service key with `conversations:write`, for its own tenant and environment; `subject_id` must be the conversation's subject.
127
+ * **What it does, before it answers.** - Ends the conversation's open sessions: their sockets get `session.closed` with `reason: deleted` and
128
+ * close with `4403` (a client that does not know `deleted` reads it as `revoked`). Their status ends as
129
+ * `deleted`.
130
+ * - Blanks every copy of the conversation's messages, cards, screening texts and state (and the guided flow's
131
+ * answers), across both sides of the app, and leaves no title or count behind.
132
+ * - Fences late writes: a turn still running, a webhook retry or a worker job that finishes after the delete
133
+ * records nothing for it (a deletion barrier, kept as long as the longest session could run).
134
+ * - Frees its `conversation_key`: a session with the same key afterwards starts a new conversation.
135
+ * **Answers.** `200` with the number of sessions revoked. An unknown id, another tenant's, environment's or subject's, one bound to another `agent`, side or business than the call names (v1-draft.24), or one already deleted or erased: `404 not_found`, the same answer for each, so it never says whether the conversation exists. Deleting is not erasing the subject (`eraseSubject`): the person's other conversations and memory stay.
136
+ */
137
+ delete: operations["deleteConversation"];
138
+ options?: never;
139
+ head?: never;
140
+ patch?: never;
141
+ trace?: never;
142
+ };
143
+ "/v1/conversations/{conversation_id}/messages": {
144
+ parameters: {
145
+ query?: never;
146
+ header?: never;
147
+ path?: never;
148
+ cookie?: never;
149
+ };
150
+ /**
151
+ * One past conversation, as one ordered timeline of messages and cards
152
+ * @description v1-draft.21 (VIN-246): what a history screen draws when the person opens a past conversation. A service key with `conversations:read`, for its own tenant and environment; `subject_id` must be the conversation's subject.
153
+ * **The timeline.** One list of messages and the cards their turns drew, in the order they happened, each with an `id` that is stable and unique within the conversation. The first page is the newest; within a page, oldest first; `next_cursor` pages toward older items. A message carries `event_id`, the identifier its live event gave it (as `TranscriptItem.id`: an `assistant` message's `turn_id`, a `person` message's `client_event_id`, a `staff` message's `message_id`), so an app merges what it drew live, or a resumed `session.ready.transcript`, into the fetched history by `role` and `event_id` instead of replacing either. An input the runtime never recorded is not in it, as in the transcript.
154
+ * **Cards from history are read-only.** An app draws their actions disabled: a past slot, price or availability is not checked again, so a tap would act on stale data. The person asks again in the conversation, and a fresh turn draws fresh cards.
155
+ * **What is kept.** Only what the tenant's `transcript_retention` still keeps; a conversation past it is `410 conversation_expired`. An unknown id, another tenant's, environment's or subject's, or a deleted or erased one, or one bound to another `agent`, side or business than the call names (v1-draft.24): `404 not_found`, the same answer for each. A cursor changed, used for another conversation or expired (24 hours) is `400 invalid_request` at `/cursor`. An outage is `503 unavailable`, never an empty timeline.
156
+ */
157
+ get: operations["listConversationMessages"];
158
+ put?: never;
159
+ post?: never;
94
160
  delete?: never;
95
161
  options?: never;
96
162
  head?: never;
@@ -113,6 +179,28 @@ interface paths {
113
179
  patch?: never;
114
180
  trace?: never;
115
181
  };
182
+ "/v1/sessions/{session_id}/context": {
183
+ parameters: {
184
+ query?: never;
185
+ header?: never;
186
+ path?: never;
187
+ cookie?: never;
188
+ };
189
+ get?: never;
190
+ /**
191
+ * Tell an open session where the person is now
192
+ * @description v1-draft.23 (VIN-115): the person moved to another screen while the chat is open; the tenant's backend, which checked that they may see what is on it, says so, and the session keeps going (no new session per navigation). A service key with `sessions:write`, for the session's own tenant and environment: another tenant's or an unknown session is `404 not_found`, an ended one `409 session_closed`.
193
+ * **The body** is a `ScreenContext` (the same type and rules as on `createSession`: a fixed vocabulary, typed filters, never page text, and it grants nothing), or `{clear: true, revision}` when the person left any screen that says something (the agent then has no screen context). Either way `revision` must be greater than the session's: the tenant's own counter for this person's screen, so a late update never overwrites a newer one. An older or equal revision is `409 context_stale` with `current_revision`, except the same revision with the same body, which is a retry and answered `200` as the first time.
194
+ * **What follows.** The session's open socket gets `context.updated` (the revision, and `cleared: true` after a clear). A turn already running keeps the context it started with; the next turn reads the new one, as data in the turn's details, never in the cached prompt.
195
+ */
196
+ put: operations["updateSessionContext"];
197
+ post?: never;
198
+ delete?: never;
199
+ options?: never;
200
+ head?: never;
201
+ patch?: never;
202
+ trace?: never;
203
+ };
116
204
  "/v1/sessions/{session_id}/revoke": {
117
205
  parameters: {
118
206
  query?: never;
@@ -140,7 +228,7 @@ interface paths {
140
228
  put?: never;
141
229
  /**
142
230
  * A fresh client secret for an open session
143
- * @description A service key with the `sessions:write` scope gets a new connect secret for an open session, for an app that missed the connect window or connects from another device (whose connection then replaces the open one, with close code 4409). The previous secret no longer authenticates a new connection or an SSE turn. A socket already open stays open and keeps its session: refreshing never drops the person's live connection. To cut it off, revoke the session (`revokeSession`). An ended session is `409 session_closed`.
231
+ * @description A service key with the `sessions:write` scope gets a new connect secret for an open session, for an app that missed the connect window or connects from another device (whose connection then replaces the open one, with close code 4409). The previous secret no longer authenticates a new connection or an SSE turn. A socket already open stays open and keeps its session: refreshing never drops the person's live connection. To cut it off, revoke the session (`revokeSession`). An ended session is `409 session_closed`. With an optional `Idempotency-Key` (declared in v1-draft.22), a retry within 24 hours answers the secret first issued for that key instead of a newer one.
144
232
  */
145
233
  post: operations["refreshClientSecret"];
146
234
  delete?: never;
@@ -158,7 +246,10 @@ interface paths {
158
246
  };
159
247
  get?: never;
160
248
  put?: never;
161
- /** Run one turn and stream its events as SSE (alternative to the WebSocket) */
249
+ /**
250
+ * Run one turn and stream its events as SSE (alternative to the WebSocket)
251
+ * @description Browsers and WebViews (v1-draft.18): the preflight (`OPTIONS`) is answered for the tenant's client origins of the session's environment only (admin-v1 `ClientOrigins.effective`), with `Access-Control-Allow-Origin` the request's origin (never `*`), `Vary: Origin`, `Access-Control-Allow-Methods: POST`, `Access-Control-Allow-Headers: Authorization, Content-Type` and `Access-Control-Max-Age: 600`; no credentials mode is needed (the client secret is a header). Any other origin, or a session that does not exist, gets no CORS headers. A request whose `Origin` is not one of them is refused with `403` `forbidden` and runs nothing; a request with no `Origin` (a native app, a server) is not checked.
252
+ */
162
253
  post: operations["createTurnSse"];
163
254
  delete?: never;
164
255
  options?: never;
@@ -175,7 +266,10 @@ interface paths {
175
266
  };
176
267
  get?: never;
177
268
  put?: never;
178
- /** Send a card action and stream the resulting turn as SSE */
269
+ /**
270
+ * Send a card action and stream the resulting turn as SSE
271
+ * @description Browsers and WebViews (v1-draft.18), as for `createTurnSse`: the preflight (`OPTIONS`) is answered for the tenant's client origins of the session's environment only (admin-v1 `ClientOrigins.effective`), with `Access-Control-Allow-Origin` the request's origin (never `*`), `Vary: Origin`, `Access-Control-Allow-Methods: POST`, `Access-Control-Allow-Headers: Authorization, Content-Type` and `Access-Control-Max-Age: 600`; no credentials mode is needed (the client secret is a header). Any other origin, or a session that does not exist, gets no CORS headers. A request whose `Origin` is not one of them is refused with `403` `forbidden` and runs nothing; a request with no `Origin` (a native app, a server) is not checked.
272
+ */
179
273
  post: operations["createCardActionSse"];
180
274
  delete?: never;
181
275
  options?: never;
@@ -231,6 +325,26 @@ interface paths {
231
325
  patch?: never;
232
326
  trace?: never;
233
327
  };
328
+ "/v1/media/avatars/{avatar_file}": {
329
+ parameters: {
330
+ query?: never;
331
+ header?: never;
332
+ path?: never;
333
+ cookie?: never;
334
+ };
335
+ /**
336
+ * An agent's avatar image (v1-draft.19), as `Session.agent.avatar_url` names it
337
+ * @description Public: no credential, since an app draws it in an image view. Only an avatar that passed the screen is served, and only while it is an agent's current or previous avatar of the last 30 days; anything else is `404`. The bytes never change under a name (`Cache-Control: public, max-age=31536000, immutable`), and the response is `image/webp` with `X-Content-Type-Options: nosniff` and a sandboxing `Content-Security-Policy`.
338
+ */
339
+ get: operations["getAgentAvatarImage"];
340
+ put?: never;
341
+ post?: never;
342
+ delete?: never;
343
+ options?: never;
344
+ head?: never;
345
+ patch?: never;
346
+ trace?: never;
347
+ };
234
348
  "/v1/media/jobs": {
235
349
  parameters: {
236
350
  query?: never;
@@ -382,7 +496,8 @@ interface paths {
382
496
  get?: never;
383
497
  put?: never;
384
498
  /**
385
- * @description Erases the subject's data: transcripts, screening texts, memory, indexes, the conversation state of its guided flows (recorded answers, remarks, transcript), and the explore suggestion decisions made for it, and revokes its open sessions. `subject.erased` follows.
499
+ * @description Erases the subject's data: transcripts, screening texts, memory (v1-draft.27: the preferences and the last search; a choice not to be remembered is kept, as a keyed hash of the subject id only), indexes, the conversation state of its guided flows (recorded answers, remarks, transcript), and the explore suggestion decisions made for it, and revokes its open sessions. `subject.erased` follows.
500
+ * Backups are not edited (v1-draft.39): the erasure is re-applied after every restore of Vinaverse's database, so a backup never brings it back, and backups expire within 35 days.
386
501
  * **A subject Vinaverse holds nothing for** (never seen, or already erased) is `404` with `code: not_found` and `type: https://docs.vinaverse.com/problems/subject-not-found`; a client matches on the `type` ending in `/problems/subject-not-found`. Only that answer means nothing is held, and nothing needs erasing. Vinaverse keeps no record of erased subjects to tell the two apart. Any other `404` (a wrong base URL, say) is not that answer, and the caller retries.
387
502
  */
388
503
  post: operations["eraseSubject"];
@@ -392,6 +507,79 @@ interface paths {
392
507
  patch?: never;
393
508
  trace?: never;
394
509
  };
510
+ "/v1/subjects/{subject_id}/memory": {
511
+ parameters: {
512
+ query?: never;
513
+ header?: never;
514
+ path?: never;
515
+ cookie?: never;
516
+ };
517
+ /**
518
+ * What the agent remembers about a person, and whether it may
519
+ * @description v1-draft.27 (VIN-223): what Vinaverse keeps to remember a person across conversations, so the tenant's app can show it to them. A service key with `memory:read`, for its own tenant and environment; the app asks the tenant's backend, never with a client secret.
520
+ * **What is kept, and nothing else.** - `remembered`: whether the person lets the agent remember them (on by default; `putSubjectMemory` turns it
521
+ * off for both sides of the app).
522
+ * - `notice_acknowledged`: the version of the memory notice the person acknowledged, if any. - `preferences`: what the person asked the agent to remember, in the words the agent said back to the
523
+ * person, each with the conversation it came from. Never inferred from what they viewed or booked. Never a
524
+ * health fact or a never-store detail (v1-draft.39); a fact about someone else is the person's own note.
525
+ * - `last_search`: the last open search intent (a service and a place, as the search resolved them, never the
526
+ * person's words), for at most 7 days, which the agent may offer as a question.
527
+ *
528
+ * Preferences and the last search belong to one side of the app: `experience_mode` (left out, `customer`) and, for a business, `business_scope_id`. A subject Vinaverse holds nothing for reads as remembered, with nothing kept. Erasing the subject (`eraseSubject`) removes preferences and the last search; a choice not to be remembered outlives it, kept as a keyed hash of the subject id only.
529
+ */
530
+ get: operations["getSubjectMemory"];
531
+ /**
532
+ * Let the agent remember a person, or not
533
+ * @description v1-draft.27 (VIN-223): the person's choice, through the tenant's backend (a service key with `memory:write`). `remembered: false` is "don't remember me": on both sides of the app at once, it deletes the preferences and the last search, and from then on no session reads or writes memory for the person and no earlier conversation is mentioned to the agent. It outlives an erasure. `remembered: true` turns memory back on, with nothing restored. Idempotent: the same choice again changes nothing.
534
+ */
535
+ put: operations["putSubjectMemory"];
536
+ post?: never;
537
+ delete?: never;
538
+ options?: never;
539
+ head?: never;
540
+ patch?: never;
541
+ trace?: never;
542
+ };
543
+ "/v1/subjects/{subject_id}/memory/notice": {
544
+ parameters: {
545
+ query?: never;
546
+ header?: never;
547
+ path?: never;
548
+ cookie?: never;
549
+ };
550
+ get?: never;
551
+ put?: never;
552
+ /**
553
+ * The person saw the memory notice
554
+ * @description v1-draft.27 (VIN-223): the app showed `Session.memory.notice` and the person dismissed it; the tenant's backend records that (a service key with `memory:write`), so no later session carries that version. A newer version is shown again. `version` must be the current notice's (`400` at `/version` otherwise). Idempotent.
555
+ */
556
+ post: operations["acknowledgeMemoryNotice"];
557
+ delete?: never;
558
+ options?: never;
559
+ head?: never;
560
+ patch?: never;
561
+ trace?: never;
562
+ };
563
+ "/v1/subjects/{subject_id}/memory/preferences/{preference_id}": {
564
+ parameters: {
565
+ query?: never;
566
+ header?: never;
567
+ path?: never;
568
+ cookie?: never;
569
+ };
570
+ get?: never;
571
+ put?: never;
572
+ post?: never;
573
+ /**
574
+ * Forget one thing the person asked the agent to remember
575
+ * @description v1-draft.27 (VIN-223): a service key with `memory:write`. The preference is deleted everywhere; the agent no longer reads it from the next turn. Another subject's, an unknown or an already deleted preference is the same `404 not_found`.
576
+ */
577
+ delete: operations["deleteMemoryPreference"];
578
+ options?: never;
579
+ head?: never;
580
+ patch?: never;
581
+ trace?: never;
582
+ };
395
583
  "/v1/events": {
396
584
  parameters: {
397
585
  query?: never;
@@ -441,6 +629,46 @@ interface paths {
441
629
  patch?: never;
442
630
  trace?: never;
443
631
  };
632
+ "/v1/agents/{agent}/experience": {
633
+ parameters: {
634
+ query?: never;
635
+ header?: never;
636
+ path?: never;
637
+ cookie?: never;
638
+ };
639
+ /**
640
+ * The chat experience the tenant set for an agent, before any session
641
+ * @description v1-draft.40 (VIN-115, the chat spec's module G TC-02): what the app needs before a session opens (the launcher, the switcher, history, an Ask row), for the tenant's backend to copy into its agent status (agent-backend `status.features.chat`). A service key with `sessions:write`, for its own tenant and environment. The value is resolved as `Session.experience` is, except for a client's capabilities. Cacheable for 60 seconds: a change in Studio reaches new sessions at once and this read within a minute. An unknown or inactive agent is `404 not_found`.
642
+ */
643
+ get: operations["getAgentExperience"];
644
+ put?: never;
645
+ post?: never;
646
+ delete?: never;
647
+ options?: never;
648
+ head?: never;
649
+ patch?: never;
650
+ trace?: never;
651
+ };
652
+ "/v1/meta": {
653
+ parameters: {
654
+ query?: never;
655
+ header?: never;
656
+ path?: never;
657
+ cookie?: never;
658
+ };
659
+ /**
660
+ * The contract version this gateway runs
661
+ * @description v1-draft.25 (VIN-115): what is deployed behind this base URL. No credential and no tenant: the same answer for everyone, cacheable for 60 seconds. `api_version` is this document's `info.version` as the gateway serves it (the `Vinaverse-Api-Version` response header carries the same); `contracts.commit` is the vinaverse-contracts commit the build pinned. A field the build did not record is absent, never guessed. The gateway's own commit is not served: it would fingerprint the exact deployment for a scanner, and an SDK needs only these two.
662
+ */
663
+ get: operations["getMeta"];
664
+ put?: never;
665
+ post?: never;
666
+ delete?: never;
667
+ options?: never;
668
+ head?: never;
669
+ patch?: never;
670
+ trace?: never;
671
+ };
444
672
  "/v1/usage": {
445
673
  parameters: {
446
674
  query?: never;
@@ -1177,16 +1405,20 @@ interface webhooks {
1177
1405
  }
1178
1406
  interface components {
1179
1407
  schemas: {
1408
+ /** @description A model as Vinaverse names it (v1-draft.34): a release `<family>@<date>.<n>` (immutable) or a family alias `<family>` or `<family>:<channel>` of a family tenants read (`vina-agent`, the language model; `vina-embed`, the embeddings), or, when no model made a decision, the producer's own id (`vv-detectors`, `mock`). Never a base model, a size, a host or a model server's id. Its display name is the object's `label` (v1-draft.35). */
1409
+ ModelName: string;
1180
1410
  Problem: {
1181
1411
  /** Format: uri */
1182
1412
  type: string;
1183
1413
  title: string;
1184
1414
  status: number;
1185
1415
  /** @enum {string} */
1186
- code: "invalid_request" | "unauthenticated" | "forbidden" | "feature_unavailable" | "not_found" | "idempotency_conflict" | "session_closed" | "payload_too_large" | "unprocessable_media" | "rate_limited" | "internal" | "unavailable" | "conversation_mismatch" | "conversation_expired" | "conversation_completed";
1416
+ code: "invalid_request" | "unauthenticated" | "forbidden" | "feature_unavailable" | "not_found" | "idempotency_conflict" | "session_closed" | "payload_too_large" | "unprocessable_media" | "rate_limited" | "internal" | "unavailable" | "conversation_mismatch" | "conversation_expired" | "conversation_completed" | "context_stale";
1187
1417
  detail?: string;
1188
1418
  request_id: string;
1189
1419
  retryable: boolean;
1420
+ /** @description v1-draft.23, with `context_stale` only, the session's screen-context revision now */
1421
+ current_revision?: number;
1190
1422
  /** @description field-level problems for invalid_request */
1191
1423
  errors?: {
1192
1424
  /** @description JSON Pointer into the request */
@@ -1218,6 +1450,7 @@ interface components {
1218
1450
  locale?: string;
1219
1451
  /** @example America/New_York */
1220
1452
  timezone?: string;
1453
+ /** @description the person's coarse area (a profile area or a device's city), never a street; like `ScreenContext.filters.near`, the gateway rounds `lat` and `lng` to 2 decimals on receipt, before either is stored, logged or forwarded to a tool */
1221
1454
  approximate_location?: {
1222
1455
  lat: number;
1223
1456
  lng: number;
@@ -1228,10 +1461,10 @@ interface components {
1228
1461
  /** @enum {string} */
1229
1462
  Feature: "assistant" | "moderation_image" | "moderation_video" | "moderation_text" | "screening" | "document_check" | "insights" | "suggestions";
1230
1463
  /**
1231
- * @description `business_setup`: grants the business's own read tools and its `propose` tools (`tool-provider-v1.yaml`, `x-effect: propose`), for the `business_setup` agent; never a write tool. Only a subject of type `professional`, `host` or `staff` may be granted it. `booking_proposals`: grants the tenant's `propose` tools for a client's bookings (a booking, a reschedule, a cancellation the client confirms in the tenant's app), for the customer agent; never a write tool. Only a subject of type `client` may be granted it (`docs/agents-guardrails-suggestions-writes.md` §3.4). These two are the only capabilities that carry `propose` tools: `create_proposal` and every tenant `propose` tool require one of them. `flows`: grants the guided-flow endpoints (`tool-provider-v1.yaml`, `/flows/*`). It names no flow and no record: the tenant limits the `tool_access` token itself to one flow and one record. `my_orders`: grants reading the customer's own kits and orders from a styling-kit retailer (`tool-provider-v1.yaml`, `list_my_kits`, `get_my_kit`, `list_my_orders`, `get_my_order`), read only. Like `my_bookings`, it is meant for a signed-in `client`, and the tenant answers for the token's customer only.
1464
+ * @description `quotes`: grants pricing a cart before booking it (`tool-provider-v1.yaml` `get_quote`), read only; never granted to a `guest` (v1-draft.31). `business_setup`: grants the business's own read tools and its `propose` tools (`tool-provider-v1.yaml`, `x-effect: propose`), for the `business_setup` agent; never a write tool. Only a subject of type `professional`, `host` or `staff` may be granted it. `booking_proposals`: grants the tenant's `propose` tools for a client's bookings (a booking, a reschedule, a cancellation the client confirms in the tenant's app), for the customer agent; never a write tool. Only a subject of type `client` may be granted it (`docs/agents-guardrails-suggestions-writes.md` §3.4). These two are the only capabilities that carry `propose` tools: `create_proposal` and every tenant `propose` tool require one of them. `flows`: grants the guided-flow endpoints (`tool-provider-v1.yaml`, `/flows/*`). It names no flow and no record: the tenant limits the `tool_access` token itself to one flow and one record. `my_orders`: grants reading the customer's own kits and orders from a styling-kit retailer (`tool-provider-v1.yaml`, `list_my_kits`, `get_my_kit`, `list_my_orders`, `get_my_order`), read only. Like `my_bookings`, it is meant for a signed-in `client`, and the tenant answers for the token's customer only. `business_bookings`: grants reading the business's own bookings, those waiting for its answer and those coming up, read only. `payouts`: grants reading the state of the business's own payout account (not set up, under review, active), read only, never an amount or a bank detail. Only a subject of type `professional`, `host` or `staff` may be granted either (v1-draft.38).
1232
1465
  * @enum {string}
1233
1466
  */
1234
- Capability: "search" | "availability" | "quotes" | "my_bookings" | "policies" | "handoff" | "flows" | "business_setup" | "booking_proposals" | "my_orders";
1467
+ Capability: "search" | "availability" | "quotes" | "my_bookings" | "policies" | "handoff" | "flows" | "business_setup" | "booking_proposals" | "my_orders" | "business_bookings" | "payouts";
1235
1468
  ToolAccess: {
1236
1469
  token: string;
1237
1470
  /** Format: date-time */
@@ -1349,87 +1582,541 @@ interface components {
1349
1582
  conversation_key?: string;
1350
1583
  /** @description Optional. Resumes the conversation with this id (`Session.conversation_id`, `ConversationState`), as an alternative to `conversation_key` or together with it; `createSession` describes resuming and the order of its checks. */
1351
1584
  conversation_id?: components["schemas"]["ConversationId"];
1585
+ experience_mode?: components["schemas"]["ExperienceMode"];
1586
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
1587
+ screen_context?: components["schemas"]["ScreenContext"];
1588
+ anchor?: components["schemas"]["SessionAnchor"];
1589
+ client?: components["schemas"]["SessionClient"];
1590
+ entry?: components["schemas"]["SessionEntry"];
1352
1591
  };
1353
- ClientSecret: {
1354
- /** @description `vv_cs_` and base64url characters, so the secret is a valid WebSocket subprotocol token in `bearer.<secret>`. */
1355
- value: string;
1592
+ /**
1593
+ * @description v1-draft.20, optional on `createSession`: the side of the tenant's app the person is on. `customer` for the customer agent (a `client` or `guest`), `business` for a business agent (a `professional`, `host` or `staff`). Left out, it follows the agent's template; `createSession` describes the binding.
1594
+ * @enum {string}
1595
+ */
1596
+ ExperienceMode: "customer" | "business";
1597
+ /** @description v1-draft.20, optional, with `experience_mode: business` only: the business the person acts for, as the tenant's backend knows it (a professional's business, a host's place). Opaque to Vinaverse; it binds the conversation, so a person acting for two businesses keeps two conversations. It never reaches a model prompt. */
1598
+ BusinessScopeId: string;
1599
+ /** @description v1-draft.20, optional on `createSession`: where the person is in the tenant's app when the session opens, as the tenant's backend vouches for it (it checked that the person may see the entity). It grants nothing: no tool, capability or record. The agent reads it as data in its working notes (never in the cached system prompt, never as something the person said), as a hint: before it states anything about the entity it reads it with a tool, and a tool that refuses means the agent says it cannot see it. A screen change while the chat is open does not open a new session: the tenant's backend sends it with `updateSessionContext` (v1-draft.23). Never arbitrary page text, URLs, names, amounts or instructions: only these fields, from this vocabulary. */
1600
+ ScreenContext: {
1356
1601
  /**
1357
- * Format: date-time
1358
- * @description The end of the connect window (120 s after issue): the secret must open a connection (over SSE: run its first turn) by then. Once used, it is bound to the session until the session ends or a new secret is issued (`refreshClientSecret`), so the app reconnects with the same secret (and sends it on every SSE turn) without asking for a new one.
1602
+ * @description A coarse screen from a fixed vocabulary; the tenant maps its own screens onto it. `other` when none fits. Customer side: `home`, `explore`, `search_results`, `professional`, `workspace`, `service`, `class`, `checkout`, `booking`, `bookings`, `inbox`, `profile`. Business side: `business_hub`, `setup`, `services`, `hours`, `location`, `gallery`, `spaces`, `space_manage`, `renters`, `payouts`, `plans`, `team`.
1603
+ * @enum {string}
1359
1604
  */
1360
- expires_at: string;
1361
- };
1362
- Session: {
1363
- id: string;
1364
- /** @enum {string} */
1365
- status: "open";
1366
- agent: {
1605
+ screen: "home" | "explore" | "search_results" | "professional" | "workspace" | "service" | "class" | "checkout" | "booking" | "bookings" | "inbox" | "profile" | "business_hub" | "setup" | "services" | "hours" | "location" | "gallery" | "spaces" | "space_manage" | "renters" | "payouts" | "plans" | "team" | "other";
1606
+ /** @description the object on the screen, as the tenant's read tools name it; `id` is a tool id (letters, digits and `_`, `.`, `:`, `-`), since it reaches the agent's working notes */
1607
+ entity?: {
1608
+ /** @enum {string} */
1609
+ type: "professional" | "workspace" | "service" | "class" | "booking" | "location";
1367
1610
  id: string;
1368
- version: string;
1369
- display_name: string;
1370
1611
  };
1371
- client_secret: components["schemas"]["ClientSecret"];
1372
- /** Format: uri */
1373
- realtime_url: string;
1374
- /** Format: uri */
1375
- turns_url: string;
1612
+ /** @description the filters the screen shows, typed; free text never */
1613
+ filters?: {
1614
+ service_ids?: string[];
1615
+ category_ids?: string[];
1616
+ area_id?: string;
1617
+ /** Format: date */
1618
+ date_from?: string;
1619
+ /** Format: date */
1620
+ date_to?: string;
1621
+ price_max?: components["schemas"]["Money"];
1622
+ /** @description v1-draft.32, the place the person is searching now, as the app shows it ("Billings, MT"); for "near me" it comes after a place named in the message and before `subject.approximate_location`. `lat` and `lng` are rounded to 2 decimals (about a kilometre) or coarser; the gateway never asks for finer, and rounds anything finer it receives (a raw GPS fix, say) to 2 decimals on receipt, before it is stored, logged or forwarded to a tool. */
1623
+ near?: {
1624
+ lat: number;
1625
+ lng: number;
1626
+ /** @description in Unicode code points; quoted to the agent as data */
1627
+ label: string;
1628
+ /** @enum {string} */
1629
+ precision?: "city" | "neighborhood";
1630
+ };
1631
+ };
1632
+ /** @description the tenant's counter for this person's screen state; a later update never goes back to a lower one (v1-draft.23: `updateSessionContext` refuses one that is not greater) */
1633
+ revision: number;
1634
+ };
1635
+ /** @description v1-draft.23: what `updateSessionContext` takes: the person's screen now (`ScreenContext`), or an explicit clear. Both carry the tenant's `revision`, which must be greater than the session's. */
1636
+ SessionContextUpdate: components["schemas"]["ScreenContext"] | {
1637
+ /** @constant */
1638
+ clear: true;
1639
+ revision: number;
1640
+ };
1641
+ /** @description v1-draft.23, the session's screen context after an update; no `screen_context` after a clear */
1642
+ SessionContextState: {
1643
+ revision: number;
1644
+ screen_context?: components["schemas"]["ScreenContext"];
1645
+ };
1646
+ /**
1647
+ * @description v1-draft.37, what a context-started chat is about (`docs/context-started-chats.md`). `booking`: a service booking, the customer's own. `rental`: a space rental, the renter's own. `professional`, `workspace`, `service` and `class`: a published profile, space, service or class. `policy`: a policy or legal page. A closed list: a new type is a contract change.
1648
+ * @enum {string}
1649
+ */
1650
+ AnchorType: "booking" | "rental" | "professional" | "workspace" | "service" | "class" | "policy";
1651
+ /**
1652
+ * @description v1-draft.37, optional on `createSession`: the object a context-started chat is about, as the tenant's backend read it for this person, server to server, with the same authorization and rules as its screen, then reduced by its disclosure policy for agents. Never from client text. Absent: an unanchored session, as before.
1653
+ * **Evidence and quoted data, never instructions.** The gateway keeps `facts` as evidence entries keyed by the object, the field and `as_of`, so a reply's amounts, money verbs and deadlines are checked against them by meaning. The agent reads the typed facts in the turn's details as quoted data under a protected lead, never as instructions and never in the cached prompt. Free text a professional wrote (a service's `description`, a class's `rules` and `cancellation_terms`) is third-party text: it is instruction-checked before the agent reads it, like a tool result's third-party text, and never decides anything. Its identity (`type`, `id`) never changes for the conversation; its facts are refreshed with the connector's anchor read (admin-v1 `Connector.anchors`).
1654
+ * **Limits.** At most 4096 bytes as serialized UTF-8 JSON; text fields at most 80 code points, except a `service` or `class` description, rules and cancellation terms, at most 500. An anchor over a limit or malformed is `400 invalid_request` with a pointer under `/anchor`, and the session is not opened. To fit, the tenant trims and marks the anchor `partial`, in this order: lists are cut from their end (a professional's services keep the ones the person can book first; a space's taken days keep the soonest first; a class's sessions the soonest first), then optional fields are left out. It never sends an anchor over the limit.
1655
+ * **completeness.** `complete`: every field the type's projection allows is present. `partial`: some could not be read; absent fields mean "not known", never zero or "nothing". `unavailable`: the read failed; only `type`, `id`, `title` and `completeness` are present, and the agent names the object from `title`, says its details could not load, and still answers.
1656
+ * **display** is what the person sees on the anchor card, formatted by the tenant in the person's language as its own screen shows it: an optional `subtitle`, at most four `{label, value}` facts and an optional `deep_link` to the object's screen. It is for the person only and never reaches the model (the model reasons from the typed `facts`). The deep link must pass the deep-link rule (admin-v1 `Connector.deep_link_prefixes`), or it is dropped and the card keeps the rest.
1657
+ * A `policy` anchor carries its `id` (the page's slug) and `title` only: the text is read through the connector's `get_policy`, never carried in the anchor.
1658
+ */
1659
+ SessionAnchor: {
1660
+ type: components["schemas"]["AnchorType"];
1661
+ /** @description the tenant's object id, the same one its tools accept (a policy's slug for `policy`) */
1662
+ id: string;
1663
+ /** @description the anchor schema version for this `type`, the tenant's */
1664
+ version?: number;
1376
1665
  /**
1377
1666
  * Format: date-time
1378
- * @description When the session ends: the earliest of the session's own `limits.max_duration_seconds` (when `createSession` sets it), the agent's and the environment's longest session, and `tool_access.expires_at`. A short-lived tool token shortens the session. On the WebSocket, `session.expiring` comes 60 s before, then `session.closed` with `expired`.
1667
+ * @description when the tenant read the object; the source revision of every fact
1379
1668
  */
1380
- expires_at: string;
1381
- disclosure: string;
1382
- /** @description The conversation this session belongs to, the same for every session of it, however it was resumed (by `conversation_key` or `conversation_id`). Present whenever the session belongs to a conversation: when the request named one, and for every guided-flow session. It is absent only for a session with no conversation, and optional in the schema so clients built on earlier drafts keep working. */
1383
- conversation_id?: components["schemas"]["ConversationId"];
1669
+ as_of?: string;
1670
+ /** @description the tenant's code for the object's state, from its closed list per `type`. Open in this draft: an unknown value is accepted and gets the generic anchored greeting. The lists are added here, additively, once the tenant's screen-parity check per value is done. */
1671
+ situation?: string;
1672
+ /** @enum {string} */
1673
+ completeness: "complete" | "partial" | "unavailable";
1674
+ /** @description in Unicode code points: "ABZ Fitness Center, Oct 16 to 24"; also the history group title */
1675
+ title: string;
1676
+ /** @description the object's facts, typed per `type` (`BookingFacts`, `RentalFacts`, `ProfessionalFacts`, `WorkspaceFacts`, `ServiceFacts`, `ClassFacts`); none for a `policy`. Only what the tenant's projection for agents allows: a field's presence is a promise, anything not listed is not sent. Absent means "can't be worked out", never zero. */
1677
+ facts?: Record<string, unknown>;
1678
+ /** @description what the person may do or ask about now, each eligible or not; the app draws chips only for ids it knows */
1679
+ intents?: components["schemas"]["AnchorIntent"][];
1680
+ deadlines?: components["schemas"]["AnchorDeadline"][];
1681
+ display?: components["schemas"]["AnchorDisplay"];
1682
+ } & (unknown & unknown & unknown & unknown & unknown & unknown & unknown & unknown);
1683
+ /** @description v1-draft.37, the anchor card's display, formatted by the tenant in the person's language, for the person only: it never reaches the model. `deep_link` must pass the deep-link rule, or it is dropped. */
1684
+ AnchorDisplay: {
1685
+ /** @description in Unicode code points */
1686
+ subtitle?: string;
1687
+ facts?: {
1688
+ /** @description in Unicode code points */
1689
+ label: string;
1690
+ /** @description in Unicode code points */
1691
+ value: string;
1692
+ }[];
1693
+ deep_link?: string;
1384
1694
  };
1385
- SessionStatus: {
1386
- id: string;
1695
+ /** @description v1-draft.37, one thing the person may do (`action`: a hand-off to the tenant's own screen, `open_action`) or ask (`explain`: answered from the manifest's template, filled from the anchor's facts). Eligibility comes only from here. An ineligible intent carries `reason`, a code; the words a person reads come from the templates. */
1696
+ AnchorIntent: {
1697
+ id: components["schemas"]["IntentId"];
1387
1698
  /** @enum {string} */
1388
- status: "open" | "closed" | "revoked" | "expired";
1389
- subject_id: components["schemas"]["SubjectId"];
1390
- agent_version?: string;
1391
- turns: number;
1392
- handoff_ticket_ids?: string[];
1699
+ kind: "action" | "explain";
1700
+ eligible: boolean;
1701
+ /** @description why it is not eligible now, a code (`outside_window`, `not_for_customers`) */
1702
+ reason?: string;
1703
+ /** @description an `action` intent's route, one the connector manifest declares (admin-v1 `Connector.actions.routes`) */
1704
+ route?: string;
1705
+ };
1706
+ /** @description v1-draft.37, a stable intent id (`cancel_rental`, `explain_hold`), the same across languages and builds */
1707
+ IntentId: string;
1708
+ /** @description v1-draft.37, a moment the person must act by, with the zone it is stated in. The sentence gate holds a reply to these: a deadline is said with its zone, in the person's zone too when it differs, and a passed one is never stated as future. */
1709
+ AnchorDeadline: {
1393
1710
  /**
1394
- * @description Why the session ended. `server_restart` is reserved and never appears here: it never ends a session (`docs/realtime-protocol.md` §2).
1711
+ * @description closed, since the gate's money and deadline rules key on it; this set is proposed (the joint spec names none) and may grow additively. `approval`: the host or professional must answer by then. `payment_due`: an amount is due. `cancel_refund_until`: cancelling before then gets the stated refund. `reschedule_until`: the booking may be moved until then. `deposit_dispute_until`: a deposit claim may be disputed until then. `deposit_return_by`: the deposit is returned by then.
1395
1712
  * @enum {string}
1396
1713
  */
1397
- end_reason?: "expired" | "revoked" | "limit_reached" | "idle" | "server_restart" | "user_left";
1398
- /** Format: date-time */
1399
- created_at: string;
1714
+ kind: "approval" | "payment_due" | "cancel_refund_until" | "reschedule_until" | "deposit_dispute_until" | "deposit_return_by";
1400
1715
  /** Format: date-time */
1401
- closed_at?: string;
1402
- usage?: components["schemas"]["Usage"];
1403
- };
1404
- TurnInput: {
1405
- client_event_id?: string;
1406
- text: string;
1716
+ at: string;
1717
+ /** @description `payment_due` only: what is due by `at` (a booking awaiting the person's payment, a rent due) */
1718
+ amount?: components["schemas"]["Money"];
1719
+ /** @description an IANA time zone: `America/New_York` */
1720
+ zone: string;
1407
1721
  };
1408
- Usage: {
1409
- /** @enum {string} */
1410
- unit: "turn" | "image" | "video_second" | "text_item" | "screening_call" | "document_page";
1411
- quantity: number;
1412
- input_tokens?: number;
1413
- output_tokens?: number;
1414
- cost: components["schemas"]["MicroMoney"];
1722
+ /**
1723
+ * @description v1-draft.37, where the money of a booking or rental is, mapped by the tenant from its own states at its boundary, with a test per value. The money verbs an agent may say follow it: "held, not charged" while `held`; "charged" only when `charged`; "released" only at `release_confirmed`; `due` and `paid_in_person` never said as a card charge.
1724
+ * @enum {string}
1725
+ */
1726
+ PaymentState: "none" | "due" | "held" | "charged" | "paid_in_person" | "release_requested" | "release_confirmed" | "release_failed" | "refund_pending" | "refund_settled" | "partially_refunded" | "refund_failed" | "manual_review";
1727
+ /** @description v1-draft.37, where, as an area only; never the street */
1728
+ AnchorPlace: {
1729
+ area: string;
1415
1730
  };
1416
- UsageReport: {
1417
- period: string;
1418
- features: {
1419
- feature: components["schemas"]["Feature"];
1420
- quantity: number;
1421
- cost: components["schemas"]["MicroMoney"];
1422
- /** @description null when the feature has no monthly cap */
1423
- cap: components["schemas"]["MicroMoney"] | null;
1731
+ AnchorParty: {
1732
+ id?: string;
1733
+ display_name: string;
1734
+ };
1735
+ /** @description v1-draft.37, a service's price per place it is offered at */
1736
+ AnchorPrices: {
1737
+ at_professional?: components["schemas"]["Money"];
1738
+ at_customer?: components["schemas"]["Money"];
1739
+ other?: components["schemas"]["Money"];
1740
+ };
1741
+ /** @description v1-draft.37, a `rental` anchor's facts (joint spec §2.1, with the tenant's review). `amount_held` only while `payment_state` is `held`, `amount_charged` only when it is `charged`; `cancel_now` absent when it cannot be worked out. An amount due is the `payment_due` deadline's `amount`. The renter's licence and insurance are standing codes only (never a scan), and documents and maintenance are counts per state (never contents). */
1742
+ RentalFacts: {
1743
+ /** @description the lease, kept distinct from the rental id */
1744
+ lease_id?: string;
1745
+ space?: {
1746
+ id?: string;
1747
+ name: string;
1748
+ area?: string;
1749
+ };
1750
+ host?: components["schemas"]["AnchorParty"];
1751
+ /** @description the rental's dates; no `end` for an open-ended rental */
1752
+ dates?: {
1753
+ /** Format: date */
1754
+ start: string;
1755
+ /** Format: date */
1756
+ end?: string;
1757
+ zone: string;
1758
+ };
1759
+ total?: components["schemas"]["Money"];
1760
+ deposit?: components["schemas"]["Money"];
1761
+ payment_state?: components["schemas"]["PaymentState"];
1762
+ amount_held?: components["schemas"]["Money"];
1763
+ amount_charged?: components["schemas"]["Money"];
1764
+ /** @description after the rental ends, where its deposit is; the return and dispute rules are the tenant's policy pages */
1765
+ deposit_return?: {
1424
1766
  /** @enum {string} */
1425
- state: "enabled" | "disabled";
1426
- }[];
1427
- };
1428
- CardRef: {
1429
- /** @enum {string} */
1430
- type: "professional" | "workspace" | "service" | "booking" | "quote" | "policy" | "slot_list" | "ticket" | "metric_series" | "metric_table" | "gap_list" | "intent_cluster" | "memory_claim" | "knowledge_snippet";
1431
- id: string;
1432
- };
1767
+ state: "being_returned" | "claimed" | "returned" | "disputed";
1768
+ amount_returned?: components["schemas"]["Money"];
1769
+ amount_claimed?: components["schemas"]["Money"];
1770
+ };
1771
+ cancellation?: {
1772
+ /** @enum {string} */
1773
+ by: "you" | "host" | "platform";
1774
+ /** Format: date-time */
1775
+ at: string;
1776
+ reason_category?: string;
1777
+ };
1778
+ /** @description the renter's own, a code (`approved`, `sent`, `missing`) */
1779
+ licence_standing?: string;
1780
+ /** @description the renter's own, a code */
1781
+ insurance_standing?: string;
1782
+ /** @description the rental's documents, counted per state; never their contents */
1783
+ documents?: components["schemas"]["AnchorCount"][];
1784
+ /** @description the rental's maintenance requests, counted per state; never their contents */
1785
+ maintenance?: components["schemas"]["AnchorCount"][];
1786
+ /** @description what cancelling now would give back, and until when those terms hold */
1787
+ cancel_now?: {
1788
+ refund?: components["schemas"]["Money"];
1789
+ deposit_refund?: components["schemas"]["Money"];
1790
+ rent_kept?: components["schemas"]["Money"];
1791
+ /** Format: date-time */
1792
+ until: string;
1793
+ };
1794
+ lease?: {
1795
+ /** @description the lease term, a code (`monthly`, `open_ended`) */
1796
+ term?: string;
1797
+ rate?: components["schemas"]["Money"];
1798
+ /** Format: date */
1799
+ paid_through?: string;
1800
+ /**
1801
+ * Format: date
1802
+ * @description the next charge date the tenant shows; not automatic billing
1803
+ */
1804
+ next_charge?: string;
1805
+ };
1806
+ } & (unknown & unknown);
1807
+ AnchorCount: {
1808
+ state: string;
1809
+ count: number;
1810
+ };
1811
+ /** @description v1-draft.37, a `booking` anchor's facts (joint spec §3.1), named from the spec's prose for the tenant's projection to confirm. `amount_paid` (the money taken, before any refund) only when `payment_state` is `charged`, `paid_in_person` or `partially_refunded`, never for a hold; `amount_refunded` only when it is `partially_refunded`, `refund_pending` or `refund_settled`. An amount due is the `payment_due` deadline's `amount`. No street, support case or free-text note. */
1812
+ BookingFacts: {
1813
+ services?: {
1814
+ name: string;
1815
+ quantity?: number;
1816
+ duration_minutes?: number;
1817
+ }[];
1818
+ professional?: components["schemas"]["AnchorParty"];
1819
+ appointment?: {
1820
+ /** Format: date-time */
1821
+ at: string;
1822
+ zone: string;
1823
+ };
1824
+ place?: components["schemas"]["AnchorPlace"];
1825
+ subtotal?: components["schemas"]["Money"];
1826
+ fees?: components["schemas"]["Money"];
1827
+ total?: components["schemas"]["Money"];
1828
+ payment_state?: components["schemas"]["PaymentState"];
1829
+ amount_paid?: components["schemas"]["Money"];
1830
+ amount_refunded?: components["schemas"]["Money"];
1831
+ deposit_paid?: components["schemas"]["Money"];
1832
+ cancel_now?: {
1833
+ refund?: components["schemas"]["Money"];
1834
+ /** @description cancelling now goes to manual review, so no refund is promised */
1835
+ goes_to_review?: boolean;
1836
+ /** Format: date-time */
1837
+ until: string;
1838
+ };
1839
+ /** @enum {string} */
1840
+ review_state?: "not_eligible" | "eligible" | "reviewed";
1841
+ cancellation?: {
1842
+ /** @enum {string} */
1843
+ by: "you" | "professional" | "platform";
1844
+ /** Format: date-time */
1845
+ at: string;
1846
+ reason_category?: string;
1847
+ };
1848
+ } & (unknown & unknown);
1849
+ /** @description v1-draft.37, a `professional` anchor's facts (joint spec §3.3), named from the spec's prose for the tenant's projection to confirm. A delisted or blocked profile is `situation: unavailable`, never a stale profile. When `display_name` and the anchor's `title` differ, the `title` is what the person is shown. */
1850
+ ProfessionalFacts: {
1851
+ display_name?: string;
1852
+ place?: components["schemas"]["AnchorPlace"];
1853
+ rating?: {
1854
+ value: number;
1855
+ count: number;
1856
+ };
1857
+ services?: {
1858
+ id: string;
1859
+ name: string;
1860
+ duration_minutes?: number;
1861
+ prices?: components["schemas"]["AnchorPrices"];
1862
+ }[];
1863
+ /** @description the next open times for the service the person chose */
1864
+ next_openings?: {
1865
+ service_id: string;
1866
+ /** Format: date-time */
1867
+ at: string;
1868
+ zone: string;
1869
+ }[];
1870
+ };
1871
+ /** @description v1-draft.37, a `workspace` anchor's facts (joint spec §3.4), named from the spec's prose for the tenant's projection to confirm. */
1872
+ WorkspaceFacts: {
1873
+ place?: components["schemas"]["AnchorPlace"];
1874
+ host?: components["schemas"]["AnchorParty"];
1875
+ rates?: {
1876
+ day?: components["schemas"]["Money"];
1877
+ week?: components["schemas"]["Money"];
1878
+ month?: components["schemas"]["Money"];
1879
+ };
1880
+ deposit?: components["schemas"]["Money"];
1881
+ minimum_stay_days?: number;
1882
+ units?: {
1883
+ chairs?: number;
1884
+ suites?: number;
1885
+ };
1886
+ amenities?: string[];
1887
+ /** @description a code (`tour_first`, `no_tour`) */
1888
+ tour_rule?: string;
1889
+ /** @enum {string} */
1890
+ licence_requirement?: "sent" | "approved";
1891
+ insurance_rule?: string;
1892
+ /** @description the days already taken in the next 90, as ranges of whole days (`to` included), soonest first */
1893
+ taken_days?: {
1894
+ /** Format: date */
1895
+ from: string;
1896
+ /** Format: date */
1897
+ to: string;
1898
+ }[];
1899
+ };
1900
+ /** @description v1-draft.37, a `service` anchor's facts (joint spec §3.5), named from the spec's prose for the tenant's projection to confirm. */
1901
+ ServiceFacts: {
1902
+ name?: string;
1903
+ duration_minutes?: number;
1904
+ professional?: {
1905
+ id?: string;
1906
+ display_name: string;
1907
+ area?: string;
1908
+ };
1909
+ prices?: components["schemas"]["AnchorPrices"];
1910
+ offered_at?: ("at_professional" | "at_customer" | "other")[];
1911
+ /** @description written by the professional, so third-party text, instruction-checked before the agent reads it */
1912
+ description?: string;
1913
+ };
1914
+ /** @description v1-draft.37, a `class` anchor's facts (joint spec §3.6), named from the spec's prose for the tenant's projection to confirm. */
1915
+ ClassFacts: {
1916
+ professional?: components["schemas"]["AnchorParty"];
1917
+ place?: components["schemas"]["AnchorPlace"];
1918
+ price_per_seat?: components["schemas"]["Money"];
1919
+ /** @description written by the professional, so third-party text, instruction-checked before the agent reads it */
1920
+ rules?: string;
1921
+ /** @description written by the professional, so third-party text, instruction-checked before the agent reads it */
1922
+ cancellation_terms?: string;
1923
+ sessions?: {
1924
+ /** Format: date-time */
1925
+ at: string;
1926
+ zone: string;
1927
+ seats_left: number;
1928
+ cancelled: boolean;
1929
+ }[];
1930
+ };
1931
+ /** @description v1-draft.37, the anchor as the person and the app see it (`Session.anchor`, `session.ready.anchor`): its identity and state, and the verified `anchor` card the app swaps in for the provisional one it drew from its screen. If a fact the app showed differs, the verified card wins and the app redraws. The same card is the conversation's first timeline item. No `facts`: the card's display strings are what is shown. */
1932
+ SessionAnchorView: {
1933
+ type: components["schemas"]["AnchorType"];
1934
+ id: string;
1935
+ title: string;
1936
+ /** Format: date-time */
1937
+ as_of?: string;
1938
+ situation?: string;
1939
+ /** @enum {string} */
1940
+ completeness: "complete" | "partial" | "unavailable";
1941
+ /** @description the anchor's eligible intents, for the chips (only with the `intents` capability) */
1942
+ intents?: components["schemas"]["AnchorIntent"][];
1943
+ card: components["schemas"]["Card"];
1944
+ };
1945
+ /** @description v1-draft.37, an `anchor` card's body: display only. The object's type, when it was read (absent when the read failed), and the anchor's `display` as the tenant formatted it: at most four facts as display strings and the `deep_link` to the object's screen, which must pass the deep-link rule (admin-v1 `Connector.deep_link_prefixes`); a card whose link fails keeps the card and loses the link. The card's `subtitle` is `display.subtitle`. An anchor with no `display` gets a card with its title and no facts. */
1946
+ AnchorCard: {
1947
+ anchor_type: components["schemas"]["AnchorType"];
1948
+ /** Format: date-time */
1949
+ as_of?: string;
1950
+ facts: {
1951
+ /** @description in Unicode code points */
1952
+ label: string;
1953
+ /** @description in Unicode code points */
1954
+ value: string;
1955
+ }[];
1956
+ deep_link?: string;
1957
+ };
1958
+ /** @description v1-draft.37, an `open_action` card's body: a hand-off to the tenant's own flow for an eligible `action` intent. The app opens `route` with `params`; **opening a screen is never doing the action**. The app reports what happened with `action.result`, echoing `action_id`, and the gateway re-reads the anchor before the agent states the outcome. `action_id` is minted by the gateway, one per hand-off across its retries: the tenant's write keys on it, so a double tap or a network retry is one action. `terms_digest` is the hash of the terms the person was shown; the tenant's write refuses changed terms. A build without the `open_action` capability gets `fallback_link` (the object's screen) as a plain link instead. */
1959
+ OpenAction: {
1960
+ action_id: string;
1961
+ intent: components["schemas"]["IntentId"];
1962
+ /** @description in Unicode code points */
1963
+ label: string;
1964
+ /** @description a route the connector manifest declares (admin-v1 `Connector.actions.routes`) */
1965
+ route: string;
1966
+ /** @description the route's typed params, as its manifest entry declares them: `string` and `id` as strings, `instant` as an RFC 3339 string, `integer` as an integer, `money` as `Money` */
1967
+ params: {
1968
+ [key: string]: string | number | components["schemas"]["Money"];
1969
+ };
1970
+ terms_digest?: string;
1971
+ /** Format: date-time */
1972
+ expires_at: string;
1973
+ /** @description the object's screen; passes the deep-link rule */
1974
+ fallback_link: string;
1975
+ };
1976
+ /** @description v1-draft.37, optional on `createSession`: what the person's app build supports, and the starter chips it showed, as its backend relays them. `shown_intents` are the intent ids of the chips on screen when the chat opened: with the `intent` a tap sends on `input.text`, they tell which chips a person saw and which they tapped. Only the ids are kept. Capabilities: `anchor_card`: it draws an `anchor` card. `intents`: it draws chips from anchor intent ids and sends `intent` on `input.text`. `open_action`: it draws an `open_action` card and opens its route. `action_result`: it sends `action.result` after a hand-off. Open: an unknown value is ignored, never refused. The gateway leaves out what the build lacks: no `open_action` means the action's `fallback_link` as a plain `links` card, no `intents` means the chips come as text, and a build with none still gets the anchored greeting and answers. */
1977
+ SessionClient: {
1978
+ /**
1979
+ * @example [
1980
+ * "anchor_card",
1981
+ * "intents",
1982
+ * "open_action",
1983
+ * "action_result"
1984
+ * ]
1985
+ */
1986
+ capabilities?: string[];
1987
+ /** @description the starter chips' intent ids, open like capabilities (an id the session's anchor does not list is kept as given) */
1988
+ shown_intents?: components["schemas"]["IntentId"][];
1989
+ };
1990
+ /** @description v1-draft.37, optional on `createSession`: how the person opened the chat. `point` is an open code: `ask_about` for an explicit "ask about this" on a page (it starts a new conversation anchored to the page's object), and `help`, `launcher`, `inbox`, `history`, `explore` or `setup` for the others; an unknown one is treated as `help`. `label` keeps the app's own word for analytics only: it never reaches a model prompt. */
1991
+ SessionEntry: {
1992
+ /** @example ask_about */
1993
+ point: string;
1994
+ /** @description in Unicode code points */
1995
+ label?: string;
1996
+ };
1997
+ /** @description v1-draft.25, what `getMeta` answers */
1998
+ Meta: {
1999
+ /** @example v1-draft.26 */
2000
+ api_version: string;
2001
+ contracts: {
2002
+ /** @description the vinaverse-contracts commit pinned */
2003
+ commit?: string;
2004
+ };
2005
+ };
2006
+ /** @description v1-draft.20: a tap target the app draws under the welcome. Tapping it sends `message` as the person's own text (an `input.text`, or `createTurnSse`), exactly as if they typed it; it starts nothing else. */
2007
+ Chip: {
2008
+ /** @description in Unicode code points */
2009
+ label: string;
2010
+ /** @description in Unicode code points */
2011
+ message: string;
2012
+ };
2013
+ /** @description v1-draft.20: the tenant's greeting for the session's mode, with the chips of the session's screen when its manifest has some for it (else the mode's own), from the connector manifest (admin-v1 `Connector.welcome`), checked when the manifest loads. The app shows it the moment the session is ready: it costs no model call and is not a turn. The agent knows it was shown and does not greet again. */
2014
+ Welcome: {
2015
+ /** @description in Unicode code points */
2016
+ text: string;
2017
+ chips: components["schemas"]["Chip"][];
2018
+ };
2019
+ ClientSecret: {
2020
+ /** @description `vv_cs_` and base64url characters, so the secret is a valid WebSocket subprotocol token in `bearer.<secret>`. */
2021
+ value: string;
2022
+ /**
2023
+ * Format: date-time
2024
+ * @description The end of the connect window (120 s after issue): the secret must open a connection (over SSE: run its first turn) by then. Once used, it is bound to the session until the session ends or a new secret is issued (`refreshClientSecret`), so the app reconnects with the same secret (and sends it on every SSE turn) without asking for a new one.
2025
+ */
2026
+ expires_at: string;
2027
+ };
2028
+ Session: {
2029
+ id: string;
2030
+ /** @enum {string} */
2031
+ status: "open";
2032
+ agent: {
2033
+ id: string;
2034
+ version: string;
2035
+ display_name: string;
2036
+ /**
2037
+ * Format: uri
2038
+ * @description v1-draft.19, optional: the agent's avatar (`getAgentAvatarImage`), a square 512 px WebP with no metadata, re-encoded by Vinaverse from the tenant's upload and passed by its image screen. The URL is immutable (it changes when the image does), so an app may cache the image for as long as it likes; it needs no credential. Absent: no avatar yet, so draw the name's monogram.
2039
+ */
2040
+ avatar_url?: string;
2041
+ };
2042
+ client_secret: components["schemas"]["ClientSecret"];
2043
+ /** Format: uri */
2044
+ realtime_url: string;
2045
+ /** Format: uri */
2046
+ turns_url: string;
2047
+ /**
2048
+ * Format: date-time
2049
+ * @description When the session ends: the earliest of the session's own `limits.max_duration_seconds` (when `createSession` sets it), the agent's and the environment's longest session, and `tool_access.expires_at`. A short-lived tool token shortens the session. On the WebSocket, `session.expiring` comes 60 s before, then `session.closed` with `expired`.
2050
+ */
2051
+ expires_at: string;
2052
+ disclosure: string;
2053
+ /** @description The conversation this session belongs to, the same for every session of it, however it was resumed (by `conversation_key` or `conversation_id`). Present whenever the session belongs to a conversation: when the request named one, and for every guided-flow session. It is absent only for a session with no conversation, and optional in the schema so clients built on earlier drafts keep working. */
2054
+ conversation_id?: components["schemas"]["ConversationId"];
2055
+ /** @description v1-draft.20, the session's mode, as given or as the agent's template sets it */
2056
+ experience_mode?: components["schemas"]["ExperienceMode"];
2057
+ /** @description v1-draft.20, present when the session acts for a business */
2058
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
2059
+ /** @description v1-draft.20, the screen context the session accepted, when `createSession` gave one */
2060
+ screen_context?: components["schemas"]["ScreenContext"];
2061
+ /** @description v1-draft.20, absent when the tenant's manifest has no welcome for the session's mode */
2062
+ welcome?: components["schemas"]["Welcome"];
2063
+ /** @description v1-draft.27, whether the agent remembers the person, and the notice to show once */
2064
+ memory?: components["schemas"]["SessionMemory"];
2065
+ /** @description v1-draft.37, what the chat is about, with its verified card, when `createSession` gave an anchor */
2066
+ anchor?: components["schemas"]["SessionAnchorView"];
2067
+ /** @description v1-draft.40, the chat experience at session start, resolved (`ChatExperience`) */
2068
+ experience?: components["schemas"]["ChatExperience"];
2069
+ };
2070
+ SessionStatus: {
2071
+ id: string;
2072
+ /** @enum {string} */
2073
+ status: "open" | "closed" | "revoked" | "expired";
2074
+ subject_id: components["schemas"]["SubjectId"];
2075
+ agent_version?: string;
2076
+ turns: number;
2077
+ handoff_ticket_ids?: string[];
2078
+ /**
2079
+ * @description Why the session ended. `server_restart` is reserved and never appears here: it never ends a session (`docs/realtime-protocol.md` §2). `deleted` (v1-draft.21): its conversation was deleted from history.
2080
+ * @enum {string}
2081
+ */
2082
+ end_reason?: "expired" | "revoked" | "limit_reached" | "idle" | "server_restart" | "user_left" | "deleted";
2083
+ /** Format: date-time */
2084
+ created_at: string;
2085
+ /** Format: date-time */
2086
+ closed_at?: string;
2087
+ usage?: components["schemas"]["Usage"];
2088
+ };
2089
+ TurnInput: {
2090
+ client_event_id?: string;
2091
+ text: string;
2092
+ /** @description v1-draft.37, optional, as on `input.text` */
2093
+ intent?: components["schemas"]["IntentId"];
2094
+ };
2095
+ Usage: {
2096
+ /** @enum {string} */
2097
+ unit: "turn" | "image" | "video_second" | "text_item" | "screening_call" | "document_page";
2098
+ quantity: number;
2099
+ input_tokens?: number;
2100
+ output_tokens?: number;
2101
+ cost: components["schemas"]["MicroMoney"];
2102
+ };
2103
+ UsageReport: {
2104
+ period: string;
2105
+ features: {
2106
+ feature: components["schemas"]["Feature"];
2107
+ quantity: number;
2108
+ cost: components["schemas"]["MicroMoney"];
2109
+ /** @description null when the feature has no monthly cap */
2110
+ cap: components["schemas"]["MicroMoney"] | null;
2111
+ /** @enum {string} */
2112
+ state: "enabled" | "disabled";
2113
+ }[];
2114
+ };
2115
+ CardRef: {
2116
+ /** @enum {string} */
2117
+ type: "professional" | "workspace" | "service" | "booking" | "quote" | "policy" | "slot_list" | "ticket" | "metric_series" | "metric_table" | "gap_list" | "intent_cluster" | "memory_claim" | "knowledge_snippet";
2118
+ id: string;
2119
+ };
1433
2120
  CardAction: {
1434
2121
  /**
1435
2122
  * @description `confirmed`, `declined`, `failed` and `expired` are reserved for proposal outcomes, never a card action
@@ -1443,6 +2130,38 @@ interface components {
1443
2130
  label: string;
1444
2131
  deep_link?: string;
1445
2132
  };
2133
+ /** @description v1-draft.20: one photo of a `professional` or `workspace` card. Https only, from a prefix the tenant registered (admin-v1 `Connector.image_url_prefixes`), compared after the URL is parsed and normalized (no user info, backslash, fragment or dot segment): an image that fails is dropped, never the card. The tenant serves only photos its own moderation approved, at URLs that stay valid as long as the transcript (a card in history draws them again). An image that fails to load draws the name's monogram; the card stays. */
2134
+ CardImage: {
2135
+ /** Format: uri */
2136
+ url: string;
2137
+ width?: number;
2138
+ height?: number;
2139
+ };
2140
+ /** @description v1-draft.20: the cards one turn draws together carry the same `id`. `layout` is a hint: `carousel` draws them in one horizontal row, `list` one under another. A client that does not know `group`, or a `layout` value, draws a list, as before. */
2141
+ CardGroup: {
2142
+ id: string;
2143
+ /** @enum {string} */
2144
+ layout: "list" | "carousel";
2145
+ };
2146
+ /** @description v1-draft.20, one link of a `links` card: one of the tenant's own destinations from its connector manifest (admin-v1 `Connector.help_links`), never a link the model wrote. */
2147
+ CardLink: {
2148
+ /** @description in Unicode code points */
2149
+ label: string;
2150
+ /** @description an https page of the tenant's, or a deep link that passes the deep-link rule (`next_step`) */
2151
+ url: string;
2152
+ /** @enum {string} */
2153
+ purpose: "help" | "support" | "policy" | "contact" | "feedback";
2154
+ };
2155
+ /** @description v1-draft.20, on a `links` card: the tenant's scope policy, at the version in force, that kept the agent from answering, and why. v1-draft.37: `policy_id` and `version` are required for the scope reasons and may be absent for `help`, which no scope policy decides. */
2156
+ ScopeProvenance: {
2157
+ policy_id?: string;
2158
+ version?: string;
2159
+ /**
2160
+ * @description why the card is shown: the scope kept the agent from answering (`off_scope`, `competitor`, `blocked_topic`), or the person asked how to reach the business or how to do something no tool answers (`help`, v1-draft.36)
2161
+ * @enum {string}
2162
+ */
2163
+ reason: "off_scope" | "competitor" | "blocked_topic" | "help";
2164
+ };
1446
2165
  /**
1447
2166
  * @description `ref` is present on every customer-agent card (professional … handoff); copilot cards (metric_chart … document) may omit it, since a metric, cluster or claim is not a tenant object. A `question` card (guided flows) carries `question`, has no `actions` (the person answers with `ui.answer`), uses `title` as a short label for the question, and its `source` names the tool call that fetched the flow's definition.
1448
2167
  * A `next_step` card is a step the person finishes on the tenant's own screen (payouts, a plan purchase, photos). It has a `title` (at most 80 code points), a `body` (at most 300) and exactly one action: `action: open`, with a `label` (at most 40) and a `deep_link`. It may omit `ref`, and its `source` names the tool call the step came from. - **Nothing is written.** Tapping the action opens `deep_link` in the tenant's app. The app sends no
@@ -1462,21 +2181,41 @@ interface components {
1462
2181
  * know.
1463
2182
  * - **It is not restored.** A `next_step` card is in neither `pending_cards` nor `transcript`; after a
1464
2183
  * reconnect the agent shows the step again once it reads the tenant's state.
2184
+ *
2185
+ * **Listing cards** (v1-draft.20). A `professional` or `workspace` card may carry `images` (up to five, the first the hero; `image_url`, when present, is the first one's URL, for clients that predate `images`), `next_available` (the earliest open time the tenant's tool returned; a hint, so a tap on its time reads availability again), `price_from` and a `group`. An app draws one hero image first and the rest on a swipe.
2186
+ * **A `links` card** (v1-draft.20) answers what the tenant's scope keeps the agent from answering: a question off its scope, a comparison with a competitor, a blocked topic. Its `links` are the tenant's own help, support, policy, contact or feedback destinations (`CardLink`), its `provenance` names the scope policy and the reason, and it has no `actions`: each link opens the tenant's page. It belongs to a recorded turn, with the agent's one line around it, never to `input.rejected`. Its `source.tool` is `scope` and its `source.call_id` the turn's id: no tool call made it. An earlier client skips the unknown kind, as it skips any.
2187
+ * **An `anchor` card** (v1-draft.37) shows what a context-started chat is about: `title` (at most 80 code points) and its `anchor` body (`AnchorCard`): display only, no `actions`. Its `source.tool` is `anchor` and its `source.call_id` the anchor read that made it (`session` for the anchor `createSession` carried). It is `Session.anchor.card` and the conversation's first timeline item.
2188
+ * **An `open_action` card** (v1-draft.37) hands an eligible `action` intent to the tenant's own flow (`OpenAction`), with no `actions` of its own: the app opens its `route`. A build without the `open_action` capability never gets one; it gets the action's `fallback_link` as a `links` card.
1465
2189
  */
1466
2190
  Card: {
1467
2191
  id: string;
1468
2192
  /**
1469
- * @description customer-agent kinds, then business-copilot kinds (Studio), then `question` (guided flows), then `proposal` (a change the person confirms or declines in the tenant's app), then `next_step` (a step the person finishes on the tenant's own screen)
2193
+ * @description customer-agent kinds, then business-copilot kinds (Studio), then `question` (guided flows), then `proposal` (a change the person confirms or declines in the tenant's app), then `next_step` (a step the person finishes on the tenant's own screen), then `links` (v1-draft.20: the tenant's own pages, when its scope keeps the agent from answering), then `anchor` and `open_action` (v1-draft.37: what a context-started chat is about, and a hand-off to the tenant's own flow)
1470
2194
  * @enum {string}
1471
2195
  */
1472
- kind: "professional" | "workspace" | "service" | "time_slots" | "booking" | "quote" | "policy" | "handoff" | "metric_chart" | "metric_table" | "gap_list" | "cluster" | "claim" | "document" | "question" | "proposal" | "next_step";
2196
+ kind: "professional" | "workspace" | "service" | "time_slots" | "booking" | "quote" | "policy" | "handoff" | "metric_chart" | "metric_table" | "gap_list" | "cluster" | "claim" | "document" | "question" | "proposal" | "next_step" | "links" | "anchor" | "open_action";
1473
2197
  question?: components["schemas"]["Question"];
1474
2198
  proposal?: components["schemas"]["Proposal"];
2199
+ anchor?: components["schemas"]["AnchorCard"];
2200
+ open_action?: components["schemas"]["OpenAction"];
1475
2201
  ref?: components["schemas"]["CardRef"];
1476
2202
  title: string;
1477
2203
  subtitle?: string;
1478
2204
  /** Format: uri */
1479
2205
  image_url?: string;
2206
+ /** @description v1-draft.20, a `professional` or `workspace` card's photos, the first the hero */
2207
+ images?: components["schemas"]["CardImage"][];
2208
+ /** @description v1-draft.29, on a `time_slots` or `professional` card only: what the person asked to be passed to the professional, in their own words and language, carried with this card's hand-off (`select_slot`, or the `open` action) for the app to prefill its checkout note, which the person can still edit. An app whose checkout has no note ignores it. Plain text, never markup. */
2209
+ booking_note?: string;
2210
+ /**
2211
+ * Format: date-time
2212
+ * @description v1-draft.20, a `professional` or `workspace` card's earliest open time, as the tenant's tool returned it
2213
+ */
2214
+ next_available?: string;
2215
+ group?: components["schemas"]["CardGroup"];
2216
+ /** @description v1-draft.20, a `links` card's links */
2217
+ links?: components["schemas"]["CardLink"][];
2218
+ provenance?: components["schemas"]["ScopeProvenance"];
1480
2219
  rating?: {
1481
2220
  value_ppm: number;
1482
2221
  count: number;
@@ -1520,7 +2259,7 @@ interface components {
1520
2259
  points: {
1521
2260
  /** Format: date */
1522
2261
  t: string;
1523
- /** @description null when suppressed by the k-anonymity floor */
2262
+ /** @description null when suppressed by the k-anonymity floor; on a metric without k_floor, nothing to measure */
1524
2263
  value: number | null;
1525
2264
  suppressed?: boolean;
1526
2265
  }[];
@@ -1538,6 +2277,7 @@ interface components {
1538
2277
  dims: {
1539
2278
  [key: string]: string;
1540
2279
  };
2280
+ /** @description null when suppressed by the metric's k_floor; on a metric without k_floor, nothing to measure */
1541
2281
  values: {
1542
2282
  [key: string]: number | null;
1543
2283
  };
@@ -1548,7 +2288,8 @@ interface components {
1548
2288
  area: string;
1549
2289
  demand: number;
1550
2290
  supply: number;
1551
- zero_result_share_ppm: number;
2291
+ /** @description v1-draft.33: null when fewer than the metric's k_floor distinct people are behind the zero results, whatever the cell's demand (a subgroup is released only on its own people) */
2292
+ zero_result_share_ppm: number | null;
1552
2293
  trend_ppm?: number | null;
1553
2294
  }[];
1554
2295
  label?: string;
@@ -1556,7 +2297,7 @@ interface components {
1556
2297
  share_ppm?: number;
1557
2298
  /** @enum {string} */
1558
2299
  outcome?: "zero_results" | "withheld" | "handoff" | "mixed";
1559
- /** @description redacted */
2300
+ /** @description Not sent since v1-draft.33: an `intent_cluster` card carries its label and counts only. Customer questions, even redacted, can name a person or a health matter. */
1560
2301
  examples?: string[];
1561
2302
  claim?: {
1562
2303
  id: string;
@@ -1582,7 +2323,7 @@ interface components {
1582
2323
  snippet?: string;
1583
2324
  /** Format: date-time */
1584
2325
  updated_at?: string;
1585
- } & (unknown & unknown & unknown);
2326
+ } & (unknown & unknown & unknown & unknown & unknown & unknown & unknown);
1586
2327
  /** @enum {string} */
1587
2328
  ValueUnit: "count" | "ppm" | "micros_usd" | "ms";
1588
2329
  MetricRef: {
@@ -1590,6 +2331,7 @@ interface components {
1590
2331
  label: string;
1591
2332
  unit: components["schemas"]["ValueUnit"];
1592
2333
  definition: string;
2334
+ /** @description The privacy floor: a value built from fewer than this many distinct people is suppressed (`null`). Absent when no floor applies, as on a business's own operational data (Studio's pages, read by the business copilot); a `null` value there means there was nothing to measure, never a hidden count. */
1593
2335
  k_floor?: number;
1594
2336
  };
1595
2337
  /** @description Links a span of the reply text to the card or tool call it came from. Offsets are in Unicode code points (not bytes, not UTF-16 units), start inclusive, end exclusive. */
@@ -1822,6 +2564,139 @@ interface components {
1822
2564
  /** Format: date-time */
1823
2565
  at: string;
1824
2566
  };
2567
+ ConversationPage: {
2568
+ data: components["schemas"]["ConversationSummary"][];
2569
+ next_cursor: string | null;
2570
+ };
2571
+ /** @description v1-draft.21: one past conversation as a history screen lists it. The title is from the conversation's last search intent or its date, never the person's words. v1-draft.37: an anchored conversation carries `anchor` (its type and title, so a history screen groups by it with no other call), and any conversation may carry `last_message`, a short preview of its last message. */
2572
+ ConversationSummary: {
2573
+ id: components["schemas"]["ConversationId"];
2574
+ agent: string;
2575
+ experience_mode: components["schemas"]["ExperienceMode"];
2576
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
2577
+ title: string;
2578
+ /**
2579
+ * @description `intent`: the last search intent (service and place); `date`: the day it started
2580
+ * @enum {string}
2581
+ */
2582
+ title_source: "intent" | "date";
2583
+ /** Format: date-time */
2584
+ started_at: string;
2585
+ /** Format: date-time */
2586
+ last_activity_at: string;
2587
+ /** @description the messages its timeline holds (cards not counted) */
2588
+ message_count: number;
2589
+ /**
2590
+ * @description `open`: a session can resume it; `ended`: it can only be read
2591
+ * @enum {string}
2592
+ */
2593
+ state: "open" | "ended";
2594
+ /** @description v1-draft.37, what an anchored conversation is about: the anchor's type and its title as the person saw it (`SessionAnchor.title`), never message text. History groups conversations by it. */
2595
+ anchor?: {
2596
+ kind: components["schemas"]["AnchorType"];
2597
+ title: string;
2598
+ };
2599
+ /** @description v1-draft.37, a preview of the conversation's last message, at most 120 code points, cut at a word with an ellipsis, made from the message as the timeline stores it: after digit masking and redaction, never from a withheld sentence. Never a refused message (`refusal: true`), and absent under the tenant's `transcript_retention` `none`. */
2600
+ last_message?: {
2601
+ /** @enum {string} */
2602
+ role: "person" | "assistant" | "staff";
2603
+ text: string;
2604
+ };
2605
+ };
2606
+ TimelinePage: {
2607
+ conversation: components["schemas"]["ConversationSummary"];
2608
+ data: components["schemas"]["TimelineItem"][];
2609
+ /** @description the next older page; null at the conversation's start */
2610
+ next_cursor: string | null;
2611
+ };
2612
+ /**
2613
+ * @description v1-draft.21: one item of a past conversation, a message or a card its turn drew. `id` is stable and unique within the conversation; a message's `event_id` is its live event's identifier (`TranscriptItem.id`), the key an app merges live and resumed messages by, with `role`. A card is read-only in history.
2614
+ * Unlike `session.ready.transcript`, the history holds the turns the tenant's scope refused (a competitor, a blocked topic, a strictly off-scope question): the person's message and the fixed line said instead of an answer both carry `refusal: true`, so the app draws them as it drew them live (muted, never offered again). An input a guardrail blocks (an instruction override, an attempt to extract the prompt or keys, a persona hijack) is never recorded and is in no history (v1-draft.26): attack text is not kept in a person's history. A `proposal` card carries `proposal_outcome`, what became of it.
2615
+ */
2616
+ TimelineItem: {
2617
+ /** @constant */
2618
+ type: "message";
2619
+ id: string;
2620
+ /**
2621
+ * @description a client tolerates a role it does not know, as in `TranscriptItem`
2622
+ * @enum {string}
2623
+ */
2624
+ role: "person" | "assistant" | "staff";
2625
+ text: string;
2626
+ /** Format: date-time */
2627
+ at: string;
2628
+ /** @description absent on a `person` message sent without a `client_event_id` */
2629
+ event_id?: string;
2630
+ turn_id?: string;
2631
+ /** @description `assistant` only, present only when true: the reply stopped before it finished */
2632
+ interrupted?: boolean;
2633
+ /** @description v1-draft.40, `assistant` only: the person's current rating and reaction, absent when there is none */
2634
+ feedback?: components["schemas"]["MessageFeedback"];
2635
+ /**
2636
+ * @description Present only when true: the tenant's scope refused this turn. On a `person` message, the text it refused (as recorded, card-like digits masked); on an `assistant` message, the fixed line said instead of an answer. An input a guardrail blocks is never recorded, so it never appears here.
2637
+ * @constant
2638
+ */
2639
+ refusal?: true;
2640
+ } | ({
2641
+ /** @constant */
2642
+ type: "card";
2643
+ id: string;
2644
+ turn_id?: string;
2645
+ /** Format: date-time */
2646
+ at: string;
2647
+ card: components["schemas"]["Card"];
2648
+ /**
2649
+ * @description A `proposal` card only, always present on one: the outcome the app reported for it (`ui.action` `confirmed`, `declined`, `failed` or `expired`), or `pending` while none was reported. Draw it as settled; a past proposal is never confirmed from history.
2650
+ * @enum {string}
2651
+ */
2652
+ proposal_outcome?: "pending" | "confirmed" | "declined" | "failed" | "expired";
2653
+ } & unknown);
2654
+ ConversationDeleted: {
2655
+ conversation_id: components["schemas"]["ConversationId"];
2656
+ sessions_revoked: number;
2657
+ };
2658
+ /** @example memory-2026-10 */
2659
+ MemoryNoticeVersion: string;
2660
+ /** @description v1-draft.27: the one-time notice that the agent remembers the person across conversations, in the subject's language, written by Vinaverse. Show it once with two choices: dismiss it (`acknowledgeMemoryNotice`) or turn memory off (`putSubjectMemory` with `remembered: false`), both through the tenant's backend. */
2661
+ MemoryNotice: {
2662
+ version: components["schemas"]["MemoryNoticeVersion"];
2663
+ /** @description in Unicode code points */
2664
+ text: string;
2665
+ };
2666
+ /** @description v1-draft.27: whether the agent remembers this person across conversations in this session, and the notice to show while they have not acknowledged its current version. `notice` is absent once acknowledged, when memory is off, and for a guest (a guest is never remembered). */
2667
+ SessionMemory: {
2668
+ remembered: boolean;
2669
+ notice?: components["schemas"]["MemoryNotice"];
2670
+ };
2671
+ MemorySource: {
2672
+ conversation_id: components["schemas"]["ConversationId"];
2673
+ /** Format: date-time */
2674
+ at: string;
2675
+ };
2676
+ /** @description v1-draft.27: one thing the person asked the agent to remember ("remember that I prefer mornings"), in the words the agent said back to the person, never inferred. Never a health fact or a never-store detail; a fact about someone else ("my daughter prefers short hair") is the person's own note (v1-draft.39). */
2677
+ MemoryPreference: {
2678
+ id: string;
2679
+ text: string;
2680
+ source: components["schemas"]["MemorySource"];
2681
+ };
2682
+ /** @description v1-draft.27: the last open search intent, as the search resolved it (a catalogue service's name, a place's name), never the person's words; kept 7 days from `at`. */
2683
+ LastSearch: {
2684
+ service?: string;
2685
+ area?: string;
2686
+ /** Format: date-time */
2687
+ at: string;
2688
+ source: components["schemas"]["MemorySource"];
2689
+ };
2690
+ SubjectMemoryChoice: {
2691
+ /** @description `false`: "don't remember me", on both sides of the app */
2692
+ remembered: boolean;
2693
+ };
2694
+ SubjectMemory: {
2695
+ remembered: boolean;
2696
+ notice_acknowledged?: components["schemas"]["MemoryNoticeVersion"];
2697
+ preferences: components["schemas"]["MemoryPreference"][];
2698
+ last_search?: components["schemas"]["LastSearch"];
2699
+ };
1825
2700
  /** @description A time slot the person picked on a `time_slots` card (v1-draft.17, `docs/realtime-protocol.md` §6): the slot's `id`, and the `call_id` of the tool call the card came from (the card's `source.call_id`), so the runtime checks the pick against the evidence it holds for that call. Both are copied from the card; never built by the app. */
1826
2701
  SlotSelection: {
1827
2702
  id: string;
@@ -1956,6 +2831,8 @@ interface components {
1956
2831
  type: "input.text";
1957
2832
  client_event_id?: string;
1958
2833
  text: string;
2834
+ /** @description v1-draft.37, optional: the anchor intent a chip carries, sent with the chip's text. Eligible now, an `action` intent gets its `open_action` card and an `explain` intent the manifest's template answer, with no model call; no longer eligible, the agent says what changed and offers the new intents; an intent the session's anchor does not list is treated as the text alone. */
2835
+ intent?: components["schemas"]["IntentId"];
1959
2836
  } | {
1960
2837
  /** @constant */
1961
2838
  type: "input.cancel";
@@ -1985,6 +2862,14 @@ interface components {
1985
2862
  card_id: string;
1986
2863
  question_id: components["schemas"]["QuestionId"];
1987
2864
  answer: components["schemas"]["AnswerInput"];
2865
+ } | {
2866
+ /** @constant */
2867
+ type: "action.result";
2868
+ client_event_id?: string;
2869
+ card_id: string;
2870
+ action_id: string;
2871
+ /** @enum {string} */
2872
+ outcome: "completed" | "cancelled" | "failed" | "unknown";
1988
2873
  } | {
1989
2874
  /** @constant */
1990
2875
  type: "ping";
@@ -2002,6 +2887,16 @@ interface components {
2002
2887
  };
2003
2888
  disclosure: string;
2004
2889
  resumed: boolean;
2890
+ /** @description v1-draft.20, the session's mode (`Session.experience_mode`), so the app keeps one store per mode */
2891
+ experience_mode?: components["schemas"]["ExperienceMode"];
2892
+ /** @description v1-draft.20, present when the session acts for a business */
2893
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
2894
+ /** @description v1-draft.27, as `Session.memory`, so an app that reconnects still shows an unacknowledged notice */
2895
+ memory?: components["schemas"]["SessionMemory"];
2896
+ /** @description v1-draft.37, as `Session.anchor`, refreshed when the session's anchor was re-read */
2897
+ anchor?: components["schemas"]["SessionAnchorView"];
2898
+ /** @description v1-draft.40, as `Session.experience`: the value at session start, the same on every reconnect of the session */
2899
+ experience?: components["schemas"]["ChatExperience"];
2005
2900
  /**
2006
2901
  * @description Optional. The last messages of the conversation, oldest first, text only, so the app can redraw it after a kill or a reconnect. Older context is not sent.
2007
2902
  * A guided flow's resumed `session.ready` always carries it. Any other session's (a chat; v1-draft.17) carries it when `resumed` is true and there is something to restore, whether the app reconnects to the same session or the backend created a new session of the conversation: - the conversation's messages across its sessions (the session's own when it has no conversation):
@@ -2044,9 +2939,12 @@ interface components {
2044
2939
  /** @constant */
2045
2940
  type: "tool.status";
2046
2941
  turn_id: string;
2942
+ /** @description the step's words for builds before v1-draft.40; a newer client draws `step` */
2047
2943
  label: string;
2048
2944
  /** @enum {string} */
2049
2945
  state: "running" | "done" | "failed";
2946
+ /** @description v1-draft.40, the named working step; absent from a server that predates it */
2947
+ step?: components["schemas"]["ToolStep"];
2050
2948
  } | {
2051
2949
  /** @constant */
2052
2950
  type: "message.delta";
@@ -2062,6 +2960,7 @@ interface components {
2062
2960
  /** @constant */
2063
2961
  type: "message.completed";
2064
2962
  turn_id: string;
2963
+ /** @description v1-draft.30: the turn's reply. It may leave out sentences streamed in a step that ended in a tool call; show it in place of the turn's streamed `message.delta` texts. */
2065
2964
  text: string;
2066
2965
  /** @description optional; spans of `text` with their source card or tool call */
2067
2966
  citations?: components["schemas"]["Citation"][];
@@ -2106,6 +3005,15 @@ interface components {
2106
3005
  */
2107
3006
  code: "screened" | "too_long" | "turn_in_progress" | "limit_reached" | "duplicate" | "guardrail" | "screen_unavailable";
2108
3007
  message: string;
3008
+ } | {
3009
+ /** @constant */
3010
+ type: "context.updated";
3011
+ revision: number;
3012
+ /**
3013
+ * @description present only after an explicit clear
3014
+ * @constant
3015
+ */
3016
+ cleared?: true;
2109
3017
  } | {
2110
3018
  /** @constant */
2111
3019
  type: "session.expiring";
@@ -2114,10 +3022,10 @@ interface components {
2114
3022
  /** @constant */
2115
3023
  type: "session.closed";
2116
3024
  /**
2117
- * @description `replaced`: a newer session of the same conversation was created (close code `4409`)
3025
+ * @description `replaced`: a newer session of the same conversation was created (close code `4409`). `deleted` (v1-draft.21): the person deleted the conversation from their history (`deleteConversation`, close code `4403`); drop it from the app's store and never resume it. A client that does not know `deleted` reads it as `revoked`, as any unknown reason.
2118
3026
  * @enum {string}
2119
3027
  */
2120
- reason: "expired" | "revoked" | "limit_reached" | "idle" | "server_restart" | "replaced";
3028
+ reason: "expired" | "revoked" | "limit_reached" | "idle" | "server_restart" | "replaced" | "deleted";
2121
3029
  } | {
2122
3030
  /** @constant */
2123
3031
  type: "ui.answer.recorded";
@@ -2283,7 +3191,10 @@ interface components {
2283
3191
  threshold_policy?: string;
2284
3192
  policy_version: string;
2285
3193
  model: {
2286
- id: string;
3194
+ id: components["schemas"]["ModelName"];
3195
+ /** @description what a person reads for `id`, such as "Vinaverse Language 0.1" (v1-draft.35) */
3196
+ label?: string;
3197
+ /** @description `vinaverse` for a Vinaverse model (v1-draft.34) */
2287
3198
  provider: string;
2288
3199
  revision?: string;
2289
3200
  };
@@ -2550,23 +3461,578 @@ interface components {
2550
3461
  text: string;
2551
3462
  };
2552
3463
  };
2553
- FeedbackRecord: {
2554
- decision_id?: string;
2555
- turn_id?: string;
2556
- /** @enum {string} */
2557
- outcome: "approved" | "rejected" | "edited" | "escalated" | "user_appealed" | "appeal_upheld" | "appeal_denied" | "thumbs_up" | "thumbs_down";
2558
- /** @enum {string} */
2559
- final_class?: "clean" | "review" | "hold" | "never";
2560
- reason_codes?: string[];
2561
- reviewer?: {
2562
- /** @enum {string} */
2563
- role?: "admin" | "moderator" | "support" | "end_user";
2564
- ref?: string;
2565
- };
3464
+ /**
3465
+ * @description v1-draft.40, the preset a tenant starts from: `minimal` (a quiet support chat), `standard` (a calm concierge, the default) or `rich` (a messaging-first companion). The values of each are in `fixtures/api/experience/valid/presets.json`.
3466
+ * @enum {string}
3467
+ */
3468
+ ChatPreset: "minimal" | "standard" | "rich";
3469
+ /**
3470
+ * @description v1-draft.40 (VIN-115, the chat spec's module G): how the in-app chat behaves for one agent, as the tenant set
3471
+ * it in Studio. Every field is optional: a field absent is the kit's own default (the `standard` preset), so an
3472
+ * app built before a field existed keeps working, except a field the gateway enforces (`edit`, `regenerate`,
3473
+ * `versions`, `fork`, `pins` stored on the server, `summary`), which resolves off when absent. That is about the
3474
+ * resolved value; in a setting's `overrides` an absent field is the preset's value. A client ignores a field it
3475
+ * does not know.
3476
+ *
3477
+ * **Resolved, not raw.** Inside a conversation the value is resolved: the tenant's preset with its changes
3478
+ * (admin-v1 `ChatExperienceSetting`), narrowed by what this server serves (a feature it does not serve yet
3479
+ * resolves off) and, where a field says so, by `createSession.client.capabilities`. The kit narrows it again
3480
+ * with the app's own choices, then with the message's state (Try again on a booking reply is never offered);
3481
+ * it never widens it.
3482
+ *
3483
+ * **What it cannot change** (spec G TC-05): the AI disclosure, the phone's Reduce Motion and text size, contrast
3484
+ * floors, performance budgets, the money and deadline rules, no edit, Try again or fork across a commitment,
3485
+ * and the privacy floors. None of those is a field here. Turning a feature off never hides or erases what
3486
+ * exists: forks, pins, reactions and versions already made stay readable.
3487
+ *
3488
+ * Attachments (spec K) are a separate group, added with the attachments contract.
3489
+ */
3490
+ ChatExperience: {
3491
+ preset?: components["schemas"]["ChatPreset"];
3492
+ header?: {
3493
+ /**
3494
+ * @description default `per_mode`
3495
+ * @enum {string}
3496
+ */
3497
+ style?: "neutral" | "brand" | "per_mode";
3498
+ /** @description the "About Riley" line; default true */
3499
+ context?: boolean;
3500
+ };
3501
+ avatar?: {
3502
+ /**
3503
+ * @description default `last_of_run`
3504
+ * @enum {string}
3505
+ */
3506
+ in_thread?: "last_of_run" | "every" | "off";
3507
+ };
3508
+ anchor?: {
3509
+ /** @description a compact strip once the anchor card scrolls away; default true */
3510
+ pin_strip?: boolean;
3511
+ };
3512
+ composer?: {
3513
+ /**
3514
+ * @description default `dictation`
3515
+ * @enum {string}
3516
+ */
3517
+ voice?: "off" | "dictation" | "live";
3518
+ };
3519
+ /**
3520
+ * @description default `outcomes`
3521
+ * @enum {string}
3522
+ */
3523
+ haptics?: "off" | "outcomes" | "selection_and_outcomes";
3524
+ /** @description voice and notifications only; default false */
3525
+ sound?: boolean;
3526
+ language?: {
3527
+ /**
3528
+ * @description default `app_locale`
3529
+ * @enum {string}
3530
+ */
3531
+ chips?: "app_locale" | "person_language";
3532
+ };
3533
+ /** @description the working line (spec A AS-10 to AS-12) */
3534
+ working?: {
3535
+ /**
3536
+ * @description `glance` shows "Working on it…" only; `steps` names the step (default); `steps_expandable` adds the step list
3537
+ * @enum {string}
3538
+ */
3539
+ detail?: "glance" | "steps" | "steps_expandable";
3540
+ /** @description "Still checking…" after; default 4 */
3541
+ slow_after_s?: number;
3542
+ /** @description "Taking longer" and Stop after; default 12 */
3543
+ stop_after_s?: number;
3544
+ /** @description the state glyph on the agent's mark; default true */
3545
+ glyph?: boolean;
3546
+ };
3547
+ thinking?: {
3548
+ /**
3549
+ * @description default `dots`
3550
+ * @enum {string}
3551
+ */
3552
+ indicator?: "dots" | "text" | "off";
3553
+ };
3554
+ stream?: {
3555
+ /**
3556
+ * @description default `paced`
3557
+ * @enum {string}
3558
+ */
3559
+ reveal?: "paced" | "sentence" | "whole_reply";
3560
+ /** @description default true */
3561
+ caret?: boolean;
3562
+ };
3563
+ motion?: {
3564
+ /**
3565
+ * @description `calm` uses crossfades only; default `standard`
3566
+ * @enum {string}
3567
+ */
3568
+ level?: "calm" | "standard";
3569
+ };
3570
+ send?: {
3571
+ /**
3572
+ * @description default `full`
3573
+ * @enum {string}
3574
+ */
3575
+ states?: "full" | "failures_only";
3576
+ };
3577
+ /**
3578
+ * @description default `off`
3579
+ * @enum {string}
3580
+ */
3581
+ receipts?: "off" | "delivered";
3582
+ skeleton?: {
3583
+ /**
3584
+ * @description `quiet` never pulses; default `shaped`
3585
+ * @enum {string}
3586
+ */
3587
+ style?: "shaped" | "quiet";
3588
+ };
3589
+ /** @description result cards (spec B) */
3590
+ cards?: {
3591
+ /** @description the card kinds drawn; absent in a setting: every kind the connector supports when it is saved (stored as that list, so a kind added later stays off). Open: an unknown kind is ignored */
3592
+ kinds?: string[];
3593
+ layout?: {
3594
+ /**
3595
+ * @description default `list`
3596
+ * @enum {string}
3597
+ */
3598
+ decision?: "list" | "strip";
3599
+ /**
3600
+ * @description default `strip`
3601
+ * @enum {string}
3602
+ */
3603
+ browse?: "strip" | "list";
3604
+ };
3605
+ /** @description the default form per card kind; absent kinds follow the layout rule */
3606
+ form?: {
3607
+ [key: string]: "row" | "tile" | "expanded";
3608
+ };
3609
+ /**
3610
+ * @description default `photo_first`
3611
+ * @enum {string}
3612
+ */
3613
+ media?: "photo_first" | "monogram" | "none";
3614
+ /**
3615
+ * @description default `on`
3616
+ * @enum {string}
3617
+ */
3618
+ prices?: "on" | "from_only" | "off";
3619
+ /** @description default true */
3620
+ ratings?: boolean;
3621
+ /** @description when supported; default true */
3622
+ distance?: boolean;
3623
+ /** @description default true */
3624
+ next_opening?: boolean;
3625
+ /** @description default false */
3626
+ compare?: boolean;
3627
+ /** @description through the host's map; default false */
3628
+ map?: boolean;
3629
+ /** @description default true */
3630
+ see_all?: boolean;
3631
+ /** @description per card kind, the actions drawn; absent kinds draw every eligible action */
3632
+ actions?: {
3633
+ [key: string]: ("open" | "book" | "message" | "save" | "compare")[];
3634
+ };
3635
+ /** @description the context changes a card's emphasis; default true */
3636
+ context_lead?: boolean;
3637
+ /** @description the "From your booking · checked just now" line; default true */
3638
+ sources?: boolean;
3639
+ };
3640
+ /** @description in-chat inputs (spec C) */
3641
+ inputs?: {
3642
+ /** @description absent: the flow's own set */
3643
+ enabled?: ("photo" | "address_map" | "hours" | "service_builder" | "trades" | "date_range" | "form")[];
3644
+ photo?: {
3645
+ /**
3646
+ * @description default `both`
3647
+ * @enum {string}
3648
+ */
3649
+ sources?: "camera" | "library" | "both";
3650
+ /**
3651
+ * @description absent: per purpose
3652
+ * @enum {string}
3653
+ */
3654
+ crop?: "square" | "circle" | "free" | "none";
3655
+ };
3656
+ address?: {
3657
+ /**
3658
+ * @description default `on`
3659
+ * @enum {string}
3660
+ */
3661
+ map?: "on" | "text_only";
3662
+ };
3663
+ hours?: {
3664
+ /** @description default true */
3665
+ breaks?: boolean;
3666
+ };
3667
+ /**
3668
+ * @description default `always`
3669
+ * @enum {string}
3670
+ */
3671
+ review?: "always" | "consequential_only";
3672
+ /** @description default true */
3673
+ natural_language_fill?: boolean;
3674
+ /** @description the customer-facing preview during setup; default true */
3675
+ preview?: boolean;
3676
+ };
3677
+ switcher?: {
3678
+ /** @description default true */
3679
+ enabled?: boolean;
3680
+ /**
3681
+ * @description default `active_mode`
3682
+ * @enum {string}
3683
+ */
3684
+ scope?: "active_mode" | "all_modes";
3685
+ };
3686
+ continue_offer?: {
3687
+ /** @description default true */
3688
+ enabled?: boolean;
3689
+ /** @description Decided, Still needed, Changed since; default false */
3690
+ checkpoint?: boolean;
3691
+ };
3692
+ reopen?: {
3693
+ /**
3694
+ * @description default `where_left`
3695
+ * @enum {string}
3696
+ */
3697
+ position?: "where_left" | "latest";
3698
+ };
3699
+ /** @description the switcher and history (spec D) */
3700
+ history?: {
3701
+ /** @description default true when the retention keeps transcripts */
3702
+ enabled?: boolean;
3703
+ /**
3704
+ * @description default `context`
3705
+ * @enum {string}
3706
+ */
3707
+ group?: "context" | "day";
3708
+ /** @description default true */
3709
+ live_status?: boolean;
3710
+ /** @description default true */
3711
+ outcomes?: boolean;
3712
+ /** @description `ConversationSummary.last_message` drawn; default false */
3713
+ preview?: boolean;
3714
+ /** @description default true */
3715
+ filters?: boolean;
3716
+ /** @description default true */
3717
+ delete?: boolean;
3718
+ /** @description default true */
3719
+ archive?: boolean;
3720
+ /** @description default true */
3721
+ rename?: boolean;
3722
+ };
3723
+ drafts?: {
3724
+ /** @description default true */
3725
+ listed?: boolean;
3726
+ };
3727
+ delete?: {
3728
+ /** @description the kit holds a delete this long; default 10 */
3729
+ undo_seconds?: number;
3730
+ };
3731
+ /** @description accuracy feedback (spec F MA-01, MA-02) */
3732
+ feedback?: {
3733
+ /** @description helpful and not helpful; default true */
3734
+ accuracy?: boolean;
3735
+ /** @description the reasons offered after "not helpful", in order; empty: none. Default every one */
3736
+ reasons?: components["schemas"]["FeedbackReason"][];
3737
+ };
3738
+ /** @description Reactions (spec F MA-03). `like` is a single heart, recorded as `love`; `emoji` offers `set`, in order. With `like`, `set` is `[love]`. A reaction is never a rating, a preference or a consent. */
3739
+ reactions?: {
3740
+ /**
3741
+ * @description default `like`
3742
+ * @enum {string}
3743
+ */
3744
+ mode?: "off" | "like" | "emoji";
3745
+ set?: components["schemas"]["ReactionKind"][];
3746
+ /** @description a double tap gives the first reaction of `set`; default true */
3747
+ double_tap?: boolean;
3748
+ };
3749
+ /** @description "How was this conversation?" once; default false */
3750
+ csat?: boolean;
3751
+ /** @description default true */
3752
+ copy?: boolean;
3753
+ share?: {
3754
+ /**
3755
+ * @description text with a preview sheet; default `preview`
3756
+ * @enum {string}
3757
+ */
3758
+ mode?: "off" | "preview";
3759
+ /** @description persistent public links (not served yet, resolves false); default false */
3760
+ links?: boolean;
3761
+ };
3762
+ /** @description the phone's own speech; default true */
3763
+ read_aloud?: boolean;
3764
+ /**
3765
+ * @description Which of the person's messages may be edited (spec F MA-21): the latest (default), any after the most recent commitment, or none. Never across a commitment, whatever this says: the gateway refuses that.
3766
+ * @enum {string}
3767
+ */
3768
+ edit?: "off" | "latest" | "after_commitment";
3769
+ /** @description Try again (spec F MA-30 to MA-32); only on replies that committed nothing, enforced by the gateway */
3770
+ regenerate?: {
3771
+ /**
3772
+ * @description default `newest`
3773
+ * @enum {string}
3774
+ */
3775
+ scope?: "off" | "newest" | "any";
3776
+ /** @description versions per turn; default 5 */
3777
+ max?: number;
3778
+ };
3779
+ /**
3780
+ * @description default `on`
3781
+ * @enum {string}
3782
+ */
3783
+ versions?: "off" | "on" | "on_with_sheet";
3784
+ /** @description forks (spec F MA-41 to MA-49) */
3785
+ fork?: {
3786
+ /** @description default false */
3787
+ enabled?: boolean;
3788
+ /**
3789
+ * @description default `nested`
3790
+ * @enum {string}
3791
+ */
3792
+ style?: "nested" | "new_conversation";
3793
+ /** @description default true */
3794
+ compare?: boolean;
3795
+ /** @description default true */
3796
+ carry_back?: boolean;
3797
+ /** @description forks per conversation; default 5 */
3798
+ max?: number;
3799
+ };
3800
+ pins?: {
3801
+ /** @description default true */
3802
+ messages?: boolean;
3803
+ /** @description pinned messages per conversation; default 3 */
3804
+ max?: number;
3805
+ /** @description default true */
3806
+ conversations?: boolean;
3807
+ /**
3808
+ * @description `server` when the server serves pins
3809
+ * @enum {string}
3810
+ */
3811
+ storage?: "server" | "device";
3812
+ };
3813
+ favorites?: {
3814
+ /** @description default false */
3815
+ messages?: boolean;
3816
+ /** @description default false */
3817
+ conversations?: boolean;
3818
+ };
3819
+ actions?: {
3820
+ /**
3821
+ * @description default `row_newest_plus_menu`
3822
+ * @enum {string}
3823
+ */
3824
+ surface?: "row_newest_plus_menu" | "menu_only";
3825
+ };
3826
+ /** @description time in the transcript (spec I LC-01); day separators, exact times and times in labels stay in every mode */
3827
+ timestamps?: {
3828
+ /**
3829
+ * @description default `run`
3830
+ * @enum {string}
3831
+ */
3832
+ mode?: "run" | "every" | "gap";
3833
+ /**
3834
+ * @description for gap labels; default 60
3835
+ * @enum {integer}
3836
+ */
3837
+ gap_minutes?: 30 | 60 | 120;
3838
+ /** @description default true */
3839
+ floating_date?: boolean;
3840
+ };
3841
+ /** @description default true */
3842
+ day_separators?: boolean;
3843
+ /** @description the person's summary (spec I LC-10 to LC-12) */
3844
+ summary?: {
3845
+ /**
3846
+ * @description default `entry`
3847
+ * @enum {string}
3848
+ */
3849
+ mode?: "off" | "entry" | "entry_with_preview";
3850
+ /** @description default true */
3851
+ outcome_chips?: boolean;
3852
+ /** @description the one-line summary on history rows; default false */
3853
+ show_in_history?: boolean;
3854
+ /** @description default true */
3855
+ editable?: boolean;
3856
+ };
3857
+ compaction?: {
3858
+ /** @description "Earlier messages summarized"; off hides the divider only, the transcript never changes; default true */
3859
+ divider?: boolean;
3860
+ };
3861
+ /** @description search (spec I LC-30); results are always the message's own text */
3862
+ search?: {
3863
+ /** @description empty: no search. Default both */
3864
+ scopes?: ("conversation" | "mode")[];
3865
+ /** @description meaning as well as words; false is words only; default true */
3866
+ semantic?: boolean;
3867
+ };
3868
+ chapters?: {
3869
+ /** @description default false */
3870
+ enabled?: boolean;
3871
+ /** @description default 80 */
3872
+ min_messages?: number;
3873
+ };
3874
+ /** @description default true */
3875
+ jump_to_latest?: boolean;
3876
+ unread?: {
3877
+ /** @description default true */
3878
+ divider?: boolean;
3879
+ };
3880
+ sources?: {
3881
+ /** @description under consequential claims; default true */
3882
+ line?: boolean;
3883
+ };
3884
+ /** @description memory in the chat (spec J); off whenever the person or the tenant turned memory off */
3885
+ memory?: {
3886
+ /** @description default true */
3887
+ chips?: boolean;
3888
+ /** @description "What the agent remembers"; default false */
3889
+ page?: boolean;
3890
+ /** @description a conversation kept out of memory; default true */
3891
+ per_conversation_off?: boolean;
3892
+ };
3893
+ handoff?: {
3894
+ /** @description absent in a setting: every route the tenant has when it is saved (stored as that list) */
3895
+ routes?: ("support" | "professional" | "host")[];
3896
+ /** @description a reviewable summary attached; default true */
3897
+ summary?: boolean;
3898
+ };
3899
+ /** @description default false */
3900
+ promises?: boolean;
3901
+ /** @description default false */
3902
+ ideas?: boolean;
3903
+ /** @description default false */
3904
+ goals?: boolean;
3905
+ };
3906
+ /** @description v1-draft.40, `getAgentExperience`: the agent's chat experience before any session, resolved as for `Session.experience` except the client's capabilities, which no session has given yet. */
3907
+ AgentExperience: {
3908
+ agent: string;
3909
+ experience_mode: components["schemas"]["ExperienceMode"];
3910
+ experience: components["schemas"]["ChatExperience"];
3911
+ /**
3912
+ * Format: date-time
3913
+ * @description when the tenant last changed it; absent while the agent runs on the defaults
3914
+ */
3915
+ updated_at?: string;
3916
+ };
3917
+ /**
3918
+ * @description v1-draft.40, what a working step does, for its icon (spec A AS-10). Open for clients: an unknown kind is drawn as `other`.
3919
+ * @enum {string}
3920
+ */
3921
+ StepKind: "search" | "availability" | "booking" | "rental" | "policy" | "price" | "setup" | "handoff" | "other";
3922
+ /**
3923
+ * @description v1-draft.40 (VIN-115, spec A AS-10 to AS-14): the working line of one tool call, on `tool.status`.
3924
+ *
3925
+ * **The words.** `text` is the step in the present tense ("Checking Riley's openings…"), and on `done` or
3926
+ * `failed` `text_done` is the past tense with its outcome ("Checked Riley's openings · 3 times on Saturday";
3927
+ * "Couldn't check Riley's openings right now"). Both come from the tenant's closed template for the tool (the
3928
+ * connector manifest's `tools[].step`, admin-v1), in the session's language, or from the platform's generic
3929
+ * step for the tool's capability when the tenant has none. The gateway fills them, so a client draws them as
3930
+ * they are and never formats a template.
3931
+ *
3932
+ * **Safe values only.** A template's placeholders are a closed list (`params`): each value comes from the
3933
+ * tool's validated arguments or its result (a resolved place's label, a resource's name the tool returned, a
3934
+ * service, a date), checked and cut to 60 code points, never from the model's text, a raw tool name or an
3935
+ * argument as the model wrote it. A value that is absent or fails its check leaves the template's generic form
3936
+ * ("Checking openings…"), never a guess.
3937
+ *
3938
+ * `template` and `params` are there for a client that localizes on its own; most draw `text`. `id` is the same
3939
+ * on the `running` and the final event of one call, so the step list replaces its row in place.
3940
+ */
3941
+ ToolStep: {
3942
+ id: string;
3943
+ kind: components["schemas"]["StepKind"];
3944
+ text: string;
3945
+ /** @description on `done` and `failed` only */
3946
+ text_done?: string;
3947
+ /**
3948
+ * @description the template the words came from: `<tool>.running`, `<tool>.done`, or `platform.<kind>`
3949
+ * @example get_availability.running
3950
+ */
3951
+ template?: string;
3952
+ params?: {
3953
+ area?: string;
3954
+ name?: string;
3955
+ service?: string;
3956
+ /** Format: date */
3957
+ date?: string;
3958
+ };
3959
+ /** Format: date-time */
3960
+ started_at: string;
3961
+ /** @description on `done` only */
3962
+ outcome?: {
3963
+ /** @description the items a read found, when it is a read */
3964
+ count?: number;
3965
+ /**
3966
+ * @description `recovered`: found after the gateway widened the search; `unchecked`: nothing to count
3967
+ * @enum {string}
3968
+ */
3969
+ summary_key?: "found" | "none" | "recovered" | "unchecked";
3970
+ };
3971
+ };
3972
+ /**
3973
+ * @description An outcome citing a decision or a turn. Append-only: a record never changes, and a later one supersedes it.
3974
+ *
3975
+ * **A person's rating and reaction of a reply** (v1-draft.40, spec F MA-01 to MA-06), with `turn_id` and
3976
+ * `reviewer.role: end_user`:
3977
+ * - `thumbs_up` (helpful) and `thumbs_down` (not helpful); `thumbs_cleared` removes the rating;
3978
+ * - `reaction`, with `reaction` `love`, `thanks`, `haha` or `wow`, or `cleared` to remove the reaction;
3979
+ * - the latest record per turn and person wins, ratings and reactions apart, and a clear takes the turn out of
3980
+ * every count. The person is the turn's subject: the tenant's backend sends only what that person chose;
3981
+ * - a `thumbs_down` may carry `reason_codes` from the closed list `FeedbackReason`, and a `note` of at most 200
3982
+ * code points; an unknown reason code or a longer note rejects that record (`rejected[].code`
3983
+ * `invalid_request`);
3984
+ * - a `reaction` or a clear carries no `reason_codes` and no `note`; `reaction_set` records the tenant's mode and
3985
+ * set when the person reacted (a change of set is a measurement break);
3986
+ * - an agent's reply may be rated or reacted to, including a refused turn's line and a reply restored from
3987
+ * history, never the person's own message. A turn of another tenant or environment, or one with no reply, is
3988
+ * rejected (`not_found`), the same answer for each.
3989
+ */
3990
+ FeedbackRecord: ({
3991
+ decision_id?: string;
3992
+ turn_id?: string;
3993
+ /** @enum {string} */
3994
+ outcome: "approved" | "rejected" | "edited" | "escalated" | "user_appealed" | "appeal_upheld" | "appeal_denied" | "thumbs_up" | "thumbs_down" | "thumbs_cleared" | "reaction";
3995
+ /**
3996
+ * @description v1-draft.40, with `outcome` `reaction` only, and required with it
3997
+ * @enum {string}
3998
+ */
3999
+ reaction?: "love" | "thanks" | "haha" | "wow" | "cleared";
4000
+ reaction_set?: components["schemas"]["ReactionSet"];
4001
+ /** @enum {string} */
4002
+ final_class?: "clean" | "review" | "hold" | "never";
4003
+ reason_codes?: string[];
4004
+ reviewer?: {
4005
+ /** @enum {string} */
4006
+ role?: "admin" | "moderator" | "support" | "end_user";
4007
+ ref?: string;
4008
+ };
2566
4009
  /** Format: date-time */
2567
4010
  decided_at: string;
4011
+ /** @description a reviewer's note; a person's, at most 200 (below) */
2568
4012
  note?: string;
2569
- } & (unknown | unknown);
4013
+ } & (unknown & unknown & unknown)) & (unknown | unknown);
4014
+ /**
4015
+ * @description v1-draft.40, why a reply was not helpful, from the person (spec F MA-02): "Not accurate", "Didn't answer my question", "Too long", "Something else…". A closed list: the tenant offers a subset of it.
4016
+ * @enum {string}
4017
+ */
4018
+ FeedbackReason: "not_accurate" | "did_not_answer" | "too_long" | "other";
4019
+ /**
4020
+ * @description v1-draft.40, a reaction to a reply (spec F MA-03): love (a heart, also the single `like`), thanks, haha, wow. There is no thumbs-up reaction, so a reaction is never mistaken for a rating.
4021
+ * @enum {string}
4022
+ */
4023
+ ReactionKind: "love" | "thanks" | "haha" | "wow";
4024
+ /** @description v1-draft.40, the tenant's reactions when the person reacted (`ChatExperience.reactions`) */
4025
+ ReactionSet: {
4026
+ /** @enum {string} */
4027
+ mode: "like" | "emoji";
4028
+ set: components["schemas"]["ReactionKind"][];
4029
+ };
4030
+ /** @description v1-draft.40, the person's current rating and reaction of an agent message: the latest feedback record of each, absent when there is none or it was cleared. */
4031
+ MessageFeedback: {
4032
+ /** @enum {string} */
4033
+ rating?: "helpful" | "not_helpful";
4034
+ reaction?: components["schemas"]["ReactionKind"];
4035
+ };
2570
4036
  FeedbackBatch: {
2571
4037
  records: components["schemas"]["FeedbackRecord"][];
2572
4038
  };
@@ -2649,7 +4115,9 @@ interface components {
2649
4115
  flags?: components["schemas"]["DocumentFlag"][];
2650
4116
  decision?: components["schemas"]["Decision"];
2651
4117
  model?: {
2652
- id?: string;
4118
+ id?: components["schemas"]["ModelName"];
4119
+ /** @description what a person reads for `id` (v1-draft.35) */
4120
+ label?: string;
2653
4121
  provider?: string;
2654
4122
  };
2655
4123
  error?: components["schemas"]["Problem"];
@@ -2695,7 +4163,16 @@ interface components {
2695
4163
  };
2696
4164
  };
2697
4165
  parameters: {
4166
+ /** @description v1-draft.24, optional: the agent the caller acts for; when given, the conversation must be this agent's, else `404` (the same answer as an unknown id) */
4167
+ BindingAgent: string;
4168
+ /** @description v1-draft.24, optional: the side of the app the caller acts on; when given, the conversation must be bound to it, else `404` */
4169
+ BindingExperienceMode: components["schemas"]["ExperienceMode"];
4170
+ /** @description v1-draft.24, optional: the business the caller acts for; when given, the conversation must be bound to it, else `404`. With `experience_mode: business` and no `business_scope_id`, a conversation bound to a business does not match. */
4171
+ BindingBusinessScope: components["schemas"]["BusinessScopeId"];
4172
+ SubjectIdPath: components["schemas"]["SubjectId"];
2698
4173
  IdempotencyKey: string;
4174
+ /** @description Accepted, not required (declared in v1-draft.22). Scoped to the tenant and environment: the same key and request within 24 hours answers the first success again (`Idempotent-Replayed: true`); the same key with another request is `409 idempotency_conflict`. */
4175
+ OptionalIdempotencyKey: string;
2699
4176
  MediaJobId: string;
2700
4177
  ConversationId: components["schemas"]["ConversationId"];
2701
4178
  SessionId: string;
@@ -2771,6 +4248,39 @@ interface operations {
2771
4248
  503: components["responses"]["Problem"];
2772
4249
  };
2773
4250
  };
4251
+ listConversations: {
4252
+ parameters: {
4253
+ query: {
4254
+ subject_id: components["schemas"]["SubjectId"];
4255
+ agent: string;
4256
+ /** @description the side of the app; left out, the agent's template's */
4257
+ experience_mode?: components["schemas"]["ExperienceMode"];
4258
+ /** @description the business the person acts for; only with `experience_mode=business` */
4259
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
4260
+ limit?: number;
4261
+ cursor?: string;
4262
+ };
4263
+ header?: never;
4264
+ path?: never;
4265
+ cookie?: never;
4266
+ };
4267
+ requestBody?: never;
4268
+ responses: {
4269
+ /** @description Newest activity first. */
4270
+ 200: {
4271
+ headers: {
4272
+ [name: string]: unknown;
4273
+ };
4274
+ content: {
4275
+ "application/json": components["schemas"]["ConversationPage"];
4276
+ };
4277
+ };
4278
+ 400: components["responses"]["Problem"];
4279
+ 401: components["responses"]["Problem"];
4280
+ 403: components["responses"]["Problem"];
4281
+ 503: components["responses"]["Problem"];
4282
+ };
4283
+ };
2774
4284
  getConversation: {
2775
4285
  parameters: {
2776
4286
  query?: never;
@@ -2797,6 +4307,78 @@ interface operations {
2797
4307
  410: components["responses"]["Problem"];
2798
4308
  };
2799
4309
  };
4310
+ deleteConversation: {
4311
+ parameters: {
4312
+ query: {
4313
+ subject_id: components["schemas"]["SubjectId"];
4314
+ /** @description v1-draft.24, optional: the agent the caller acts for; when given, the conversation must be this agent's, else `404` (the same answer as an unknown id) */
4315
+ agent?: components["parameters"]["BindingAgent"];
4316
+ /** @description v1-draft.24, optional: the side of the app the caller acts on; when given, the conversation must be bound to it, else `404` */
4317
+ experience_mode?: components["parameters"]["BindingExperienceMode"];
4318
+ /** @description v1-draft.24, optional: the business the caller acts for; when given, the conversation must be bound to it, else `404`. With `experience_mode: business` and no `business_scope_id`, a conversation bound to a business does not match. */
4319
+ business_scope_id?: components["parameters"]["BindingBusinessScope"];
4320
+ };
4321
+ header?: never;
4322
+ path: {
4323
+ conversation_id: components["parameters"]["ConversationId"];
4324
+ };
4325
+ cookie?: never;
4326
+ };
4327
+ requestBody?: never;
4328
+ responses: {
4329
+ /** @description Deleted. */
4330
+ 200: {
4331
+ headers: {
4332
+ [name: string]: unknown;
4333
+ };
4334
+ content: {
4335
+ "application/json": components["schemas"]["ConversationDeleted"];
4336
+ };
4337
+ };
4338
+ 401: components["responses"]["Problem"];
4339
+ 403: components["responses"]["Problem"];
4340
+ 404: components["responses"]["Problem"];
4341
+ 503: components["responses"]["Problem"];
4342
+ };
4343
+ };
4344
+ listConversationMessages: {
4345
+ parameters: {
4346
+ query: {
4347
+ subject_id: components["schemas"]["SubjectId"];
4348
+ /** @description v1-draft.24, optional: the agent the caller acts for; when given, the conversation must be this agent's, else `404` (the same answer as an unknown id) */
4349
+ agent?: components["parameters"]["BindingAgent"];
4350
+ /** @description v1-draft.24, optional: the side of the app the caller acts on; when given, the conversation must be bound to it, else `404` */
4351
+ experience_mode?: components["parameters"]["BindingExperienceMode"];
4352
+ /** @description v1-draft.24, optional: the business the caller acts for; when given, the conversation must be bound to it, else `404`. With `experience_mode: business` and no `business_scope_id`, a conversation bound to a business does not match. */
4353
+ business_scope_id?: components["parameters"]["BindingBusinessScope"];
4354
+ limit?: number;
4355
+ cursor?: string;
4356
+ };
4357
+ header?: never;
4358
+ path: {
4359
+ conversation_id: components["parameters"]["ConversationId"];
4360
+ };
4361
+ cookie?: never;
4362
+ };
4363
+ requestBody?: never;
4364
+ responses: {
4365
+ /** @description The newest page first; oldest first within a page. From v1-draft.28 every page also carries `conversation`, the conversation's summary as the list gives it. */
4366
+ 200: {
4367
+ headers: {
4368
+ [name: string]: unknown;
4369
+ };
4370
+ content: {
4371
+ "application/json": components["schemas"]["TimelinePage"];
4372
+ };
4373
+ };
4374
+ 400: components["responses"]["Problem"];
4375
+ 401: components["responses"]["Problem"];
4376
+ 403: components["responses"]["Problem"];
4377
+ 404: components["responses"]["Problem"];
4378
+ 410: components["responses"]["Problem"];
4379
+ 503: components["responses"]["Problem"];
4380
+ };
4381
+ };
2800
4382
  getSession: {
2801
4383
  parameters: {
2802
4384
  query?: never;
@@ -2820,7 +4402,7 @@ interface operations {
2820
4402
  404: components["responses"]["Problem"];
2821
4403
  };
2822
4404
  };
2823
- revokeSession: {
4405
+ updateSessionContext: {
2824
4406
  parameters: {
2825
4407
  query?: never;
2826
4408
  header?: never;
@@ -2829,6 +4411,40 @@ interface operations {
2829
4411
  };
2830
4412
  cookie?: never;
2831
4413
  };
4414
+ requestBody: {
4415
+ content: {
4416
+ "application/json": components["schemas"]["SessionContextUpdate"];
4417
+ };
4418
+ };
4419
+ responses: {
4420
+ /** @description The session's screen context now. */
4421
+ 200: {
4422
+ headers: {
4423
+ [name: string]: unknown;
4424
+ };
4425
+ content: {
4426
+ "application/json": components["schemas"]["SessionContextState"];
4427
+ };
4428
+ };
4429
+ 400: components["responses"]["Problem"];
4430
+ 401: components["responses"]["Problem"];
4431
+ 403: components["responses"]["Problem"];
4432
+ 404: components["responses"]["Problem"];
4433
+ 409: components["responses"]["Problem"];
4434
+ };
4435
+ };
4436
+ revokeSession: {
4437
+ parameters: {
4438
+ query?: never;
4439
+ header?: {
4440
+ /** @description Accepted, not required (declared in v1-draft.22). Scoped to the tenant and environment: the same key and request within 24 hours answers the first success again (`Idempotent-Replayed: true`); the same key with another request is `409 idempotency_conflict`. */
4441
+ "Idempotency-Key"?: components["parameters"]["OptionalIdempotencyKey"];
4442
+ };
4443
+ path: {
4444
+ session_id: components["parameters"]["SessionId"];
4445
+ };
4446
+ cookie?: never;
4447
+ };
2832
4448
  requestBody?: never;
2833
4449
  responses: {
2834
4450
  /** @description Revoked (idempotent) */
@@ -2841,12 +4457,16 @@ interface operations {
2841
4457
  };
2842
4458
  };
2843
4459
  404: components["responses"]["Problem"];
4460
+ 409: components["responses"]["Problem"];
2844
4461
  };
2845
4462
  };
2846
4463
  refreshClientSecret: {
2847
4464
  parameters: {
2848
4465
  query?: never;
2849
- header?: never;
4466
+ header?: {
4467
+ /** @description Accepted, not required (declared in v1-draft.22). Scoped to the tenant and environment: the same key and request within 24 hours answers the first success again (`Idempotent-Replayed: true`); the same key with another request is `409 idempotency_conflict`. */
4468
+ "Idempotency-Key"?: components["parameters"]["OptionalIdempotencyKey"];
4469
+ };
2850
4470
  path: {
2851
4471
  session_id: components["parameters"]["SessionId"];
2852
4472
  };
@@ -2891,6 +4511,7 @@ interface operations {
2891
4511
  };
2892
4512
  };
2893
4513
  401: components["responses"]["Problem"];
4514
+ 403: components["responses"]["Problem"];
2894
4515
  409: components["responses"]["Problem"];
2895
4516
  429: components["responses"]["RateLimited"];
2896
4517
  };
@@ -2927,6 +4548,7 @@ interface operations {
2927
4548
  "text/event-stream": string;
2928
4549
  };
2929
4550
  };
4551
+ 403: components["responses"]["Problem"];
2930
4552
  };
2931
4553
  };
2932
4554
  createModerationJob: {
@@ -3005,6 +4627,30 @@ interface operations {
3005
4627
  404: components["responses"]["Problem"];
3006
4628
  };
3007
4629
  };
4630
+ getAgentAvatarImage: {
4631
+ parameters: {
4632
+ query?: never;
4633
+ header?: never;
4634
+ path: {
4635
+ /** @description the SHA-256 of the image's bytes, its first 32 hex digits, then `.webp` */
4636
+ avatar_file: string;
4637
+ };
4638
+ cookie?: never;
4639
+ };
4640
+ requestBody?: never;
4641
+ responses: {
4642
+ /** @description the image */
4643
+ 200: {
4644
+ headers: {
4645
+ [name: string]: unknown;
4646
+ };
4647
+ content: {
4648
+ "image/webp": string;
4649
+ };
4650
+ };
4651
+ 404: components["responses"]["Problem"];
4652
+ };
4653
+ };
3008
4654
  listMediaJobs: {
3009
4655
  parameters: {
3010
4656
  query?: {
@@ -3303,6 +4949,121 @@ interface operations {
3303
4949
  };
3304
4950
  };
3305
4951
  };
4952
+ getSubjectMemory: {
4953
+ parameters: {
4954
+ query?: {
4955
+ experience_mode?: components["schemas"]["ExperienceMode"];
4956
+ /** @description the business the person acts for; only with `experience_mode=business` */
4957
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
4958
+ };
4959
+ header?: never;
4960
+ path: {
4961
+ subject_id: components["parameters"]["SubjectIdPath"];
4962
+ };
4963
+ cookie?: never;
4964
+ };
4965
+ requestBody?: never;
4966
+ responses: {
4967
+ /** @description OK */
4968
+ 200: {
4969
+ headers: {
4970
+ [name: string]: unknown;
4971
+ };
4972
+ content: {
4973
+ "application/json": components["schemas"]["SubjectMemory"];
4974
+ };
4975
+ };
4976
+ 400: components["responses"]["Problem"];
4977
+ 401: components["responses"]["Problem"];
4978
+ 403: components["responses"]["Problem"];
4979
+ 503: components["responses"]["Problem"];
4980
+ };
4981
+ };
4982
+ putSubjectMemory: {
4983
+ parameters: {
4984
+ query?: never;
4985
+ header?: never;
4986
+ path: {
4987
+ subject_id: components["parameters"]["SubjectIdPath"];
4988
+ };
4989
+ cookie?: never;
4990
+ };
4991
+ requestBody: {
4992
+ content: {
4993
+ "application/json": components["schemas"]["SubjectMemoryChoice"];
4994
+ };
4995
+ };
4996
+ responses: {
4997
+ /** @description The choice as stored. */
4998
+ 200: {
4999
+ headers: {
5000
+ [name: string]: unknown;
5001
+ };
5002
+ content: {
5003
+ "application/json": components["schemas"]["SubjectMemoryChoice"];
5004
+ };
5005
+ };
5006
+ 400: components["responses"]["Problem"];
5007
+ 401: components["responses"]["Problem"];
5008
+ 403: components["responses"]["Problem"];
5009
+ 503: components["responses"]["Problem"];
5010
+ };
5011
+ };
5012
+ acknowledgeMemoryNotice: {
5013
+ parameters: {
5014
+ query?: never;
5015
+ header?: never;
5016
+ path: {
5017
+ subject_id: components["parameters"]["SubjectIdPath"];
5018
+ };
5019
+ cookie?: never;
5020
+ };
5021
+ requestBody: {
5022
+ content: {
5023
+ "application/json": {
5024
+ version: components["schemas"]["MemoryNoticeVersion"];
5025
+ };
5026
+ };
5027
+ };
5028
+ responses: {
5029
+ /** @description Recorded. */
5030
+ 204: {
5031
+ headers: {
5032
+ [name: string]: unknown;
5033
+ };
5034
+ content?: never;
5035
+ };
5036
+ 400: components["responses"]["Problem"];
5037
+ 401: components["responses"]["Problem"];
5038
+ 403: components["responses"]["Problem"];
5039
+ 503: components["responses"]["Problem"];
5040
+ };
5041
+ };
5042
+ deleteMemoryPreference: {
5043
+ parameters: {
5044
+ query?: never;
5045
+ header?: never;
5046
+ path: {
5047
+ subject_id: components["parameters"]["SubjectIdPath"];
5048
+ preference_id: string;
5049
+ };
5050
+ cookie?: never;
5051
+ };
5052
+ requestBody?: never;
5053
+ responses: {
5054
+ /** @description Forgotten. */
5055
+ 204: {
5056
+ headers: {
5057
+ [name: string]: unknown;
5058
+ };
5059
+ content?: never;
5060
+ };
5061
+ 401: components["responses"]["Problem"];
5062
+ 403: components["responses"]["Problem"];
5063
+ 404: components["responses"]["Problem"];
5064
+ 503: components["responses"]["Problem"];
5065
+ };
5066
+ };
3306
5067
  sendEvents: {
3307
5068
  parameters: {
3308
5069
  query?: never;
@@ -3433,6 +5194,59 @@ interface operations {
3433
5194
  403: components["responses"]["Problem"];
3434
5195
  };
3435
5196
  };
5197
+ getAgentExperience: {
5198
+ parameters: {
5199
+ query?: {
5200
+ /** @description the side of the app; left out, the agent's template's */
5201
+ experience_mode?: components["schemas"]["ExperienceMode"];
5202
+ };
5203
+ header?: never;
5204
+ path: {
5205
+ agent: string;
5206
+ };
5207
+ cookie?: never;
5208
+ };
5209
+ requestBody?: never;
5210
+ responses: {
5211
+ /** @description OK */
5212
+ 200: {
5213
+ headers: {
5214
+ /** @description `private, max-age=60` */
5215
+ "Cache-Control"?: string;
5216
+ [name: string]: unknown;
5217
+ };
5218
+ content: {
5219
+ "application/json": components["schemas"]["AgentExperience"];
5220
+ };
5221
+ };
5222
+ 401: components["responses"]["Problem"];
5223
+ 403: components["responses"]["Problem"];
5224
+ 404: components["responses"]["Problem"];
5225
+ 503: components["responses"]["Problem"];
5226
+ };
5227
+ };
5228
+ getMeta: {
5229
+ parameters: {
5230
+ query?: never;
5231
+ header?: never;
5232
+ path?: never;
5233
+ cookie?: never;
5234
+ };
5235
+ requestBody?: never;
5236
+ responses: {
5237
+ /** @description OK */
5238
+ 200: {
5239
+ headers: {
5240
+ /** @description `public, max-age=60` */
5241
+ "Cache-Control"?: string;
5242
+ [name: string]: unknown;
5243
+ };
5244
+ content: {
5245
+ "application/json": components["schemas"]["Meta"];
5246
+ };
5247
+ };
5248
+ };
5249
+ };
3436
5250
  getUsage: {
3437
5251
  parameters: {
3438
5252
  query?: {