@deepagents/context 0.8.0 → 0.9.0

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.js CHANGED
@@ -1,3 +1,6 @@
1
+ // packages/context/src/index.ts
2
+ import { generateId } from "ai";
3
+
1
4
  // packages/context/src/lib/context.ts
2
5
  function isFragment(data) {
3
6
  return typeof data === "object" && data !== null && "name" in data && "data" in data && typeof data.name === "string";
@@ -119,7 +122,8 @@ var ModelsRegistry = class {
119
122
  context: model.limit.context,
120
123
  output: model.limit.output,
121
124
  exceedsContext: tokens > model.limit.context
122
- }
125
+ },
126
+ fragments: []
123
127
  };
124
128
  }
125
129
  };
@@ -130,6 +134,43 @@ function getModelsRegistry() {
130
134
  }
131
135
  return _registry;
132
136
  }
137
+ async function estimate(modelId, renderer, ...fragments) {
138
+ const registry = getModelsRegistry();
139
+ await registry.load();
140
+ const input = renderer.render(fragments);
141
+ const model = registry.get(modelId);
142
+ if (!model) {
143
+ throw new Error(
144
+ `Model "${modelId}" not found. Call load() first or check model ID.`
145
+ );
146
+ }
147
+ const tokenizer = registry.getTokenizer(modelId);
148
+ const totalTokens = tokenizer.count(input);
149
+ const totalCost = totalTokens / 1e6 * model.cost.input;
150
+ const fragmentEstimates = fragments.map((fragment2) => {
151
+ const rendered = renderer.render([fragment2]);
152
+ const tokens = tokenizer.count(rendered);
153
+ const cost = tokens / 1e6 * model.cost.input;
154
+ return {
155
+ id: fragment2.id,
156
+ name: fragment2.name,
157
+ tokens,
158
+ cost
159
+ };
160
+ });
161
+ return {
162
+ model: model.id,
163
+ provider: model.provider,
164
+ tokens: totalTokens,
165
+ cost: totalCost,
166
+ limits: {
167
+ context: model.limit.context,
168
+ output: model.limit.output,
169
+ exceedsContext: totalTokens > model.limit.context
170
+ },
171
+ fragments: fragmentEstimates
172
+ };
173
+ }
133
174
 
134
175
  // packages/context/src/lib/renderers/abstract.renderer.ts
135
176
  import pluralize from "pluralize";
@@ -789,12 +830,13 @@ var ContextStore = class {
789
830
  import { DatabaseSync } from "node:sqlite";
790
831
  var STORE_DDL = `
791
832
  -- Chats table
833
+ -- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
792
834
  CREATE TABLE IF NOT EXISTS chats (
793
835
  id TEXT PRIMARY KEY,
794
836
  title TEXT,
795
837
  metadata TEXT,
796
- createdAt INTEGER NOT NULL,
797
- updatedAt INTEGER NOT NULL
838
+ createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
839
+ updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
798
840
  );
799
841
 
800
842
  CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
@@ -807,7 +849,6 @@ CREATE TABLE IF NOT EXISTS messages (
807
849
  name TEXT NOT NULL,
808
850
  type TEXT,
809
851
  data TEXT NOT NULL,
810
- persist INTEGER NOT NULL DEFAULT 1,
811
852
  createdAt INTEGER NOT NULL,
812
853
  FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
813
854
  FOREIGN KEY (parentId) REFERENCES messages(id)
@@ -846,13 +887,13 @@ CREATE TABLE IF NOT EXISTS checkpoints (
846
887
  CREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);
847
888
 
848
889
  -- FTS5 virtual table for full-text search
849
- -- Using external content mode (content='') so we manage sync manually
890
+ -- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
891
+ -- Only 'content' is indexed for full-text search
850
892
  CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
851
- messageId,
852
- chatId,
853
- name,
893
+ messageId UNINDEXED,
894
+ chatId UNINDEXED,
895
+ name UNINDEXED,
854
896
  content,
855
- content='',
856
897
  tokenize='porter unicode61'
857
898
  );
858
899
  `;
@@ -869,15 +910,32 @@ var SqliteContextStore = class extends ContextStore {
869
910
  // ==========================================================================
870
911
  async createChat(chat) {
871
912
  this.#db.prepare(
872
- `INSERT INTO chats (id, title, metadata, createdAt, updatedAt)
873
- VALUES (?, ?, ?, ?, ?)`
913
+ `INSERT INTO chats (id, title, metadata)
914
+ VALUES (?, ?, ?)`
874
915
  ).run(
875
916
  chat.id,
876
917
  chat.title ?? null,
877
- chat.metadata ? JSON.stringify(chat.metadata) : null,
878
- chat.createdAt,
879
- chat.updatedAt
918
+ chat.metadata ? JSON.stringify(chat.metadata) : null
919
+ );
920
+ }
921
+ async upsertChat(chat) {
922
+ const row = this.#db.prepare(
923
+ `INSERT INTO chats (id, title, metadata)
924
+ VALUES (?, ?, ?)
925
+ ON CONFLICT(id) DO UPDATE SET id = excluded.id
926
+ RETURNING *`
927
+ ).get(
928
+ chat.id,
929
+ chat.title ?? null,
930
+ chat.metadata ? JSON.stringify(chat.metadata) : null
880
931
  );
932
+ return {
933
+ id: row.id,
934
+ title: row.title ?? void 0,
935
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
936
+ createdAt: row.createdAt,
937
+ updatedAt: row.updatedAt
938
+ };
881
939
  }
