@deepagents/context 0.11.0 → 0.12.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
@@ -175,12 +175,6 @@ function fragment(name, ...children) {
175
175
  data: children
176
176
  };
177
177
  }
178
- function role(content) {
179
- return {
180
- name: "role",
181
- data: content
182
- };
183
- }
184
178
  function user(content) {
185
179
  const message2 = typeof content === "string" ? {
186
180
  id: generateId(),
@@ -277,6 +271,68 @@ var ContextRenderer = class {
277
271
  }
278
272
  return groups;
279
273
  }
274
+ /**
275
+ * Remove null/undefined from fragments and fragment data recursively.
276
+ * This protects renderers from nullish values and ensures they are ignored
277
+ * consistently across all output formats.
278
+ */
279
+ sanitizeFragments(fragments) {
280
+ const sanitized = [];
281
+ for (const fragment2 of fragments) {
282
+ const cleaned = this.sanitizeFragment(fragment2, /* @__PURE__ */ new WeakSet());
283
+ if (cleaned) {
284
+ sanitized.push(cleaned);
285
+ }
286
+ }
287
+ return sanitized;
288
+ }
289
+ sanitizeFragment(fragment2, seen) {
290
+ const data = this.sanitizeData(fragment2.data, seen);
291
+ if (data == null) {
292
+ return null;
293
+ }
294
+ return {
295
+ ...fragment2,
296
+ data
297
+ };
298
+ }
299
+ sanitizeData(data, seen) {
300
+ if (data == null) {
301
+ return void 0;
302
+ }
303
+ if (isFragment(data)) {
304
+ return this.sanitizeFragment(data, seen) ?? void 0;
305
+ }
306
+ if (Array.isArray(data)) {
307
+ if (seen.has(data)) {
308
+ return void 0;
309
+ }
310
+ seen.add(data);
311
+ const cleaned = [];
312
+ for (const item of data) {
313
+ const sanitizedItem = this.sanitizeData(item, seen);
314
+ if (sanitizedItem != null) {
315
+ cleaned.push(sanitizedItem);
316
+ }
317
+ }
318
+ return cleaned;
319
+ }
320
+ if (isFragmentObject(data)) {
321
+ if (seen.has(data)) {
322
+ return void 0;
323
+ }
324
+ seen.add(data);
325
+ const cleaned = {};
326
+ for (const [key, value] of Object.entries(data)) {
327
+ const sanitizedValue = this.sanitizeData(value, seen);
328
+ if (sanitizedValue != null) {
329
+ cleaned[key] = sanitizedValue;
330
+ }
331
+ }
332
+ return cleaned;
333
+ }
334
+ return data;
335
+ }
280
336
  /**
281
337
  * Template method - dispatches value to appropriate handler.
282
338
  */
@@ -304,7 +360,8 @@ var ContextRenderer = class {
304
360
  };
305
361
  var XmlRenderer = class extends ContextRenderer {
306
362
  render(fragments) {
307
- return fragments.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
363
+ const sanitized = this.sanitizeFragments(fragments);
364
+ return sanitized.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
308
365
  }
309
366
  #renderTopLevel(fragment2) {
310
367
  if (this.isPrimitive(fragment2.data)) {
@@ -317,10 +374,13 @@ var XmlRenderer = class extends ContextRenderer {
317
374
  const child = this.renderFragment(fragment2.data, { depth: 1, path: [] });
318
375
  return this.#wrap(fragment2.name, [child]);
319
376
  }
320
- return this.#wrap(
321
- fragment2.name,
322
- this.renderEntries(fragment2.data, { depth: 1, path: [] })
323
- );
377
+ if (isFragmentObject(fragment2.data)) {
378
+ return this.#wrap(
379
+ fragment2.name,
380
+ this.renderEntries(fragment2.data, { depth: 1, path: [] })
381
+ );
382
+ }
383
+ return "";
324
384
  }
325
385
  #renderArray(name, items, depth) {
326
386
  const fragmentItems = items.filter(isFragment);
@@ -328,9 +388,19 @@ var XmlRenderer = class extends ContextRenderer {
328
388
  const children = [];
329
389
  for (const item of nonFragmentItems) {
330
390
  if (item != null) {
331
- children.push(
332
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
333
- );
391
+ if (isFragmentObject(item)) {
392
+ children.push(
393
+ this.#wrapIndented(
394
+ pluralize.singular(name),
395
+ this.renderEntries(item, { depth: depth + 2, path: [] }),
396
+ depth + 1
397
+ )
398
+ );
399
+ } else {
400
+ children.push(
401
+ this.#leaf(pluralize.singular(name), String(item), depth + 1)
402
+ );
403
+ }
334
404
  }
335
405
  }
