@deepagents/context 0.11.0 → 0.12.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.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(),
@@ -228,7 +222,7 @@ function message(content) {
228
222
  } : content;
229
223
  return {
230
224
  id: message2.id,
231
- name: "message",
225
+ name: message2.role,
232
226
  data: "content",
233
227
  type: "message",
234
228
  persist: true,
@@ -250,6 +244,22 @@ function assistantText(content, options) {
250
244
  parts: [{ type: "text", text: content }]
251
245
  });
252
246
  }
247
+ var LAZY_ID = Symbol("lazy-id");
248
+ function isLazyFragment(fragment2) {
249
+ return LAZY_ID in fragment2;
250
+ }
251
+ function lastAssistantMessage(content) {
252
+ return {
253
+ name: "assistant",
254
+ type: "message",
255
+ persist: true,
256
+ data: "content",
257
+ [LAZY_ID]: {
258
+ type: "last-assistant",
259
+ content
260
+ }
261
+ };
262
+ }
253
263
 
254
264
  // packages/context/src/lib/renderers/abstract.renderer.ts
255
265
  import pluralize from "pluralize";
@@ -277,6 +287,68 @@ var ContextRenderer = class {
277
287
  }
278
288
  return groups;
279
289
  }
290
+ /**
291
+ * Remove null/undefined from fragments and fragment data recursively.
292
+ * This protects renderers from nullish values and ensures they are ignored
293
+ * consistently across all output formats.
294
+ */
295
+ sanitizeFragments(fragments) {
296
+ const sanitized = [];
297
+ for (const fragment2 of fragments) {
298
+ const cleaned = this.sanitizeFragment(fragment2, /* @__PURE__ */ new WeakSet());
299
+ if (cleaned) {
300
+ sanitized.push(cleaned);
301
+ }
302
+ }
303
+ return sanitized;
304
+ }
305
+ sanitizeFragment(fragment2, seen) {
306
+ const data = this.sanitizeData(fragment2.data, seen);
307
+ if (data == null) {
308
+ return null;
309
+ }
310
+ return {
311
+ ...fragment2,
312
+ data
313
+ };
314
+ }
315
+ sanitizeData(data, seen) {
316
+ if (data == null) {
317
+ return void 0;
318
+ }
319
+ if (isFragment(data)) {
320
+ return this.sanitizeFragment(data, seen) ?? void 0;
321
+ }
322
+ if (Array.isArray(data)) {
323
+ if (seen.has(data)) {
324
+ return void 0;
325
+ }
326
+ seen.add(data);
327
+ const cleaned = [];
328
+ for (const item of data) {
329
+ const sanitizedItem = this.sanitizeData(item, seen);
330
+ if (sanitizedItem != null) {
331
+ cleaned.push(sanitizedItem);
332
+ }
333
+ }
334
+ return cleaned;
335
+ }
336
+ if (isFragmentObject(data)) {
337
+ if (seen.has(data)) {
338
+ return void 0;
339
+ }
340
+ seen.add(data);
341
+ const cleaned = {};
342
+ for (const [key, value] of Object.entries(data)) {
343
+ const sanitizedValue = this.sanitizeData(value, seen);
344
+ if (sanitizedValue != null) {
345
+ cleaned[key] = sanitizedValue;
346
+ }
347
+ }
348
+ return cleaned;
349
+ }
350
+ return data;
351
+ }
280
352
  /**
281
353
  * Template method - dispatches value to appropriate handler.
282
354
  */
@@ -304,7 +376,8 @@ var ContextRenderer = class {
304
376
  };
305
377
  var XmlRenderer = class extends ContextRenderer {
306
378
  render(fragments) {
307
- return fragments.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
379
+ const sanitized = this.sanitizeFragments(fragments);
380
+ return sanitized.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
308
381
  }
309
382
  #renderTopLevel(fragment2) {
310
383
  if (this.isPrimitive(fragment2.data)) {
@@ -317,10 +390,13 @@ var XmlRenderer = class extends ContextRenderer {
317
390
  const child = this.renderFragment(fragment2.data, { depth: 1, path: [] });
318
391
  return this.#wrap(fragment2.name, [child]);
319
392
  }
320
- return this.#wrap(
321
- fragment2.name,
322
- this.renderEntries(fragment2.data, { depth: 1, path: [] })
323
- );
393
+ if (isFragmentObject(fragment2.data)) {
394
+ return this.#wrap(
395
+ fragment2.name,
396
+ this.renderEntries(fragment2.data, { depth: 1, path: [] })
397
+ );
398
+ }
399
+ return "";
324
400
  }
325
401
  #renderArray(name, items, depth) {
326
402
  const fragmentItems = items.filter(isFragment);
@@ -328,9 +404,19 @@ var XmlRenderer = class extends ContextRenderer {
328
404
  const children = [];
329
405
  for (const item of nonFragmentItems) {
330
406
  if (item != null) {
331
- children.push(
332
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
333
- );
407
+ if (isFragmentObject(item)) {
408
+ children.push(
409
+ this.#wrapIndented(
410
+ pluralize.singular(name),
411
+ this.renderEntries(item, { depth: depth + 2, path: [] }),
412
+ depth + 1
413
+ )
414
+ );
415
+ } else {
416
+ children.push(
417
+ this.#leaf(pluralize.singular(name), String(item), depth + 1)
418
+ );
419
+ }
334
420
  }
335
421
  }
