@vinaverse/server 0.1.0-alpha.3 → 0.1.0-alpha.5

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,15 @@ 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
+ * **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`.
22
+ * **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
23
  * **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`.
24
+ * `subject.id`, or (v1-draft.20) was started with another `experience_mode` or `business_scope_id`: `404
25
+ * not_found`. A `conversation_key` names a conversation within the same binding,
26
+ * so the same key under another binding names another conversation.
23
27
  * 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
28
  * conversation instead lets a new one start under the key; the id never starts one silently.
25
29
  * 4. The conversation is completed (its flow was accepted): `409 conversation_completed`. This holds by key too,
@@ -158,7 +162,10 @@ interface paths {
158
162
  };
159
163
  get?: never;
160
164
  put?: never;
161
- /** Run one turn and stream its events as SSE (alternative to the WebSocket) */
165
+ /**
166
+ * Run one turn and stream its events as SSE (alternative to the WebSocket)
167
+ * @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.
168
+ */
162
169
  post: operations["createTurnSse"];
163
170
  delete?: never;
164
171
  options?: never;
@@ -175,7 +182,10 @@ interface paths {
175
182
  };
176
183
  get?: never;
177
184
  put?: never;
178
- /** Send a card action and stream the resulting turn as SSE */
185
+ /**
186
+ * Send a card action and stream the resulting turn as SSE
187
+ * @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.
188
+ */
179
189
  post: operations["createCardActionSse"];
180
190
  delete?: never;
181
191
  options?: never;
@@ -231,6 +241,26 @@ interface paths {
231
241
  patch?: never;
232
242
  trace?: never;
233
243
  };