336
406
  if (this.options.groupFragments && fragmentItems.length > 0) {
@@ -372,8 +442,14 @@ ${this.#indent(safe, 2)}
372
442
  if (Array.isArray(data)) {
373
443
  return this.#renderArrayIndented(name, data, ctx.depth);
374
444
  }
375
- const children = this.renderEntries(data, { ...ctx, depth: ctx.depth + 1 });
376
- return this.#wrapIndented(name, children, ctx.depth);
445
+ if (isFragmentObject(data)) {
446
+ const children = this.renderEntries(data, {
447
+ ...ctx,
448
+ depth: ctx.depth + 1
449
+ });
450
+ return this.#wrapIndented(name, children, ctx.depth);
451
+ }
452
+ return "";
377
453
  }
378
454
  #renderArrayIndented(name, items, depth) {
379
455
  const fragmentItems = items.filter(isFragment);
@@ -381,9 +457,19 @@ ${this.#indent(safe, 2)}
381
457
  const children = [];
382
458
  for (const item of nonFragmentItems) {
383
459
  if (item != null) {
384
- children.push(
385
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
386
- );
460
+ if (isFragmentObject(item)) {
461
+ children.push(
462
+ this.#wrapIndented(
463
+ pluralize.singular(name),
464
+ this.renderEntries(item, { depth: depth + 2, path: [] }),
465
+ depth + 1
466
+ )
467
+ );
468
+ } else {
469
+ children.push(
470
+ this.#leaf(pluralize.singular(name), String(item), depth + 1)
471
+ );
472
+ }
387
473
  }
388
474
  }
389
475
  if (this.options.groupFragments && fragmentItems.length > 0) {
@@ -412,7 +498,19 @@ ${this.#indent(safe, 2)}
412
498
  return "";
413
499
  }
414
500
  const itemTag = pluralize.singular(key);
415
- const children = items.filter((item) => item != null).map((item) => this.#leaf(itemTag, String(item), ctx.depth + 1));
501
+ const children = items.filter((item) => item != null).map((item) => {
502
+ if (isFragment(item)) {
503
+ return this.renderFragment(item, { ...ctx, depth: ctx.depth + 1 });
504
+ }
505
+ if (isFragmentObject(item)) {
506
+ return this.#wrapIndented(
507
+ itemTag,
508
+ this.renderEntries(item, { ...ctx, depth: ctx.depth + 2 }),
509
+ ctx.depth + 1
510
+ );
511
+ }
512
+ return this.#leaf(itemTag, String(item), ctx.depth + 1);
513
+ });
416
514
  return this.#wrapIndented(key, children, ctx.depth);
417
515
  }
418
516
  renderObject(key, obj, ctx) {
@@ -464,7 +562,7 @@ ${pad}</${tag}>`;
464
562
  };
465
563
  var MarkdownRenderer = class extends ContextRenderer {
466
564
  render(fragments) {
467
- return fragments.map((f) => {
565
+ return this.sanitizeFragments(fragments).map((f) => {
468
566
  const title = `## ${titlecase(f.name)}`;
469
567
  if (this.isPrimitive(f.data)) {
470
568
  return `${title}
@@ -478,8 +576,12 @@ ${this.#renderArray(f.data, 0)}`;
478
576
  return `${title}
479
577
  ${this.renderFragment(f.data, { depth: 0, path: [] })}`;
480
578
  }
481
- return `${title}
579
+ if (isFragmentObject(f.data)) {
580
+ return `${title}
482
581
  ${this.renderEntries(f.data, { depth: 0, path: [] }).join("\n")}`;
582
+ }
583
+ return `${title}
584
+ `;
483
585
  }).join("\n\n");
484
586
  }
485
587
  #renderArray(items, depth) {
@@ -536,14 +638,17 @@ ${this.renderEntries(f.data, { depth: 0, path: [] }).join("\n")}`;
536
638
  return [header, child].join("\n");
537
639
  }
538
640
  if (Array.isArray(data)) {
539
- const children2 = data.filter((item) => item != null).map((item) => this.#arrayItem(item, ctx.depth + 1));
540
- return [header, ...children2].join("\n");
641
+ const children = data.filter((item) => item != null).map((item) => this.#arrayItem(item, ctx.depth + 1));
642
+ return [header, ...children].join("\n");
541
643
  }
542
- const children = this.renderEntries(data, {
543
- ...ctx,
544
- depth: ctx.depth + 1
545
- }).join("\n");
546
- return [header, children].join("\n");
644
+ if (isFragmentObject(data)) {
645
+ const children = this.renderEntries(data, {
646
+ ...ctx,
647
+ depth: ctx.depth + 1
648
+ }).join("\n");
649
+ return [header, children].join("\n");
650
+ }
651
+ return header;
547
652
  }
548
653
  renderPrimitive(key, value, ctx) {
549
654
  return this.#leaf(key, value, ctx.depth);
@@ -564,22 +669,25 @@ ${this.renderEntries(f.data, { depth: 0, path: [] }).join("\n")}`;
564
669
  };
565
670
  var TomlRenderer = class extends ContextRenderer {
566
671
  render(fragments) {
567
- return fragments.map((f) => {
672
+ const rendered = [];
673
+ for (const f of this.sanitizeFragments(fragments)) {
568
674
  if (this.isPrimitive(f.data)) {
569
- return `${f.name} = ${this.#formatValue(f.data)}`;
570
- }
571
- if (Array.isArray(f.data)) {
572
- return this.#renderTopLevelArray(f.name, f.data);
573
- }
574
- if (isFragment(f.data)) {
575
- return [
576
- `[${f.name}]`,
577
- this.renderFragment(f.data, { depth: 0, path: [f.name] })
578
- ].join("\n");
675
+ rendered.push(`${f.name} = ${this.#formatValue(f.data)}`);
676
+ } else if (Array.isArray(f.data)) {
677
+ rendered.push(this.#renderTopLevelArray(f.name, f.data));
678
+ } else if (isFragment(f.data)) {
679
+ rendered.push(
680
+ [
681
+ `[${f.name}]`,
682
+ this.renderFragment(f.data, { depth: 0, path: [f.name] })
683
+ ].join("\n")
684
+ );
685
+ } else if (isFragmentObject(f.data)) {
686
+ const entries = this.#renderObjectEntries(f.data, [f.name]);
687
+ rendered.push([`[${f.name}]`, ...entries].join("\n"));
579
688
  }
580
- const entries = this.#renderObjectEntries(f.data, [f.name]);
581
- return [`[${f.name}]`, ...entries].join("\n");
582
- }).join("\n\n");
689
+ }
690
+ return rendered.join("\n\n");
583
691
  }
