veryfront 0.1.286 → 0.1.287

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.
@@ -0,0 +1,546 @@
1
+ import { z } from "zod";
2
+ const textPartSchema = z.object({ type: z.literal("text"), text: z.string() });
3
+ const imagePartSchema = z.object({
4
+ type: z.literal("image"),
5
+ upload_id: z.string().uuid(),
6
+ media_type: z.string(),
7
+ url: z.string().optional(),
8
+ });
9
+ const filePartSchema = z.object({
10
+ type: z.literal("file"),
11
+ upload_id: z.string().uuid(),
12
+ media_type: z.string(),
13
+ filename: z.string().optional(),
14
+ url: z.string().optional(),
15
+ });
16
+ const toolCallPartSchema = z.object({
17
+ type: z.literal("tool_call"),
18
+ id: z.string(),
19
+ name: z.string(),
20
+ input: z.record(z.string(), z.unknown()),
21
+ state: z.enum(["streaming", "pending", "completed", "error"]),
22
+ });
23
+ const toolResultPartSchema = z.object({
24
+ type: z.literal("tool_result"),
25
+ tool_call_id: z.string(),
26
+ output: z.unknown(),
27
+ is_error: z.boolean().optional(),
28
+ });
29
+ const reasoningPartSchema = z.object({
30
+ type: z.literal("reasoning"),
31
+ text: z.string(),
32
+ signature: z.string().optional(),
33
+ });
34
+ const citationPartSchema = z.object({
35
+ type: z.literal("citation"),
36
+ source_id: z.string(),
37
+ url: z.string().optional(),
38
+ title: z.string().optional(),
39
+ quote: z.string().optional(),
40
+ });
41
+ const stepStartPartSchema = z.object({ type: z.literal("step_start") });
42
+ const errorPartSchema = z.object({
43
+ type: z.literal("error"),
44
+ code: z.string(),
45
+ message: z.string(),
46
+ });
47
+ const dataPartSchema = z.object({ type: z.literal("data"), name: z.string(), value: z.unknown() });
48
+ export const messagePartSchema = z.discriminatedUnion("type", [
49
+ textPartSchema,
50
+ imagePartSchema,
51
+ filePartSchema,
52
+ toolCallPartSchema,
53
+ toolResultPartSchema,
54
+ reasoningPartSchema,
55
+ citationPartSchema,
56
+ stepStartPartSchema,
57
+ errorPartSchema,
58
+ dataPartSchema,
59
+ ]);
60
+ export const conversationTypeSchema = z.enum([
61
+ "chat",
62
+ "agent_task",
63
+ "support",
64
+ "channel",
65
+ "project_agent",
66
+ ]);
67
+ export const messageStatusSchema = z.enum([
68
+ "pending",
69
+ "streaming",
70
+ "completed",
71
+ "error",
72
+ "failed",
73
+ "cancelled",
74
+ "stopped",
75
+ ]);
76
+ export const apiConversationSchema = z.object({
77
+ id: z.string(),
78
+ projectId: z.string().nullable().optional(),
79
+ type: conversationTypeSchema,
80
+ title: z.string().nullable().optional(),
81
+ status: z.enum(["active", "archived", "deleted"]),
82
+ summary: z.string().nullable().optional(),
83
+ currentNode: z.string().nullable().optional(),
84
+ messageCount: z.number(),
85
+ lastMessageAt: z.string().nullable().optional(),
86
+ metadata: z.record(z.string(), z.unknown()).nullable().optional(),
87
+ createdBy: z.string(),
88
+ archivedAt: z.string().nullable().optional(),
89
+ createdAt: z.string(),
90
+ updatedAt: z.string(),
91
+ });
92
+ export const apiMessageSchema = z.object({
93
+ id: z.string(),
94
+ conversationId: z.string(),
95
+ parentId: z.string().nullable(),
96
+ seq: z.number(),
97
+ role: z.enum(["user", "assistant", "tool"]),
98
+ parts: z.array(messagePartSchema),
99
+ status: messageStatusSchema,
100
+ model: z.string().nullable(),
101
+ tokenUsage: z.object({ input: z.number(), output: z.number() }).nullable(),
102
+ finishReason: z.string().nullable(),
103
+ costCredits: z.string().nullable().optional(),
104
+ createdBy: z.string().nullable(),
105
+ editedAt: z.string().nullable().optional(),
106
+ idempotencyKey: z.string().nullable().optional(),
107
+ metadata: z.record(z.string(), z.unknown()).nullable(),
108
+ createdAt: z.string(),
109
+ updatedAt: z.string().nullable(),
110
+ });
111
+ export const UUID_PATTERN = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/i;
112
+ export function isUuid(value) {
113
+ return typeof value === "string" && UUID_PATTERN.test(value);
114
+ }
115
+ export function extractUploadId(url) {
116
+ const match = url.match(UUID_PATTERN);
117
+ return match ? match[0] : null;
118
+ }
119
+ export function mapToolState(sdkState) {
120
+ switch (sdkState) {
121
+ case "input-streaming":
122
+ return "streaming";
123
+ case "input-available":
124
+ case "approval-requested":
125
+ case "approval-responded":
126
+ return "pending";
127
+ case "output-available":
128
+ return "completed";
129
+ case "output-error":
130
+ case "output-denied":
131
+ case "error":
132
+ return "error";
133
+ default:
134
+ return "pending";
135
+ }
136
+ }
137
+ export function isRecord(value) {
138
+ return typeof value === "object" && value !== null && !Array.isArray(value);
139
+ }
140
+ export function getStringField(value, field, fallback) {
141
+ if (!isRecord(value) || typeof value[field] !== "string") {
142
+ return fallback;
143
+ }
144
+ return value[field];
145
+ }
146
+ function getOptionalStringField(value, key) {
147
+ if (!isRecord(value)) {
148
+ return undefined;
149
+ }
150
+ const field = value[key];
151
+ return typeof field === "string" ? field : undefined;
152
+ }
153
+ function getNonEmptyStringField(value, key) {
154
+ const field = getOptionalStringField(value, key);
155
+ return field && field.length > 0 ? field : undefined;
156
+ }
157
+ function toRecord(value) {
158
+ return isRecord(value) ? Object.fromEntries(Object.entries(value)) : {};
159
+ }
160
+ export function stringifyUnknown(value) {
161
+ if (typeof value === "string")
162
+ return value;
163
+ try {
164
+ const serialized = JSON.stringify(value);
165
+ return serialized ?? String(value);
166
+ }
167
+ catch {
168
+ return String(value);
169
+ }
170
+ }
171
+ export function isDataUiPart(part) {
172
+ return part.type.startsWith("data-") && "data" in part;
173
+ }
174
+ export function isToolUiPart(part) {
175
+ return ((part.type === "dynamic-tool" || part.type.startsWith("tool-")) &&
176
+ typeof getOptionalStringField(part, "toolCallId") === "string" &&
177
+ typeof getOptionalStringField(part, "state") === "string");
178
+ }
179
+ export function getUiToolName(part) {
180
+ const explicitToolName = getOptionalStringField(part, "toolName");
181
+ if (explicitToolName) {
182
+ return explicitToolName;
183
+ }
184
+ return part.type.startsWith("tool-") ? part.type.replace(/^tool-/, "") : undefined;
185
+ }
186
+ export function pushToolParts(parts, toolName, toolCallId, state, part) {
187
+ const input = toRecord(part.input);
188
+ const isErroredState = state === "output-error" || state === "error" || state === "output-denied";
189
+ const hasResultState = state === "output-available" || isErroredState;
190
+ if (hasResultState) {
191
+ parts.push({
192
+ type: "tool_call",
193
+ id: toolCallId,
194
+ name: toolName,
195
+ input,
196
+ state: "completed",
197
+ });
198
+ const resultOutput = isErroredState
199
+ ? part.output ?? part.errorText ?? "Tool error"
200
+ : part.output ?? null;
201
+ parts.push({
202
+ type: "tool_result",
203
+ tool_call_id: toolCallId,
204
+ output: resultOutput,
205
+ is_error: isErroredState,
206
+ });
207
+ return;
208
+ }
209
+ parts.push({
210
+ type: "tool_call",
211
+ id: toolCallId,
212
+ name: toolName,
213
+ input,
214
+ state: mapToolState(state),
215
+ });
216
+ }
217
+ export function isToolCallPart(value) {
218
+ return (isRecord(value) &&
219
+ value.type === "tool-call" &&
220
+ typeof value.toolCallId === "string" &&
221
+ typeof value.toolName === "string");
222
+ }
223
+ export function isToolResultPart(value) {
224
+ return (isRecord(value) &&
225
+ value.type === "tool-result" &&
226
+ typeof value.toolCallId === "string" &&
227
+ typeof value.toolName === "string");
228
+ }
229
+ export function isTextPart(value) {
230
+ return isRecord(value) && value.type === "text" && typeof value.text === "string";
231
+ }
232
+ export function isReasoningPart(value) {
233
+ return isRecord(value) && value.type === "reasoning" && typeof value.text === "string";
234
+ }
235
+ export function extractTextFromMessage(message) {
236
+ if (!message || !message.content)
237
+ return "";
238
+ const { content } = message;
239
+ if (typeof content === "string") {
240
+ return content;
241
+ }
242
+ if (Array.isArray(content)) {
243
+ const textParts = [];
244
+ for (const part of content) {
245
+ if (isTextPart(part)) {
246
+ textParts.push(part.text);
247
+ }
248
+ }
249
+ return textParts.join(" ");
250
+ }
251
+ return "";
252
+ }
253
+ function toJsonValue(value) {
254
+ if (value == null) {
255
+ return null;
256
+ }
257
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
258
+ return value;
259
+ }
260
+ if (Array.isArray(value)) {
261
+ return value.map((entry) => toJsonValue(entry));
262
+ }
263
+ if (isRecord(value)) {
264
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, toJsonValue(entry)]));
265
+ }
266
+ return JSON.stringify(value);
267
+ }
268
+ function getFilePart(part) {
269
+ if (!isRecord(part) || part.type !== "file") {
270
+ return null;
271
+ }
272
+ const mediaType = getNonEmptyStringField(part, "mediaType");
273
+ const data = getNonEmptyStringField(part, "url");
274
+ if (!mediaType || !data) {
275
+ return null;
276
+ }
277
+ const filename = getNonEmptyStringField(part, "filename");
278
+ const uploadId = getNonEmptyStringField(part, "uploadId");
279
+ const uploadPath = getNonEmptyStringField(part, "uploadPath");
280
+ return {
281
+ type: "file",
282
+ mediaType,
283
+ data,
284
+ url: data,
285
+ ...(filename ? { filename } : {}),
286
+ ...(uploadId ? { uploadId } : {}),
287
+ ...(uploadPath ? { uploadPath } : {}),
288
+ };
289
+ }
290
+ function getToolPart(part) {
291
+ if (!isRecord(part) || typeof part.type !== "string") {
292
+ return null;
293
+ }
294
+ const type = part.type;
295
+ const toolCallId = getNonEmptyStringField(part, "toolCallId");
296
+ const state = getNonEmptyStringField(part, "state");
297
+ const explicitToolName = getNonEmptyStringField(part, "toolName") ??
298
+ getNonEmptyStringField(part, "name");
299
+ const derivedToolName = type === "dynamic-tool" || type === "tool_call" || !type.startsWith("tool-")
300
+ ? undefined
301
+ : type.replace(/^tool-/, "");
302
+ const toolName = explicitToolName ?? derivedToolName;
303
+ if (!toolCallId || !state || !toolName) {
304
+ return null;
305
+ }
306
+ const errorText = getOptionalStringField(part, "errorText");
307
+ const output = Object.hasOwn(part, "output") ? part.output : undefined;
308
+ return {
309
+ toolCallId,
310
+ toolName,
311
+ input: toRecord(part.input),
312
+ state,
313
+ ...(output !== undefined ? { output } : {}),
314
+ ...(errorText !== undefined ? { errorText } : {}),
315
+ };
316
+ }
317
+ function getRawToolCallPart(part) {
318
+ if (!isRecord(part) || part.type !== "tool_call") {
319
+ return null;
320
+ }
321
+ const toolCallId = getNonEmptyStringField(part, "toolCallId") ??
322
+ getNonEmptyStringField(part, "tool_call_id") ??
323
+ getNonEmptyStringField(part, "id");
324
+ const toolName = getNonEmptyStringField(part, "toolName") ??
325
+ getNonEmptyStringField(part, "tool_name") ??
326
+ getNonEmptyStringField(part, "name");
327
+ if (!toolCallId || !toolName) {
328
+ return null;
329
+ }
330
+ return {
331
+ toolCallId,
332
+ toolName,
333
+ input: toRecord(part.input),
334
+ };
335
+ }
336
+ function getRawToolResultPart(part) {
337
+ if (!isRecord(part) || part.type !== "tool_result") {
338
+ return null;
339
+ }
340
+ const toolCallId = getNonEmptyStringField(part, "toolCallId") ??
341
+ getNonEmptyStringField(part, "tool_call_id") ??
342
+ getNonEmptyStringField(part, "id");
343
+ if (!toolCallId) {
344
+ return null;
345
+ }
346
+ const toolName = getNonEmptyStringField(part, "toolName") ??
347
+ getNonEmptyStringField(part, "tool_name") ??
348
+ getNonEmptyStringField(part, "name");
349
+ const isError = part.is_error === true || part.isError === true;
350
+ const output = isError
351
+ ? {
352
+ type: "error-text",
353
+ value: stringifyUnknown(part.output ?? "Tool error"),
354
+ }
355
+ : {
356
+ type: "json",
357
+ value: toJsonValue(part.output),
358
+ };
359
+ return {
360
+ toolCallId,
361
+ ...(toolName ? { toolName } : {}),
362
+ output,
363
+ };
364
+ }
365
+ function buildRawToolNameMap(parts) {
366
+ const toolNames = new Map();
367
+ for (const part of parts) {
368
+ const rawToolCall = getRawToolCallPart(part);
369
+ if (!rawToolCall) {
370
+ continue;
371
+ }
372
+ toolNames.set(rawToolCall.toolCallId, rawToolCall.toolName);
373
+ }
374
+ return toolNames;
375
+ }
376
+ function buildToolResultOutput(toolPart) {
377
+ if (toolPart.state === "output-available") {
378
+ return {
379
+ type: "json",
380
+ value: toJsonValue(toolPart.output),
381
+ };
382
+ }
383
+ if (toolPart.state === "output-error" || toolPart.state === "output-denied" ||
384
+ toolPart.state === "error") {
385
+ return {
386
+ type: "error-text",
387
+ value: toolPart.errorText ?? stringifyUnknown(toolPart.output ?? "Tool error"),
388
+ };
389
+ }
390
+ return null;
391
+ }
392
+ function convertSystemMessage(message) {
393
+ const content = message.parts.flatMap((part) => (isTextPart(part) ? [part.text] : [])).join("");
394
+ if (content.length === 0) {
395
+ return [];
396
+ }
397
+ return [
398
+ {
399
+ role: "system",
400
+ content,
401
+ },
402
+ ];
403
+ }
404
+ function convertUserMessage(message) {
405
+ const content = [];
406
+ for (const part of message.parts) {
407
+ if (isTextPart(part)) {
408
+ content.push({ type: "text", text: part.text });
409
+ continue;
410
+ }
411
+ const filePart = getFilePart(part);
412
+ if (filePart) {
413
+ content.push(filePart);
414
+ }
415
+ }
416
+ if (content.length === 0) {
417
+ return [];
418
+ }
419
+ return [
420
+ {
421
+ role: "user",
422
+ content,
423
+ },
424
+ ];
425
+ }
426
+ function convertAssistantMessage(message) {
427
+ const rawToolNamesById = buildRawToolNameMap(message.parts);
428
+ const assistantContent = [];
429
+ const deferredAssistantContent = [];
430
+ const toolResults = [];
431
+ const pendingToolCallIds = new Set();
432
+ const messages = [];
433
+ const flushAssistantMessage = (content) => {
434
+ if (content.length === 0) {
435
+ return;
436
+ }
437
+ messages.push({
438
+ role: "assistant",
439
+ content: [...content],
440
+ });
441
+ content.length = 0;
442
+ };
443
+ const flushToolMessage = () => {
444
+ if (toolResults.length === 0) {
445
+ return;
446
+ }
447
+ messages.push({
448
+ role: "tool",
449
+ content: [...toolResults],
450
+ });
451
+ toolResults.length = 0;
452
+ };
453
+ const pushAssistantPart = (part) => {
454
+ if (toolResults.length > 0) {
455
+ flushToolMessage();
456
+ flushAssistantMessage(deferredAssistantContent);
457
+ }
458
+ if (part.type === "tool-call") {
459
+ assistantContent.push(part);
460
+ pendingToolCallIds.add(part.toolCallId);
461
+ return;
462
+ }
463
+ if (pendingToolCallIds.size > 0) {
464
+ deferredAssistantContent.push(part);
465
+ return;
466
+ }
467
+ assistantContent.push(part);
468
+ };
469
+ const pushToolResult = (part) => {
470
+ flushAssistantMessage(assistantContent);
471
+ toolResults.push(part);
472
+ pendingToolCallIds.delete(part.toolCallId);
473
+ };
474
+ for (const part of message.parts) {
475
+ if (isTextPart(part)) {
476
+ pushAssistantPart({ type: "text", text: part.text });
477
+ continue;
478
+ }
479
+ if (isReasoningPart(part)) {
480
+ pushAssistantPart({ type: "reasoning", text: part.text });
481
+ continue;
482
+ }
483
+ const filePart = getFilePart(part);
484
+ if (filePart) {
485
+ pushAssistantPart(filePart);
486
+ continue;
487
+ }
488
+ const toolPart = getToolPart(part);
489
+ if (toolPart) {
490
+ pushAssistantPart({
491
+ type: "tool-call",
492
+ toolCallId: toolPart.toolCallId,
493
+ toolName: toolPart.toolName,
494
+ input: toolPart.input,
495
+ });
496
+ const resultOutput = buildToolResultOutput(toolPart);
497
+ if (resultOutput) {
498
+ pushToolResult({
499
+ type: "tool-result",
500
+ toolCallId: toolPart.toolCallId,
501
+ toolName: toolPart.toolName,
502
+ output: resultOutput,
503
+ });
504
+ }
505
+ continue;
506
+ }
507
+ const rawToolCall = getRawToolCallPart(part);
508
+ if (rawToolCall) {
509
+ pushAssistantPart({
510
+ type: "tool-call",
511
+ toolCallId: rawToolCall.toolCallId,
512
+ toolName: rawToolCall.toolName,
513
+ input: rawToolCall.input,
514
+ });
515
+ continue;
516
+ }
517
+ const rawToolResult = getRawToolResultPart(part);
518
+ if (rawToolResult) {
519
+ pushToolResult({
520
+ type: "tool-result",
521
+ toolCallId: rawToolResult.toolCallId,
522
+ toolName: rawToolResult.toolName ?? rawToolNamesById.get(rawToolResult.toolCallId) ??
523
+ "unknown",
524
+ output: rawToolResult.output,
525
+ });
526
+ }
527
+ }
528
+ flushAssistantMessage(assistantContent);
529
+ flushToolMessage();
530
+ flushAssistantMessage(deferredAssistantContent);
531
+ return messages;
532
+ }
533
+ export function convertUiMessagesToModelMessages(messages) {
534
+ return messages.flatMap((message) => {
535
+ switch (message.role) {
536
+ case "system":
537
+ return convertSystemMessage(message);
538
+ case "user":
539
+ return convertUserMessage(message);
540
+ case "assistant":
541
+ return convertAssistantMessage(message);
542
+ default:
543
+ return [];
544
+ }
545
+ });
546
+ }
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.286";
1
+ export declare const VERSION = "0.1.287";
2
2
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.286";
3
+ export const VERSION = "0.1.287";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.286",
3
+ "version": "0.1.287",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -177,6 +177,10 @@
177
177
  "import": "./esm/cli/main.js",