244
+ "/v1/media/avatars/{avatar_file}": {
245
+ parameters: {
246
+ query?: never;
247
+ header?: never;
248
+ path?: never;
249
+ cookie?: never;
250
+ };
251
+ /**
252
+ * An agent's avatar image (v1-draft.19), as `Session.agent.avatar_url` names it
253
+ * @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`.
254
+ */
255
+ get: operations["getAgentAvatarImage"];
256
+ put?: never;
257
+ post?: never;
258
+ delete?: never;
259
+ options?: never;
260
+ head?: never;
261
+ patch?: never;
262
+ trace?: never;
263
+ };
234
264
  "/v1/media/jobs": {
235
265
  parameters: {
236
266
  query?: never;
@@ -1349,6 +1379,56 @@ interface components {
1349
1379
  conversation_key?: string;
1350
1380
  /** @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
1381
  conversation_id?: components["schemas"]["ConversationId"];
1382
+ experience_mode?: components["schemas"]["ExperienceMode"];
1383
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
1384
+ screen_context?: components["schemas"]["ScreenContext"];
1385
+ };
1386
+ /**
1387
+ * @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.
1388
+ * @enum {string}
1389
+ */
1390
+ ExperienceMode: "customer" | "business";
1391
+ /** @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. */
1392
+ BusinessScopeId: string;
1393
+ /** @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. */
1394
+ ScreenContext: {
1395
+ /**
1396
+ * @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`.
1397
+ * @enum {string}
1398
+ */
1399
+ 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";
1400
+ /** @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 */
1401
+ entity?: {
1402
+ /** @enum {string} */
1403
+ type: "professional" | "workspace" | "service" | "class" | "booking" | "location";
1404
+ id: string;
1405
+ };
1406
+ /** @description the filters the screen shows, typed; free text never */
1407
+ filters?: {
1408
+ service_ids?: string[];
1409
+ category_ids?: string[];
1410
+ area_id?: string;
1411
+ /** Format: date */
1412
+ date_from?: string;
1413
+ /** Format: date */
1414
+ date_to?: string;
1415
+ price_max?: components["schemas"]["Money"];
1416
+ };
1417
+ /** @description the tenant's counter for this person's screen state; a later update never goes back to a lower one */
1418
+ revision: number;
1419
+ };
1420
+ /** @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. */
1421
+ Chip: {
1422
+ /** @description in Unicode code points */
1423
+ label: string;
1424
+ /** @description in Unicode code points */
1425
+ message: string;
1426
+ };
1427
+ /** @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. */
1428
+ Welcome: {
1429
+ /** @description in Unicode code points */
1430
+ text: string;
1431
+ chips: components["schemas"]["Chip"][];
1352
1432
  };
1353
1433
  ClientSecret: {
1354
1434
  /** @description `vv_cs_` and base64url characters, so the secret is a valid WebSocket subprotocol token in `bearer.<secret>`. */
@@ -1367,6 +1447,11 @@ interface components {
1367
1447
  id: string;
1368
1448
  version: string;
1369
1449
  display_name: string;
1450
+ /**
1451
+ * Format: uri
1452
+ * @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.
1453
+ */
1454
+ avatar_url?: string;
1370
1455
  };
1371
1456
  client_secret: components["schemas"]["ClientSecret"];
1372
1457
  /** Format: uri */
@@ -1381,6 +1466,14 @@ interface components {
1381
1466
  disclosure: string;
1382
1467
  /** @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
1468
  conversation_id?: components["schemas"]["ConversationId"];
1469
+ /** @description v1-draft.20, the session's mode, as given or as the agent's template sets it */
1470
+ experience_mode?: components["schemas"]["ExperienceMode"];
1471
+ /** @description v1-draft.20, present when the session acts for a business */
1472
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
1473
+ /** @description v1-draft.20, the screen context the session accepted, when `createSession` gave one */
1474
+ screen_context?: components["schemas"]["ScreenContext"];
1475
+ /** @description v1-draft.20, absent when the tenant's manifest has no welcome for the session's mode */
1476
+ welcome?: components["schemas"]["Welcome"];
1384
1477
  };
1385
1478
  SessionStatus: {
1386
1479
  id: string;
@@ -1443,6 +1536,35 @@ interface components {
1443
1536
  label: string;
1444
1537
  deep_link?: string;
1445
1538
  };
1539
+ /** @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. */
1540
+ CardImage: {
1541
+ /** Format: uri */
1542
+ url: string;
1543
+ width?: number;
1544
+ height?: number;
1545
+ };
1546
+ /** @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. */
1547
+ CardGroup: {
1548
+ id: string;
1549
+ /** @enum {string} */
1550
+ layout: "list" | "carousel";
1551
+ };
1552
+ /** @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. */
1553
+ CardLink: {
1554
+ /** @description in Unicode code points */
1555
+ label: string;
1556
+ /** @description an https page of the tenant's, or a deep link that passes the deep-link rule (`next_step`) */
1557
+ url: string;
1558
+ /** @enum {string} */
1559
+ purpose: "help" | "support" | "policy" | "contact" | "feedback";
1560
+ };
1561
+ /** @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. */
1562
+ ScopeProvenance: {
1563
+ policy_id: string;
1564
+ version: string;
1565
+ /** @enum {string} */
1566
+ reason: "off_scope" | "competitor" | "blocked_topic";
1567
+ };
1446
1568
  /**
1447
1569
  * @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
1570
  * 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,14 +1584,17 @@ interface components {
1462
1584
  * know.
1463
1585
  * - **It is not restored.** A `next_step` card is in neither `pending_cards` nor `transcript`; after a
1464
1586
  * reconnect the agent shows the step again once it reads the tenant's state.
1587
+ *
1588
+ * **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.
1589
+ * **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.
1465
1590
  */
1466
1591
  Card: {
1467
1592
  id: string;
1468
1593
  /**
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)
1594
+ * @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)
1470
1595
  * @enum {string}
1471
1596
  */
1472
- kind: "professional" | "workspace" | "service" | "time_slots" | "booking" | "quote" | "policy" | "handoff" | "metric_chart" | "metric_table" | "gap_list" | "cluster" | "claim" | "document" | "question" | "proposal" | "next_step";
1597
+ kind: "professional" | "workspace" | "service" | "time_slots" | "booking" | "quote" | "policy" | "handoff" | "metric_chart" | "metric_table" | "gap_list" | "cluster" | "claim" | "document" | "question" | "proposal" | "next_step" | "links";
1473
1598
  question?: components["schemas"]["Question"];
1474
1599
  proposal?: components["schemas"]["Proposal"];
1475
1600
  ref?: components["schemas"]["CardRef"];
@@ -1477,6 +1602,17 @@ interface components {
1477
1602
  subtitle?: string;
1478
1603
  /** Format: uri */
1479
1604
  image_url?: string;
1605
+ /** @description v1-draft.20, a `professional` or `workspace` card's photos, the first the hero */
1606
+ images?: components["schemas"]["CardImage"][];
1607
+ /**
1608
+ * Format: date-time
1609
+ * @description v1-draft.20, a `professional` or `workspace` card's earliest open time, as the tenant's tool returned it
1610
+ */
1611
+ next_available?: string;
1612
+ group?: components["schemas"]["CardGroup"];
1613
+ /** @description v1-draft.20, a `links` card's links */
1614
+ links?: components["schemas"]["CardLink"][];
1615
+ provenance?: components["schemas"]["ScopeProvenance"];
1480
1616
  rating?: {
1481
1617
  value_ppm: number;
1482
1618
  count: number;
@@ -1520,7 +1656,7 @@ interface components {
1520
1656
  points: {
1521
1657
  /** Format: date */
1522
1658
  t: string;
1523
- /** @description null when suppressed by the k-anonymity floor */
1659
+ /** @description null when suppressed by the k-anonymity floor; on a metric without k_floor, nothing to measure */
1524
1660
  value: number | null;
1525
1661
  suppressed?: boolean;
1526
1662
  }[];
@@ -1538,6 +1674,7 @@ interface components {
1538
1674
  dims: {
1539
1675
  [key: string]: string;
1540
1676
  };
1677
+ /** @description null when suppressed by the metric's k_floor; on a metric without k_floor, nothing to measure */
1541
1678
  values: {
1542
1679
  [key: string]: number | null;
1543
1680
  };
@@ -1582,7 +1719,7 @@ interface components {
1582
1719
  snippet?: string;
1583
1720
  /** Format: date-time */
1584
1721
  updated_at?: string;
1585
- } & (unknown & unknown & unknown);
1722
+ } & (unknown & unknown & unknown & unknown);
1586
1723
  /** @enum {string} */
1587
1724
  ValueUnit: "count" | "ppm" | "micros_usd" | "ms";
1588
1725
  MetricRef: {
@@ -1590,6 +1727,7 @@ interface components {
1590
1727
  label: string;
1591
1728
  unit: components["schemas"]["ValueUnit"];
1592
1729
  definition: string;
1730
+ /** @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
1731
  k_floor?: number;
1594
1732
  };
1595
1733
  /** @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. */
@@ -2002,6 +2140,10 @@ interface components {
2002
2140
  };
2003
2141
  disclosure: string;
2004
2142
  resumed: boolean;
2143
+ /** @description v1-draft.20, the session's mode (`Session.experience_mode`), so the app keeps one store per mode */
2144
+ experience_mode?: components["schemas"]["ExperienceMode"];
2145
+ /** @description v1-draft.20, present when the session acts for a business */
2146
+ business_scope_id?: components["schemas"]["BusinessScopeId"];
2005
2147
  /**
2006
2148
  * @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
2149
  * 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):
@@ -2891,6 +3033,7 @@ interface operations {
2891
3033
  };
2892
3034
  };
2893
3035
  401: components["responses"]["Problem"];
3036
+ 403: components["responses"]["Problem"];
2894
3037
  409: components["responses"]["Problem"];
2895
3038
  429: components["responses"]["RateLimited"];
2896
3039
  };
@@ -2927,6 +3070,7 @@ interface operations {
2927
3070
  "text/event-stream": string;
2928
3071
  };
2929
3072
  };
3073
+ 403: components["responses"]["Problem"];
2930
3074
  };
2931
3075
  };
2932
3076
  createModerationJob: {
@@ -3005,6 +3149,30 @@ interface operations {
3005
3149
  404: components["responses"]["Problem"];
3006
3150
  };
3007
3151
  };
3152
+ getAgentAvatarImage: {
3153
+ parameters: {
3154
+ query?: never;
3155
+ header?: never;
3156
+ path: {
3157
+ /** @description the SHA-256 of the image's bytes, its first 32 hex digits, then `.webp` */
3158
+ avatar_file: string;
3159
+ };
3160
+ cookie?: never;
3161
+ };
3162
+ requestBody?: never;
3163
+ responses: {
3164
+ /** @description the image */
3165
+ 200: {
3166
+ headers: {
3167
+ [name: string]: unknown;
3168
+ };
3169
+ content: {
3170
+ "image/webp": string;
3171
+ };
3172
+ };
3173
+ 404: components["responses"]["Problem"];
3174
+ };
3175
+ };
3008
3176
  listMediaJobs: {
3009
3177
  parameters: {
3010
3178
  query?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vinaverse/server",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.5",
4
4
  "description": "Vinaverse for Node backends: verify webhooks into typed contract events, with adapters for AWS Lambda, Express, Fastify and Hono, serve tools, and call the Vinaverse API typed.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Vinaverse, Inc.",