584
692
  #renderTopLevelArray(name, items) {
585
693
  const fragmentItems = items.filter(isFragment);
@@ -614,10 +722,12 @@ var TomlRenderer = class extends ContextRenderer {
614
722
  }
615
723
  return `${key} = ${this.#formatValue(value)}`;
616
724
  }
617
- renderPrimitive(key, value, _ctx) {
725
+ renderPrimitive(key, value, ctx) {
726
+ void ctx;
618
727
  return `${key} = ${this.#formatValue(value)}`;
619
728
  }
620
- renderArray(key, items, _ctx) {
729
+ renderArray(key, items, ctx) {
730
+ void ctx;
621
731
  const values = items.filter((item) => item != null).map((item) => this.#formatValue(item));
622
732
  return `${key} = [${values.join(", ")}]`;
623
733
  }
@@ -671,8 +781,11 @@ var TomlRenderer = class extends ContextRenderer {
671
781
  const values = nonFragmentItems.map((item) => this.#formatValue(item));
672
782
  return `${name} = [${values.join(", ")}]`;
673
783
  }
674
- const entries = this.#renderObjectEntries(data, newPath);
675
- return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
784
+ if (isFragmentObject(data)) {
785
+ const entries = this.#renderObjectEntries(data, newPath);
786
+ return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
787
+ }
788
+ return "";
676
789
  }
677
790
  #escape(value) {
678
791
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
@@ -692,7 +805,8 @@ var TomlRenderer = class extends ContextRenderer {
692
805
  };
693
806
  var ToonRenderer = class extends ContextRenderer {
694
807
  render(fragments) {
695
- return fragments.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
808
+ const sanitized = this.sanitizeFragments(fragments);
809
+ return sanitized.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
696
810
  }
697
811
  #renderTopLevel(fragment2) {
698
812
  const { name, data } = fragment2;
@@ -741,18 +855,18 @@ ${entries}`;
741
855
  if (items.length === 0) return false;
742
856
  const objects = items.filter(isFragmentObject);
743
857
  if (objects.length !== items.length) return false;
744
- const firstKeys = Object.keys(objects[0]).sort().join(",");
858
+ let intersection = new Set(Object.keys(objects[0]));
745
859
  for (const obj of objects) {
746
- if (Object.keys(obj).sort().join(",") !== firstKeys) {
747
- return false;
748
- }
860
+ const keys = new Set(Object.keys(obj));
861
+ intersection = new Set([...intersection].filter((k) => keys.has(k)));
749
862
  for (const value of Object.values(obj)) {
750
- if (!this.#isPrimitiveValue(value) && value !== null) {
863
+ if (value == null) continue;
864
+ if (!this.#isPrimitiveValue(value)) {
751
865
  return false;
752
866
  }
753
867
  }
754
868
  }
755
- return true;
869
+ return intersection.size > 0;
756
870
  }
757
871
  #renderPrimitiveArray(key, items, depth) {
758
872
  const values = items.map((item) => this.#formatValue(item)).join(",");
@@ -762,10 +876,16 @@ ${entries}`;
762
876
  if (items.length === 0) {
763
877
  return `${this.#pad(depth)}${key}[0]:`;
764
878
  }
765
- const fields = Object.keys(items[0]);
879
+ const fields = Array.from(
880
+ new Set(items.flatMap((obj) => Object.keys(obj)))
881
+ );
766
882
  const header = `${this.#pad(depth)}${key}[${items.length}]{${fields.join(",")}}:`;
767
883
  const rows = items.map((obj) => {
768
- const values = fields.map((f) => this.#formatValue(obj[f]));
884
+ const values = fields.map((f) => {
885
+ const value = obj[f];
886
+ if (value == null) return "";
887
+ return this.#formatValue(value);
888
+ });
769
889
  return `${this.#pad(depth + 1)}${values.join(",")}`;
770
890
  });
771
891
  return [header, ...rows].join("\n");
@@ -913,6 +1033,7 @@ var ContextEngine = class {
913
1033
  #pendingMessages = [];
914
1034
  #store;
915
1035
  #chatId;
1036
+ #userId;
916
1037
  #branchName;
917
1038
  #branch = null;
918
1039
  #chatData = null;
@@ -921,9 +1042,13 @@ var ContextEngine = class {
921
1042
  if (!options.chatId) {
922
1043
  throw new Error("chatId is required");
923
1044
  }
1045
+ if (!options.userId) {
1046
+ throw new Error("userId is required");
1047
+ }
924
1048
  this.#store = options.store;
925
1049
  this.#chatId = options.chatId;
926
- this.#branchName = options.branch ?? "main";
1050
+ this.#userId = options.userId;
1051
+ this.#branchName = "main";
927
1052
  }
928
1053
  /**
929
1054
  * Initialize the chat and branch if they don't exist.
@@ -932,24 +1057,11 @@ var ContextEngine = class {
932
1057
  if (this.#initialized) {
933
1058
  return;
934
1059
  }
935
- this.#chatData = await this.#store.upsertChat({ id: this.#chatId });
936
- const existingBranch = await this.#store.getBranch(
937
- this.#chatId,
938
- this.#branchName
939
- );
940
- if (existingBranch) {
941
- this.#branch = existingBranch;
942
- } else {
943
- this.#branch = {
944
- id: crypto.randomUUID(),
945
- chatId: this.#chatId,
946
- name: this.#branchName,
947
- headMessageId: null,
948
- isActive: true,
949
- createdAt: Date.now()
950
- };
951
- await this.#store.createBranch(this.#branch);
952
- }
1060
+ this.#chatData = await this.#store.upsertChat({
1061
+ id: this.#chatId,
1062
+ userId: this.#userId
1063
+ });
1064
+ this.#branch = await this.#store.getActiveBranch(this.#chatId);
953
1065
  this.#initialized = true;
954
1066
  }
955
1067
  /**
@@ -1009,6 +1121,7 @@ var ContextEngine = class {
1009
1121
  }
1010
1122
  return {
1011
1123
  id: this.#chatData.id,
1124
+ userId: this.#chatData.userId,
1012
1125
  createdAt: this.#chatData.createdAt,
1013
1126
  updatedAt: this.#chatData.updatedAt,
1014
1127
  title: this.#chatData.title,
@@ -1051,7 +1164,7 @@ var ContextEngine = class {
1051
1164
  *
1052
1165
  * @example
1053
1166
  * ```ts
1054
- * const context = new ContextEngine({ store, chatId: 'chat-1' })
1167
+ * const context = new ContextEngine({ store, chatId: 'chat-1', userId: 'user-1' })
1055
1168
  * .set(role('You are helpful'), user('Hello'));
1056
1169
  *
1057
1170
  * const { systemPrompt, messages } = await context.resolve();
@@ -1395,7 +1508,7 @@ var ContextEngine = class {
1395
1508
  }
1396
1509
  /**
1397
1510
  * Inspect the full context state for debugging.
1398
- * Returns a comprehensive JSON-serializable object with all context information.
1511
+ * Returns a JSON-serializable object with context information.
1399
1512
  *
1400
1513
  * @param options - Inspection options (modelId and renderer required)
1401
1514
  * @returns Complete inspection data including estimates, rendered output, fragments, and graph
@@ -1547,6 +1660,33 @@ function glossary(entries) {
1547
1660
  }))
1548
1661
  };
1549
1662
  }
1663
+ function role(content) {
1664
+ return {
1665
+ name: "role",
1666
+ data: content
1667
+ };
1668
+ }
1669
+ function principle(input) {
1670
+ return {
1671
+ name: "principle",
1672
+ data: {
1673
+ title: input.title,
1674
+ description: input.description,
1675
+ ...input.policies?.length && { policies: input.policies }
1676
+ }
1677
+ };
1678
+ }
1679
+ function policy(input) {
1680
+ return {
1681
+ name: "policy",
1682
+ data: {
1683
+ rule: input.rule,
1684
+ ...input.before && { before: input.before },
1685
+ ...input.reason && { reason: input.reason },
1686
+ ...input.policies?.length && { policies: input.policies }
1687
+ }
1688
+ };
1689
+ }
1550
1690
 
1551
1691
  // packages/context/src/lib/fragments/user.ts
1552
1692
  function identity(input) {
@@ -1563,7 +1703,8 @@ function persona(input) {
1563
1703
  name: "persona",
1564
1704
  data: {
1565
1705
  name: input.name,
1566
- role: input.role,
1706
+ ...input.role && { role: input.role },
1707
+ ...input.objective && { objective: input.objective },
1567
1708
  ...input.tone && { tone: input.tone }
1568
1709
  }
1569
1710
  };
@@ -1612,11 +1753,79 @@ function runGuardrailChain(part, guardrails, context) {
1612
1753
  return pass(currentPart);
1613
1754
  }
1614
1755
 
1756
+ // packages/context/src/lib/guardrails/error-recovery.guardrail.ts
1757
+ import chalk from "chalk";
1758
+ var errorRecoveryGuardrail = {
1759
+ id: "error-recovery",
1760
+ name: "API Error Recovery",
1761
+ handle: (part, context) => {
1762
+ if (part.type !== "error") {
1763
+ return pass(part);
1764
+ }
1765
+ const errorText = part.errorText || "";
1766
+ const prefix = chalk.bold.magenta("[ErrorRecovery]");
1767
+ console.log(
1768
+ `${prefix} ${chalk.red("Caught error:")} ${chalk.dim(errorText.slice(0, 150))}`
1769
+ );
1770
+ const logAndFail = (pattern, feedback) => {
1771
+ console.log(
1772
+ `${prefix} ${chalk.yellow("Pattern:")} ${chalk.cyan(pattern)}`
1773
+ );
1774
+ console.log(
1775
+ `${prefix} ${chalk.green("Feedback:")} ${chalk.dim(feedback.slice(0, 80))}...`
1776
+ );
1777
+ return fail(feedback);
1778
+ };
1779
+ if (errorText.includes("Tool choice is none")) {
1780
+ if (context.availableTools.length > 0) {
1781
+ return logAndFail(
1782
+ "Tool choice is none",
1783
+ `I tried to call a tool that doesn't exist. Available tools: ${context.availableTools.join(", ")}. Let me use one of these instead.`
1784
+ );
1785
+ }
1786
+ return logAndFail(
1787
+ "Tool choice is none (no tools)",
1788
+ "I tried to call a tool, but no tools are available. Let me respond with plain text instead."
1789
+ );
1790
+ }
1791
+ if (errorText.includes("not in request.tools") || errorText.includes("tool") && errorText.includes("not found")) {
1792
+ const toolMatch = errorText.match(/tool '([^']+)'/);
1793
+ const toolName = toolMatch ? toolMatch[1] : "unknown";
1794
+ if (context.availableTools.length > 0) {
1795
+ return logAndFail(
1796
+ `Unregistered tool: ${toolName}`,
1797
+ `I tried to call "${toolName}" but it doesn't exist. Available tools: ${context.availableTools.join(", ")}. Let me use one of these instead.`
1798
+ );
1799
+ }
1800
+ return logAndFail(
1801
+ `Unregistered tool: ${toolName} (no tools)`,
1802
+ `I tried to call "${toolName}" but no tools are available. Let me respond with plain text instead.`
1803
+ );
1804
+ }
1805
+ if (errorText.includes("Failed to parse tool call arguments") || errorText.includes("parse tool call") || errorText.includes("invalid JSON")) {
1806
+ return logAndFail(
1807
+ "Malformed JSON arguments",
1808
+ "I generated malformed JSON for the tool arguments. Let me format my tool call properly with valid JSON."
1809
+ );
1810
+ }
1811
+ if (errorText.includes("Parsing failed")) {
1812
+ return logAndFail(
1813
+ "Parsing failed",
1814
+ "My response format was invalid. Let me try again with a properly formatted response."
1815
+ );
1816
+ }
1817
+ return logAndFail(
1818
+ "Unknown error",
1819
+ `An error occurred: ${errorText.slice(0, 100)}. Let me try a different approach.`
1820
+ );
1821
+ }
1822
+ };
1823
+
1615
1824
  // packages/context/src/lib/sandbox/binary-bridges.ts
1825
+ import { existsSync } from "fs";
1616
1826
  import { defineCommand } from "just-bash";
1617
1827
  import spawn from "nano-spawn";
1618
1828
  import * as path from "path";
1619
- import { existsSync } from "fs";
1620
1829
  function createBinaryBridges(...binaries) {
1621
1830
  return binaries.map((input) => {
1622
1831
  const config = typeof input === "string" ? { name: input } : input;
@@ -1662,6 +1871,17 @@ function createBinaryBridges(...binaries) {
1662
1871
  exitCode: 0
1663
1872
  };
1664
1873
  } catch (error) {
1874
+ if (error && typeof error === "object") {
1875
+ const err = error;
1876
+ const cause = err.cause;
1877
+ if (cause?.code === "ENOENT") {
1878
+ return {
1879
+ stdout: "",
1880
+ stderr: `${name}: ${binaryPath} not found`,
1881
+ exitCode: 127
1882
+ };
1883
+ }
1884
+ }
1665
1885
  if (error && typeof error === "object" && "exitCode" in error) {
1666
1886
  const subprocessError = error;
1667
1887
  return {
@@ -2379,7 +2599,9 @@ function parseFrontmatter(content) {
2379
2599
  throw new Error('Invalid SKILL.md: frontmatter must have a "name" field');
2380
2600
  }
2381
2601
  if (!frontmatter.description || typeof frontmatter.description !== "string") {
2382
- throw new Error('Invalid SKILL.md: frontmatter must have a "description" field');
2602
+ throw new Error(
2603
+ 'Invalid SKILL.md: frontmatter must have a "description" field'
2604
+ );
2383
2605
  }
2384
2606
  return {
2385
2607
  frontmatter,
@@ -2472,6 +2694,7 @@ var STORE_DDL = `
2472
2694
  -- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
2473
2695
  CREATE TABLE IF NOT EXISTS chats (
2474
2696
  id TEXT PRIMARY KEY,
2697
+ userId TEXT NOT NULL,
2475
2698
  title TEXT,
2476
2699
  metadata TEXT,
2477
2700
  createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
@@ -2479,6 +2702,7 @@ CREATE TABLE IF NOT EXISTS chats (
2479
2702
  );
2480
2703
 
2481
2704
  CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
2705
+ CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
2482
2706
 
2483
2707
  -- Messages table (nodes in the DAG)
2484
2708
  CREATE TABLE IF NOT EXISTS messages (
@@ -2544,37 +2768,67 @@ var SqliteContextStore = class extends ContextStore {
2544
2768
  this.#db.exec("PRAGMA foreign_keys = ON");
2545
2769
  this.#db.exec(STORE_DDL);
2546
2770
  }
2771
+ /**
2772
+ * Execute a function within a transaction.
2773
+ * Automatically commits on success or rolls back on error.
2774
+ */
2775
+ #useTransaction(fn) {
2776
+ this.#db.exec("BEGIN TRANSACTION");
2777
+ try {
2778
+ const result = fn();
2779
+ this.#db.exec("COMMIT");
2780
+ return result;
2781
+ } catch (error) {
2782
+ this.#db.exec("ROLLBACK");
2783
+ throw error;
2784
+ }
2785
+ }
2547
2786
  // ==========================================================================
2548
2787
  // Chat Operations
2549
2788
  // ==========================================================================
2550
2789
  async createChat(chat) {
2551
- this.#db.prepare(
2552
- `INSERT INTO chats (id, title, metadata)
2553
- VALUES (?, ?, ?)`
2554
- ).run(
2555
- chat.id,
2556
- chat.title ?? null,
2557
- chat.metadata ? JSON.stringify(chat.metadata) : null
2558
- );
2790
+ this.#useTransaction(() => {
2791
+ this.#db.prepare(
2792
+ `INSERT INTO chats (id, userId, title, metadata)
2793
+ VALUES (?, ?, ?, ?)`
2794
+ ).run(
2795
+ chat.id,
2796
+ chat.userId,
2797
+ chat.title ?? null,
2798
+ chat.metadata ? JSON.stringify(chat.metadata) : null
2799
+ );
2800
+ this.#db.prepare(
2801
+ `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
2802
+ VALUES (?, ?, 'main', NULL, 1, ?)`
2803
+ ).run(crypto.randomUUID(), chat.id, Date.now());
2804
+ });
2559
2805
  }