178
178
  "types": "./esm/cli/main.d.ts"
179
179
  },
180
+ "./chat/conversation": {
181
+ "import": "./esm/src/chat/conversation.js",
182
+ "types": "./esm/src/chat/conversation.d.ts"
183
+ },
180
184
  "./tsconfig.json": "./tsconfig.json"
181
185
  },
182
186
  "scripts": {},
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.286",
3
+ "version": "0.1.287",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "workspace": [
@@ -63,7 +63,8 @@ export default {
63
63
  "./embedding": "./src/embedding/index.ts",
64
64
  "./extensions": "./src/extensions/index.ts",
65
65
  "./extensions/interfaces": "./src/extensions/interfaces/index.ts",
66
- "./cli": "./cli/main.ts"
66
+ "./cli": "./cli/main.ts",
67
+ "./chat/conversation": "./src/chat/conversation.ts"
67
68
  },
68
69
  "imports": {
69
70
  "veryfront/head": "./src/react/runtime/core.ts",
@@ -273,7 +274,8 @@ export default {
273
274
  "unified": "npm:unified@11.0.5",
274
275
  "vfile": "npm:vfile@6.0.3",
275
276
  "class-variance-authority": "npm:class-variance-authority@0.7.1",
276
- "tailwind-merge": "npm:tailwind-merge@3.5.0"
277
+ "tailwind-merge": "npm:tailwind-merge@3.5.0",
278
+ "veryfront/chat/conversation": "./src/chat/conversation.ts"
277
279
  },
278
280
  "compilerOptions": {
279
281
  "jsx": "react-jsx",