@deepagents/context 0.33.0 → 0.35.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/browser.js +442 -415
- package/dist/browser.js.map +4 -4
- package/dist/index.js +526 -472
- package/dist/index.js.map +4 -4
- package/dist/lib/agent.d.ts.map +1 -1
- package/dist/lib/engine.d.ts +3 -20
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/fragments/message/user.d.ts +71 -15
- package/dist/lib/fragments/message/user.d.ts.map +1 -1
- package/dist/lib/text.d.ts +4 -0
- package/dist/lib/text.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -151,8 +151,10 @@ var Agent = class _Agent {
|
|
|
151
151
|
providerOptions: this.#options.providerOptions,
|
|
152
152
|
model: this.#options.model,
|
|
153
153
|
system: systemPrompt,
|
|
154
|
-
messages: await convertToModelMessages(messages
|
|
155
|
-
|
|
154
|
+
messages: await convertToModelMessages(messages, {
|
|
155
|
+
ignoreIncompleteToolCalls: true
|
|
156
|
+
}),
|
|
157
|
+
stopWhen: stepCountIs(200),
|
|
156
158
|
tools: this.#options.tools,
|
|
157
159
|
experimental_context: contextVariables,
|
|
158
160
|
experimental_repairToolCall: createRepairToolCall(this.#options.model),
|
|
@@ -218,9 +220,11 @@ var Agent = class _Agent {
|
|
|
218
220
|
providerOptions: this.#options.providerOptions,
|
|
219
221
|
model,
|
|
220
222
|
system: systemPrompt,
|
|
221
|
-
messages: await convertToModelMessages(messages
|
|
223
|
+
messages: await convertToModelMessages(messages, {
|
|
224
|
+
ignoreIncompleteToolCalls: true
|
|
225
|
+
}),
|
|
222
226
|
experimental_repairToolCall: createRepairToolCall(model),
|
|
223
|
-
stopWhen: stepCountIs(
|
|
227
|
+
stopWhen: stepCountIs(200),
|
|
224
228
|
experimental_transform: config?.transform ?? smoothStream(),
|
|
225
229
|
tools: this.#options.tools,
|
|
226
230
|
experimental_context: contextVariables,
|
|
@@ -360,8 +364,10 @@ function structuredOutput(options) {
|
|
|
360
364
|
providerOptions: options.providerOptions,
|
|
361
365
|
model: options.model,
|
|
362
366
|
system: systemPrompt,
|
|
363
|
-
messages: await convertToModelMessages(messages
|
|
364
|
-
|
|
367
|
+
messages: await convertToModelMessages(messages, {
|
|
368
|
+
ignoreIncompleteToolCalls: true
|
|
369
|
+
}),
|
|
370
|
+
stopWhen: stepCountIs(200),
|
|
365
371
|
experimental_repairToolCall: createRepairToolCall(options.model),
|
|
366
372
|
experimental_context: contextVariables,
|
|
367
373
|
output: Output.object({ schema: options.schema }),
|
|
@@ -385,8 +391,10 @@ function structuredOutput(options) {
|
|
|
385
391
|
model: options.model,
|
|
386
392
|
system: systemPrompt,
|
|
387
393
|
experimental_repairToolCall: createRepairToolCall(options.model),
|
|
388
|
-
messages: await convertToModelMessages(messages
|
|
389
|
-
|
|
394
|
+
messages: await convertToModelMessages(messages, {
|
|
395
|
+
ignoreIncompleteToolCalls: true
|
|
396
|
+
}),
|
|
397
|
+
stopWhen: stepCountIs(200),
|
|
390
398
|
experimental_transform: config?.transform ?? smoothStream(),
|
|
391
399
|
experimental_context: contextVariables,
|
|
392
400
|
output: Output.object({ schema: options.schema }),
|
|
@@ -591,432 +599,95 @@ async function estimate(modelId, renderer, ...fragments) {
|
|
|
591
599
|
}
|
|
592
600
|
|
|
593
601
|
// packages/context/src/lib/fragments/message/user.ts
|
|
594
|
-
import { generateId as generateId3
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}
|
|
604
|
-
function afterTurn(n) {
|
|
605
|
-
return ({ turn }) => turn > n;
|
|
606
|
-
}
|
|
607
|
-
function and(...predicates) {
|
|
608
|
-
return (ctx) => predicates.every((p) => p(ctx));
|
|
609
|
-
}
|
|
610
|
-
function or(...predicates) {
|
|
611
|
-
return (ctx) => predicates.some((p) => p(ctx));
|
|
612
|
-
}
|
|
613
|
-
function not(predicate) {
|
|
614
|
-
return (ctx) => !predicate(ctx);
|
|
615
|
-
}
|
|
616
|
-
function contentIncludes(keywords) {
|
|
617
|
-
const lower = keywords.map((k) => k.toLowerCase());
|
|
618
|
-
return (ctx) => {
|
|
619
|
-
const text = ctx.content.toLowerCase();
|
|
620
|
-
return lower.some((kw) => text.includes(kw));
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
function contentPattern(pattern) {
|
|
624
|
-
return (ctx) => {
|
|
625
|
-
pattern.lastIndex = 0;
|
|
626
|
-
return pattern.test(ctx.content);
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
function toDateParts(date, tz) {
|
|
630
|
-
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
631
|
-
timeZone: tz,
|
|
632
|
-
year: "numeric",
|
|
633
|
-
month: "2-digit",
|
|
634
|
-
day: "2-digit",
|
|
635
|
-
hour: "2-digit",
|
|
636
|
-
hourCycle: "h23"
|
|
637
|
-
}).formatToParts(date);
|
|
638
|
-
const get = (type) => parts.find((p) => p.type === type).value;
|
|
639
|
-
return {
|
|
640
|
-
year: get("year"),
|
|
641
|
-
month: get("month"),
|
|
642
|
-
day: get("day"),
|
|
643
|
-
hour: get("hour")
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function temporalChanged(ctx, tz, getKey) {
|
|
647
|
-
if (ctx.lastMessageAt === void 0) return true;
|
|
648
|
-
const nowParts = toDateParts(/* @__PURE__ */ new Date(), tz);
|
|
649
|
-
const prevParts = toDateParts(new Date(ctx.lastMessageAt), tz);
|
|
650
|
-
return getKey(nowParts) !== getKey(prevParts);
|
|
651
|
-
}
|
|
652
|
-
function getSeason(month) {
|
|
653
|
-
if (month >= 2 && month <= 4) return "Spring";
|
|
654
|
-
if (month >= 5 && month <= 7) return "Summer";
|
|
655
|
-
if (month >= 8 && month <= 10) return "Fall";
|
|
656
|
-
return "Winter";
|
|
657
|
-
}
|
|
658
|
-
function isoWeekKey(parts) {
|
|
659
|
-
const d = new Date(
|
|
660
|
-
Date.UTC(
|
|
661
|
-
parseInt(parts.year),
|
|
662
|
-
parseInt(parts.month) - 1,
|
|
663
|
-
parseInt(parts.day)
|
|
664
|
-
)
|
|
665
|
-
);
|
|
666
|
-
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
|
|
667
|
-
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
668
|
-
const weekNo = Math.ceil(
|
|
669
|
-
((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7
|
|
670
|
-
);
|
|
671
|
-
return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
|
|
672
|
-
}
|
|
673
|
-
function dayChanged(tz = "UTC") {
|
|
674
|
-
return (ctx) => temporalChanged(ctx, tz, (p) => `${p.year}-${p.month}-${p.day}`);
|
|
675
|
-
}
|
|
676
|
-
function hourChanged(tz = "UTC") {
|
|
677
|
-
return (ctx) => temporalChanged(ctx, tz, (p) => `${p.year}-${p.month}-${p.day}-${p.hour}`);
|
|
678
|
-
}
|
|
679
|
-
function monthChanged(tz = "UTC") {
|
|
680
|
-
return (ctx) => temporalChanged(ctx, tz, (p) => `${p.year}-${p.month}`);
|
|
681
|
-
}
|
|
682
|
-
function yearChanged(tz = "UTC") {
|
|
683
|
-
return (ctx) => temporalChanged(ctx, tz, (p) => p.year);
|
|
684
|
-
}
|
|
685
|
-
function seasonChanged(tz = "UTC") {
|
|
686
|
-
return (ctx) => temporalChanged(ctx, tz, (p) => getSeason(parseInt(p.month) - 1));
|
|
687
|
-
}
|
|
688
|
-
function weekChanged(tz = "UTC") {
|
|
689
|
-
return (ctx) => temporalChanged(ctx, tz, isoWeekKey);
|
|
690
|
-
}
|
|
691
|
-
function isConditionalReminder(fragment2) {
|
|
692
|
-
return fragment2.name === "reminder" && !!fragment2.metadata?.reminder;
|
|
693
|
-
}
|
|
694
|
-
function getConditionalReminder(fragment2) {
|
|
695
|
-
return fragment2.metadata.reminder;
|
|
696
|
-
}
|
|
697
|
-
var SYSTEM_REMINDER_OPEN_TAG = "<system-reminder>";
|
|
698
|
-
var SYSTEM_REMINDER_CLOSE_TAG = "</system-reminder>";
|
|
699
|
-
function getReminderRanges(metadata) {
|
|
700
|
-
return metadata?.reminders ?? [];
|
|
701
|
-
}
|
|
702
|
-
function stripTextByRanges(text, ranges) {
|
|
703
|
-
if (ranges.length === 0) {
|
|
704
|
-
return text;
|
|
602
|
+
import { generateId as generateId3 } from "ai";
|
|
603
|
+
|
|
604
|
+
// packages/context/src/lib/renderers/abstract.renderer.ts
|
|
605
|
+
import pluralize from "pluralize";
|
|
606
|
+
import { titlecase } from "stringcase";
|
|
607
|
+
var ContextRenderer = class {
|
|
608
|
+
options;
|
|
609
|
+
constructor(options = {}) {
|
|
610
|
+
this.options = options;
|
|
705
611
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
return text;
|
|
612
|
+
/**
|
|
613
|
+
* Check if data is a primitive (string, number, boolean).
|
|
614
|
+
*/
|
|
615
|
+
isPrimitive(data) {
|
|
616
|
+
return typeof data === "string" || typeof data === "number" || typeof data === "boolean";
|
|
712
617
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
618
|
+
/**
|
|
619
|
+
* Group fragments by name for groupFragments option.
|
|
620
|
+
*/
|
|
621
|
+
groupByName(fragments) {
|
|
622
|
+
const groups = /* @__PURE__ */ new Map();
|
|
623
|
+
for (const fragment2 of fragments) {
|
|
624
|
+
const existing = groups.get(fragment2.name) ?? [];
|
|
625
|
+
existing.push(fragment2);
|
|
626
|
+
groups.set(fragment2.name, existing);
|
|
627
|
+
}
|
|
628
|
+
return groups;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Remove null/undefined from fragments and fragment data recursively.
|
|
632
|
+
* This protects renderers from nullish values and ensures they are ignored
|
|
633
|
+
* consistently across all output formats.
|
|
634
|
+
*/
|
|
635
|
+
sanitizeFragments(fragments) {
|
|
636
|
+
const sanitized = [];
|
|
637
|
+
for (const fragment2 of fragments) {
|
|
638
|
+
const cleaned = this.sanitizeFragment(fragment2, /* @__PURE__ */ new WeakSet());
|
|
639
|
+
if (cleaned) {
|
|
640
|
+
sanitized.push(cleaned);
|
|
719
641
|
}
|
|
720
|
-
continue;
|
|
721
642
|
}
|
|
722
|
-
|
|
723
|
-
cursor = range.end;
|
|
643
|
+
return sanitized;
|
|
724
644
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const partRanges = rangesByPartIndex.get(range.partIndex) ?? [];
|
|
735
|
-
partRanges.push({ start: range.start, end: range.end });
|
|
736
|
-
rangesByPartIndex.set(range.partIndex, partRanges);
|
|
645
|
+
sanitizeFragment(fragment2, seen) {
|
|
646
|
+
const data = this.sanitizeData(getFragmentData(fragment2), seen);
|
|
647
|
+
if (data == null) {
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
return {
|
|
651
|
+
...fragment2,
|
|
652
|
+
data
|
|
653
|
+
};
|
|
737
654
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if (clonedPart.type !== "text" || ranges === void 0) {
|
|
742
|
-
return [clonedPart];
|
|
655
|
+
sanitizeData(data, seen) {
|
|
656
|
+
if (data == null) {
|
|
657
|
+
return void 0;
|
|
743
658
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
return [];
|
|
659
|
+
if (isFragment(data)) {
|
|
660
|
+
return this.sanitizeFragment(data, seen) ?? void 0;
|
|
747
661
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
662
|
+
if (Array.isArray(data)) {
|
|
663
|
+
if (seen.has(data)) {
|
|
664
|
+
return void 0;
|
|
665
|
+
}
|
|
666
|
+
seen.add(data);
|
|
667
|
+
const cleaned = [];
|
|
668
|
+
for (const item of data) {
|
|
669
|
+
const sanitizedItem = this.sanitizeData(item, seen);
|
|
670
|
+
if (sanitizedItem != null) {
|
|
671
|
+
cleaned.push(sanitizedItem);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return cleaned;
|
|
761
675
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
function formatTaggedReminder(text) {
|
|
777
|
-
return `${SYSTEM_REMINDER_OPEN_TAG}${text}${SYSTEM_REMINDER_CLOSE_TAG}`;
|
|
778
|
-
}
|
|
779
|
-
function findLastTextPartIndex(message2) {
|
|
780
|
-
for (let i = message2.parts.length - 1; i >= 0; i--) {
|
|
781
|
-
if (message2.parts[i].type === "text") {
|
|
782
|
-
return i;
|
|
676
|
+
if (isFragmentObject(data)) {
|
|
677
|
+
if (seen.has(data)) {
|
|
678
|
+
return void 0;
|
|
679
|
+
}
|
|
680
|
+
seen.add(data);
|
|
681
|
+
const cleaned = {};
|
|
682
|
+
for (const [key, value] of Object.entries(data)) {
|
|
683
|
+
const sanitizedValue = this.sanitizeData(value, seen);
|
|
684
|
+
if (sanitizedValue != null) {
|
|
685
|
+
cleaned[key] = sanitizedValue;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return cleaned;
|
|
783
689
|
}
|
|
784
|
-
|
|
785
|
-
return void 0;
|
|
786
|
-
}
|
|
787
|
-
function ensureTextPart(message2) {
|
|
788
|
-
const existingIndex = findLastTextPartIndex(message2);
|
|
789
|
-
if (existingIndex !== void 0) {
|
|
790
|
-
return existingIndex;
|
|
791
|
-
}
|
|
792
|
-
const reminderPart = {
|
|
793
|
-
type: "text",
|
|
794
|
-
text: ""
|
|
795
|
-
};
|
|
796
|
-
message2.parts.push(reminderPart);
|
|
797
|
-
return message2.parts.length - 1;
|
|
798
|
-
}
|
|
799
|
-
function applyInlineReminder(message2, value) {
|
|
800
|
-
const partIndex = ensureTextPart(message2);
|
|
801
|
-
const textPart = message2.parts[partIndex];
|
|
802
|
-
if (textPart.type !== "text") {
|
|
803
|
-
throw new Error("Failed to resolve text part for inline reminder");
|
|
804
|
-
}
|
|
805
|
-
const reminderText = formatTaggedReminder(value);
|
|
806
|
-
const start = textPart.text.length;
|
|
807
|
-
const updatedText = `${textPart.text}${reminderText}`;
|
|
808
|
-
message2.parts[partIndex] = { ...textPart, text: updatedText };
|
|
809
|
-
return {
|
|
810
|
-
id: generateId3(),
|
|
811
|
-
text: value,
|
|
812
|
-
partIndex,
|
|
813
|
-
start,
|
|
814
|
-
end: start + reminderText.length,
|
|
815
|
-
mode: "inline"
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
function applyPartReminder(message2, value) {
|
|
819
|
-
const part = { type: "text", text: value };
|
|
820
|
-
message2.parts.push(part);
|
|
821
|
-
const partIndex = message2.parts.length - 1;
|
|
822
|
-
return {
|
|
823
|
-
id: generateId3(),
|
|
824
|
-
text: value,
|
|
825
|
-
partIndex,
|
|
826
|
-
start: 0,
|
|
827
|
-
end: value.length,
|
|
828
|
-
mode: "part"
|
|
829
|
-
};
|
|
830
|
-
}
|
|
831
|
-
function resolveReminderText(item, ctx) {
|
|
832
|
-
return resolveReminder(item, ctx)?.text ?? "";
|
|
833
|
-
}
|
|
834
|
-
function normalizeReminderResolution(value) {
|
|
835
|
-
if (typeof value === "string") {
|
|
836
|
-
return value.trim().length === 0 ? null : { text: value };
|
|
837
|
-
}
|
|
838
|
-
if (value.text.trim().length === 0) {
|
|
839
|
-
return null;
|
|
840
|
-
}
|
|
841
|
-
return value;
|
|
842
|
-
}
|
|
843
|
-
function resolveReminder(item, ctx) {
|
|
844
|
-
const resolvedText = typeof item.text === "function" ? item.text(ctx) : item.text;
|
|
845
|
-
const resolved = normalizeReminderResolution(resolvedText);
|
|
846
|
-
if (!resolved) {
|
|
847
|
-
return null;
|
|
848
|
-
}
|
|
849
|
-
const metadata = item.metadata || resolved.metadata ? {
|
|
850
|
-
...item.metadata ?? {},
|
|
851
|
-
...resolved.metadata ?? {}
|
|
852
|
-
} : void 0;
|
|
853
|
-
return metadata ? { ...resolved, metadata } : resolved;
|
|
854
|
-
}
|
|
855
|
-
function mergeMessageMetadata(message2, addedMetadata) {
|
|
856
|
-
if (Object.keys(addedMetadata).length === 0) {
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
|
-
const metadata = isRecord(message2.metadata) ? { ...message2.metadata } : {};
|
|
860
|
-
message2.metadata = { ...metadata, ...addedMetadata };
|
|
861
|
-
}
|
|
862
|
-
function applyReminderToMessage(message2, item, ctx) {
|
|
863
|
-
const resolved = resolveReminder(item, ctx);
|
|
864
|
-
if (!resolved) {
|
|
865
|
-
return null;
|
|
866
|
-
}
|
|
867
|
-
if (resolved.metadata) {
|
|
868
|
-
mergeMessageMetadata(message2, resolved.metadata);
|
|
869
|
-
}
|
|
870
|
-
return item.asPart ? applyPartReminder(message2, resolved.text) : applyInlineReminder(message2, resolved.text);
|
|
871
|
-
}
|
|
872
|
-
function mergeReminderMetadata(message2, addedReminders) {
|
|
873
|
-
if (addedReminders.length === 0) return;
|
|
874
|
-
const metadata = isRecord(message2.metadata) ? { ...message2.metadata } : {};
|
|
875
|
-
const existing = Array.isArray(metadata.reminders) ? metadata.reminders : [];
|
|
876
|
-
metadata.reminders = [...existing, ...addedReminders];
|
|
877
|
-
message2.metadata = metadata;
|
|
878
|
-
}
|
|
879
|
-
function reminder(text, options) {
|
|
880
|
-
if (typeof text === "string") {
|
|
881
|
-
assertReminderText(text);
|
|
882
|
-
}
|
|
883
|
-
if (options && "when" in options && options.when) {
|
|
884
|
-
return {
|
|
885
|
-
name: "reminder",
|
|
886
|
-
metadata: {
|
|
887
|
-
reminder: {
|
|
888
|
-
text,
|
|
889
|
-
when: options.when,
|
|
890
|
-
asPart: options.asPart ?? false
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
return {
|
|
896
|
-
text,
|
|
897
|
-
asPart: options?.asPart ?? false
|
|
898
|
-
};
|
|
899
|
-
}
|
|
900
|
-
function user(content, ...reminders) {
|
|
901
|
-
const message2 = typeof content === "string" ? {
|
|
902
|
-
id: generateId3(),
|
|
903
|
-
role: "user",
|
|
904
|
-
parts: [{ type: "text", text: content }]
|
|
905
|
-
} : { ...content, role: "user", parts: [...content.parts] };
|
|
906
|
-
if (reminders.length > 0) {
|
|
907
|
-
const plainText = extractPlainText(message2);
|
|
908
|
-
const added = [];
|
|
909
|
-
for (const item of reminders) {
|
|
910
|
-
const meta = applyReminderToMessage(message2, item, {
|
|
911
|
-
content: plainText
|
|
912
|
-
});
|
|
913
|
-
if (meta) added.push(meta);
|
|
914
|
-
}
|
|
915
|
-
mergeReminderMetadata(message2, added);
|
|
916
|
-
}
|
|
917
|
-
return {
|
|
918
|
-
id: message2.id,
|
|
919
|
-
name: "user",
|
|
920
|
-
type: "message",
|
|
921
|
-
persist: true,
|
|
922
|
-
codec: {
|
|
923
|
-
decode() {
|
|
924
|
-
return message2;
|
|
925
|
-
},
|
|
926
|
-
encode() {
|
|
927
|
-
return message2;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
};
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// packages/context/src/lib/renderers/abstract.renderer.ts
|
|
934
|
-
import pluralize from "pluralize";
|
|
935
|
-
import { titlecase } from "stringcase";
|
|
936
|
-
var ContextRenderer = class {
|
|
937
|
-
options;
|
|
938
|
-
constructor(options = {}) {
|
|
939
|
-
this.options = options;
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Check if data is a primitive (string, number, boolean).
|
|
943
|
-
*/
|
|
944
|
-
isPrimitive(data) {
|
|
945
|
-
return typeof data === "string" || typeof data === "number" || typeof data === "boolean";
|
|
946
|
-
}
|
|
947
|
-
/**
|
|
948
|
-
* Group fragments by name for groupFragments option.
|
|
949
|
-
*/
|
|
950
|
-
groupByName(fragments) {
|
|
951
|
-
const groups = /* @__PURE__ */ new Map();
|
|
952
|
-
for (const fragment2 of fragments) {
|
|
953
|
-
const existing = groups.get(fragment2.name) ?? [];
|
|
954
|
-
existing.push(fragment2);
|
|
955
|
-
groups.set(fragment2.name, existing);
|
|
956
|
-
}
|
|
957
|
-
return groups;
|
|
958
|
-
}
|
|
959
|
-
/**
|
|
960
|
-
* Remove null/undefined from fragments and fragment data recursively.
|
|
961
|
-
* This protects renderers from nullish values and ensures they are ignored
|
|
962
|
-
* consistently across all output formats.
|
|
963
|
-
*/
|
|
964
|
-
sanitizeFragments(fragments) {
|
|
965
|
-
const sanitized = [];
|
|
966
|
-
for (const fragment2 of fragments) {
|
|
967
|
-
const cleaned = this.sanitizeFragment(fragment2, /* @__PURE__ */ new WeakSet());
|
|
968
|
-
if (cleaned) {
|
|
969
|
-
sanitized.push(cleaned);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
return sanitized;
|
|
973
|
-
}
|
|
974
|
-
sanitizeFragment(fragment2, seen) {
|
|
975
|
-
const data = this.sanitizeData(getFragmentData(fragment2), seen);
|
|
976
|
-
if (data == null) {
|
|
977
|
-
return null;
|
|
978
|
-
}
|
|
979
|
-
return {
|
|
980
|
-
...fragment2,
|
|
981
|
-
data
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
sanitizeData(data, seen) {
|
|
985
|
-
if (data == null) {
|
|
986
|
-
return void 0;
|
|
987
|
-
}
|
|
988
|
-
if (isFragment(data)) {
|
|
989
|
-
return this.sanitizeFragment(data, seen) ?? void 0;
|
|
990
|
-
}
|
|
991
|
-
if (Array.isArray(data)) {
|
|
992
|
-
if (seen.has(data)) {
|
|
993
|
-
return void 0;
|
|
994
|
-
}
|
|
995
|
-
seen.add(data);
|
|
996
|
-
const cleaned = [];
|
|
997
|
-
for (const item of data) {
|
|
998
|
-
const sanitizedItem = this.sanitizeData(item, seen);
|
|
999
|
-
if (sanitizedItem != null) {
|
|
1000
|
-
cleaned.push(sanitizedItem);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
return cleaned;
|
|
1004
|
-
}
|
|
1005
|
-
if (isFragmentObject(data)) {
|
|
1006
|
-
if (seen.has(data)) {
|
|
1007
|
-
return void 0;
|
|
1008
|
-
}
|
|
1009
|
-
seen.add(data);
|
|
1010
|
-
const cleaned = {};
|
|
1011
|
-
for (const [key, value] of Object.entries(data)) {
|
|
1012
|
-
const sanitizedValue = this.sanitizeData(value, seen);
|
|
1013
|
-
if (sanitizedValue != null) {
|
|
1014
|
-
cleaned[key] = sanitizedValue;
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
return cleaned;
|
|
1018
|
-
}
|
|
1019
|
-
return data;
|
|
690
|
+
return data;
|
|
1020
691
|
}
|
|
1021
692
|
/**
|
|
1022
693
|
* Template method - dispatches value to appropriate handler.
|
|
@@ -1806,24 +1477,391 @@ ${entries}`;
|
|
|
1806
1477
|
}
|
|
1807
1478
|
};
|
|
1808
1479
|
|
|
1809
|
-
// packages/context/src/lib/
|
|
1810
|
-
|
|
1811
|
-
|
|
1480
|
+
// packages/context/src/lib/text.ts
|
|
1481
|
+
import { isTextUIPart } from "ai";
|
|
1482
|
+
function getTextParts(message2) {
|
|
1483
|
+
return message2.parts.filter(isTextUIPart).map((part) => part.text);
|
|
1484
|
+
}
|
|
1485
|
+
function extractPlainText(message2) {
|
|
1486
|
+
return getTextParts(message2).join(" ");
|
|
1487
|
+
}
|
|
1812
1488
|
|
|
1813
|
-
// packages/context/src/lib/
|
|
1814
|
-
function
|
|
1815
|
-
return
|
|
1489
|
+
// packages/context/src/lib/fragments/message/user.ts
|
|
1490
|
+
function everyNTurns(n) {
|
|
1491
|
+
return ({ turn }) => turn % n === 0;
|
|
1816
1492
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1493
|
+
function once() {
|
|
1494
|
+
return ({ turn }) => turn === 1;
|
|
1495
|
+
}
|
|
1496
|
+
function firstN(n) {
|
|
1497
|
+
return ({ turn }) => turn <= n;
|
|
1498
|
+
}
|
|
1499
|
+
function afterTurn(n) {
|
|
1500
|
+
return ({ turn }) => turn > n;
|
|
1501
|
+
}
|
|
1502
|
+
function and(...predicates) {
|
|
1503
|
+
return async (ctx) => {
|
|
1504
|
+
for (const it of predicates) {
|
|
1505
|
+
if (!await it(ctx)) return false;
|
|
1506
|
+
}
|
|
1507
|
+
return true;
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
function or(...predicates) {
|
|
1511
|
+
return async (ctx) => {
|
|
1512
|
+
for (const it of predicates) {
|
|
1513
|
+
if (await it(ctx)) return true;
|
|
1514
|
+
}
|
|
1515
|
+
return false;
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
function not(predicate) {
|
|
1519
|
+
return async (ctx) => !await predicate(ctx);
|
|
1520
|
+
}
|
|
1521
|
+
function contentIncludes(keywords) {
|
|
1522
|
+
const lower = keywords.map((k) => k.toLowerCase());
|
|
1523
|
+
return (ctx) => {
|
|
1524
|
+
const text = ctx.content.toLowerCase();
|
|
1525
|
+
return lower.some((kw) => text.includes(kw));
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function contentPattern(pattern) {
|
|
1529
|
+
return (ctx) => {
|
|
1530
|
+
pattern.lastIndex = 0;
|
|
1531
|
+
return pattern.test(ctx.content);
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
function toDateParts(date, tz) {
|
|
1535
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
1536
|
+
timeZone: tz,
|
|
1537
|
+
year: "numeric",
|
|
1538
|
+
month: "2-digit",
|
|
1539
|
+
day: "2-digit",
|
|
1540
|
+
hour: "2-digit",
|
|
1541
|
+
hourCycle: "h23"
|
|
1542
|
+
}).formatToParts(date);
|
|
1543
|
+
const get = (type) => parts.find((p) => p.type === type).value;
|
|
1544
|
+
return {
|
|
1545
|
+
year: get("year"),
|
|
1546
|
+
month: get("month"),
|
|
1547
|
+
day: get("day"),
|
|
1548
|
+
hour: get("hour")
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
function temporalChanged(ctx, tz, getKey) {
|
|
1552
|
+
if (ctx.lastMessageAt === void 0) return true;
|
|
1553
|
+
const nowParts = toDateParts(/* @__PURE__ */ new Date(), tz);
|
|
1554
|
+
const prevParts = toDateParts(new Date(ctx.lastMessageAt), tz);
|
|
1555
|
+
return getKey(nowParts) !== getKey(prevParts);
|
|
1556
|
+
}
|
|
1557
|
+
function getSeason(month) {
|
|
1558
|
+
if (month >= 2 && month <= 4) return "Spring";
|
|
1559
|
+
if (month >= 5 && month <= 7) return "Summer";
|
|
1560
|
+
if (month >= 8 && month <= 10) return "Fall";
|
|
1561
|
+
return "Winter";
|
|
1562
|
+
}
|
|
1563
|
+
function isoWeekKey(parts) {
|
|
1564
|
+
const d = new Date(
|
|
1565
|
+
Date.UTC(
|
|
1566
|
+
parseInt(parts.year),
|
|
1567
|
+
parseInt(parts.month) - 1,
|
|
1568
|
+
parseInt(parts.day)
|
|
1569
|
+
)
|
|
1570
|
+
);
|
|
1571
|
+
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
|
|
1572
|
+
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
1573
|
+
const weekNo = Math.ceil(
|
|
1574
|
+
((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7
|
|
1575
|
+
);
|
|
1576
|
+
return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
|
|
1577
|
+
}
|
|
1578
|
+
function dayChanged(tz = "UTC") {
|
|
1579
|
+
return (ctx) => temporalChanged(ctx, tz, (p) => `${p.year}-${p.month}-${p.day}`);
|
|
1580
|
+
}
|
|
1581
|
+
function hourChanged(tz = "UTC") {
|
|
1582
|
+
return (ctx) => temporalChanged(ctx, tz, (p) => `${p.year}-${p.month}-${p.day}-${p.hour}`);
|
|
1583
|
+
}
|
|
1584
|
+
function monthChanged(tz = "UTC") {
|
|
1585
|
+
return (ctx) => temporalChanged(ctx, tz, (p) => `${p.year}-${p.month}`);
|
|
1586
|
+
}
|
|
1587
|
+
function yearChanged(tz = "UTC") {
|
|
1588
|
+
return (ctx) => temporalChanged(ctx, tz, (p) => p.year);
|
|
1589
|
+
}
|
|
1590
|
+
function seasonChanged(tz = "UTC") {
|
|
1591
|
+
return (ctx) => temporalChanged(ctx, tz, (p) => getSeason(parseInt(p.month) - 1));
|
|
1592
|
+
}
|
|
1593
|
+
function weekChanged(tz = "UTC") {
|
|
1594
|
+
return (ctx) => temporalChanged(ctx, tz, isoWeekKey);
|
|
1595
|
+
}
|
|
1596
|
+
function isConditionalReminder(fragment2) {
|
|
1597
|
+
return fragment2.name === "reminder" && !!fragment2.metadata?.reminder;
|
|
1598
|
+
}
|
|
1599
|
+
function getConditionalReminder(fragment2) {
|
|
1600
|
+
return fragment2.metadata.reminder;
|
|
1601
|
+
}
|
|
1602
|
+
var SYSTEM_REMINDER_OPEN_TAG = "<system-reminder>";
|
|
1603
|
+
var SYSTEM_REMINDER_CLOSE_TAG = "</system-reminder>";
|
|
1604
|
+
function getReminderRanges(metadata) {
|
|
1605
|
+
return metadata?.reminders ?? [];
|
|
1606
|
+
}
|
|
1607
|
+
function stripTextByRanges(text, ranges) {
|
|
1608
|
+
if (ranges.length === 0) {
|
|
1609
|
+
return text;
|
|
1610
|
+
}
|
|
1611
|
+
const normalized = ranges.map((range) => ({
|
|
1612
|
+
start: Math.max(0, Math.min(text.length, range.start)),
|
|
1613
|
+
end: Math.max(0, Math.min(text.length, range.end))
|
|
1614
|
+
})).filter((range) => range.end > range.start).sort((a, b) => a.start - b.start);
|
|
1615
|
+
if (normalized.length === 0) {
|
|
1616
|
+
return text;
|
|
1617
|
+
}
|
|
1618
|
+
let cursor = 0;
|
|
1619
|
+
let output = "";
|
|
1620
|
+
for (const range of normalized) {
|
|
1621
|
+
if (range.start < cursor) {
|
|
1622
|
+
if (range.end > cursor) {
|
|
1623
|
+
cursor = range.end;
|
|
1624
|
+
}
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
output += text.slice(cursor, range.start);
|
|
1628
|
+
cursor = range.end;
|
|
1629
|
+
}
|
|
1630
|
+
output += text.slice(cursor);
|
|
1631
|
+
return output.trimEnd();
|
|
1632
|
+
}
|
|
1633
|
+
function stripReminders(message2) {
|
|
1634
|
+
const reminderRanges = getReminderRanges(
|
|
1635
|
+
isRecord(message2.metadata) ? message2.metadata : void 0
|
|
1636
|
+
);
|
|
1637
|
+
const rangesByPartIndex = /* @__PURE__ */ new Map();
|
|
1638
|
+
for (const range of reminderRanges) {
|
|
1639
|
+
const partRanges = rangesByPartIndex.get(range.partIndex) ?? [];
|
|
1640
|
+
partRanges.push({ start: range.start, end: range.end });
|
|
1641
|
+
rangesByPartIndex.set(range.partIndex, partRanges);
|
|
1642
|
+
}
|
|
1643
|
+
const strippedParts = message2.parts.flatMap((part, partIndex) => {
|
|
1644
|
+
const clonedPart = { ...part };
|
|
1645
|
+
const ranges = rangesByPartIndex.get(partIndex);
|
|
1646
|
+
if (clonedPart.type !== "text" || ranges === void 0) {
|
|
1647
|
+
return [clonedPart];
|
|
1648
|
+
}
|
|
1649
|
+
const strippedText = stripTextByRanges(clonedPart.text, ranges);
|
|
1650
|
+
if (strippedText.length === 0) {
|
|
1651
|
+
return [];
|
|
1652
|
+
}
|
|
1653
|
+
return [{ ...clonedPart, text: strippedText }];
|
|
1654
|
+
});
|
|
1655
|
+
const nextMessage = {
|
|
1656
|
+
...message2,
|
|
1657
|
+
parts: strippedParts
|
|
1658
|
+
};
|
|
1659
|
+
if (isRecord(message2.metadata)) {
|
|
1660
|
+
const metadata = { ...message2.metadata };
|
|
1661
|
+
delete metadata.reminders;
|
|
1662
|
+
if (Object.keys(metadata).length > 0) {
|
|
1663
|
+
nextMessage.metadata = metadata;
|
|
1664
|
+
} else {
|
|
1665
|
+
delete nextMessage.metadata;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
return nextMessage;
|
|
1669
|
+
}
|
|
1670
|
+
function isRecord(value) {
|
|
1671
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1672
|
+
}
|
|
1673
|
+
function assertReminderText(text) {
|
|
1674
|
+
if (text.trim().length === 0) {
|
|
1675
|
+
throw new Error("Reminder text must not be empty");
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
function formatTaggedReminder(text) {
|
|
1679
|
+
return `${SYSTEM_REMINDER_OPEN_TAG}${text}${SYSTEM_REMINDER_CLOSE_TAG}`;
|
|
1680
|
+
}
|
|
1681
|
+
function findLastTextPartIndex(message2) {
|
|
1682
|
+
for (let i = message2.parts.length - 1; i >= 0; i--) {
|
|
1683
|
+
if (message2.parts[i].type === "text") {
|
|
1684
|
+
return i;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return void 0;
|
|
1688
|
+
}
|
|
1689
|
+
function ensureTextPart(message2) {
|
|
1690
|
+
const existingIndex = findLastTextPartIndex(message2);
|
|
1691
|
+
if (existingIndex !== void 0) {
|
|
1692
|
+
return existingIndex;
|
|
1693
|
+
}
|
|
1694
|
+
const reminderPart = {
|
|
1695
|
+
type: "text",
|
|
1696
|
+
text: ""
|
|
1697
|
+
};
|
|
1698
|
+
message2.parts.push(reminderPart);
|
|
1699
|
+
return message2.parts.length - 1;
|
|
1700
|
+
}
|
|
1701
|
+
function applyInlineReminder(message2, value) {
|
|
1702
|
+
const partIndex = ensureTextPart(message2);
|
|
1703
|
+
const textPart = message2.parts[partIndex];
|
|
1704
|
+
if (textPart.type !== "text") {
|
|
1705
|
+
throw new Error("Failed to resolve text part for inline reminder");
|
|
1706
|
+
}
|
|
1707
|
+
const reminderText = formatTaggedReminder(value);
|
|
1708
|
+
const start = textPart.text.length;
|
|
1709
|
+
const updatedText = `${textPart.text}${reminderText}`;
|
|
1710
|
+
message2.parts[partIndex] = { ...textPart, text: updatedText };
|
|
1711
|
+
return {
|
|
1712
|
+
id: generateId3(),
|
|
1713
|
+
text: value,
|
|
1714
|
+
partIndex,
|
|
1715
|
+
start,
|
|
1716
|
+
end: start + reminderText.length,
|
|
1717
|
+
mode: "inline"
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
function applyPartReminder(message2, value) {
|
|
1721
|
+
const part = { type: "text", text: value };
|
|
1722
|
+
message2.parts.push(part);
|
|
1723
|
+
const partIndex = message2.parts.length - 1;
|
|
1724
|
+
return {
|
|
1725
|
+
id: generateId3(),
|
|
1726
|
+
text: value,
|
|
1727
|
+
partIndex,
|
|
1728
|
+
start: 0,
|
|
1729
|
+
end: value.length,
|
|
1730
|
+
mode: "part"
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
function resolveReminderText(item, ctx) {
|
|
1734
|
+
return resolveReminder(item, ctx)?.text ?? "";
|
|
1735
|
+
}
|
|
1736
|
+
function normalizeReminderResolution(value) {
|
|
1737
|
+
if (typeof value === "string") {
|
|
1738
|
+
return value.trim().length === 0 ? null : { text: value };
|
|
1739
|
+
}
|
|
1740
|
+
if (value.text.trim().length === 0) {
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1743
|
+
return value;
|
|
1744
|
+
}
|
|
1745
|
+
function resolveReminder(item, ctx) {
|
|
1746
|
+
const resolvedText = typeof item.text === "function" ? item.text(ctx) : item.text;
|
|
1747
|
+
const resolved = normalizeReminderResolution(resolvedText);
|
|
1748
|
+
if (!resolved) {
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
const metadata = item.metadata || resolved.metadata ? {
|
|
1752
|
+
...item.metadata ?? {},
|
|
1753
|
+
...resolved.metadata ?? {}
|
|
1754
|
+
} : void 0;
|
|
1755
|
+
return metadata ? { ...resolved, metadata } : resolved;
|
|
1756
|
+
}
|
|
1757
|
+
async function resolveReminderAsync(item, ctx) {
|
|
1758
|
+
const text = await (typeof item.text === "function" ? item.text(ctx) : item.text);
|
|
1759
|
+
const resolved = normalizeReminderResolution(text);
|
|
1760
|
+
if (!resolved) return null;
|
|
1761
|
+
const metadata = item.metadata || resolved.metadata ? { ...item.metadata ?? {}, ...resolved.metadata ?? {} } : void 0;
|
|
1762
|
+
return metadata ? { ...resolved, metadata } : resolved;
|
|
1763
|
+
}
|
|
1764
|
+
function mergeMessageMetadata(message2, addedMetadata) {
|
|
1765
|
+
if (Object.keys(addedMetadata).length === 0) {
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
const metadata = isRecord(message2.metadata) ? { ...message2.metadata } : {};
|
|
1769
|
+
message2.metadata = { ...metadata, ...addedMetadata };
|
|
1770
|
+
}
|
|
1771
|
+
function applyReminderToMessage(message2, item, ctx) {
|
|
1772
|
+
const resolved = resolveReminder(item, ctx);
|
|
1773
|
+
if (!resolved) {
|
|
1774
|
+
return null;
|
|
1775
|
+
}
|
|
1776
|
+
if (resolved.metadata) {
|
|
1777
|
+
mergeMessageMetadata(message2, resolved.metadata);
|
|
1778
|
+
}
|
|
1779
|
+
return item.asPart ? applyPartReminder(message2, resolved.text) : applyInlineReminder(message2, resolved.text);
|
|
1780
|
+
}
|
|
1781
|
+
function mergeReminderMetadata(message2, addedReminders) {
|
|
1782
|
+
if (addedReminders.length === 0) return;
|
|
1783
|
+
const metadata = isRecord(message2.metadata) ? { ...message2.metadata } : {};
|
|
1784
|
+
const existing = Array.isArray(metadata.reminders) ? metadata.reminders : [];
|
|
1785
|
+
metadata.reminders = [...existing, ...addedReminders];
|
|
1786
|
+
message2.metadata = metadata;
|
|
1787
|
+
}
|
|
1788
|
+
function reminder(textOrFragment, options) {
|
|
1789
|
+
const text = isFragment(textOrFragment) ? new XmlRenderer().render([textOrFragment]) : textOrFragment;
|
|
1790
|
+
if (typeof text === "string") {
|
|
1791
|
+
assertReminderText(text);
|
|
1792
|
+
}
|
|
1793
|
+
if (options && "when" in options && options.when) {
|
|
1794
|
+
return {
|
|
1795
|
+
name: "reminder",
|
|
1796
|
+
data: null,
|
|
1797
|
+
metadata: {
|
|
1798
|
+
reminder: {
|
|
1799
|
+
text,
|
|
1800
|
+
when: options.when,
|
|
1801
|
+
asPart: options.asPart ?? false
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
return {
|
|
1807
|
+
text,
|
|
1808
|
+
asPart: options?.asPart ?? false
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
function user(content, ...reminders) {
|
|
1812
|
+
const message2 = typeof content === "string" ? {
|
|
1813
|
+
id: generateId3(),
|
|
1814
|
+
role: "user",
|
|
1815
|
+
parts: [{ type: "text", text: content }]
|
|
1816
|
+
} : { ...content, role: "user", parts: [...content.parts] };
|
|
1817
|
+
if (reminders.length > 0) {
|
|
1818
|
+
const plainText = extractPlainText(message2);
|
|
1819
|
+
const added = [];
|
|
1820
|
+
for (const item of reminders) {
|
|
1821
|
+
const meta = applyReminderToMessage(message2, item, {
|
|
1822
|
+
content: plainText
|
|
1823
|
+
});
|
|
1824
|
+
if (meta) added.push(meta);
|
|
1825
|
+
}
|
|
1826
|
+
mergeReminderMetadata(message2, added);
|
|
1827
|
+
}
|
|
1828
|
+
return {
|
|
1829
|
+
id: message2.id,
|
|
1830
|
+
name: "user",
|
|
1831
|
+
type: "message",
|
|
1832
|
+
persist: true,
|
|
1833
|
+
codec: {
|
|
1834
|
+
decode() {
|
|
1835
|
+
return message2;
|
|
1836
|
+
},
|
|
1837
|
+
encode() {
|
|
1838
|
+
return message2;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
// packages/context/src/lib/store/store.ts
|
|
1845
|
+
var ContextStore = class {
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
// packages/context/src/lib/engine.ts
|
|
1849
|
+
function estimateMessageContent(data) {
|
|
1850
|
+
return typeof data === "string" ? data : JSON.stringify(data);
|
|
1851
|
+
}
|
|
1852
|
+
function isLanguageModelUsage(value) {
|
|
1853
|
+
return typeof value === "object" && value !== null && "totalTokens" in value;
|
|
1854
|
+
}
|
|
1855
|
+
var ContextEngine = class {
|
|
1856
|
+
/** Non-message fragments (role, hints, etc.) - not persisted in graph */
|
|
1857
|
+
#fragments = [];
|
|
1858
|
+
/** Pending message fragments to be added to graph */
|
|
1859
|
+
#pendingMessages = [];
|
|
1860
|
+
#store;
|
|
1861
|
+
#chatId;
|
|
1862
|
+
#userId;
|
|
1863
|
+
#branchName;
|
|
1864
|
+
#branch = null;
|
|
1827
1865
|
#chatData = null;
|
|
1828
1866
|
#initialized = false;
|
|
1829
1867
|
/** Initial metadata to merge on first initialization */
|
|
@@ -1949,17 +1987,7 @@ var ContextEngine = class {
|
|
|
1949
1987
|
* Returns null if the chat hasn't been initialized yet.
|
|
1950
1988
|
*/
|
|
1951
1989
|
get chat() {
|
|
1952
|
-
|
|
1953
|
-
return null;
|
|
1954
|
-
}
|
|
1955
|
-
return {
|
|
1956
|
-
id: this.#chatData.id,
|
|
1957
|
-
userId: this.#chatData.userId,
|
|
1958
|
-
createdAt: this.#chatData.createdAt,
|
|
1959
|
-
updatedAt: this.#chatData.updatedAt,
|
|
1960
|
-
title: this.#chatData.title,
|
|
1961
|
-
metadata: this.#chatData.metadata
|
|
1962
|
-
};
|
|
1990
|
+
return this.#chatData;
|
|
1963
1991
|
}
|
|
1964
1992
|
/**
|
|
1965
1993
|
* Count user turns in the conversation and return the previous saved user message context.
|
|
@@ -1968,13 +1996,19 @@ var ContextEngine = class {
|
|
|
1968
1996
|
async #getChainContext() {
|
|
1969
1997
|
await this.#ensureInitialized();
|
|
1970
1998
|
let turn = 0;
|
|
1999
|
+
let messageCount = 0;
|
|
1971
2000
|
let lastMessageAt;
|
|
1972
2001
|
let lastMessage;
|
|
2002
|
+
let lastAssistantMessage2;
|
|
1973
2003
|
if (this.#branch?.headMessageId) {
|
|
1974
2004
|
const chain = await this.#store.getMessageChain(
|
|
1975
2005
|
this.#branch.headMessageId
|
|
1976
2006
|
);
|
|
1977
2007
|
for (const msg of chain) {
|
|
2008
|
+
messageCount++;
|
|
2009
|
+
if (msg.name === "assistant") {
|
|
2010
|
+
lastAssistantMessage2 = msg.data;
|
|
2011
|
+
}
|
|
1978
2012
|
if (msg.name !== "user") {
|
|
1979
2013
|
continue;
|
|
1980
2014
|
}
|
|
@@ -1984,9 +2018,16 @@ var ContextEngine = class {
|
|
|
1984
2018
|
}
|
|
1985
2019
|
}
|
|
1986
2020
|
for (const fragment2 of this.#pendingMessages) {
|
|
2021
|
+
messageCount++;
|
|
1987
2022
|
if (fragment2.name === "user") turn++;
|
|
1988
2023
|
}
|
|
1989
|
-
return {
|
|
2024
|
+
return {
|
|
2025
|
+
turn,
|
|
2026
|
+
messageCount,
|
|
2027
|
+
lastMessageAt,
|
|
2028
|
+
lastMessage,
|
|
2029
|
+
lastAssistantMessage: lastAssistantMessage2
|
|
2030
|
+
};
|
|
1990
2031
|
}
|
|
1991
2032
|
async getTurnCount() {
|
|
1992
2033
|
const { turn } = await this.#getChainContext();
|
|
@@ -2116,32 +2157,45 @@ var ContextEngine = class {
|
|
|
2116
2157
|
if (lastUserIndex >= 0) {
|
|
2117
2158
|
const lastUserFragment = this.#pendingMessages[lastUserIndex];
|
|
2118
2159
|
if (lastUserFragment.codec) {
|
|
2119
|
-
const {
|
|
2160
|
+
const {
|
|
2161
|
+
turn,
|
|
2162
|
+
messageCount,
|
|
2163
|
+
lastMessageAt,
|
|
2164
|
+
lastMessage,
|
|
2165
|
+
lastAssistantMessage: lastAssistantMessage2
|
|
2166
|
+
} = await this.#getChainContext();
|
|
2120
2167
|
const original = lastUserFragment.codec.encode();
|
|
2121
2168
|
const plainText = extractPlainText(original);
|
|
2122
|
-
const
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2169
|
+
const rawUsage = this.#chatData?.metadata?.usage;
|
|
2170
|
+
const usage = isLanguageModelUsage(rawUsage) ? rawUsage : void 0;
|
|
2171
|
+
const elapsed = lastMessageAt !== void 0 ? Date.now() - lastMessageAt : void 0;
|
|
2172
|
+
const whenCtx = {
|
|
2173
|
+
turn,
|
|
2174
|
+
content: plainText,
|
|
2175
|
+
lastMessageAt,
|
|
2176
|
+
lastMessage,
|
|
2177
|
+
chat: this.#chatData,
|
|
2178
|
+
usage,
|
|
2179
|
+
branch: this.#branchName,
|
|
2180
|
+
elapsed,
|
|
2181
|
+
messageCount,
|
|
2182
|
+
lastAssistantMessage: lastAssistantMessage2
|
|
2183
|
+
};
|
|
2184
|
+
const configs = conditionalReminders.map(getConditionalReminder);
|
|
2185
|
+
const whenResults = await Promise.all(
|
|
2186
|
+
configs.map((it) => it.when(whenCtx))
|
|
2129
2187
|
);
|
|
2188
|
+
const firedReminders = configs.filter((_, i) => whenResults[i]);
|
|
2130
2189
|
if (firedReminders.length > 0) {
|
|
2131
|
-
const
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
lastMessage
|
|
2137
|
-
});
|
|
2138
|
-
if (!resolved) {
|
|
2139
|
-
return [];
|
|
2140
|
-
}
|
|
2190
|
+
const resolvedOrNull = await Promise.all(
|
|
2191
|
+
firedReminders.map((it) => resolveReminderAsync(it, whenCtx))
|
|
2192
|
+
);
|
|
2193
|
+
const reminders = resolvedOrNull.flatMap((resolved, i) => {
|
|
2194
|
+
if (!resolved) return [];
|
|
2141
2195
|
return [
|
|
2142
2196
|
{
|
|
2143
2197
|
text: resolved.text,
|
|
2144
|
-
asPart:
|
|
2198
|
+
asPart: firedReminders[i].asPart,
|
|
2145
2199
|
metadata: resolved.metadata
|
|
2146
2200
|
}
|
|
2147
2201
|
];
|
|
@@ -7959,7 +8013,6 @@ export {
|
|
|
7959
8013
|
everyNTurns,
|
|
7960
8014
|
example,
|
|
7961
8015
|
explain,
|
|
7962
|
-
extractPlainText,
|
|
7963
8016
|
fail,
|
|
7964
8017
|
firstN,
|
|
7965
8018
|
fragment,
|
|
@@ -8010,6 +8063,7 @@ export {
|
|
|
8010
8063
|
render,
|
|
8011
8064
|
resetAdaptivePolling,
|
|
8012
8065
|
resolveReminder,
|
|
8066
|
+
resolveReminderAsync,
|
|
8013
8067
|
resolveReminderText,
|
|
8014
8068
|
role,
|
|
8015
8069
|
runGuardrailChain,
|