336
422
  if (this.options.groupFragments && fragmentItems.length > 0) {
@@ -372,8 +458,14 @@ ${this.#indent(safe, 2)}
372
458
  if (Array.isArray(data)) {
373
459
  return this.#renderArrayIndented(name, data, ctx.depth);
374
460
  }
375
- const children = this.renderEntries(data, { ...ctx, depth: ctx.depth + 1 });
376
- return this.#wrapIndented(name, children, ctx.depth);
461
+ if (isFragmentObject(data)) {
462
+ const children = this.renderEntries(data, {
463
+ ...ctx,
464
+ depth: ctx.depth + 1
465
+ });
466
+ return this.#wrapIndented(name, children, ctx.depth);
467
+ }
468
+ return "";
377
469
  }
378
470
  #renderArrayIndented(name, items, depth) {
379
471
  const fragmentItems = items.filter(isFragment);
@@ -381,9 +473,19 @@ ${this.#indent(safe, 2)}
381
473
  const children = [];
382
474
  for (const item of nonFragmentItems) {
383
475
  if (item != null) {
384
- children.push(
385
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
386
- );
476
+ if (isFragmentObject(item)) {
477
+ children.push(
478
+ this.#wrapIndented(
479
+ pluralize.singular(name),
480
+ this.renderEntries(item, { depth: depth + 2, path: [] }),
481
+ depth + 1
482
+ )
483
+ );
484
+ } else {
485
+ children.push(
486
+ this.#leaf(pluralize.singular(name), String(item), depth + 1)
487
+ );
488
+ }
387
489
  }
388
490
  }
389
491
  if (this.options.groupFragments && fragmentItems.length > 0) {
@@ -412,7 +514,19 @@ ${this.#indent(safe, 2)}
412
514
  return "";
413
515
  }
414
516
  const itemTag = pluralize.singular(key);
415
- const children = items.filter((item) => item != null).map((item) => this.#leaf(itemTag, String(item), ctx.depth + 1));
517
+ const children = items.filter((item) => item != null).map((item) => {
518
+ if (isFragment(item)) {
519
+ return this.renderFragment(item, { ...ctx, depth: ctx.depth + 1 });
520
+ }
521
+ if (isFragmentObject(item)) {
522
+ return this.#wrapIndented(
523
+ itemTag,
524
+ this.renderEntries(item, { ...ctx, depth: ctx.depth + 2 }),
525
+ ctx.depth + 1
526
+ );
527
+ }
528
+ return this.#leaf(itemTag, String(item), ctx.depth + 1);
529
+ });
416
530
  return this.#wrapIndented(key, children, ctx.depth);
417
531
  }
418
532
  renderObject(key, obj, ctx) {
@@ -464,7 +578,7 @@ ${pad}</${tag}>`;
464
578
  };
465
579
  var MarkdownRenderer = class extends ContextRenderer {
466
580
  render(fragments) {
467
- return fragments.map((f) => {
581
+ return this.sanitizeFragments(fragments).map((f) => {
468
582
  const title = `## ${titlecase(f.name)}`;
469
583
  if (this.isPrimitive(f.data)) {
470
584
  return `${title}
@@ -478,8 +592,12 @@ ${this.#renderArray(f.data, 0)}`;
478
592
  return `${title}
479
593
  ${this.renderFragment(f.data, { depth: 0, path: [] })}`;
480
594
  }
481
- return `${title}
595
+ if (isFragmentObject(f.data)) {
596
+ return `${title}
482
597
  ${this.renderEntries(f.data, { depth: 0, path: [] }).join("\n")}`;
598
+ }
599
+ return `${title}
600
+ `;
483
601
  }).join("\n\n");
484
602
  }
485
603
  #renderArray(items, depth) {
@@ -536,14 +654,17 @@ ${this.renderEntries(f.data, { depth: 0, path: [] }).join("\n")}`;
536
654
  return [header, child].join("\n");
537
655
  }
538
656
  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");
657
+ const children = data.filter((item) => item != null).map((item) => this.#arrayItem(item, ctx.depth + 1));
658
+ return [header, ...children].join("\n");
541
659
  }
542
- const children = this.renderEntries(data, {
543
- ...ctx,
544
- depth: ctx.depth + 1
545
- }).join("\n");
546
- return [header, children].join("\n");
660
+ if (isFragmentObject(data)) {
661
+ const children = this.renderEntries(data, {
662
+ ...ctx,
663
+ depth: ctx.depth + 1
664
+ }).join("\n");
665
+ return [header, children].join("\n");
666
+ }
667
+ return header;
547
668
  }
548
669
  renderPrimitive(key, value, ctx) {
549
670
  return this.#leaf(key, value, ctx.depth);
@@ -564,22 +685,25 @@ ${this.renderEntries(f.data, { depth: 0, path: [] }).join("\n")}`;
564
685
  };
565
686
  var TomlRenderer = class extends ContextRenderer {
566
687
  render(fragments) {
567
- return fragments.map((f) => {
688
+ const rendered = [];
689
+ for (const f of this.sanitizeFragments(fragments)) {
568
690
  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");
691
+ rendered.push(`${f.name} = ${this.#formatValue(f.data)}`);
692
+ } else if (Array.isArray(f.data)) {
693
+ rendered.push(this.#renderTopLevelArray(f.name, f.data));
694
+ } else if (isFragment(f.data)) {
695
+ rendered.push(
696
+ [
697
+ `[${f.name}]`,
698
+ this.renderFragment(f.data, { depth: 0, path: [f.name] })
699
+ ].join("\n")
700
+ );
701
+ } else if (isFragmentObject(f.data)) {
702
+ const entries = this.#renderObjectEntries(f.data, [f.name]);
703
+ rendered.push([`[${f.name}]`, ...entries].join("\n"));
579
704
  }
580
- const entries = this.#renderObjectEntries(f.data, [f.name]);
581
- return [`[${f.name}]`, ...entries].join("\n");
582
- }).join("\n\n");
705
+ }
706
+ return rendered.join("\n\n");
583
707
  }
584
708
  #renderTopLevelArray(name, items) {
585
709
  const fragmentItems = items.filter(isFragment);
@@ -614,10 +738,12 @@ var TomlRenderer = class extends ContextRenderer {
614
738
  }
615
739
  return `${key} = ${this.#formatValue(value)}`;
616
740
  }
