@vinaverse/client 0.1.0-alpha.10 → 0.1.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +19 -3
  2. package/dist/agent/index.cjs +2 -0
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.d.cts +3 -3
  5. package/dist/agent/index.d.ts +3 -3
  6. package/dist/agent/index.js +1 -1
  7. package/dist/backend/index.cjs +37 -10
  8. package/dist/backend/index.cjs.map +1 -1
  9. package/dist/backend/index.d.cts +13 -3
  10. package/dist/backend/index.d.ts +13 -3
  11. package/dist/backend/index.js +38 -11
  12. package/dist/backend/index.js.map +1 -1
  13. package/dist/chunk-D56MNG3E.js +9 -0
  14. package/dist/{chunk-YNUHXJD6.js.map → chunk-D56MNG3E.js.map} +1 -1
  15. package/dist/{chunk-O3NLAZGZ.js → chunk-JI6HUXH3.js} +4 -2
  16. package/dist/chunk-JI6HUXH3.js.map +1 -0
  17. package/dist/contract-Be2oVHNr.d.cts +6 -0
  18. package/dist/contract-Be2oVHNr.d.ts +6 -0
  19. package/dist/conversation/index.cjs +2 -2
  20. package/dist/conversation/index.cjs.map +1 -1
  21. package/dist/conversation/index.d.cts +5 -5
  22. package/dist/conversation/index.d.ts +5 -5
  23. package/dist/conversation/index.js +1 -1
  24. package/dist/history/index.cjs +282 -0
  25. package/dist/history/index.cjs.map +1 -0
  26. package/dist/history/index.d.cts +91 -0
  27. package/dist/history/index.d.ts +91 -0
  28. package/dist/history/index.js +261 -0
  29. package/dist/history/index.js.map +1 -0
  30. package/dist/index.cjs +2 -2
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +2 -2
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.js +1 -1
  35. package/dist/{runtime-Z1RI10lU.d.ts → runtime-6O0qIPmo.d.cts} +25 -2
  36. package/dist/{runtime-CISPp9nJ.d.cts → runtime-CNgz2_8G.d.ts} +25 -2
  37. package/dist/{session-CPz4pcPu.d.cts → session-D0ijOQX3.d.cts} +98 -5
  38. package/dist/{session-CPz4pcPu.d.ts → session-D0ijOQX3.d.ts} +98 -5
  39. package/dist/{store-DZka9T2-.d.ts → store-CGRSxVcL.d.ts} +1 -1
  40. package/dist/{store-BxiicOKG.d.cts → store-CxoHwd64.d.cts} +1 -1
  41. package/dist/testing/index.cjs +158 -14
  42. package/dist/testing/index.cjs.map +1 -1
  43. package/dist/testing/index.d.cts +42 -5
  44. package/dist/testing/index.d.ts +42 -5
  45. package/dist/testing/index.js +159 -15
  46. package/dist/testing/index.js.map +1 -1
  47. package/package.json +24 -1
  48. package/dist/chunk-O3NLAZGZ.js.map +0 -1
  49. package/dist/chunk-YNUHXJD6.js +0 -9
  50. package/dist/contract-DmQs8amJ.d.cts +0 -6
  51. package/dist/contract-DmQs8amJ.d.ts +0 -6
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as CONTRACT } from './contract-DmQs8amJ.cjs';
2
- export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-CPz4pcPu.cjs';
1
+ export { C as CONTRACT } from './contract-Be2oVHNr.cjs';
2
+ export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-D0ijOQX3.cjs';
3
3
 
