@mastra/pg 0.10.1 → 0.10.2-alpha.1

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.
package/dist/index.cjs CHANGED
@@ -949,6 +949,7 @@ var PostgresStore = class extends storage.MastraStorage {
949
949
  const parsedSchemaName = this.schema ? utils.parseSqlIdentifier(this.schema, "schema name") : void 0;
950
950
  return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
951
951
  }
952
+ /** @deprecated use getEvals instead */
952
953
  async getEvalsByAgentName(agentName, type) {
953
954
  try {
954
955
  const baseQuery = `SELECT * FROM ${this.getTableName(storage.TABLE_EVALS)} WHERE agent_name = $1`;
@@ -999,76 +1000,77 @@ var PostgresStore = class extends storage.MastraStorage {
999
1000
  throw error;
1000
1001
  }
1001
1002
  }
1002
- async getTraces({
1003
- name,
1004
- scope,
1005
- page,
1006
- perPage,
1007
- attributes,
1008
- filters,
1009
- fromDate,
1010
- toDate
1011
- }) {
1012
- let idx = 1;
1013
- const limit = perPage;
1014
- const offset = page * perPage;
1015
- const args = [];
1003
+ /**
1004
+ * @deprecated use getTracesPaginated instead
1005
+ */
1006
+ async getTraces(args) {
1007
+ if (args.fromDate || args.toDate) {
1008
+ args.dateRange = {
1009
+ start: args.fromDate,
1010
+ end: args.toDate
1011
+ };
1012
+ }
1013
+ const result = await this.getTracesPaginated(args);
1014
+ return result.traces;
1015
+ }
1016
+ async getTracesPaginated(args) {
1017
+ const { name, scope, page = 0, perPage: perPageInput, attributes, filters, dateRange } = args;
1018
+ const fromDate = dateRange?.start;
1019
+ const toDate = dateRange?.end;
1020
+ const perPage = perPageInput !== void 0 ? perPageInput : 100;
1021
+ const currentOffset = page * perPage;
1022
+ const queryParams = [];
1016
1023
  const conditions = [];
1024
+ let paramIndex = 1;
1017
1025
  if (name) {
1018
- conditions.push(`name LIKE CONCAT($${idx++}, '%')`);
1026
+ conditions.push(`name LIKE $${paramIndex++}`);
1027
+ queryParams.push(`${name}%`);
1019
1028
  }
1020
1029
  if (scope) {
1021
- conditions.push(`scope = $${idx++}`);
1030
+ conditions.push(`scope = $${paramIndex++}`);
1031
+ queryParams.push(scope);
1022
1032
  }
1023
1033
  if (attributes) {
1024
- Object.keys(attributes).forEach((key) => {
1034
+ Object.entries(attributes).forEach(([key, value]) => {
1025
1035
  const parsedKey = utils.parseSqlIdentifier(key, "attribute key");
1026
- conditions.push(`attributes->>'${parsedKey}' = $${idx++}`);
1036
+ conditions.push(`attributes->>'${parsedKey}' = $${paramIndex++}`);
1037
+ queryParams.push(value);
1027
1038
  });
1028
1039
  }
1029
1040
  if (filters) {
1030
- Object.entries(filters).forEach(([key]) => {
1041
+ Object.entries(filters).forEach(([key, value]) => {
1031
1042
  const parsedKey = utils.parseSqlIdentifier(key, "filter key");
1032
- conditions.push(`${parsedKey} = $${idx++}`);
1043
+ conditions.push(`"${parsedKey}" = $${paramIndex++}`);
1044
+ queryParams.push(value);
1033
1045
  });
1034
1046
  }
1035
1047
  if (fromDate) {
1036
- conditions.push(`createdAt >= $${idx++}`);
1048
+ conditions.push(`"createdAt" >= $${paramIndex++}`);
1049
+ queryParams.push(fromDate);
1037
1050
  }
1038
1051
  if (toDate) {
1039
- conditions.push(`createdAt <= $${idx++}`);
1052
+ conditions.push(`"createdAt" <= $${paramIndex++}`);
1053
+ queryParams.push(toDate);
1040
1054
  }
1041
1055
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1042
- if (name) {
1043
- args.push(name);
1044
- }
1045
- if (scope) {
1046
- args.push(scope);
1047
- }
1048
- if (attributes) {
1049
- for (const [_key, value] of Object.entries(attributes)) {
1050
- args.push(value);
1051
- }
1052
- }
1053
- if (filters) {
1054
- for (const [, value] of Object.entries(filters)) {
1055
- args.push(value);
1056
- }
1057
- }
1058
- if (fromDate) {
1059
- args.push(fromDate.toISOString());
1060
- }
1061
- if (toDate) {
1062
- args.push(toDate.toISOString());
1063
- }
1064
- const result = await this.db.manyOrNone(
1065
- `SELECT * FROM ${this.getTableName(storage.TABLE_TRACES)} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
1066
- args
1067
- );
1068
- if (!result) {
1069
- return [];
1056
+ const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(storage.TABLE_TRACES)} ${whereClause}`;
1057
+ const countResult = await this.db.one(countQuery, queryParams);
1058
+ const total = parseInt(countResult.count, 10);
1059
+ if (total === 0) {
1060
+ return {
1061
+ traces: [],
1062
+ total: 0,
1063
+ page,
1064
+ perPage,
1065
+ hasMore: false
1066
+ };
1070
1067
  }
1071
- return result.map((row) => ({
1068
+ const dataQuery = `SELECT * FROM ${this.getTableName(
1069
+ storage.TABLE_TRACES
1070
+ )} ${whereClause} ORDER BY "createdAt" DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1071
+ const finalQueryParams = [...queryParams, perPage, currentOffset];
1072
+ const rows = await this.db.manyOrNone(dataQuery, finalQueryParams);
1073
+ const traces = rows.map((row) => ({
1072
1074
  id: row.id,
1073
1075
  parentSpanId: row.parentSpanId,
1074
1076
  traceId: row.traceId,
@@ -1084,6 +1086,13 @@ var PostgresStore = class extends storage.MastraStorage {
1084
1086
  other: row.other,
1085
1087
  createdAt: row.createdAt
1086
1088
  }));
1089
+ return {
1090
+ traces,
1091
+ total,
1092
+ page,
1093
+ perPage,
1094
+ hasMore: currentOffset + traces.length < total
1095
+ };
1087
1096
  }
1088
1097
  async setupSchema() {
1089
1098
  if (!this.schema || this.schemaSetupComplete) {
@@ -1162,6 +1171,48 @@ var PostgresStore = class extends storage.MastraStorage {
1162
1171
  throw error;
1163
1172
  }
1164
1173
  }
1174
+ getDefaultValue(type) {
1175
+ switch (type) {
1176
+ case "timestamp":
1177
+ return "DEFAULT NOW()";
1178
+ case "jsonb":
1179
+ return "DEFAULT '{}'::jsonb";
1180
+ default:
1181
+ return super.getDefaultValue(type);
1182
+ }
1183
+ }
1184
+ /**
1185
+ * Alters table schema to add columns if they don't exist
1186
+ * @param tableName Name of the table
1187
+ * @param schema Schema of the table
1188
+ * @param ifNotExists Array of column names to add if they don't exist
1189
+ */
1190
+ async alterTable({
1191
+ tableName,
1192
+ schema,
1193
+ ifNotExists
1194
+ }) {
1195
+ const fullTableName = this.getTableName(tableName);
1196
+ try {
1197
+ for (const columnName of ifNotExists) {
1198
+ if (schema[columnName]) {
1199
+ const columnDef = schema[columnName];
1200
+ const sqlType = this.getSqlType(columnDef.type);
1201
+ const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1202
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1203
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
1204
+ const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
1205
+ await this.db.none(alterSql);
1206
+ this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
1207
+ }
1208
+ }
1209
+ } catch (error) {
1210
+ this.logger?.error?.(
1211
+ `Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`
1212
+ );
1213
+ throw new Error(`Failed to alter table ${tableName}: ${error}`);
1214
+ }
1215
+ }
1165
1216
  async clearTable({ tableName }) {
1166
1217
  try {
1167
1218
  await this.db.none(`TRUNCATE TABLE ${this.getTableName(tableName)} CASCADE`);
@@ -1237,29 +1288,65 @@ var PostgresStore = class extends storage.MastraStorage {
1237
1288
  throw error;
1238
1289
  }
1239
1290
  }
1240
- async getThreadsByResourceId({ resourceId }) {
1291
+ /**
1292
+ * @deprecated use getThreadsByResourceIdPaginated instead
1293
+ */
1294
+ async getThreadsByResourceId(args) {
1295
+ const { resourceId } = args;
1241
1296
  try {
1242
- const threads = await this.db.manyOrNone(
1243
- `SELECT
1244
- id,
1245
- "resourceId",
1246
- title,
1247
- metadata,
1248
- "createdAt",
1249
- "updatedAt"
1250
- FROM ${this.getTableName(storage.TABLE_THREADS)}
1251
- WHERE "resourceId" = $1`,
1252
- [resourceId]
1253
- );
1254
- return threads.map((thread) => ({
1297
+ const baseQuery = `FROM ${this.getTableName(storage.TABLE_THREADS)} WHERE "resourceId" = $1`;
1298
+ const queryParams = [resourceId];
1299
+ const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC`;
1300
+ const rows = await this.db.manyOrNone(dataQuery, queryParams);
1301
+ return (rows || []).map((thread) => ({
1255
1302
  ...thread,
1256
1303
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1257
1304
  createdAt: thread.createdAt,
1258
1305
  updatedAt: thread.updatedAt
1259
1306
  }));
1260
1307
  } catch (error) {
1261
- console.error(`Error getting threads for resource ${resourceId}:`, error);
1262
- throw error;
1308
+ this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
1309
+ return [];
1310
+ }
1311
+ }
1312
+ async getThreadsByResourceIdPaginated(args) {
1313
+ const { resourceId, page = 0, perPage: perPageInput } = args;
1314
+ try {
1315
+ const baseQuery = `FROM ${this.getTableName(storage.TABLE_THREADS)} WHERE "resourceId" = $1`;
1316
+ const queryParams = [resourceId];
1317
+ const perPage = perPageInput !== void 0 ? perPageInput : 100;
1318
+ const currentOffset = page * perPage;
1319
+ const countQuery = `SELECT COUNT(*) ${baseQuery}`;
1320
+ const countResult = await this.db.one(countQuery, queryParams);
1321
+ const total = parseInt(countResult.count, 10);
1322
+ if (total === 0) {
1323
+ return {
1324
+ threads: [],
1325
+ total: 0,
1326
+ page,
1327
+ perPage,
1328
+ hasMore: false
1329
+ };
1330
+ }
1331
+ const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC LIMIT $2 OFFSET $3`;
1332
+ const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1333
+ const threads = (rows || []).map((thread) => ({
1334
+ ...thread,
1335
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1336
+ createdAt: thread.createdAt,
1337
+ // Assuming already Date objects or ISO strings
1338
+ updatedAt: thread.updatedAt
1339
+ }));
1340
+ return {
1341
+ threads,
1342
+ total,
1343
+ page,
1344
+ perPage,
1345
+ hasMore: currentOffset + threads.length < total
1346
+ };
1347
+ } catch (error) {
1348
+ this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
1349
+ return { threads: [], total: 0, page, perPage: perPageInput || 100, hasMore: false };
1263
1350
  }
1264
1351
  }
1265
1352
  async saveThread({ thread }) {
@@ -1339,84 +1426,140 @@ var PostgresStore = class extends storage.MastraStorage {
1339
1426
  throw error;
1340
1427
  }
1341
1428
  }
1342
- async getMessages({ threadId, selectBy }) {
1429
+ async getMessages(args) {
1430
+ const { threadId, format, selectBy } = args;
1431
+ const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId"`;
1432
+ const orderByStatement = `ORDER BY "createdAt" DESC`;
1343
1433
  try {
1344
- const messages = [];
1345
- const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
1434
+ let rows = [];
1346
1435
  const include = selectBy?.include || [];
1347
1436
  if (include.length) {
1348
- const includeResult = await this.db.manyOrNone(
1437
+ rows = await this.db.manyOrNone(
1349
1438
  `
1350
- WITH ordered_messages AS (
1351
- SELECT
1352
- *,
1353
- ROW_NUMBER() OVER (ORDER BY "createdAt" DESC) as row_num
1354
- FROM ${this.getTableName(storage.TABLE_MESSAGES)}
1355
- WHERE thread_id = $1
1356
- )
1357
- SELECT
1358
- m.id,
1359
- m.content,
1360
- m.role,
1361
- m.type,
1362
- m."createdAt",
1363
- m.thread_id AS "threadId"
1364
- FROM ordered_messages m
1365
- WHERE m.id = ANY($2)
1366
- OR EXISTS (
1367
- SELECT 1 FROM ordered_messages target
1368
- WHERE target.id = ANY($2)
1369
- AND (
1370
- -- Get previous messages based on the max withPreviousMessages
1371
- (m.row_num <= target.row_num + $3 AND m.row_num > target.row_num)
1372
- OR
1373
- -- Get next messages based on the max withNextMessages
1374
- (m.row_num >= target.row_num - $4 AND m.row_num < target.row_num)
1439
+ WITH ordered_messages AS (
1440
+ SELECT
1441
+ *,
1442
+ ROW_NUMBER() OVER (${orderByStatement}) as row_num
1443
+ FROM ${this.getTableName(storage.TABLE_MESSAGES)}
1444
+ WHERE thread_id = $1
1375
1445
  )
1376
- )
1377
- ORDER BY m."createdAt" DESC
1378
- `,
1446
+ SELECT
1447
+ m.id,
1448
+ m.content,
1449
+ m.role,
1450
+ m.type,
1451
+ m."createdAt",
1452
+ m.thread_id AS "threadId"
1453
+ FROM ordered_messages m
1454
+ WHERE m.id = ANY($2)
1455
+ OR EXISTS (
1456
+ SELECT 1 FROM ordered_messages target
1457
+ WHERE target.id = ANY($2)
1458
+ AND (
1459
+ -- Get previous messages based on the max withPreviousMessages
1460
+ (m.row_num <= target.row_num + $3 AND m.row_num > target.row_num)
1461
+ OR
1462
+ -- Get next messages based on the max withNextMessages
1463
+ (m.row_num >= target.row_num - $4 AND m.row_num < target.row_num)
1464
+ )
1465
+ )
1466
+ ORDER BY m."createdAt" ASC
1467
+ `,
1468
+ // Keep ASC for final sorting after fetching context
1379
1469
  [
1380
1470
  threadId,
1381
1471
  include.map((i) => i.id),
1382
- Math.max(...include.map((i) => i.withPreviousMessages || 0)),
1383
- Math.max(...include.map((i) => i.withNextMessages || 0))
1472
+ Math.max(0, ...include.map((i) => i.withPreviousMessages || 0)),
1473
+ // Ensure non-negative
1474
+ Math.max(0, ...include.map((i) => i.withNextMessages || 0))
1475
+ // Ensure non-negative
1384
1476
  ]
1385
1477
  );
1386
- messages.push(...includeResult);
1478
+ } else {
1479
+ const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
1480
+ if (limit === 0 && selectBy?.last !== false) ; else {
1481
+ let query = `${selectStatement} FROM ${this.getTableName(
1482
+ storage.TABLE_MESSAGES
1483
+ )} WHERE thread_id = $1 ${orderByStatement}`;
1484
+ const queryParams = [threadId];
1485
+ if (limit !== void 0 && selectBy?.last !== false) {
1486
+ query += ` LIMIT $2`;
1487
+ queryParams.push(limit);
1488
+ }
1489
+ rows = await this.db.manyOrNone(query, queryParams);
1490
+ }
1387
1491
  }
1388
- const result = await this.db.manyOrNone(
1389
- `
1390
- SELECT
1391
- id,
1392
- content,
1393
- role,
1394
- type,
1395
- "createdAt",
1396
- thread_id AS "threadId"
1397
- FROM ${this.getTableName(storage.TABLE_MESSAGES)}
1398
- WHERE thread_id = $1
1399
- AND id != ALL($2)
1400
- ORDER BY "createdAt" DESC
1401
- LIMIT $3
1402
- `,
1403
- [threadId, messages.map((m) => m.id), limit]
1404
- );
1405
- messages.push(...result);
1406
- messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1407
- messages.forEach((message) => {
1492
+ const fetchedMessages = (rows || []).map((message) => {
1408
1493
  if (typeof message.content === "string") {
1409
1494
  try {
1410
1495
  message.content = JSON.parse(message.content);
1411
1496
  } catch {
1412
1497
  }
1413
1498
  }
1414
- if (message.type === `v2`) delete message.type;
1499
+ if (message.type === "v2") delete message.type;
1500
+ return message;
1415
1501
  });
1416
- return messages;
1502
+ const sortedMessages = fetchedMessages.sort(
1503
+ (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
1504
+ );
1505
+ return format === "v2" ? sortedMessages.map(
1506
+ (m) => ({ ...m, content: m.content || { format: 2, parts: [{ type: "text", text: "" }] } })
1507
+ ) : sortedMessages;
1417
1508
  } catch (error) {
1418
- console.error("Error getting messages:", error);
1419
- throw error;
1509
+ this.logger.error("Error getting messages:", error);
1510
+ return [];
1511
+ }
1512
+ }
1513
+ async getMessagesPaginated(args) {
1514
+ const { threadId, format, selectBy } = args;
1515
+ const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
1516
+ const fromDate = dateRange?.start;
1517
+ const toDate = dateRange?.end;
1518
+ const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId"`;
1519
+ const orderByStatement = `ORDER BY "createdAt" DESC`;
1520
+ try {
1521
+ const perPage = perPageInput !== void 0 ? perPageInput : 40;
1522
+ const currentOffset = page * perPage;
1523
+ const conditions = [`thread_id = $1`];
1524
+ const queryParams = [threadId];
1525
+ let paramIndex = 2;
1526
+ if (fromDate) {
1527
+ conditions.push(`"createdAt" >= $${paramIndex++}`);
1528
+ queryParams.push(fromDate);
1529
+ }
1530
+ if (toDate) {
1531
+ conditions.push(`"createdAt" <= $${paramIndex++}`);
1532
+ queryParams.push(toDate);
1533
+ }
1534
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1535
+ const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(storage.TABLE_MESSAGES)} ${whereClause}`;
1536
+ const countResult = await this.db.one(countQuery, queryParams);
1537
+ const total = parseInt(countResult.count, 10);
1538
+ if (total === 0) {
1539
+ return {
1540
+ messages: [],
1541
+ total: 0,
1542
+ page,
1543
+ perPage,
1544
+ hasMore: false
1545
+ };
1546
+ }
1547
+ const dataQuery = `${selectStatement} FROM ${this.getTableName(
1548
+ storage.TABLE_MESSAGES
1549
+ )} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1550
+ const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1551
+ const list = new agent.MessageList().add(rows || [], "memory");
1552
+ const messagesToReturn = format === `v2` ? list.get.all.v2() : list.get.all.v1();
1553
+ return {
1554
+ messages: messagesToReturn,
1555
+ total,
1556
+ page,
1557
+ perPage,
1558
+ hasMore: currentOffset + rows.length < total
1559
+ };
1560
+ } catch (error) {
1561
+ this.logger.error("Error getting messages:", error);
1562
+ return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
1420
1563
  }
1421
1564
  }
1422
1565
  async saveMessages({
@@ -1629,6 +1772,56 @@ var PostgresStore = class extends storage.MastraStorage {
1629
1772
  async close() {
1630
1773
  this.pgp.end();
1631
1774
  }
1775
+ async getEvals(options = {}) {
1776
+ const { agentName, type, page = 0, perPage = 100, dateRange } = options;
1777
+ const fromDate = dateRange?.start;
1778
+ const toDate = dateRange?.end;
1779
+ const conditions = [];
1780
+ const queryParams = [];
1781
+ let paramIndex = 1;
1782
+ if (agentName) {
1783
+ conditions.push(`agent_name = $${paramIndex++}`);
1784
+ queryParams.push(agentName);
1785
+ }
1786
+ if (type === "test") {
1787
+ conditions.push(`(test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL)`);
1788
+ } else if (type === "live") {
1789
+ conditions.push(`(test_info IS NULL OR test_info->>'testPath' IS NULL)`);
1790
+ }
1791
+ if (fromDate) {
1792
+ conditions.push(`created_at >= $${paramIndex++}`);
1793
+ queryParams.push(fromDate);
1794
+ }
1795
+ if (toDate) {
1796
+ conditions.push(`created_at <= $${paramIndex++}`);
1797
+ queryParams.push(toDate);
1798
+ }
1799
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1800
+ const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(storage.TABLE_EVALS)} ${whereClause}`;
1801
+ const countResult = await this.db.one(countQuery, queryParams);
1802
+ const total = parseInt(countResult.count, 10);
1803
+ const currentOffset = page * perPage;
1804
+ if (total === 0) {
1805
+ return {
1806
+ evals: [],
1807
+ total: 0,
1808
+ page,
1809
+ perPage,
1810
+ hasMore: false
1811
+ };
1812
+ }
1813
+ const dataQuery = `SELECT * FROM ${this.getTableName(
1814
+ storage.TABLE_EVALS
1815
+ )} ${whereClause} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1816
+ const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1817
+ return {
1818
+ evals: rows?.map((row) => this.transformEvalRow(row)) ?? [],
1819
+ total,
1820
+ page,
1821
+ perPage,
1822
+ hasMore: currentOffset + (rows?.length ?? 0) < total
1823
+ };
1824
+ }
1632
1825
  };
1633
1826
 
1634
1827
  // src/vector/prompt.ts