617
- renderPrimitive(key, value, _ctx) {
741
+ renderPrimitive(key, value, ctx) {
742
+ void ctx;
618
743
  return `${key} = ${this.#formatValue(value)}`;
619
744
  }
620
- renderArray(key, items, _ctx) {
745
+ renderArray(key, items, ctx) {
746
+ void ctx;
621
747
  const values = items.filter((item) => item != null).map((item) => this.#formatValue(item));
622
748
  return `${key} = [${values.join(", ")}]`;
623
749
  }
@@ -671,8 +797,11 @@ var TomlRenderer = class extends ContextRenderer {
671
797
  const values = nonFragmentItems.map((item) => this.#formatValue(item));
672
798
  return `${name} = [${values.join(", ")}]`;
673
799
  }
674
- const entries = this.#renderObjectEntries(data, newPath);
675
- return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
800
+ if (isFragmentObject(data)) {
801
+ const entries = this.#renderObjectEntries(data, newPath);
802
+ return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
803
+ }
804
+ return "";
676
805
  }
677
806
  #escape(value) {
678
807
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
@@ -692,7 +821,8 @@ var TomlRenderer = class extends ContextRenderer {
692
821
  };
693
822
  var ToonRenderer = class extends ContextRenderer {
694
823
  render(fragments) {
695
- return fragments.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
824
+ const sanitized = this.sanitizeFragments(fragments);
825
+ return sanitized.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
696
826
  }
697
827
  #renderTopLevel(fragment2) {
698
828
  const { name, data } = fragment2;
@@ -741,18 +871,18 @@ ${entries}`;
741
871
  if (items.length === 0) return false;
742
872
  const objects = items.filter(isFragmentObject);
743
873
  if (objects.length !== items.length) return false;
744
- const firstKeys = Object.keys(objects[0]).sort().join(",");
874
+ let intersection = new Set(Object.keys(objects[0]));
745
875
  for (const obj of objects) {
746
- if (Object.keys(obj).sort().join(",") !== firstKeys) {
747
- return false;
748
- }
876
+ const keys = new Set(Object.keys(obj));
877
+ intersection = new Set([...intersection].filter((k) => keys.has(k)));
749
878
  for (const value of Object.values(obj)) {
750
- if (!this.#isPrimitiveValue(value) && value !== null) {
879
+ if (value == null) continue;
880
+ if (!this.#isPrimitiveValue(value)) {
751
881
  return false;
752
882
  }
753
883
  }
754
884
  }
755
- return true;
885
+ return intersection.size > 0;
756
886
  }
757
887
  #renderPrimitiveArray(key, items, depth) {
758
888
  const values = items.map((item) => this.#formatValue(item)).join(",");
@@ -762,10 +892,16 @@ ${entries}`;
762
892
  if (items.length === 0) {
763
893
  return `${this.#pad(depth)}${key}[0]:`;
764
894
  }
765
- const fields = Object.keys(items[0]);
895
+ const fields = Array.from(
896
+ new Set(items.flatMap((obj) => Object.keys(obj)))
897
+ );
766
898
  const header = `${this.#pad(depth)}${key}[${items.length}]{${fields.join(",")}}:`;
767
899
  const rows = items.map((obj) => {
768
- const values = fields.map((f) => this.#formatValue(obj[f]));
900
+ const values = fields.map((f) => {
901
+ const value = obj[f];
902
+ if (value == null) return "";
903
+ return this.#formatValue(value);
904
+ });
769
905
  return `${this.#pad(depth + 1)}${values.join(",")}`;
770
906
  });
771
907
  return [header, ...rows].join("\n");
@@ -913,6 +1049,7 @@ var ContextEngine = class {
913
1049
  #pendingMessages = [];
914
1050
  #store;
915
1051
  #chatId;
1052
+ #userId;
916
1053
  #branchName;
917
1054
  #branch = null;
918
1055
  #chatData = null;
@@ -921,9 +1058,13 @@ var ContextEngine = class {
921
1058
  if (!options.chatId) {
922
1059
  throw new Error("chatId is required");
923
1060
  }
1061
+ if (!options.userId) {
1062
+ throw new Error("userId is required");
1063
+ }
924
1064
  this.#store = options.store;
925
1065
  this.#chatId = options.chatId;
926
- this.#branchName = options.branch ?? "main";
1066
+ this.#userId = options.userId;
1067
+ this.#branchName = "main";
927
1068
  }
928
1069
  /**
929
1070
  * Initialize the chat and branch if they don't exist.
@@ -932,24 +1073,11 @@ var ContextEngine = class {
932
1073
  if (this.#initialized) {
933
1074
  return;
934
1075
  }
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
- }
1076
+ this.#chatData = await this.#store.upsertChat({
1077
+ id: this.#chatId,
1078
+ userId: this.#userId
1079
+ });
1080
+ this.#branch = await this.#store.getActiveBranch(this.#chatId);
953
1081
  this.#initialized = true;
954
1082
  }
955
1083
  /**
@@ -1009,6 +1137,7 @@ var ContextEngine = class {
1009
1137
  }
1010
1138
  return {
1011
1139
  id: this.#chatData.id,
1140
+ userId: this.#chatData.userId,
1012
1141
  createdAt: this.#chatData.createdAt,
1013
1142
  updatedAt: this.#chatData.updatedAt,
1014
1143
  title: this.#chatData.title,
@@ -1051,7 +1180,7 @@ var ContextEngine = class {
1051
1180
  *
1052
1181
  * @example
1053
1182
  * ```ts
1054
- * const context = new ContextEngine({ store, chatId: 'chat-1' })
1183
+ * const context = new ContextEngine({ store, chatId: 'chat-1', userId: 'user-1' })
1055
1184
  * .set(role('You are helpful'), user('Hello'));
1056
1185
  *
1057
1186
  * const { systemPrompt, messages } = await context.resolve();
@@ -1095,6 +1224,12 @@ var ContextEngine = class {
1095
1224
  if (this.#pendingMessages.length === 0) {
1096
1225
  return;
1097
1226
  }
1227
+ for (let i = 0; i < this.#pendingMessages.length; i++) {
1228
+ const fragment2 = this.#pendingMessages[i];
1229
+ if (isLazyFragment(fragment2)) {
1230
+ this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
1231
+ }
1232
+ }
1098
1233
  let parentId = this.#branch.headMessageId;
1099
1234
  const now = Date.now();
1100
1235
  for (const fragment2 of this.#pendingMessages) {
@@ -1114,6 +1249,39 @@ var ContextEngine = class {
1114
1249
  this.#branch.headMessageId = parentId;
1115
1250
  this.#pendingMessages = [];
1116
1251
  }
1252
+ /**
1253
+ * Resolve a lazy fragment by finding the appropriate ID.
1254
+ */
1255
+ async #resolveLazyFragment(fragment2) {
1256
+ const lazy = fragment2[LAZY_ID];
1257
+ if (lazy.type === "last-assistant") {
1258
+ const lastId = await this.#getLastAssistantId();
1259
+ return assistantText(lazy.content, { id: lastId ?? crypto.randomUUID() });
1260
+ }
1261
+ throw new Error(`Unknown lazy fragment type: ${lazy.type}`);
1262
+ }
1263
+ /**
1264
+ * Find the most recent assistant message ID (pending or persisted).
1265
+ */
1266
+ async #getLastAssistantId() {
1267
+ for (let i = this.#pendingMessages.length - 1; i >= 0; i--) {
1268
+ const msg = this.#pendingMessages[i];
1269
+ if (msg.name === "assistant" && !isLazyFragment(msg)) {
1270
+ return msg.id;
1271
+ }
1272
+ }
1273
+ if (this.#branch?.headMessageId) {
1274
+ const chain = await this.#store.getMessageChain(
1275
+ this.#branch.headMessageId
1276
+ );
1277
+ for (let i = chain.length - 1; i >= 0; i--) {
1278
+ if (chain[i].name === "assistant") {
1279
+ return chain[i].id;
1280
+ }
1281
+ }
1282
+ }
1283
+ return void 0;
1284
+ }
1117
1285
  /**
1118
1286
  * Estimate token count and cost for the full context.
1119
1287
  *
@@ -1393,9 +1561,38 @@ var ContextEngine = class {
1393
1561
  consolidate() {
1394
1562
  return void 0;
1395
1563
  }
1564
+ /**
1565
+ * Extract skill path mappings from available_skills fragments.
1566
+ * Returns array of { host, sandbox } for mounting in sandbox filesystem.
1567
+ *
1568
+ * Reads the original `paths` configuration stored in fragment metadata
1569
+ * by the skills() fragment helper.
1570
+ *
1571
+ * @example
1572
+ * ```ts
1573
+ * const context = new ContextEngine({ store, chatId, userId })
1574
+ * .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));
1575
+ *
1576
+ * const mounts = context.getSkillMounts();
1577
+ * // [{ host: './skills', sandbox: '/skills' }]
1578
+ * ```
1579
+ */
1580
+ getSkillMounts() {
1581
+ const mounts = [];
1582
+ for (const fragment2 of this.#fragments) {
1583
+ if (fragment2.name === "available_skills" && fragment2.metadata && Array.isArray(fragment2.metadata.paths)) {
1584
+ for (const mapping of fragment2.metadata.paths) {
1585
+ if (typeof mapping === "object" && mapping !== null && typeof mapping.host === "string" && typeof mapping.sandbox === "string") {
1586
+ mounts.push({ host: mapping.host, sandbox: mapping.sandbox });
1587
+ }
1588
+ }
1589
+ }
1590
+ }
1591
+ return mounts;
1592
+ }
1396
1593
  /**
1397
1594
  * Inspect the full context state for debugging.
1398
- * Returns a comprehensive JSON-serializable object with all context information.
1595
+ * Returns a JSON-serializable object with context information.
1399
1596
  *
1400
1597
  * @param options - Inspection options (modelId and renderer required)
1401
1598
  * @returns Complete inspection data including estimates, rendered output, fragments, and graph
@@ -1547,6 +1744,33 @@ function glossary(entries) {
1547
1744
  }))
1548
1745
  };
1549
1746
  }
1747
+ function role(content) {
1748
+ return {
1749
+ name: "role",
1750
+ data: content
1751
+ };
1752
+ }
1753
+ function principle(input) {
1754
+ return {
1755
+ name: "principle",
1756
+ data: {
1757
+ title: input.title,
1758
+ description: input.description,
1759
+ ...input.policies?.length && { policies: input.policies }
1760
+ }
1761
+ };
1762
+ }
1763
+ function policy(input) {
1764
+ return {
1765
+ name: "policy",
1766
+ data: {
1767
+ rule: input.rule,
1768
+ ...input.before && { before: input.before },
1769
+ ...input.reason && { reason: input.reason },
1770
+ ...input.policies?.length && { policies: input.policies }
1771
+ }
1772
+ };
1773
+ }
1550
1774
 
1551
1775
  // packages/context/src/lib/fragments/user.ts
1552
1776
  function identity(input) {
@@ -1563,7 +1787,8 @@ function persona(input) {
1563
1787
  name: "persona",
1564
1788
  data: {
1565
1789
  name: input.name,
1566
- role: input.role,
1790
+ ...input.role && { role: input.role },
1791
+ ...input.objective && { objective: input.objective },
1567
1792
  ...input.tone && { tone: input.tone }
1568
1793
  }
1569
1794
  };
@@ -1612,11 +1837,79 @@ function runGuardrailChain(part, guardrails, context) {
1612
1837
  return pass(currentPart);
1613
1838
  }
1614
1839
 
1840
+ // packages/context/src/lib/guardrails/error-recovery.guardrail.ts
1841
+ import chalk from "chalk";
1842
+ var errorRecoveryGuardrail = {
1843
+ id: "error-recovery",
1844
+ name: "API Error Recovery",
1845
+ handle: (part, context) => {
1846
+ if (part.type !== "error") {
1847
+ return pass(part);
1848
+ }
1849
+ const errorText = part.errorText || "";
1850
+ const prefix = chalk.bold.magenta("[ErrorRecovery]");
1851
+ console.log(
1852
+ `${prefix} ${chalk.red("Caught error:")} ${chalk.dim(errorText.slice(0, 150))}`
1853
+ );
1854
+ const logAndFail = (pattern, feedback) => {
1855
+ console.log(
1856
+ `${prefix} ${chalk.yellow("Pattern:")} ${chalk.cyan(pattern)}`
1857
+ );
1858
+ console.log(
1859
+ `${prefix} ${chalk.green("Feedback:")} ${chalk.dim(feedback.slice(0, 80))}...`
1860
+ );
1861
+ return fail(feedback);
1862
+ };
1863
+ if (errorText.includes("Tool choice is none")) {
1864
+ if (context.availableTools.length > 0) {
1865
+ return logAndFail(
1866
+ "Tool choice is none",
1867
+ `I tried to call a tool that doesn't exist. Available tools: ${context.availableTools.join(", ")}. Let me use one of these instead.`
1868
+ );
1869
+ }
1870
+ return logAndFail(
1871
+ "Tool choice is none (no tools)",
1872
+ "I tried to call a tool, but no tools are available. Let me respond with plain text instead."
1873
+ );
1874
+ }
1875
+ if (errorText.includes("not in request.tools") || errorText.includes("tool") && errorText.includes("not found")) {
1876
+ const toolMatch = errorText.match(/tool '([^']+)'/);
1877
+ const toolName = toolMatch ? toolMatch[1] : "unknown";
1878
+ if (context.availableTools.length > 0) {
1879
+ return logAndFail(
1880
+ `Unregistered tool: ${toolName}`,
1881
+ `I tried to call "${toolName}" but it doesn't exist. Available tools: ${context.availableTools.join(", ")}. Let me use one of these instead.`
1882
+ );
1883
+ }
1884
+ return logAndFail(
1885
+ `Unregistered tool: ${toolName} (no tools)`,
1886
+ `I tried to call "${toolName}" but no tools are available. Let me respond with plain text instead.`
1887
+ );
1888
+ }
1889
+ if (errorText.includes("Failed to parse tool call arguments") || errorText.includes("parse tool call") || errorText.includes("invalid JSON")) {
1890
+ return logAndFail(
1891
+ "Malformed JSON arguments",
1892
+ "I generated malformed JSON for the tool arguments. Let me format my tool call properly with valid JSON."
1893
+ );
1894
+ }
1895
+ if (errorText.includes("Parsing failed")) {
1896
+ return logAndFail(
1897
+ "Parsing failed",
1898
+ "My response format was invalid. Let me try again with a properly formatted response."
1899
+ );
1900
+ }
1901
+ return logAndFail(
1902
+ "Unknown error",
1903
+ `An error occurred: ${errorText}. Let me try a different approach.`
1904
+ );
1905
+ }
1906
+ };
1907
+
1615
1908
  // packages/context/src/lib/sandbox/binary-bridges.ts
1909
+ import { existsSync } from "fs";
1616
1910
  import { defineCommand } from "just-bash";
1617
1911
  import spawn from "nano-spawn";
1618
1912
  import * as path from "path";
1619
- import { existsSync } from "fs";
1620
1913
  function createBinaryBridges(...binaries) {
1621
1914
  return binaries.map((input) => {
1622
1915
  const config = typeof input === "string" ? { name: input } : input;
@@ -1662,6 +1955,17 @@ function createBinaryBridges(...binaries) {
1662
1955
  exitCode: 0
1663
1956
  };
1664
1957
  } catch (error) {
1958
+ if (error && typeof error === "object") {
1959
+ const err = error;
1960
+ const cause = err.cause;
1961
+ if (cause?.code === "ENOENT") {
1962
+ return {
1963
+ stdout: "",
1964
+ stderr: `${name}: ${binaryPath} not found`,
1965
+ exitCode: 127
1966
+ };
1967
+ }
1968
+ }
1665
1969
  if (error && typeof error === "object" && "exitCode" in error) {
1666
1970
  const subprocessError = error;
1667
1971
  return {
@@ -2379,7 +2683,9 @@ function parseFrontmatter(content) {
2379
2683
  throw new Error('Invalid SKILL.md: frontmatter must have a "name" field');
2380
2684
  }
2381
2685
  if (!frontmatter.description || typeof frontmatter.description !== "string") {
2382
- throw new Error('Invalid SKILL.md: frontmatter must have a "description" field');
2686
+ throw new Error(
2687
+ 'Invalid SKILL.md: frontmatter must have a "description" field'
2688
+ );
2383
2689
  }
2384
2690
  return {
2385
2691
  frontmatter,
@@ -2420,9 +2726,13 @@ function discoverSkillsInDirectory(directory) {
2420
2726
 
2421
2727
  // packages/context/src/lib/skills/fragments.ts
2422
2728
  function skills(options) {
2729
+ const pathMapping = /* @__PURE__ */ new Map();
2730
+ for (const { host, sandbox } of options.paths) {
2731
+ pathMapping.set(host, sandbox);
2732
+ }
2423
2733
  const skillsMap = /* @__PURE__ */ new Map();
2424
- for (const dir of options.paths) {
2425
- const discovered = discoverSkillsInDirectory(dir);
2734
+ for (const { host } of options.paths) {
2735
+ const discovered = discoverSkillsInDirectory(host);
2426
2736
  for (const skill of discovered) {
2427
2737
  skillsMap.set(skill.name, skill);
2428
2738
  }
@@ -2437,14 +2747,26 @@ function skills(options) {
2437
2747
  (s) => !options.exclude.includes(s.name)
2438
2748
  );
2439
2749
  }
2440
- const skillFragments = filteredSkills.map((skill) => ({
2441
- name: "skill",
2442
- data: {
2443
- name: skill.name,
2444
- path: skill.skillMdPath,
2445
- description: skill.description
2750
+ const skillFragments = filteredSkills.map((skill) => {
2751
+ const originalPath = skill.skillMdPath;
2752
+ let sandboxPath = originalPath;
2753
+ for (const [host, sandbox] of pathMapping) {
2754
+ if (originalPath.startsWith(host)) {
2755
+ const relativePath = originalPath.slice(host.length);
2756
+ sandboxPath = sandbox + relativePath;
2757
+ break;
2758
+ }
2446
2759
  }
2447
- }));
2760
+ return {
2761
+ name: "skill",
2762
+ data: {
2763
+ name: skill.name,
2764
+ path: sandboxPath,
2765
+ description: skill.description
2766
+ },
2767
+ metadata: { originalPath }
2768
+ };
2769
+ });
2448
2770
  return {
2449
2771
  name: "available_skills",
2450
2772
  data: [
@@ -2453,7 +2775,11 @@ function skills(options) {
2453
2775
  data: SKILLS_INSTRUCTIONS
2454
2776
  },
2455
2777
  ...skillFragments
2456
- ]
2778
+ ],
2779
+ metadata: {
2780
+ paths: options.paths
2781
+ // Store original path mappings for getSkillMounts()
2782
+ }
2457
2783
  };
2458
2784
  }
2459
2785
  var SKILLS_INSTRUCTIONS = `When a user's request matches one of the skills listed below, read the skill's SKILL.md file to get detailed instructions before proceeding. Skills provide specialized knowledge and workflows for specific tasks.
@@ -2472,6 +2798,7 @@ var STORE_DDL = `
2472
2798
  -- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
2473
2799
  CREATE TABLE IF NOT EXISTS chats (
2474
2800
  id TEXT PRIMARY KEY,
2801
+ userId TEXT NOT NULL,
2475
2802
  title TEXT,
2476
2803
  metadata TEXT,
2477
2804
  createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
@@ -2479,6 +2806,7 @@ CREATE TABLE IF NOT EXISTS chats (
2479
2806
  );
2480
2807
 
2481
2808
  CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
2809
+ CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
2482
2810
 
2483
2811
  -- Messages table (nodes in the DAG)
2484
2812
  CREATE TABLE IF NOT EXISTS messages (
@@ -2544,37 +2872,67 @@ var SqliteContextStore = class extends ContextStore {
2544
2872
  this.#db.exec("PRAGMA foreign_keys = ON");
2545
2873
  this.#db.exec(STORE_DDL);
2546
2874
  }
2875
+ /**
2876
+ * Execute a function within a transaction.
2877
+ * Automatically commits on success or rolls back on error.
2878
+ */
2879
+ #useTransaction(fn) {
2880
+ this.#db.exec("BEGIN TRANSACTION");
2881
+ try {
2882
+ const result = fn();
2883
+ this.#db.exec("COMMIT");
2884
+ return result;
2885
+ } catch (error) {
2886
+ this.#db.exec("ROLLBACK");
2887
+ throw error;
2888
+ }
2889
+ }
2547
2890
  // ==========================================================================
2548
2891
  // Chat Operations
2549
2892
  // ==========================================================================
2550
2893
  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
- );
2894
+ this.#useTransaction(() => {
2895
+ this.#db.prepare(
2896
+ `INSERT INTO chats (id, userId, title, metadata)
2897
+ VALUES (?, ?, ?, ?)`
2898
+ ).run(
2899
+ chat.id,
2900
+ chat.userId,
2901
+ chat.title ?? null,
2902
+ chat.metadata ? JSON.stringify(chat.metadata) : null
2903
+ );
2904
+ this.#db.prepare(
2905
+ `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
2906
+ VALUES (?, ?, 'main', NULL, 1, ?)`
2907
+ ).run(crypto.randomUUID(), chat.id, Date.now());
2908
+ });
2559
2909
  }