4
4
  /**
5
5
  * The client secret, checked: an app holds only the session's `vv_cs_` secret, which the tenant's backend got from
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as CONTRACT } from './contract-DmQs8amJ.js';
2
- export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-CPz4pcPu.js';
1
+ export { C as CONTRACT } from './contract-Be2oVHNr.js';
2
+ export { A as AnswerInput, h as AuthMode, a as Card, d as CardKind, e as ClientErrorCode, j as ClientEvent, k as ClientSecret, l as Connection, m as ConversationEngine, n as ConversationState, F as FetchLike, o as Frame, H as HeldCard, f as HeldQuestion, M as Message, P as Proposal, g as ProposalOutcome, Q as Question, R as Reconnect, S as Schemas, p as ServerEvent, q as ServerEventOf, r as ServerEventType, s as SessionEvents, t as SessionInfo, u as SessionOptions, v as SessionStatus, w as SseTarget, T as ToolLine, x as TranscriptItem, y as TurnInput, V as VinaverseClientError, z as VinaverseSession, B as WebSocketFactory, D as WebSocketLike, E as cardActionSse, G as components, i as isKnownCard, I as operations, J as paths, K as sseFrames, L as turnSse } from './session-D0ijOQX3.js';
3
3
 
4
4
  /**
5
5
  * The client secret, checked: an app holds only the session's `vv_cs_` secret, which the tenant's backend got from
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { CONTRACT } from './chunk-YNUHXJD6.js';
1
+ export { CONTRACT } from './chunk-D56MNG3E.js';
2
2
  export { ConversationEngine, VinaverseClientError, VinaverseSession, cardActionSse, clientSecret, isKnownCard, sseFrames, turnSse } from './chunk-GWLLUOFY.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
- import { S as SessionRequest, b as ConversationSession, C as ConversationStore, f as ConversationPersistence, a as ConversationOptions } from './store-DZka9T2-.js';
2
- import { S as Schemas, k as ClientSecret } from './session-CPz4pcPu.js';
1
+ import { S as SessionRequest, b as ConversationSession, C as ConversationStore, f as ConversationPersistence, a as ConversationOptions } from './store-CxoHwd64.cjs';
2
+ import { S as Schemas, k as ClientSecret } from './session-D0ijOQX3.cjs';
3
3
 
4
4
  /** Whose conversation: a person (null for a guest), the app's side, the business acted for, and the thread. */