882
940
  async getChat(chatId) {
883
941
  const row = this.#db.prepare("SELECT * FROM chats WHERE id = ?").get(chatId);
@@ -893,7 +951,7 @@ var SqliteContextStore = class extends ContextStore {
893
951
  };
894
952
  }
895
953
  async updateChat(chatId, updates) {
896
- const setClauses = [];
954
+ const setClauses = ["updatedAt = strftime('%s', 'now') * 1000"];
897
955
  const params = [];
898
956
  if (updates.title !== void 0) {
899
957
  setClauses.push("title = ?");
@@ -903,15 +961,17 @@ var SqliteContextStore = class extends ContextStore {
903
961
  setClauses.push("metadata = ?");
904
962
  params.push(JSON.stringify(updates.metadata));
905
963
  }
906
- if (updates.updatedAt !== void 0) {
907
- setClauses.push("updatedAt = ?");
908
- params.push(updates.updatedAt);
909
- }
910
- if (setClauses.length === 0) {
911
- return;
912
- }
913
964
  params.push(chatId);
914
- this.#db.prepare(`UPDATE chats SET ${setClauses.join(", ")} WHERE id = ?`).run(...params);
965
+ const row = this.#db.prepare(
966
+ `UPDATE chats SET ${setClauses.join(", ")} WHERE id = ? RETURNING *`
967
+ ).get(...params);
968
+ return {
969
+ id: row.id,
970
+ title: row.title ?? void 0,
971
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
972
+ createdAt: row.createdAt,
973
+ updatedAt: row.updatedAt
974
+ };
915
975
  }
