@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
@@ -17,35 +17,113 @@ var AgentRefusal = class extends Error {
17
17
  };
18
18
 
19
19
  // src/testing/index.ts
20
+ var CURSOR = /^mock:(\d+)$/;
21
+ var refusal = (kind, reason) => Object.assign(
22
+ new AgentRefusal(kind, {
23
+ reason,
24
+ code: reason === "not_found" ? "conversation_not_found" : "unavailable"
25
+ }),
26
+ { retryable: false }
27
+ );
20
28
  var sentences = (text) => text.match(/[^.!?]+[.!?]*\s*/g) ?? [text];
21
29
  function createMockAgent(options = {}) {
22
30
  const now = options.now ?? (() => Date.now());
31
+ const at = () => new Date(now()).toISOString();
23
32
  const conversations = /* @__PURE__ */ new Map();
24
33
  const sessions = /* @__PURE__ */ new Map();
25
34
  const log = {
26
35
  sessions: [],
27
36
  feedback: [],
28
37
  contexts: [],
29
- ended: 0
38
+ ended: 0,
39
+ lists: [],
40
+ reads: [],
41
+ deleted: []
30
42
  };
31
43
  let attempts = 0;
32
44
  let count = 0;
45
+ for (const c of options.history ?? []) {
46
+ const started = c.startedAt ?? at();
47
+ const items = c.items.map((item, n) => {
48
+ const id = `h_${c.id}_${n + 1}`;
49
+ const turn = `turn_${c.id}_${n + 1}`;
50
+ if ("card" in item)
51
+ return {
52
+ type: "card",
53
+ id,
54
+ turn_id: turn,
55
+ at: item.at ?? started,
56
+ card: item.card,
57
+ ...item.card.kind === "proposal" ? { proposal_outcome: item.proposal_outcome ?? "pending" } : {}
58
+ };
59
+ return {
60
+ type: "message",
61
+ id,
62
+ role: item.role,
63
+ text: item.text,
64
+ at: item.at ?? started,
65
+ event_id: id,
66
+ turn_id: turn,
67
+ ...item.interrupted ? { interrupted: true } : {},
68
+ ...item.refusal ? { refusal: true } : {}
69
+ };
70
+ });
71
+ conversations.set(c.id, {
72
+ id: c.id,
73
+ mode: c.mode ?? "customer",
74
+ scope: c.scope ?? null,
75
+ agent: c.agent ?? "mock_agent",
76
+ title: c.title ?? null,
77
+ started_at: started,
78
+ state: c.state ?? "ended",
79
+ items
80
+ });
81
+ }
82
+ const lastActivity = (held) => held.items.at(-1)?.at ?? held.started_at;
83
+ const summaryOf = (held) => ({
84
+ id: held.id,
85
+ agent: held.agent,
86
+ experience_mode: held.mode,
87
+ ...held.scope === null ? {} : { business_scope_id: held.scope },
88
+ title: held.title ?? held.started_at.slice(0, 10),
89
+ title_source: held.title ? "intent" : "date",
90
+ started_at: held.started_at,
91
+ last_activity_at: lastActivity(held),
92
+ message_count: held.items.filter((i) => i.type === "message").length,
93
+ state: held.state
94
+ });
95
+ const offsetOf = (cursor) => {
96
+ if (cursor === null) return 0;
97
+ const m = CURSOR.exec(cursor);
98
+ if (!m) throw refusal("unavailable", "server");
99
+ return Number(m[1]);
100
+ };
33
101
  const backend = {
34
102
  async openSession(request) {
35
103
  log.sessions.push(request);
36
104
  attempts++;
37
- const refusal = options.refuse?.(request, attempts);
38
- if (refusal === "unreachable")
105
+ const refusal2 = options.refuse?.(request, attempts);
106
+ if (refusal2 === "unreachable")
39
107
  throw Object.assign(new Error("mock: unreachable"), {
40
108
  retryable: true,
41
109
  reason: "network"
42
110
  });
43
- if (refusal) throw new AgentRefusal(refusal);
111
+ if (refusal2) throw new AgentRefusal(refusal2);
44
112
  if (request.conversationId !== null && !conversations.has(request.conversationId))
45
113
  throw new AgentRefusal("ended", { code: "conversation_not_found" });
46
114
  count++;
47
115
  const conversation = request.conversationId ?? `cnv_MOCK${count}`;
48
- if (!conversations.has(conversation)) conversations.set(conversation, []);
116
+ if (!conversations.has(conversation))
117
+ conversations.set(conversation, {
118
+ id: conversation,
119
+ mode: request.key.mode,
120
+ scope: request.key.scope ?? null,
121
+ agent: request.agent ?? "mock_agent",
122
+ title: null,
123
+ started_at: at(),
124
+ state: "open",
125
+ items: []
126
+ });
49
127
  const id = `ses_MOCK${count}`;
50
128
  sessions.set(id, { conversation, key: `${request.key.mode}` });
51
129
  const session = {
@@ -81,13 +159,50 @@ function createMockAgent(options = {}) {
81
159
  },
82
160
  async endSessions() {
83
161
  log.ended++;
162
+ },
163
+ async listConversations(query) {
164
+ log.lists.push(query);
165
+ const from = offsetOf(query.cursor);
166
+ const limit = query.limit ?? 20;
167
+ const all = [...conversations.values()].filter(
168
+ (c) => c.mode === query.mode && c.scope === (query.scope ?? null) && (query.agent === null || c.agent === query.agent) && c.items.some((i) => i.type === "message")
169
+ ).map(summaryOf).sort(
170
+ (a, b) => b.last_activity_at.localeCompare(a.last_activity_at) || b.id.localeCompare(a.id)
171
+ );
172
+ const data = all.slice(from, from + limit);
173
+ return {
174
+ data,
175
+ next_cursor: from + limit < all.length ? `mock:${from + limit}` : null
176
+ };
177
+ },
178
+ async readConversation(conversationId, page) {
179
+ log.reads.push({ conversationId, ...page });
180
+ const held = conversations.get(conversationId);
181
+ if (!held) throw refusal("ended", "not_found");
182
+ const limit = page.limit ?? 30;
183
+ const end = page.cursor === null ? held.items.length : offsetOf(page.cursor);
184
+ const start = Math.max(0, end - limit);
185
+ return {
186
+ data: held.items.slice(start, end),
187
+ next_cursor: start > 0 ? `mock:${start}` : null
188
+ };
189
+ },
190
+ async deleteConversation(conversationId) {
191
+ log.deleted.push(conversationId);
192
+ if (!conversations.delete(conversationId))
193
+ throw refusal("ended", "not_found");
194
+ for (const [id, s] of sessions)
195
+ if (s.conversation === conversationId) sessions.delete(id);
84
196
  }
85
197
  };
86
198
  const webSocket = (url) => {
87
199
  const sessionId = /sessions\/([^/]+)\/realtime/.exec(url)?.[1] ?? "";
88
200
  const held = sessions.get(sessionId);
89
- const said = held ? conversations.get(held.conversation) ?? [] : [];
90
- let turn = said.filter((s) => s.role === "person").length;
201
+ const items = held ? conversations.get(held.conversation)?.items ?? [] : [];
202
+ const said = () => items.filter(
203
+ (i) => i.type === "message"
204
+ );
205
+ let turn = said().filter((s) => s.role === "person").length;
91
206
  let state = 0;
92
207
  const socket = {
93
208
  get readyState() {
@@ -105,7 +220,15 @@ function createMockAgent(options = {}) {
105
220
  const text = String(frame.text ?? "");
106
221
  turn++;
107
222
  const turnId = `turn_MOCK${turn}`;
108
- said.push({ role: "person", text, id });
223
+ items.push({
224
+ type: "message",
225
+ id: `p_${turnId}`,
226
+ role: "person",
227
+ text,
228
+ at: at(),
229
+ event_id: id,
230
+ turn_id: turnId
231
+ });
109
232
  const answer = options.script?.[turn - 1] ?? options.reply?.(text, { turn, key: held?.key ?? "" }) ?? `You said: ${text}`;
110
233
  const reply = typeof answer === "string" ? { text: answer } : answer;
111
234
  deliver({ type: "turn.started", turn_id: turnId, client_event_id: id });
@@ -130,10 +253,28 @@ function createMockAgent(options = {}) {
130
253
  turn_id: turnId,
131
254
  text: so.trim()
132
255
  });
133
- said.push({ role: "assistant", text: so.trim(), id: turnId });
256
+ items.push({
257
+ type: "message",
258
+ id: `a_${turnId}`,
259
+ role: "assistant",
260
+ text: so.trim(),
261
+ at: at(),
262
+ event_id: turnId,
263
+ turn_id: turnId
264
+ });
134
265
  }
135
- if (reply.cards?.length)
266
+ if (reply.cards?.length) {
136
267
  deliver({ type: "ui.cards", turn_id: turnId, cards: reply.cards });
268
+ for (const card of reply.cards)
269
+ items.push({
270
+ type: "card",
271
+ id: card.id,
272
+ turn_id: turnId,
273
+ at: at(),
274
+ card,
275
+ ...card.kind === "proposal" ? { proposal_outcome: "pending" } : {}
276
+ });
277
+ }
137
278
  deliver({ type: "turn.completed", turn_id: turnId });
138
279
  },
139
280
  close(code) {
@@ -150,7 +291,8 @@ function createMockAgent(options = {}) {
150
291
  setTimeout(() => {
151
292
  state = 1;
152
293
  socket.onopen?.({});
153
- const resumed = said.length > 0;
294
+ const transcript = said();
295
+ const resumed = transcript.length > 0;
154
296
  socket.onmessage?.({
155
297
  data: JSON.stringify({
156
298
  type: "session.ready",
@@ -159,11 +301,13 @@ function createMockAgent(options = {}) {
159
301
  limits: {},
160
302
  disclosure: options.disclosure ?? "An AI agent. Powered by Vinaverse.",
161
303
  ...resumed ? {
162
- transcript: said.map((s) => ({
304
+ transcript: transcript.map((s) => ({
163
305
  role: s.role,
164
306
  text: s.text,
165
- id: s.id,
166
- at: new Date(now()).toISOString()
307
+ id: s.event_id,
308
+ at: s.at,
309
+ ...s.turn_id ? { turn_id: s.turn_id } : {},
310
+ ...s.interrupted ? { interrupted: true } : {}
167
311
  }))
168
312
  } : {}
169
313
  })