5
5
  interface AgentKey {
@@ -144,6 +144,22 @@ interface AgentBackend {
144
144
  endSessions?(): Promise<void>;
145
145
  /** History's delete: the tenant's API deletes the person's conversation (authenticated, theirs only). */
146
146
  deleteConversation?(conversationId: string): Promise<void>;
147
+ /**
148
+ * History: the person's past conversations with the agent, newest activity first, a page at a time (`cursor` from
149
+ * the page before). The person comes from the backend's authentication, never from here.
150
+ */
151
+ listConversations?(query: {
152
+ readonly mode: string;
153
+ readonly scope: string | null;
154
+ readonly agent: string | null;
155
+ readonly limit?: number;
156
+ readonly cursor: string | null;
157
+ }, signal?: AbortSignal): Promise<Schemas['ConversationPage']>;
158
+ /** History: a past conversation's messages and cards, the newest page first, oldest first within a page. */
159
+ readConversation?(conversationId: string, page: {
160
+ readonly limit?: number;
161
+ readonly cursor: string | null;
162
+ }, signal?: AbortSignal): Promise<Schemas['TimelinePage']>;
147
163
  }
148
164
  type AgentRating = 'up' | 'down';
149
165
  type AgentFailure = 'ended' | 'unreachable' | 'unavailable';
@@ -283,6 +299,8 @@ interface AgentInsecureAddresses {
283
299
  readonly turns?: boolean;
284
300
  }
285
301
  interface AgentRuntime {
302
+ /** The app's backend, as given: history (`@vinaverse/client/history`) reads its pages through it. */
303
+ readonly backend: AgentBackend;
286
304
  /** The key's conversation: the same while it lives; a new one after `startNew`, `dispose` or its end. */
287
305
  conversation(key: AgentKey): AgentConversation;
288
306
  /**
@@ -338,6 +356,11 @@ interface AgentRuntime {
338
356
  * (the status held before, if any, stays). One object until it changes, for `useSyncExternalStore`.
339
357
  */
340
358
  getStatusState(): AgentStatusState;
359
+ /**
360
+ * Which person's turn it is on this device: a number that changes at every sign-out and erasure. Work begun under an
361
+ * older one (a history page, a transcript) is dropped when it lands.
362
+ */
363
+ epoch(): number;
341
364
  /** The agent's name: from the newest session, else from the status. */
342
365
  getName(): string | null;
343
366
  /** Told when the status or name changes, and when a key's conversation is let go (its next one is another). */
@@ -1,5 +1,5 @@
1
- import { S as SessionRequest, b as ConversationSession, C as ConversationStore, f as ConversationPersistence, a as ConversationOptions } from './store-BxiicOKG.cjs';
2
- import { S as Schemas, k as ClientSecret } from './session-CPz4pcPu.cjs';
1
+ import { S as SessionRequest, b as ConversationSession, C as ConversationStore, f as ConversationPersistence, a as ConversationOptions } from './store-CGRSxVcL.js';
2
+ import { S as Schemas, k as ClientSecret } from './session-D0ijOQX3.js';
3
3
 
4
4
  /** Whose conversation: a person (null for a guest), the app's side, the business acted for, and the thread. */
5
5
  interface AgentKey {
@@ -144,6 +144,22 @@ interface AgentBackend {
144
144
  endSessions?(): Promise<void>;
145
145
  /** History's delete: the tenant's API deletes the person's conversation (authenticated, theirs only). */
146
146
  deleteConversation?(conversationId: string): Promise<void>;
147
+ /**
148
+ * History: the person's past conversations with the agent, newest activity first, a page at a time (`cursor` from
149
+ * the page before). The person comes from the backend's authentication, never from here.
150
+ */
151
+ listConversations?(query: {
152
+ readonly mode: string;
153
+ readonly scope: string | null;
154
+ readonly agent: string | null;
155
+ readonly limit?: number;
156
+ readonly cursor: string | null;
157
+ }, signal?: AbortSignal): Promise<Schemas['ConversationPage']>;
158
+ /** History: a past conversation's messages and cards, the newest page first, oldest first within a page. */
159
+ readConversation?(conversationId: string, page: {
160
+ readonly limit?: number;
161
+ readonly cursor: string | null;
162
+ }, signal?: AbortSignal): Promise<Schemas['TimelinePage']>;
147
163
  }
148
164
  type AgentRating = 'up' | 'down';
149
165
  type AgentFailure = 'ended' | 'unreachable' | 'unavailable';
@@ -283,6 +299,8 @@ interface AgentInsecureAddresses {
283
299
  readonly turns?: boolean;
284
300
  }
285
301
  interface AgentRuntime {
302
+ /** The app's backend, as given: history (`@vinaverse/client/history`) reads its pages through it. */
303
+ readonly backend: AgentBackend;
286
304
  /** The key's conversation: the same while it lives; a new one after `startNew`, `dispose` or its end. */
287
305
  conversation(key: AgentKey): AgentConversation;
288
306
  /**
@@ -338,6 +356,11 @@ interface AgentRuntime {
338
356
  * (the status held before, if any, stays). One object until it changes, for `useSyncExternalStore`.
339
357
  */
340
358
  getStatusState(): AgentStatusState;
359
+ /**
360
+ * Which person's turn it is on this device: a number that changes at every sign-out and erasure. Work begun under an
361
+ * older one (a history page, a transcript) is dropped when it lands.
362
+ */
363
+ epoch(): number;
341
364
  /** The agent's name: from the newest session, else from the status. */
342
365
  getName(): string | null;
343
366
  /** Told when the status or name changes, and when a key's conversation is let go (its next one is another). */
@@ -130,7 +130,7 @@ interface paths {
130
130
  * - Fences late writes: a turn still running, a webhook retry or a worker job that finishes after the delete
131
131
  * records nothing for it (a deletion barrier, kept as long as the longest session could run).
132
132
  * - Frees its `conversation_key`: a session with the same key afterwards starts a new conversation.
133
- * **Answers.** `200` with the number of sessions revoked. An unknown id, another tenant's, environment's or subject's, 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.
133
+ * **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.
134
134
  */
135
135
  delete: operations["deleteConversation"];
136
136
  options?: never;
@@ -150,7 +150,7 @@ interface paths {
150
150
  * @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.
151
151
  * **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.
152
152
  * **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.
153
- * **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: `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.
153
+ * **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.
154
154
  */
155
155
  get: operations["listConversationMessages"];
156
156
  put?: never;
@@ -177,6 +177,28 @@ interface paths {
177
177
  patch?: never;
178
178
  trace?: never;
179
179
  };
180
+ "/v1/sessions/{session_id}/context": {
181
+ parameters: {
182
+ query?: never;
183
+ header?: never;
184
+ path?: never;
185
+ cookie?: never;
186
+ };
187
+ get?: never;
188
+ /**
189
+ * Tell an open session where the person is now
190
+ * @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`.
191
+ * **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.
192
+ * **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.
193
+ */
194
+ put: operations["updateSessionContext"];
195
+ post?: never;
196
+ delete?: never;
197
+ options?: never;
198
+ head?: never;
199
+ patch?: never;
200
+ trace?: never;
201
+ };
180
202
  "/v1/sessions/{session_id}/revoke": {
181
203
  parameters: {
182
204
  query?: never;
@@ -556,10 +578,12 @@ interface components {
556
578
  title: string;
557
579
  status: number;
558
580
  /** @enum {string} */
559
- 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";
581
+ 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";
560
582
  detail?: string;
561
583
  request_id: string;
562
584
  retryable: boolean;
585
+ /** @description v1-draft.23, with `context_stale` only, the session's screen-context revision now */
586
+ current_revision?: number;
563
587
  /** @description field-level problems for invalid_request */
564
588
  errors?: {
565
589
  /** @description JSON Pointer into the request */
@@ -733,7 +757,7 @@ interface components {
733
757
  ExperienceMode: "customer" | "business";
734
758
  /** @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. */
735
759
  BusinessScopeId: string;
736
- /** @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; live updates are a later addition. Never arbitrary page text, URLs, names, amounts or instructions: only these fields, from this vocabulary. */
760
+ /** @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. */
737
761
  ScreenContext: {
738
762
  /**
739
763
  * @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`.
@@ -757,8 +781,19 @@ interface components {
757
781
  date_to?: string;
758
782
  price_max?: components["schemas"]["Money"];
759
783
  };
760
- /** @description the tenant's counter for this person's screen state; a later update never goes back to a lower one */
784
+ /** @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) */
785
+ revision: number;
786
+ };
787
+ /** @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. */
788
+ SessionContextUpdate: components["schemas"]["ScreenContext"] | {
789
+ /** @constant */
790
+ clear: true;
791
+ revision: number;
792
+ };
793
+ /** @description v1-draft.23, the session's screen context after an update; no `screen_context` after a clear */
794
+ SessionContextState: {
761
795
  revision: number;
796
+ screen_context?: components["schemas"]["ScreenContext"];
762
797
  };
763
798
  /** @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. */
764
799
  Chip: {
@@ -1668,6 +1703,15 @@ interface components {
1668
1703
  */
1669
1704
  code: "screened" | "too_long" | "turn_in_progress" | "limit_reached" | "duplicate" | "guardrail" | "screen_unavailable";
1670
1705
  message: string;
1706
+ } | {
1707
+ /** @constant */
1708
+ type: "context.updated";
1709
+ revision: number;
1710
+ /**
1711
+ * @description present only after an explicit clear
1712
+ * @constant
1713
+ */
1714
+ cleared?: true;
1671
1715
  } | {
1672
1716
  /** @constant */
1673
1717
  type: "session.expiring";
@@ -2257,6 +2301,12 @@ interface components {
2257
2301
  };
2258
2302
  };
2259
2303
  parameters: {
2304
+ /** @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) */
2305
+ BindingAgent: string;
2306
+ /** @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` */
2307
+ BindingExperienceMode: components["schemas"]["ExperienceMode"];
2308
+ /** @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. */
2309
+ BindingBusinessScope: components["schemas"]["BusinessScopeId"];
2260
2310
  IdempotencyKey: string;
2261
2311
  /** @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`. */
2262
2312
  OptionalIdempotencyKey: string;
@@ -2398,6 +2448,12 @@ interface operations {
2398
2448
  parameters: {
2399
2449
  query: {
2400
2450
  subject_id: components["schemas"]["SubjectId"];
2451
+ /** @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) */
2452
+ agent?: components["parameters"]["BindingAgent"];
2453
+ /** @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` */
2454
+ experience_mode?: components["parameters"]["BindingExperienceMode"];
2455
+ /** @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. */
2456
+ business_scope_id?: components["parameters"]["BindingBusinessScope"];
2401
2457
  };
2402
2458
  header?: never;
2403
2459
  path: {
@@ -2426,6 +2482,12 @@ interface operations {
2426
2482
  parameters: {
2427
2483
  query: {
2428
2484
  subject_id: components["schemas"]["SubjectId"];
2485
+ /** @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) */
2486
+ agent?: components["parameters"]["BindingAgent"];
2487
+ /** @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` */
2488
+ experience_mode?: components["parameters"]["BindingExperienceMode"];
2489
+ /** @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. */
2490
+ business_scope_id?: components["parameters"]["BindingBusinessScope"];
2429
2491
  limit?: number;
2430
2492
  cursor?: string;
2431
2493
  };
@@ -2477,6 +2539,37 @@ interface operations {
2477
2539
  404: components["responses"]["Problem"];
2478
2540
  };
2479
2541
  };
2542
+ updateSessionContext: {
2543
+ parameters: {
2544
+ query?: never;
2545
+ header?: never;
2546
+ path: {
2547
+ session_id: components["parameters"]["SessionId"];
2548
+ };
2549
+ cookie?: never;
2550
+ };
2551
+ requestBody: {
2552
+ content: {
2553
+ "application/json": components["schemas"]["SessionContextUpdate"];
2554
+ };
2555
+ };
2556
+ responses: {
2557
+ /** @description The session's screen context now. */
2558
+ 200: {
2559
+ headers: {
2560
+ [name: string]: unknown;
2561
+ };
2562
+ content: {
2563
+ "application/json": components["schemas"]["SessionContextState"];
2564
+ };
2565
+ };
2566
+ 400: components["responses"]["Problem"];
2567
+ 401: components["responses"]["Problem"];
2568
+ 403: components["responses"]["Problem"];
2569
+ 404: components["responses"]["Problem"];
2570
+ 409: components["responses"]["Problem"];
2571
+ };
2572
+ };
2480
2573
  revokeSession: {
2481
2574
  parameters: {
2482
2575
  query?: never;
@@ -130,7 +130,7 @@ interface paths {
130
130
  * - Fences late writes: a turn still running, a webhook retry or a worker job that finishes after the delete
131
131
  * records nothing for it (a deletion barrier, kept as long as the longest session could run).
132
132
  * - Frees its `conversation_key`: a session with the same key afterwards starts a new conversation.
133
- * **Answers.** `200` with the number of sessions revoked. An unknown id, another tenant's, environment's or subject's, 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.
133
+ * **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.
134
134
  */
135
135
  delete: operations["deleteConversation"];
136
136
  options?: never;
@@ -150,7 +150,7 @@ interface paths {
150
150
  * @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.
151
151
  * **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.
152
152
  * **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.
153
- * **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: `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.
153
+ * **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.
154
154
  */
155
155
  get: operations["listConversationMessages"];
156
156
  put?: never;
@@ -177,6 +177,28 @@ interface paths {
177
177
  patch?: never;
178
178
  trace?: never;
179
179
  };
180
+ "/v1/sessions/{session_id}/context": {
181
+ parameters: {
182
+ query?: never;
183
+ header?: never;
184
+ path?: never;
185
+ cookie?: never;
186
+ };
187
+ get?: never;
188
+ /**
189
+ * Tell an open session where the person is now
190
+ * @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`.
191
+ * **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.
192
+ * **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.
193
+ */
194
+ put: operations["updateSessionContext"];
195
+ post?: never;
196
+ delete?: never;
197
+ options?: never;
198
+ head?: never;
199
+ patch?: never;
200
+ trace?: never;
201
+ };
180
202
  "/v1/sessions/{session_id}/revoke": {
181
203
  parameters: {
182
204
  query?: never;
@@ -556,10 +578,12 @@ interface components {
556
578
  title: string;
557
579
  status: number;
558
580
  /** @enum {string} */
559
- 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";
581
+ 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";
560
582
  detail?: string;
561
583
  request_id: string;
562
584
  retryable: boolean;
585
+ /** @description v1-draft.23, with `context_stale` only, the session's screen-context revision now */
586
+ current_revision?: number;
563
587
  /** @description field-level problems for invalid_request */
564
588
  errors?: {
565
589
  /** @description JSON Pointer into the request */
@@ -733,7 +757,7 @@ interface components {
733
757
  ExperienceMode: "customer" | "business";
734
758
  /** @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. */
735
759
  BusinessScopeId: string;
736
- /** @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; live updates are a later addition. Never arbitrary page text, URLs, names, amounts or instructions: only these fields, from this vocabulary. */
760
+ /** @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. */
737
761
  ScreenContext: {
738
762
  /**
739
763
  * @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`.
@@ -757,8 +781,19 @@ interface components {
757
781
  date_to?: string;
758
782
  price_max?: components["schemas"]["Money"];
759
783
  };
760
- /** @description the tenant's counter for this person's screen state; a later update never goes back to a lower one */
784
+ /** @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) */
785
+ revision: number;
786
+ };
787
+ /** @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. */
788
+ SessionContextUpdate: components["schemas"]["ScreenContext"] | {
789
+ /** @constant */
790
+ clear: true;
791
+ revision: number;
792
+ };
793
+ /** @description v1-draft.23, the session's screen context after an update; no `screen_context` after a clear */
794
+ SessionContextState: {
761
795
  revision: number;
796
+ screen_context?: components["schemas"]["ScreenContext"];
762
797
  };
763
798
  /** @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. */
764
799
  Chip: {
@@ -1668,6 +1703,15 @@ interface components {
1668
1703
  */
1669
1704
  code: "screened" | "too_long" | "turn_in_progress" | "limit_reached" | "duplicate" | "guardrail" | "screen_unavailable";
1670
1705
  message: string;
1706
+ } | {
1707
+ /** @constant */
1708
+ type: "context.updated";
1709
+ revision: number;
1710
+ /**
1711
+ * @description present only after an explicit clear
1712
+ * @constant
1713
+ */
1714
+ cleared?: true;
1671
1715
  } | {
1672
1716
  /** @constant */
1673
1717
  type: "session.expiring";
@@ -2257,6 +2301,12 @@ interface components {
2257
2301
  };
2258
2302
  };
2259
2303
  parameters: {
2304
+ /** @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) */
2305
+ BindingAgent: string;
2306
+ /** @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` */
2307
+ BindingExperienceMode: components["schemas"]["ExperienceMode"];
2308
+ /** @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. */
2309
+ BindingBusinessScope: components["schemas"]["BusinessScopeId"];
2260
2310
  IdempotencyKey: string;
2261
2311
  /** @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`. */
2262
2312
  OptionalIdempotencyKey: string;
@@ -2398,6 +2448,12 @@ interface operations {
2398
2448
  parameters: {
2399
2449
  query: {
2400
2450
  subject_id: components["schemas"]["SubjectId"];
2451
+ /** @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) */
2452
+ agent?: components["parameters"]["BindingAgent"];
2453
+ /** @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` */
2454
+ experience_mode?: components["parameters"]["BindingExperienceMode"];
2455
+ /** @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. */
2456
+ business_scope_id?: components["parameters"]["BindingBusinessScope"];
2401
2457
  };
2402
2458
  header?: never;
2403
2459
  path: {
@@ -2426,6 +2482,12 @@ interface operations {
2426
2482
  parameters: {
2427
2483
  query: {
2428
2484
  subject_id: components["schemas"]["SubjectId"];
2485
+ /** @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) */
2486
+ agent?: components["parameters"]["BindingAgent"];
2487
+ /** @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` */
2488
+ experience_mode?: components["parameters"]["BindingExperienceMode"];
2489
+ /** @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. */
2490
+ business_scope_id?: components["parameters"]["BindingBusinessScope"];
2429
2491
  limit?: number;
2430
2492
  cursor?: string;
2431
2493
  };
@@ -2477,6 +2539,37 @@ interface operations {
2477
2539
  404: components["responses"]["Problem"];
2478
2540
  };
2479
2541
  };
2542
+ updateSessionContext: {
2543
+ parameters: {
2544
+ query?: never;
2545
+ header?: never;
2546
+ path: {
2547
+ session_id: components["parameters"]["SessionId"];
2548
+ };
2549
+ cookie?: never;
2550
+ };
2551
+ requestBody: {
2552
+ content: {
2553
+ "application/json": components["schemas"]["SessionContextUpdate"];
2554
+ };
2555
+ };
2556
+ responses: {
2557
+ /** @description The session's screen context now. */
2558
+ 200: {
2559
+ headers: {
2560
+ [name: string]: unknown;
2561
+ };
2562
+ content: {
2563
+ "application/json": components["schemas"]["SessionContextState"];
2564
+ };
2565
+ };
2566
+ 400: components["responses"]["Problem"];
2567
+ 401: components["responses"]["Problem"];
2568
+ 403: components["responses"]["Problem"];
2569
+ 404: components["responses"]["Problem"];
2570
+ 409: components["responses"]["Problem"];
2571
+ };
2572
+ };
2480
2573
  revokeSession: {
2481
2574
  parameters: {
2482
2575
  query?: never;
@@ -1,4 +1,4 @@
1
- import { W as Welcome, H as HeldCard, T as ToolLine, t as SessionInfo, k as ClientSecret, u as SessionOptions, A as AnswerInput, g as ProposalOutcome } from './session-CPz4pcPu.js';
1
+ import { W as Welcome, H as HeldCard, T as ToolLine, t as SessionInfo, k as ClientSecret, u as SessionOptions, A as AnswerInput, g as ProposalOutcome } from './session-D0ijOQX3.js';
2
2
 
3
3
  type ConversationStatus =
4
4
  /** Asking the tenant's backend for a session. */
@@ -1,4 +1,4 @@
1
- import { W as Welcome, H as HeldCard, T as ToolLine, t as SessionInfo, k as ClientSecret, u as SessionOptions, A as AnswerInput, g as ProposalOutcome } from './session-CPz4pcPu.cjs';
1
+ import { W as Welcome, H as HeldCard, T as ToolLine, t as SessionInfo, k as ClientSecret, u as SessionOptions, A as AnswerInput, g as ProposalOutcome } from './session-D0ijOQX3.cjs';
2
2
 
3
3
  type ConversationStatus =
4
4
  /** Asking the tenant's backend for a session. */