2560
2806
  async upsertChat(chat) {
2561
- const row = this.#db.prepare(
2562
- `INSERT INTO chats (id, title, metadata)
2563
- VALUES (?, ?, ?)
2564
- ON CONFLICT(id) DO UPDATE SET id = excluded.id
2565
- RETURNING *`
2566
- ).get(
2567
- chat.id,
2568
- chat.title ?? null,
2569
- chat.metadata ? JSON.stringify(chat.metadata) : null
2570
- );
2571
- return {
2572
- id: row.id,
2573
- title: row.title ?? void 0,
2574
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2575
- createdAt: row.createdAt,
2576
- updatedAt: row.updatedAt
2577
- };
2807
+ return this.#useTransaction(() => {
2808
+ const row = this.#db.prepare(
2809
+ `INSERT INTO chats (id, userId, title, metadata)
2810
+ VALUES (?, ?, ?, ?)
2811
+ ON CONFLICT(id) DO UPDATE SET id = excluded.id
2812
+ RETURNING *`
2813
+ ).get(
2814
+ chat.id,
2815
+ chat.userId,
2816
+ chat.title ?? null,
2817
+ chat.metadata ? JSON.stringify(chat.metadata) : null
2818
+ );
2819
+ this.#db.prepare(
2820
+ `INSERT OR IGNORE INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
2821
+ VALUES (?, ?, 'main', NULL, 1, ?)`
2822
+ ).run(crypto.randomUUID(), chat.id, Date.now());
2823
+ return {
2824
+ id: row.id,
2825
+ userId: row.userId,
2826
+ title: row.title ?? void 0,
2827
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2828
+ createdAt: row.createdAt,
2829
+ updatedAt: row.updatedAt
2830
+ };
2831
+ });
2578
2832
  }