916
976
  async listChats() {
917
977
  const rows = this.#db.prepare(
@@ -940,25 +1000,30 @@ var SqliteContextStore = class extends ContextStore {
940
1000
  // ==========================================================================
941
1001
  // Message Operations (Graph Nodes)
942
1002
  // ==========================================================================
943
- async addMessage(message) {
1003
+ async addMessage(message2) {
944
1004
  this.#db.prepare(
945
- `INSERT INTO messages (id, chatId, parentId, name, type, data, persist, createdAt)
946
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
1005
+ `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
1006
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1007
+ ON CONFLICT(id) DO UPDATE SET
1008
+ parentId = excluded.parentId,
1009
+ name = excluded.name,
1010
+ type = excluded.type,
1011
+ data = excluded.data`
947
1012
  ).run(
948
- message.id,
949
- message.chatId,
950
- message.parentId,
951
- message.name,
952
- message.type ?? null,
953
- JSON.stringify(message.data),
954
- message.persist ? 1 : 0,
955
- message.createdAt
1013
+ message2.id,
1014
+ message2.chatId,
1015
+ message2.parentId,
1016
+ message2.name,
1017
+ message2.type ?? null,
1018
+ JSON.stringify(message2.data),
1019
+ message2.createdAt
956
1020
  );
957
- const content = typeof message.data === "string" ? message.data : JSON.stringify(message.data);
1021
+ const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
1022
+ this.#db.prepare(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
958
1023
  this.#db.prepare(
959
1024
  `INSERT INTO messages_fts(messageId, chatId, name, content)
960
1025
  VALUES (?, ?, ?, ?)`
961
- ).run(message.id, message.chatId, message.name, content);
1026
+ ).run(message2.id, message2.chatId, message2.name, content);
962
1027
  }
963
1028
  async getMessage(messageId) {
964
1029
  const row = this.#db.prepare("SELECT * FROM messages WHERE id = ?").get(messageId);
@@ -972,7 +1037,6 @@ var SqliteContextStore = class extends ContextStore {
972
1037
  name: row.name,
973
1038
  type: row.type ?? void 0,
974
1039
  data: JSON.parse(row.data),
975
- persist: row.persist === 1,
976
1040
  createdAt: row.createdAt
977
1041
  };
978
1042
  }
@@ -994,7 +1058,6 @@ var SqliteContextStore = class extends ContextStore {
994
1058
  name: row.name,
995
1059
  type: row.type ?? void 0,
996
1060
  data: JSON.parse(row.data),
997
- persist: row.persist === 1,
998
1061
  createdAt: row.createdAt
999
1062
  }));
1000
1063
  }
@@ -1155,7 +1218,6 @@ var SqliteContextStore = class extends ContextStore {
1155
1218
  m.name,
1156
1219
  m.type,
1157
1220
  m.data,
1158
- m.persist,
1159
1221
  m.createdAt,
1160
1222
  fts.rank,
1161
1223
  snippet(messages_fts, 3, '<mark>', '</mark>', '...', 32) as snippet
@@ -1181,7 +1243,6 @@ var SqliteContextStore = class extends ContextStore {
1181
1243
  name: row.name,
1182
1244
  type: row.type ?? void 0,
1183
1245
  data: JSON.parse(row.data),
1184
- persist: row.persist === 1,
1185
1246
  createdAt: row.createdAt
1186
1247
  },
1187
1248
  rank: row.rank,
@@ -1305,7 +1366,7 @@ function visualizeGraph(data) {
1305
1366
  // packages/context/src/index.ts
1306
1367
  var ContextEngine = class {
1307
1368
  /** Non-message fragments (role, hints, etc.) - not persisted in graph */
1308
- #contextFragments = [];
1369
+ #fragments = [];
1309
1370
  /** Pending message fragments to be added to graph */
1310
1371
  #pendingMessages = [];
1311
1372
  #store;
@@ -1329,18 +1390,7 @@ var ContextEngine = class {
1329
1390
  if (this.#initialized) {
1330
1391
  return;
1331
1392
  }
1332
- const existingChat = await this.#store.getChat(this.#chatId);
1333
- if (existingChat) {
1334
- this.#chatData = existingChat;
1335
- } else {
1336
- const now = Date.now();
1337
- this.#chatData = {
1338
- id: this.#chatId,
1339
- createdAt: now,
1340
- updatedAt: now
1341
- };
1342
- await this.#store.createChat(this.#chatData);
1343
- }
1393
+ this.#chatData = await this.#store.upsertChat({ id: this.#chatId });
1344
1394
  const existingBranch = await this.#store.getBranch(
1345
1395
  this.#chatId,
1346
1396
  this.#branchName
@@ -1399,7 +1449,7 @@ var ContextEngine = class {
1399
1449
  if (isMessageFragment(fragment2)) {
1400
1450
  this.#pendingMessages.push(fragment2);
1401
1451
  } else {
1402
- this.#contextFragments.push(fragment2);
1452
+ this.#fragments.push(fragment2);
1403
1453
  }
1404
1454
  }
1405
1455
  return this;
@@ -1409,7 +1459,7 @@ var ContextEngine = class {
1409
1459
  * @internal Use resolve() instead for public API.
1410
1460
  */
1411
1461
  render(renderer) {
1412
- return renderer.render(this.#contextFragments);
1462
+ return renderer.render(this.#fragments);
1413
1463
  }
1414
1464
  /**
1415
1465
  * Resolve context into AI SDK-ready format.
@@ -1428,26 +1478,21 @@ var ContextEngine = class {
1428
1478
  * await generateText({ system: systemPrompt, messages });
1429
1479
  * ```
1430
1480
  */
1431
- async resolve(options = {}) {
1481
+ async resolve(options) {
1432
1482
  await this.#ensureInitialized();
1433
- const renderer = options.renderer ?? new XmlRenderer();
1434
- const systemPrompt = renderer.render(this.#contextFragments);
1435
- const persistedMessages = [];
1483
+ const systemPrompt = options.renderer.render(this.#fragments);
1484
+ const messages = [];
1436
1485
  if (this.#branch?.headMessageId) {
1437
1486
  const chain = await this.#store.getMessageChain(
1438
1487
  this.#branch.headMessageId
1439
1488
  );
1440
- persistedMessages.push(...chain);
1489
+ for (const msg of chain) {
1490
+ messages.push(message(msg.data).codec?.decode());
1491
+ }
1441
1492
  }
1442
- const messages = persistedMessages.map((msg) => ({
1443
- role: msg.name,
1444
- content: String(msg.data)
1445
- }));
1446
1493
  for (const fragment2 of this.#pendingMessages) {
1447
- messages.push({
1448
- role: fragment2.name,
1449
- content: String(fragment2.data)
1450
- });
1494
+ const decoded = fragment2.codec.decode();
1495
+ messages.push(decoded);
1451
1496
  }
1452
1497
  return { systemPrompt, messages };
1453
1498
  }
@@ -1479,8 +1524,7 @@ var ContextEngine = class {
1479
1524
  parentId,
1480
1525
  name: fragment2.name,
1481
1526
  type: fragment2.type,
1482
- data: fragment2.data,
1483
- persist: fragment2.persist ?? true,
1527
+ data: fragment2.codec.encode(),
1484
1528
  createdAt: now
1485
1529
  };
1486
1530
  await this.#store.addMessage(messageData);
@@ -1488,25 +1532,85 @@ var ContextEngine = class {
1488
1532
  }
1489
1533
  await this.#store.updateBranchHead(this.#branch.id, parentId);
1490
1534
  this.#branch.headMessageId = parentId;
1491
- await this.#store.updateChat(this.#chatId, { updatedAt: now });
1492
- if (this.#chatData) {
1493
- this.#chatData.updatedAt = now;
1494
- }
1495
1535
  this.#pendingMessages = [];
1496
1536
  }
1497
1537
  /**
1498
- * Estimate token count and cost for the current context.
1538
+ * Estimate token count and cost for the full context.
1539
+ *
1540
+ * Includes:
1541
+ * - System prompt fragments (role, hints, etc.)
1542
+ * - Persisted chat messages (from store)
1543
+ * - Pending messages (not yet saved)
1499
1544
  *
1500
1545
  * @param modelId - Model ID (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet")
1501
1546
  * @param options - Optional settings
1502
- * @returns Estimate result with token counts and costs
1547
+ * @returns Estimate result with token counts, costs, and per-fragment breakdown
1503
1548
  */
1504
1549
  async estimate(modelId, options = {}) {
1550
+ await this.#ensureInitialized();
1505
1551
  const renderer = options.renderer ?? new XmlRenderer();
1506
- const renderedContext = this.render(renderer);
1507
1552
  const registry = getModelsRegistry();
1508
1553
  await registry.load();
1509
- return registry.estimate(modelId, renderedContext);
1554
+ const model = registry.get(modelId);
1555
+ if (!model) {
1556
+ throw new Error(
1557
+ `Model "${modelId}" not found. Call load() first or check model ID.`
1558
+ );
1559
+ }
1560
+ const tokenizer = registry.getTokenizer(modelId);
1561
+ const fragmentEstimates = [];
1562
+ for (const fragment2 of this.#fragments) {
1563
+ const rendered = renderer.render([fragment2]);
1564
+ const tokens = tokenizer.count(rendered);
1565
+ const cost = tokens / 1e6 * model.cost.input;
1566
+ fragmentEstimates.push({
1567
+ id: fragment2.id,
1568
+ name: fragment2.name,
1569
+ tokens,
1570
+ cost
1571
+ });
1572
+ }
1573
+ if (this.#branch?.headMessageId) {
1574
+ const chain = await this.#store.getMessageChain(
1575
+ this.#branch.headMessageId
1576
+ );
1577
+ for (const msg of chain) {
1578
+ const content = String(msg.data);
1579
+ const tokens = tokenizer.count(content);
1580
+ const cost = tokens / 1e6 * model.cost.input;
1581
+ fragmentEstimates.push({
1582
+ name: msg.name,
1583
+ id: msg.id,
1584
+ tokens,
1585
+ cost
1586
+ });
1587
+ }
1588
+ }
1589
+ for (const fragment2 of this.#pendingMessages) {
1590
+ const content = String(fragment2.data);
1591
+ const tokens = tokenizer.count(content);
1592
+ const cost = tokens / 1e6 * model.cost.input;
1593
+ fragmentEstimates.push({
1594
+ name: fragment2.name,
1595
+ id: fragment2.id,
1596
+ tokens,
1597
+ cost
1598
+ });
1599
+ }
1600
+ const totalTokens = fragmentEstimates.reduce((sum, f) => sum + f.tokens, 0);
1601
+ const totalCost = fragmentEstimates.reduce((sum, f) => sum + f.cost, 0);
1602
+ return {
1603
+ model: model.id,
1604
+ provider: model.provider,
1605
+ tokens: totalTokens,
1606
+ cost: totalCost,
1607
+ limits: {
1608
+ context: model.limit.context,
1609
+ output: model.limit.output,
1610
+ exceedsContext: totalTokens > model.limit.context
1611
+ },
1612
+ fragments: fragmentEstimates
1613
+ };
1510
1614
  }
1511
1615
  /**
1512
1616
  * Rewind to a specific message by ID.
@@ -1533,11 +1637,11 @@ var ContextEngine = class {
1533
1637
  */
1534
1638
  async rewind(messageId) {
1535
1639
  await this.#ensureInitialized();
1536
- const message = await this.#store.getMessage(messageId);
1537
- if (!message) {
1640
+ const message2 = await this.#store.getMessage(messageId);
1641
+ if (!message2) {
1538
1642
  throw new Error(`Message "${messageId}" not found`);
1539
1643
  }
1540
- if (message.chatId !== this.#chatId) {
1644
+ if (message2.chatId !== this.#chatId) {
1541
1645
  throw new Error(`Message "${messageId}" belongs to a different chat`);
1542
1646
  }
1543
1647
  const branches = await this.#store.listBranches(this.#chatId);
@@ -1671,10 +1775,7 @@ var ContextEngine = class {
1671
1775
  */
1672
1776
  async updateChat(updates) {
1673
1777
  await this.#ensureInitialized();
1674
- const now = Date.now();
1675
- const storeUpdates = {
1676
- updatedAt: now
1677
- };
1778
+ const storeUpdates = {};
1678
1779
  if (updates.title !== void 0) {
1679
1780
  storeUpdates.title = updates.title;
1680
1781
  }
@@ -1684,16 +1785,7 @@ var ContextEngine = class {
1684
1785
  ...updates.metadata
1685
1786
  };
1686
1787
  }
1687
- await this.#store.updateChat(this.#chatId, storeUpdates);
1688
- if (this.#chatData) {
1689
- if (storeUpdates.title !== void 0) {
1690
- this.#chatData.title = storeUpdates.title;
1691
- }
1692
- if (storeUpdates.metadata !== void 0) {
1693
- this.#chatData.metadata = storeUpdates.metadata;
1694
- }
1695
- this.#chatData.updatedAt = now;
1696
- }
1788
+ this.#chatData = await this.#store.updateChat(this.#chatId, storeUpdates);
1697
1789
  }
1698
1790
  /**
1699
1791
  * Consolidate context fragments (no-op for now).
@@ -1706,6 +1798,54 @@ var ContextEngine = class {
1706
1798
  consolidate() {
1707
1799
  return void 0;
1708
1800
  }
1801
+ /**
1802
+ * Inspect the full context state for debugging.
1803
+ * Returns a comprehensive JSON-serializable object with all context information.
1804
+ *
1805
+ * @param options - Inspection options (modelId and renderer required)
1806
+ * @returns Complete inspection data including estimates, rendered output, fragments, and graph
1807
+ *
1808
+ * @example
1809
+ * ```ts
1810
+ * const inspection = await context.inspect({
1811
+ * modelId: 'openai:gpt-4o',
1812
+ * renderer: new XmlRenderer(),
1813
+ * });
1814
+ * console.log(JSON.stringify(inspection, null, 2));
1815
+ *
1816
+ * // Or write to file for analysis
1817
+ * await fs.writeFile('context-debug.json', JSON.stringify(inspection, null, 2));
1818
+ * ```
1819
+ */
1820
+ async inspect(options) {
1821
+ await this.#ensureInitialized();
1822
+ const { renderer } = options;
1823
+ const estimateResult = await this.estimate(options.modelId, { renderer });
1824
+ const rendered = renderer.render(this.#fragments);
1825
+ const persistedMessages = [];
1826
+ if (this.#branch?.headMessageId) {
1827
+ const chain = await this.#store.getMessageChain(
1828
+ this.#branch.headMessageId
1829
+ );
1830
+ persistedMessages.push(...chain);
1831
+ }
1832
+ const graph = await this.#store.getGraph(this.#chatId);
1833
+ return {
1834
+ estimate: estimateResult,
1835
+ rendered,
1836
+ fragments: {
1837
+ context: [...this.#fragments],
1838
+ pending: [...this.#pendingMessages],
1839
+ persisted: persistedMessages
1840
+ },
1841
+ graph,
1842
+ meta: {
1843
+ chatId: this.#chatId,
1844
+ branch: this.#branchName,
1845
+ timestamp: Date.now()
1846
+ }
1847
+ };
1848
+ }
1709
1849
  };
1710
1850
  function hint(text) {
1711
1851
  return {
@@ -1725,24 +1865,75 @@ function role(content) {
1725
1865
  data: content
1726
1866
  };
1727
1867
  }
1728
- function user(content, options) {
1868
+ function user(content) {
1869
+ const message2 = typeof content === "string" ? {
1870
+ id: generateId(),
1871
+ role: "user",
1872
+ parts: [{ type: "text", text: content }]
1873
+ } : content;
1729
1874
  return {
1730
- id: options?.id ?? crypto.randomUUID(),
1875
+ id: message2.id,
1731
1876
  name: "user",
1732
- data: content,
1877
+ data: "content",
1733
1878
  type: "message",
1734
- persist: true
1879
+ persist: true,
1880
+ codec: {
1881
+ decode() {
1882
+ return message2;
1883
+ },
1884
+ encode() {
1885
+ return message2;
1886
+ }
1887
+ }
1735
1888
  };
1736
1889
  }
1737
- function assistant(content, options) {
1890
+ function assistant(message2) {
1738
1891
  return {
1739
- id: options?.id ?? crypto.randomUUID(),
1892
+ id: message2.id,
1740
1893
  name: "assistant",
1741
- data: content,
1894
+ data: "content",
1895
+ type: "message",
1896
+ persist: true,
1897
+ codec: {
1898
+ decode() {
1899
+ return message2;
1900
+ },
1901
+ encode() {
1902
+ return message2;
1903
+ }
1904
+ }
1905
+ };
1906
+ }
1907
+ function message(content) {
1908
+ const message2 = typeof content === "string" ? {
1909
+ id: generateId(),
1910
+ role: "user",
1911
+ parts: [{ type: "text", text: content }]
1912
+ } : content;
1913
+ return {
1914
+ id: message2.id,
1915
+ name: "message",
1916
+ data: "content",
1742
1917
  type: "message",
1743
- persist: true
1918
+ persist: true,
1919
+ codec: {
1920
+ decode() {
1921
+ return message2;
1922
+ },
1923
+ encode() {
1924
+ return message2;
1925
+ }
1926
+ }
1744
1927
  };
1745
1928
  }
1929
+ function assistantText(content, options) {
1930
+ const id = options?.id ?? crypto.randomUUID();
1931
+ return assistant({
1932
+ id,
1933
+ role: "assistant",
1934
+ parts: [{ type: "text", text: content }]
1935
+ });
1936
+ }
1746
1937
  export {
1747
1938
  ContextEngine,
1748
1939
  ContextStore,
@@ -1754,11 +1945,14 @@ export {
1754
1945
  ToonRenderer,
1755
1946
  XmlRenderer,
1756
1947
  assistant,
1948
+ assistantText,
1757
1949
  defaultTokenizer,
1950
+ estimate,
1758
1951
  fragment,
1759
1952
  getModelsRegistry,
1760
1953
  hint,
1761
1954
  isMessageFragment,
1955
+ message,
1762
1956
  role,
1763
1957
  user,
1764
1958
  visualizeGraph