2560
2910
  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
- };
2911
+ return this.#useTransaction(() => {
2912
+ const row = this.#db.prepare(
2913
+ `INSERT INTO chats (id, userId, title, metadata)
2914
+ VALUES (?, ?, ?, ?)
2915
+ ON CONFLICT(id) DO UPDATE SET id = excluded.id
2916
+ RETURNING *`
2917
+ ).get(
2918
+ chat.id,
2919
+ chat.userId,
2920
+ chat.title ?? null,
2921
+ chat.metadata ? JSON.stringify(chat.metadata) : null
2922
+ );
2923
+ this.#db.prepare(
2924
+ `INSERT OR IGNORE INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
2925
+ VALUES (?, ?, 'main', NULL, 1, ?)`
2926
+ ).run(crypto.randomUUID(), chat.id, Date.now());
2927
+ return {
2928
+ id: row.id,
2929
+ userId: row.userId,
2930
+ title: row.title ?? void 0,
2931
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2932
+ createdAt: row.createdAt,
2933
+ updatedAt: row.updatedAt
2934
+ };
2935
+ });
2578
2936
  }
2579
2937
  async getChat(chatId) {
2580
2938
  const row = this.#db.prepare("SELECT * FROM chats WHERE id = ?").get(chatId);
@@ -2583,6 +2941,7 @@ var SqliteContextStore = class extends ContextStore {
2583
2941
  }
2584
2942
  return {
2585
2943
  id: row.id,
2944
+ userId: row.userId,
2586
2945
  title: row.title ?? void 0,
2587
2946
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2588
2947
  createdAt: row.createdAt,
@@ -2606,16 +2965,33 @@ var SqliteContextStore = class extends ContextStore {
2606
2965
  ).get(...params);
2607
2966
  return {
2608
2967
  id: row.id,
2968
+ userId: row.userId,
2609
2969
  title: row.title ?? void 0,
2610
2970
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2611
2971
  createdAt: row.createdAt,
2612
2972
  updatedAt: row.updatedAt
2613
2973
  };
2614
2974
  }
2615
- async listChats() {
2975
+ async listChats(options) {
2976
+ const params = [];
2977
+ let whereClause = "";
2978
+ let limitClause = "";
2979
+ if (options?.userId) {
2980
+ whereClause = "WHERE c.userId = ?";
2981
+ params.push(options.userId);
2982
+ }
2983
+ if (options?.limit !== void 0) {
2984
+ limitClause = " LIMIT ?";
2985
+ params.push(options.limit);
2986
+ if (options.offset !== void 0) {
2987
+ limitClause += " OFFSET ?";
2988
+ params.push(options.offset);
2989
+ }
2990
+ }
2616
2991
  const rows = this.#db.prepare(
2617
2992
  `SELECT
2618
2993
  c.id,
2994
+ c.userId,
2619
2995
  c.title,
2620
2996
  c.createdAt,
2621
2997
  c.updatedAt,
@@ -2624,11 +3000,13 @@ var SqliteContextStore = class extends ContextStore {
2624
3000
  FROM chats c
2625
3001
  LEFT JOIN messages m ON m.chatId = c.id
2626
3002
  LEFT JOIN branches b ON b.chatId = c.id
3003
+ ${whereClause}
2627
3004
  GROUP BY c.id
2628
- ORDER BY c.updatedAt DESC`
2629
- ).all();
3005
+ ORDER BY c.updatedAt DESC${limitClause}`
3006
+ ).all(...params);
2630
3007
  return rows.map((row) => ({
2631
3008
  id: row.id,
3009
+ userId: row.userId,
2632
3010
  title: row.title ?? void 0,
2633
3011
  messageCount: row.messageCount,
2634
3012
  branchCount: row.branchCount,
@@ -2636,22 +3014,42 @@ var SqliteContextStore = class extends ContextStore {
2636
3014
  updatedAt: row.updatedAt
2637
3015
  }));
2638
3016
  }
3017
+ async deleteChat(chatId, options) {
3018
+ return this.#useTransaction(() => {
3019
+ const messageIds = this.#db.prepare("SELECT id FROM messages WHERE chatId = ?").all(chatId);
3020
+ let sql = "DELETE FROM chats WHERE id = ?";
3021
+ const params = [chatId];
3022
+ if (options?.userId !== void 0) {
3023
+ sql += " AND userId = ?";
3024
+ params.push(options.userId);
3025
+ }
3026
+ const result = this.#db.prepare(sql).run(...params);
3027
+ if (result.changes > 0 && messageIds.length > 0) {
3028
+ const placeholders = messageIds.map(() => "?").join(", ");
3029
+ this.#db.prepare(
3030
+ `DELETE FROM messages_fts WHERE messageId IN (${placeholders})`
3031
+ ).run(...messageIds.map((m) => m.id));
3032
+ }
3033
+ return result.changes > 0;
3034
+ });
3035
+ }
2639
3036
  // ==========================================================================
2640
3037
  // Message Operations (Graph Nodes)
2641
3038
  // ==========================================================================
2642
3039
  async addMessage(message2) {
3040
+ const existingParent = message2.parentId === message2.id ? this.#db.prepare("SELECT parentId FROM messages WHERE id = ?").get(message2.id) : void 0;
3041
+ const parentId = message2.parentId === message2.id ? existingParent?.parentId ?? null : message2.parentId;
2643
3042
  this.#db.prepare(
2644
3043
  `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
2645
3044
  VALUES (?, ?, ?, ?, ?, ?, ?)
2646
3045
  ON CONFLICT(id) DO UPDATE SET
2647
- parentId = excluded.parentId,
2648
3046
  name = excluded.name,
2649
3047
  type = excluded.type,
2650
3048
  data = excluded.data`
2651
3049
  ).run(
2652
3050
  message2.id,
2653
3051
  message2.chatId,
2654
- message2.parentId,
3052
+ parentId,
2655
3053
  message2.name,
2656
3054
  message2.type ?? null,
2657
3055
  JSON.stringify(message2.data),
@@ -2706,6 +3104,17 @@ var SqliteContextStore = class extends ContextStore {
2706
3104
  ).get(messageId);
2707
3105
  return row.hasChildren === 1;
2708
3106
  }
3107
+ async getMessages(chatId) {
3108
+ const chat = await this.getChat(chatId);
3109
+ if (!chat) {
3110
+ throw new Error(`Chat "${chatId}" not found`);
3111
+ }
3112
+ const activeBranch = await this.getActiveBranch(chatId);
3113
+ if (!activeBranch?.headMessageId) {
3114
+ return [];
3115
+ }
3116
+ return this.getMessageChain(activeBranch.headMessageId);
3117
+ }
2709
3118
  // ==========================================================================
2710
3119
  // Branch Operations
2711
3120
  // ==========================================================================
@@ -3003,7 +3412,9 @@ function visualizeGraph(data) {
3003
3412
  }
3004
3413
 
3005
3414
  // packages/context/src/lib/agent.ts
3415
+ import { groq } from "@ai-sdk/groq";
3006
3416
  import {
3417
+ NoSuchToolError,
3007
3418
  Output,
3008
3419
  convertToModelMessages,
3009
3420
  createUIMessageStream,
@@ -3013,7 +3424,7 @@ import {
3013
3424
  stepCountIs,
3014
3425
  streamText
3015
3426
  } from "ai";
3016
- import chalk from "chalk";
3427
+ import chalk2 from "chalk";
3017
3428
  import "zod";
3018
3429
  import "@deepagents/agent";
3019
3430
  var Agent = class _Agent {
@@ -3040,16 +3451,17 @@ var Agent = class _Agent {
3040
3451
  providerOptions: this.#options.providerOptions,
3041
3452
  model: this.#options.model,
3042
3453
  system: systemPrompt,
3043
- messages: convertToModelMessages(messages),
3454
+ messages: await convertToModelMessages(messages),
3044
3455
  stopWhen: stepCountIs(25),
3045
3456
  tools: this.#options.tools,
3046
3457
  experimental_context: contextVariables,
3458
+ experimental_repairToolCall: repairToolCall,
3047
3459
  toolChoice: this.#options.toolChoice,
3048
3460
  onStepFinish: (step) => {
3049
3461
  const toolCall = step.toolCalls.at(-1);
3050
3462
  if (toolCall) {
3051
3463
  console.log(
3052
- `Debug: ${chalk.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3464
+ `Debug: ${chalk2.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3053
3465
  );
3054
3466
  }
3055
3467
  }
@@ -3098,8 +3510,9 @@ var Agent = class _Agent {
3098
3510
  providerOptions: this.#options.providerOptions,
3099
3511
  model: this.#options.model,
3100
3512
  system: systemPrompt,
3101
- messages: convertToModelMessages(messages),
3102
- stopWhen: stepCountIs(25),
3513
+ messages: await convertToModelMessages(messages),
3514
+ experimental_repairToolCall: repairToolCall,
3515
+ stopWhen: stepCountIs(50),
3103
3516
  experimental_transform: config?.transform ?? smoothStream(),
3104
3517
  tools: this.#options.tools,
3105
3518
  experimental_context: contextVariables,
@@ -3108,7 +3521,7 @@ var Agent = class _Agent {
3108
3521
  const toolCall = step.toolCalls.at(-1);
3109
3522
  if (toolCall) {
3110
3523
  console.log(
3111
- `Debug: (${runId}) ${chalk.bold.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3524
+ `Debug: (${runId}) ${chalk2.bold.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
3112
3525
  );
3113
3526
  }
3114
3527
  }
@@ -3155,7 +3568,7 @@ var Agent = class _Agent {
3155
3568
  guardrailFailed = true;
3156
3569
  failureFeedback = checkResult.feedback;
3157
3570
  console.log(
3158
- chalk.yellow(
3571
+ chalk2.yellow(
3159
3572
  `[${this.#options.name}] Guardrail triggered (attempt ${attempt}/${maxRetries}): ${failureFeedback.slice(0, 50)}...`
3160
3573
  )
3161
3574
  );
@@ -3172,20 +3585,16 @@ var Agent = class _Agent {
3172
3585
  }
3173
3586
  if (attempt >= maxRetries) {
3174
3587
  console.error(
3175
- chalk.red(
3588
+ chalk2.red(
3176
3589
  `[${this.#options.name}] Guardrail retry limit (${maxRetries}) exceeded.`
3177
3590
  )
3178
3591
  );
3179
3592
  writer.write({ type: "finish" });
3180
3593
  return;
3181
3594
  }
3182
- writer.write({
3183
- type: "text-delta",
3184
- id: generateId2(),
3185
- delta: ` ${failureFeedback}`
3186
- });
3595
+ writeText(writer, failureFeedback);
3187
3596
  const selfCorrectionText = accumulatedText + " " + failureFeedback;
3188
- context.set(assistantText(selfCorrectionText));
3597
+ context.set(lastAssistantMessage(selfCorrectionText));
3189
3598
  await context.save();
3190
3599
  currentResult = await this.#createRawStream(
3191
3600
  contextVariables,
@@ -3215,14 +3624,10 @@ function structuredOutput(options) {
3215
3624
  return {
3216
3625
  async generate(contextVariables, config) {
3217
3626
  if (!options.context) {
3218
- throw new Error(
3219
- `structuredOutput "${options.name}" is missing a context.`
3220
- );
3627
+ throw new Error(`structuredOutput is missing a context.`);
3221
3628
  }
3222
3629
  if (!options.model) {
3223
- throw new Error(
3224
- `structuredOutput "${options.name}" is missing a model.`
3225
- );
3630
+ throw new Error(`structuredOutput is missing a model.`);
3226
3631
  }
3227
3632
  const { messages, systemPrompt } = await options.context.resolve({
3228
3633
  renderer: new XmlRenderer()
@@ -3232,23 +3637,21 @@ function structuredOutput(options) {
3232
3637
  providerOptions: options.providerOptions,
3233
3638
  model: options.model,
3234
3639
  system: systemPrompt,
3235
- messages: convertToModelMessages(messages),
3640
+ messages: await convertToModelMessages(messages),
3236
3641
  stopWhen: stepCountIs(25),
3642
+ experimental_repairToolCall: repairToolCall,
3237
3643
  experimental_context: contextVariables,
3238
- experimental_output: Output.object({ schema: options.schema })
3644
+ output: Output.object({ schema: options.schema }),
3645
+ tools: options.tools
3239
3646
  });
3240
- return result.experimental_output;
3647
+ return result.output;
3241
3648
  },
3242
3649
  async stream(contextVariables, config) {
3243
3650
  if (!options.context) {
3244
- throw new Error(
3245
- `structuredOutput "${options.name}" is missing a context.`
3246
- );
3651
+ throw new Error(`structuredOutput is missing a context.`);
3247
3652
  }
3248
3653
  if (!options.model) {
3249
- throw new Error(
3250
- `structuredOutput "${options.name}" is missing a model.`
3251
- );
3654
+ throw new Error(`structuredOutput is missing a model.`);
3252
3655
  }
3253
3656
  const { messages, systemPrompt } = await options.context.resolve({
3254
3657
  renderer: new XmlRenderer()
@@ -3258,15 +3661,70 @@ function structuredOutput(options) {
3258
3661
  providerOptions: options.providerOptions,
3259
3662
  model: options.model,
3260
3663
  system: systemPrompt,
3261
- messages: convertToModelMessages(messages),
3262
- stopWhen: stepCountIs(25),
3664
+ experimental_repairToolCall: repairToolCall,
3665
+ messages: await convertToModelMessages(messages),
3666
+ stopWhen: stepCountIs(50),
3263
3667
  experimental_transform: config?.transform ?? smoothStream(),
3264
3668
  experimental_context: contextVariables,
3265
- experimental_output: Output.object({ schema: options.schema })
3669
+ output: Output.object({ schema: options.schema }),
3670
+ tools: options.tools
3266
3671
  });
3267
3672
  }
3268
3673
  };
3269
3674
  }
3675
+ var repairToolCall = async ({
3676
+ toolCall,
3677
+ tools,
3678
+ inputSchema,
3679
+ error
3680
+ }) => {
3681
+ console.log(
3682
+ `Debug: ${chalk2.yellow("RepairingToolCall")}: ${toolCall.toolName}`,
3683
+ error.name
3684
+ );
3685
+ if (NoSuchToolError.isInstance(error)) {
3686
+ return null;
3687
+ }
3688
+ const tool = tools[toolCall.toolName];
3689
+ const { output } = await generateText({
3690
+ model: groq("openai/gpt-oss-20b"),
3691
+ output: Output.object({ schema: tool.inputSchema }),
3692
+ prompt: [
3693
+ `The model tried to call the tool "${toolCall.toolName}" with the following inputs:`,
3694
+ JSON.stringify(toolCall.input),
3695
+ `The tool accepts the following schema:`,
3696
+ JSON.stringify(inputSchema(toolCall)),
3697
+ "Please fix the inputs."
3698
+ ].join("\n")
3699
+ });
3700
+ return { ...toolCall, input: JSON.stringify(output) };
3701
+ };
3702
+ function writeText(writer, text) {
3703
+ const feedbackPartId = generateId2();
3704
+ writer.write({
3705
+ id: feedbackPartId,
3706
+ type: "text-start"
3707
+ });
3708
+ writer.write({
3709
+ id: feedbackPartId,
3710
+ type: "text-delta",
3711
+ delta: ` ${text}`
3712
+ });
3713
+ writer.write({
3714
+ id: feedbackPartId,
3715
+ type: "text-end"
3716
+ });
3717
+ }
3718
+
3719
+ // packages/context/src/lib/render.ts
3720
+ function render(tag, ...fragments) {
3721
+ if (fragments.length === 0) {
3722
+ return "";
3723
+ }
3724
+ const renderer = new XmlRenderer();
3725
+ const wrapped = fragment(tag, ...fragments);
3726
+ return renderer.render([wrapped]);
3727
+ }
3270
3728
  export {
3271
3729
  BinaryInstallError,
3272
3730
  ComposeStartError,
@@ -3281,6 +3739,7 @@ export {
3281
3739
  DockerfileBuildError,
3282
3740
  DockerfileStrategy,
3283
3741
  InMemoryContextStore,
3742
+ LAZY_ID,
3284
3743
  MarkdownRenderer,
3285
3744
  ModelsRegistry,
3286
3745
  MountPathError,
@@ -3302,6 +3761,7 @@ export {
3302
3761
  createDockerSandbox,
3303
3762
  defaultTokenizer,
3304
3763
  discoverSkillsInDirectory,
3764
+ errorRecoveryGuardrail,
3305
3765
  estimate,
3306
3766
  example,
3307
3767
  explain,
@@ -3316,14 +3776,19 @@ export {
3316
3776
  isDockerfileOptions,
3317
3777
  isFragment,
3318
3778
  isFragmentObject,
3779
+ isLazyFragment,
3319
3780
  isMessageFragment,
3781
+ lastAssistantMessage,
3320
3782
  loadSkillMetadata,
3321
3783
  message,
3322
3784
  parseFrontmatter,
3323
3785
  pass,
3324
3786
  persona,
3787
+ policy,
3325
3788
  preference,
3789
+ principle,
3326
3790
  quirk,
3791
+ render,
3327
3792
  role,
3328
3793
  runGuardrailChain,
3329
3794
  skills,