2579
2833
  async getChat(chatId) {
2580
2834
  const row = this.#db.prepare("SELECT * FROM chats WHERE id = ?").get(chatId);
@@ -2583,6 +2837,7 @@ var SqliteContextStore = class extends ContextStore {
2583
2837
  }
2584
2838
  return {
2585
2839
  id: row.id,
2840
+ userId: row.userId,
2586
2841
  title: row.title ?? void 0,
2587
2842
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2588
2843
  createdAt: row.createdAt,
@@ -2606,16 +2861,33 @@ var SqliteContextStore = class extends ContextStore {
2606
2861
  ).get(...params);
2607
2862
  return {
2608
2863
  id: row.id,
2864
+ userId: row.userId,
2609
2865
  title: row.title ?? void 0,
2610
2866
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2611
2867
  createdAt: row.createdAt,
2612
2868
  updatedAt: row.updatedAt
2613
2869
  };
2614
2870
  }
2615
- async listChats() {
2871
+ async listChats(options) {
2872
+ const params = [];
2873
+ let whereClause = "";
2874
+ let limitClause = "";
2875
+ if (options?.userId) {
2876
+ whereClause = "WHERE c.userId = ?";
2877
+ params.push(options.userId);
2878
+ }
2879
+ if (options?.limit !== void 0) {
2880
+ limitClause = " LIMIT ?";
2881
+ params.push(options.limit);
2882
+ if (options.offset !== void 0) {
2883
+ limitClause += " OFFSET ?";
2884
+ params.push(options.offset);
2885
+ }
2886
+ }
2616
2887
  const rows = this.#db.prepare(
2617
2888
  `SELECT
2618
2889
  c.id,
2890
+ c.userId,
2619
2891
  c.title,
2620
2892
  c.createdAt,
2621
2893
  c.updatedAt,
@@ -2624,11 +2896,13 @@ var SqliteContextStore = class extends ContextStore {
2624
2896
  FROM chats c
2625
2897
  LEFT JOIN messages m ON m.chatId = c.id
2626
2898
  LEFT JOIN branches b ON b.chatId = c.id
2899
+ ${whereClause}
2627
2900
  GROUP BY c.id
2628
- ORDER BY c.updatedAt DESC`
2629
- ).all();
2901
+ ORDER BY c.updatedAt DESC${limitClause}`
2902
+ ).all(...params);
2630
2903
  return rows.map((row) => ({
2631
2904
  id: row.id,
2905
+ userId: row.userId,
2632
2906
  title: row.title ?? void 0,
2633
2907
  messageCount: row.messageCount,
2634
2908
  branchCount: row.branchCount,
@@ -2636,6 +2910,25 @@ var SqliteContextStore = class extends ContextStore {
2636
2910
  updatedAt: row.updatedAt
2637
2911
  }));
2638
2912
  }
2913
+ async deleteChat(chatId, options) {
2914
+ return this.#useTransaction(() => {
2915
+ const messageIds = this.#db.prepare("SELECT id FROM messages WHERE chatId = ?").all(chatId);
2916
+ let sql = "DELETE FROM chats WHERE id = ?";
2917
+ const params = [chatId];
2918
+ if (options?.userId !== void 0) {
2919
+ sql += " AND userId = ?";
2920
+ params.push(options.userId);
2921
+ }
2922
+ const result = this.#db.prepare(sql).run(...params);
2923
+ if (result.changes > 0 && messageIds.length > 0) {
2924
+ const placeholders = messageIds.map(() => "?").join(", ");
2925
+ this.#db.prepare(
2926
+ `DELETE FROM messages_fts WHERE messageId IN (${placeholders})`
2927
+ ).run(...messageIds.map((m) => m.id));
2928
+ }
2929
+ return result.changes > 0;
2930
+ });
2931
+ }
2639
2932
  // ==========================================================================
2640
2933
  // Message Operations (Graph Nodes)
2641
2934
  // ==========================================================================
@@ -2706,6 +2999,17 @@ var SqliteContextStore = class extends ContextStore {
2706
2999
  ).get(messageId);
2707
3000
  return row.hasChildren === 1;
2708
3001
  }
3002
+ async getMessages(chatId) {
3003
+ const chat = await this.getChat(chatId);
3004
+ if (!chat) {
3005
+ throw new Error(`Chat "${chatId}" not found`);
3006
+ }
3007
+ const activeBranch = await this.getActiveBranch(chatId);
3008
+ if (!activeBranch?.headMessageId) {
3009
+ return [];
3010
+ }
3011
+ return this.getMessageChain(activeBranch.headMessageId);
3012
+ }
2709
3013
  // ==========================================================================
2710
3014
  // Branch Operations
2711
3015
  // ==========================================================================
@@ -3003,7 +3307,9 @@ function visualizeGraph(data) {
3003
3307
  }
3004
3308
 
3005
3309
  // packages/context/src/lib/agent.ts
3310
+ import { groq } from "@ai-sdk/groq";
3006
3311
  import {
3312
+ NoSuchToolError,
3007
3313
  Output,
3008
3314
  convertToModelMessages,
3009
3315
  createUIMessageStream,
@@ -3013,7 +3319,7 @@ import {
3013
3319
  stepCountIs,
3014
3320
  streamText
3015
3321
  } from "ai";
3016
- import chalk from "chalk";
3322
+ import chalk2 from "chalk";
3017
3323
  import "zod";
3018
3324
  import "@deepagents/agent";
3019
3325
  var Agent = class _Agent {
@@ -3040,16 +3346,17 @@ var Agent = class _Agent {
3040
3346
  providerOptions: this.#options.providerOptions,
3041
3347
  model: this.#options.model,
3042
3348
  system: systemPrompt,
3043
- messages: convertToModelMessages(messages),
3349
+ messages: await convertToModelMessages(messages),
3044
3350
  stopWhen: stepCountIs(25),
3045
3351
  tools: this.#options.tools,
3046
3352
  experimental_context: contextVariables,
3353
+ experimental_repairToolCall: repairToolCall,
3047
3354
  toolChoice: this.#options.toolChoice,
3048
3355
  onStepFinish: (step) => {
3049
3356
  const toolCall = step.toolCalls.at(-1);
3050
3357
  if (toolCall) {
3051
3358
  console.log(
3052
- `Debug: ${chalk.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3359
+ `Debug: ${chalk2.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3053
3360
  );
3054
3361
  }
3055
3362
  }
@@ -3098,8 +3405,9 @@ var Agent = class _Agent {
3098
3405
  providerOptions: this.#options.providerOptions,
3099
3406
  model: this.#options.model,
3100
3407
  system: systemPrompt,
3101
- messages: convertToModelMessages(messages),
3102
- stopWhen: stepCountIs(25),
3408
+ messages: await convertToModelMessages(messages),
3409
+ experimental_repairToolCall: repairToolCall,
3410
+ stopWhen: stepCountIs(50),
3103
3411
  experimental_transform: config?.transform ?? smoothStream(),
3104
3412
  tools: this.#options.tools,
3105
3413
  experimental_context: contextVariables,
@@ -3108,7 +3416,7 @@ var Agent = class _Agent {
3108
3416
  const toolCall = step.toolCalls.at(-1);
3109
3417
  if (toolCall) {
3110
3418
  console.log(
3111
- `Debug: (${runId}) ${chalk.bold.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3419
+ `Debug: (${runId}) ${chalk2.bold.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3112
3420
  );
3113
3421
  }
3114
3422
  }
@@ -3155,7 +3463,7 @@ var Agent = class _Agent {
3155
3463
  guardrailFailed = true;
3156
3464
  failureFeedback = checkResult.feedback;
3157
3465
  console.log(
3158
- chalk.yellow(
3466
+ chalk2.yellow(
3159
3467
  `[${this.#options.name}] Guardrail triggered (attempt ${attempt}/${maxRetries}): ${failureFeedback.slice(0, 50)}...`
3160
3468
  )
3161
3469
  );
@@ -3172,7 +3480,7 @@ var Agent = class _Agent {
3172
3480
  }
3173
3481
  if (attempt >= maxRetries) {
3174
3482
  console.error(
3175
- chalk.red(
3483
+ chalk2.red(
3176
3484
  `[${this.#options.name}] Guardrail retry limit (${maxRetries}) exceeded.`
3177
3485
  )
3178
3486
  );
@@ -3215,14 +3523,10 @@ function structuredOutput(options) {
3215
3523
  return {
3216
3524
  async generate(contextVariables, config) {
3217
3525
  if (!options.context) {
3218
- throw new Error(
3219
- `structuredOutput "${options.name}" is missing a context.`
3220
- );
3526
+ throw new Error(`structuredOutput is missing a context.`);
3221
3527
  }
3222
3528
  if (!options.model) {
3223
- throw new Error(
3224
- `structuredOutput "${options.name}" is missing a model.`
3225
- );
3529
+ throw new Error(`structuredOutput is missing a model.`);
3226
3530
  }
3227
3531
  const { messages, systemPrompt } = await options.context.resolve({
3228
3532
  renderer: new XmlRenderer()
@@ -3232,23 +3536,21 @@ function structuredOutput(options) {
3232
3536
  providerOptions: options.providerOptions,
3233
3537
  model: options.model,
3234
3538
  system: systemPrompt,
3235
- messages: convertToModelMessages(messages),
3539
+ messages: await convertToModelMessages(messages),
3236
3540
  stopWhen: stepCountIs(25),
3541
+ experimental_repairToolCall: repairToolCall,
3237
3542
  experimental_context: contextVariables,
3238
- experimental_output: Output.object({ schema: options.schema })
3543
+ output: Output.object({ schema: options.schema }),
3544
+ tools: options.tools
3239
3545
  });
3240
- return result.experimental_output;
3546
+ return result.output;
3241
3547
  },
3242
3548
  async stream(contextVariables, config) {
3243
3549
  if (!options.context) {
3244
- throw new Error(
3245
- `structuredOutput "${options.name}" is missing a context.`
3246
- );
3550
+ throw new Error(`structuredOutput is missing a context.`);
3247
3551
  }
3248
3552
  if (!options.model) {
3249
- throw new Error(
3250
- `structuredOutput "${options.name}" is missing a model.`
3251
- );
3553
+ throw new Error(`structuredOutput is missing a model.`);
3252
3554
  }
3253
3555
  const { messages, systemPrompt } = await options.context.resolve({
3254
3556
  renderer: new XmlRenderer()
@@ -3258,15 +3560,54 @@ function structuredOutput(options) {
3258
3560
  providerOptions: options.providerOptions,
3259
3561
  model: options.model,
3260
3562
  system: systemPrompt,
3261
- messages: convertToModelMessages(messages),
3262
- stopWhen: stepCountIs(25),
3563
+ experimental_repairToolCall: repairToolCall,
3564
+ messages: await convertToModelMessages(messages),
3565
+ stopWhen: stepCountIs(50),
3263
3566
  experimental_transform: config?.transform ?? smoothStream(),
3264
3567
  experimental_context: contextVariables,
3265
- experimental_output: Output.object({ schema: options.schema })
3568
+ output: Output.object({ schema: options.schema }),
3569
+ tools: options.tools
3266
3570
  });
3267
3571
  }
3268
3572
  };
3269
3573
  }
3574
+ var repairToolCall = async ({
3575
+ toolCall,
3576
+ tools,
3577
+ inputSchema,
3578
+ error
3579
+ }) => {
3580
+ console.log(
3581
+ `Debug: ${chalk2.yellow("RepairingToolCall")}: ${toolCall.toolName}`,
3582
+ error.name
3583
+ );
3584
+ if (NoSuchToolError.isInstance(error)) {
3585
+ return null;
3586
+ }
3587
+ const tool = tools[toolCall.toolName];
3588
+ const { output } = await generateText({
3589
+ model: groq("openai/gpt-oss-20b"),
3590
+ output: Output.object({ schema: tool.inputSchema }),
3591
+ prompt: [
3592
+ `The model tried to call the tool "${toolCall.toolName}" with the following inputs:`,
3593
+ JSON.stringify(toolCall.input),
3594
+ `The tool accepts the following schema:`,
3595
+ JSON.stringify(inputSchema(toolCall)),
3596
+ "Please fix the inputs."
3597
+ ].join("\n")
3598
+ });
3599
+ return { ...toolCall, input: JSON.stringify(output) };
3600
+ };
3601
+
3602
+ // packages/context/src/lib/render.ts
3603
+ function render(tag, ...fragments) {
3604
+ if (fragments.length === 0) {
3605
+ return "";
3606
+ }
3607
+ const renderer = new XmlRenderer();
3608
+ const wrapped = fragment(tag, ...fragments);
3609
+ return renderer.render([wrapped]);
3610
+ }
3270
3611
  export {
3271
3612
  BinaryInstallError,
3272
3613
  ComposeStartError,
@@ -3302,6 +3643,7 @@ export {
3302
3643
  createDockerSandbox,
3303
3644
  defaultTokenizer,
3304
3645
  discoverSkillsInDirectory,
3646
+ errorRecoveryGuardrail,
3305
3647
  estimate,
3306
3648
  example,
3307
3649
  explain,
@@ -3322,8 +3664,11 @@ export {
3322
3664
  parseFrontmatter,
3323
3665
  pass,
3324
3666
  persona,
3667
+ policy,
3325
3668
  preference,
3669
+ principle,
3326
3670
  quirk,
3671
+ render,
3327
3672
  role,
3328
3673
  runGuardrailChain,
3329
3674
  skills,