@botpress/runtime 1.13.16 → 1.13.17

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/library.js CHANGED
@@ -48,7 +48,7 @@ var init_define_BUILD = __esm({
48
48
  var define_PACKAGE_VERSIONS_default;
49
49
  var init_define_PACKAGE_VERSIONS = __esm({
50
50
  "<define:__PACKAGE_VERSIONS__>"() {
51
- define_PACKAGE_VERSIONS_default = { runtime: "1.13.16", adk: "1.13.16", sdk: "5.0.2", llmz: "0.0.37", zai: "2.5.6", cognitive: "0.3.3" };
51
+ define_PACKAGE_VERSIONS_default = { runtime: "1.13.17", adk: "1.13.17", sdk: "5.0.2", llmz: "0.0.37", zai: "2.5.6", cognitive: "0.3.3" };
52
52
  }
53
53
  });
54
54
 
@@ -35994,6 +35994,9 @@ function isEventMessage(event) {
35994
35994
  function isWorkflowCallback(event) {
35995
35995
  return event !== null && typeof event === "object" && "type" in event && event.type === "workflowCallback" && "payload" in event && event.payload !== null && typeof event.payload === "object";
35996
35996
  }
35997
+ function isMessage(message) {
35998
+ return message !== null && typeof message === "object" && "id" in message && "type" in message && "payload" in message;
35999
+ }
35997
36000
  function isEventOfType(event, eventName) {
35998
36001
  return isEvent(event) && event.type === eventName;
35999
36002
  }
@@ -37495,7 +37498,13 @@ var init_tracked_state = __esm({
37495
37498
  function isSystemTag(key) {
37496
37499
  return key.includes(":");
37497
37500
  }
37498
- var TrackedTags;
37501
+ function isBuiltInTag(key) {
37502
+ return key in BUILT_IN_TAGS.bot || key in BUILT_IN_TAGS.user || key in BUILT_IN_TAGS.conversation || key in BUILT_IN_TAGS.message || key in BUILT_IN_TAGS.workflow;
37503
+ }
37504
+ function getBuiltInTagsForType(type) {
37505
+ return Object.keys(BUILT_IN_TAGS[type]);
37506
+ }
37507
+ var BUILT_IN_TAGS, TrackedTags;
37499
37508
  var init_tracked_tags = __esm({
37500
37509
  "src/runtime/tracked-tags.ts"() {
37501
37510
  "use strict";
@@ -37504,6 +37513,19 @@ var init_tracked_tags = __esm({
37504
37513
  init_context();
37505
37514
  init_tracing();
37506
37515
  init_adk();
37516
+ BUILT_IN_TAGS = {
37517
+ conversation: {
37518
+ /** Last synced message timestamp - automatically updated when messages are processed */
37519
+ adkSyncTs: {
37520
+ title: "ADK Sync Timestamp",
37521
+ description: "Built-in: Last synced message timestamp. Automatically updated by the ADK when messages are processed."
37522
+ }
37523
+ },
37524
+ user: {},
37525
+ bot: {},
37526
+ message: {},
37527
+ workflow: {}
37528
+ };
37507
37529
  TrackedTags = class _TrackedTags {
37508
37530
  type;
37509
37531
  id;
@@ -37533,10 +37555,18 @@ var init_tracked_tags = __esm({
37533
37555
  return match2;
37534
37556
  }
37535
37557
  const instance = new _TrackedTags(props);
37558
+ const builtInTagKeys = getBuiltInTagsForType(props.type);
37559
+ const builtInTagDefaults = {};
37560
+ for (const key of builtInTagKeys) {
37561
+ builtInTagDefaults[key] = void 0;
37562
+ }
37536
37563
  if (props.initialTags) {
37537
- instance._tags = { ...props.initialTags };
37538
- instance._initialTags = { ...props.initialTags };
37564
+ instance._tags = { ...builtInTagDefaults, ...props.initialTags };
37565
+ instance._initialTags = { ...builtInTagDefaults, ...props.initialTags };
37539
37566
  instance._loaded = true;
37567
+ } else {
37568
+ instance._tags = { ...builtInTagDefaults };
37569
+ instance._initialTags = { ...builtInTagDefaults };
37540
37570
  }
37541
37571
  tags?.push(instance);
37542
37572
  return instance;
@@ -37635,9 +37665,14 @@ var init_tracked_tags = __esm({
37635
37665
  id: this.id
37636
37666
  },
37637
37667
  async () => {
37668
+ const builtInTagKeys = getBuiltInTagsForType(this.type);
37669
+ const builtInTagDefaults = {};
37670
+ for (const key of builtInTagKeys) {
37671
+ builtInTagDefaults[key] = void 0;
37672
+ }
37638
37673
  const tags = await this.fetchTags();
37639
- this._tags = { ...tags };
37640
- this._initialTags = { ...tags };
37674
+ this._tags = { ...builtInTagDefaults, ...tags };
37675
+ this._initialTags = { ...builtInTagDefaults, ...tags };
37641
37676
  this._loaded = true;
37642
37677
  }
37643
37678
  );
@@ -37758,7 +37793,7 @@ var init_tracked_tags = __esm({
37758
37793
  if (value === void 0 || isSystemTag(key)) {
37759
37794
  continue;
37760
37795
  }
37761
- if (validKeys.size === 0 || validKeys.has(key)) {
37796
+ if (isBuiltInTag(key) || validKeys.size === 0 || validKeys.has(key)) {
37762
37797
  tagsForApi[key] = value;
37763
37798
  } else {
37764
37799
  skippedTags.push(key);
@@ -37881,6 +37916,99 @@ var init_handlers = __esm({
37881
37916
  });
37882
37917
 
37883
37918
  // src/runtime/chat/truncate-object.ts
37919
+ function truncateObject(value, maxChars) {
37920
+ let currentSize = 0;
37921
+ function truncateValue(val) {
37922
+ if (currentSize >= maxChars) {
37923
+ return void 0;
37924
+ }
37925
+ if (val === null || val === void 0) {
37926
+ const size = 4;
37927
+ currentSize += size;
37928
+ return val;
37929
+ }
37930
+ if (typeof val === "boolean") {
37931
+ const size = val ? 4 : 5;
37932
+ currentSize += size;
37933
+ return val;
37934
+ }
37935
+ if (typeof val === "number") {
37936
+ const size = String(val).length;
37937
+ currentSize += size;
37938
+ return val;
37939
+ }
37940
+ if (typeof val === "string") {
37941
+ const availableChars2 = maxChars - currentSize;
37942
+ if (availableChars2 <= 0) {
37943
+ return void 0;
37944
+ }
37945
+ if (val.length <= availableChars2) {
37946
+ currentSize += val.length;
37947
+ return val;
37948
+ }
37949
+ const truncated2 = val.slice(0, availableChars2);
37950
+ currentSize += truncated2.length;
37951
+ return truncated2;
37952
+ }
37953
+ if (Array.isArray(val)) {
37954
+ currentSize += 2;
37955
+ const result2 = [];
37956
+ for (let i = 0; i < val.length; i++) {
37957
+ if (currentSize >= maxChars) {
37958
+ break;
37959
+ }
37960
+ if (i > 0) {
37961
+ currentSize += 1;
37962
+ }
37963
+ const truncated2 = truncateValue(val[i]);
37964
+ if (truncated2 !== void 0 || val[i] === void 0) {
37965
+ result2.push(truncated2);
37966
+ } else {
37967
+ break;
37968
+ }
37969
+ }
37970
+ return result2;
37971
+ }
37972
+ if (typeof val === "object") {
37973
+ currentSize += 2;
37974
+ const result2 = {};
37975
+ const entries = Object.entries(val);
37976
+ for (let i = 0; i < entries.length; i++) {
37977
+ if (currentSize >= maxChars) {
37978
+ break;
37979
+ }
37980
+ const [key, value2] = entries[i];
37981
+ if (i > 0) {
37982
+ currentSize += 1;
37983
+ }
37984
+ const keySize = key.length + 3;
37985
+ if (currentSize + keySize >= maxChars) {
37986
+ break;
37987
+ }
37988
+ currentSize += keySize;
37989
+ const truncatedValue = truncateValue(value2);
37990
+ if (truncatedValue !== void 0 || value2 === void 0) {
37991
+ result2[key] = truncatedValue;
37992
+ }
37993
+ }
37994
+ return result2;
37995
+ }
37996
+ const str = String(val);
37997
+ const availableChars = maxChars - currentSize;
37998
+ if (availableChars <= 0) {
37999
+ return void 0;
38000
+ }
38001
+ if (str.length <= availableChars) {
38002
+ currentSize += str.length;
38003
+ return str;
38004
+ }
38005
+ const truncated = str.slice(0, availableChars);
38006
+ currentSize += truncated.length;
38007
+ return truncated;
38008
+ }
38009
+ const result = truncateValue(value);
38010
+ return { result, size: currentSize };
38011
+ }
37884
38012
  var init_truncate_object = __esm({
37885
38013
  "src/runtime/chat/truncate-object.ts"() {
37886
38014
  "use strict";
@@ -37890,6 +38018,46 @@ var init_truncate_object = __esm({
37890
38018
  });
37891
38019
 
37892
38020
  // src/runtime/chat/truncate-transcript.ts
38021
+ function truncateTranscript(transcript, options) {
38022
+ const { maxSize, maxSizePerItem } = options;
38023
+ const result = [];
38024
+ let totalSize = 0;
38025
+ for (let i = transcript.length - 1; i >= 0; i--) {
38026
+ const item = transcript[i];
38027
+ const itemSize = getItemSize(item);
38028
+ const effectiveSize = maxSizePerItem ? Math.min(itemSize, maxSizePerItem) : itemSize;
38029
+ if (totalSize + effectiveSize > maxSize) {
38030
+ break;
38031
+ }
38032
+ let processedItem = item;
38033
+ if (maxSizePerItem && itemSize > maxSizePerItem) {
38034
+ if ("content" in item && item.content) {
38035
+ processedItem = {
38036
+ ...item,
38037
+ content: item.content.slice(0, maxSizePerItem)
38038
+ };
38039
+ } else if (item.role === "event" && item.payload) {
38040
+ const truncated = truncateObject(item.payload, maxSizePerItem);
38041
+ processedItem = {
38042
+ ...item,
38043
+ payload: truncated.result
38044
+ };
38045
+ }
38046
+ }
38047
+ result.unshift(processedItem);
38048
+ totalSize += effectiveSize;
38049
+ }
38050
+ return result;
38051
+ }
38052
+ function getItemSize(item) {
38053
+ if ("content" in item && item.content) {
38054
+ return item.content.length;
38055
+ }
38056
+ if (item.role === "event" && item.payload) {
38057
+ return JSON.stringify(item.payload).length;
38058
+ }
38059
+ return 0;
38060
+ }
37893
38061
  var init_truncate_transcript = __esm({
37894
38062
  "src/runtime/chat/truncate-transcript.ts"() {
37895
38063
  "use strict";
@@ -38210,18 +38378,19 @@ ${indent}`);
38210
38378
  }
38211
38379
  return value;
38212
38380
  }
38213
- var dedent;
38381
+ var dedent, dedent_default;
38214
38382
  var init_dedent = __esm({
38215
38383
  "../../node_modules/dedent/dist/dedent.mjs"() {
38216
38384
  init_define_BUILD();
38217
38385
  init_define_PACKAGE_VERSIONS();
38218
38386
  dedent = createDedent({});
38387
+ dedent_default = dedent;
38219
38388
  }
38220
38389
  });
38221
38390
 
38222
38391
  // src/runtime/config.ts
38223
38392
  import { limitConfigs as limitConfigs2 } from "@bpinternal/const";
38224
- var Transcript;
38393
+ var Transcript, Analysis, Config;
38225
38394
  var init_config = __esm({
38226
38395
  "src/runtime/config.ts"() {
38227
38396
  "use strict";
@@ -38237,12 +38406,23 @@ var init_config = __esm({
38237
38406
  /** Max bytes for a single message in the transcript */
38238
38407
  TRANSCRIPT_ITEM_MAX_BYTES: 1e4
38239
38408
  };
38409
+ Analysis = {
38410
+ /** How frequently the analysis will be run */
38411
+ ANALYSIS_FREQUENCY_CRON: "*/5 * * * *",
38412
+ // Every 5 minutes
38413
+ /** How many conversations to process in parallel */
38414
+ CONCURRENT_ANALYSIS_LIMIT: 5
38415
+ };
38416
+ Config = {
38417
+ Transcript,
38418
+ Analysis
38419
+ };
38240
38420
  }
38241
38421
  });
38242
38422
 
38243
38423
  // src/runtime/chat/components.ts
38244
38424
  import { messages, z as z17 } from "@botpress/sdk";
38245
- var DefaultMessageTypes, TextComponent, AudioComponent, ImageComponent, VideoComponent, LocationComponent, ChoiceComponent, DropdownComponent, CarouselComponent;
38425
+ var DefaultMessageTypes, TextComponent, AudioComponent, ImageComponent, VideoComponent, LocationComponent, ChoiceComponent, DropdownComponent, CarouselComponent, BUILT_IN_INTEGRATIONS, DefaultComponents;
38246
38426
  var init_components = __esm({
38247
38427
  "src/runtime/chat/components.ts"() {
38248
38428
  "use strict";
@@ -38528,11 +38708,23 @@ yield <Message>
38528
38708
  })
38529
38709
  }
38530
38710
  });
38711
+ BUILT_IN_INTEGRATIONS = ["webchat", "slack", "teams", "telegram", "whatsapp", "chat"];
38712
+ DefaultComponents = {
38713
+ Audio: AudioComponent,
38714
+ Image: ImageComponent,
38715
+ Video: VideoComponent,
38716
+ Location: LocationComponent,
38717
+ Choice: ChoiceComponent,
38718
+ Dropdown: DropdownComponent,
38719
+ Carousel: CarouselComponent,
38720
+ Text: TextComponent
38721
+ };
38531
38722
  }
38532
38723
  });
38533
38724
 
38534
38725
  // src/runtime/chat/chat.ts
38535
- import { Chat, isAnyComponent as isAnyComponent2 } from "llmz";
38726
+ import { Chat as _llmzChat, isAnyComponent as isAnyComponent2 } from "llmz";
38727
+ var Chat;
38536
38728
  var init_chat = __esm({
38537
38729
  "src/runtime/chat/chat.ts"() {
38538
38730
  "use strict";
@@ -38546,6 +38738,426 @@ var init_chat = __esm({
38546
38738
  init_adk();
38547
38739
  init_config();
38548
38740
  init_components();
38741
+ init_tracked_tags();
38742
+ Chat = class extends _llmzChat {
38743
+ _transcript;
38744
+ client;
38745
+ conversation;
38746
+ botId;
38747
+ logger;
38748
+ citations;
38749
+ trackedTags;
38750
+ componentRegistry = /* @__PURE__ */ new Map();
38751
+ constructor(context3) {
38752
+ super({
38753
+ components: async () => this.getComponents(),
38754
+ transcript: async () => this.fetchTranscript(),
38755
+ handler: async (message) => this.handle(message)
38756
+ });
38757
+ this.client = context3.client;
38758
+ this.conversation = context3.conversation;
38759
+ this.botId = context3.botId;
38760
+ this.logger = context3.logger;
38761
+ this.citations = context3.citations;
38762
+ this.trackedTags = TrackedTags.create({
38763
+ type: "conversation",
38764
+ client: this.client._inner,
38765
+ id: this.conversation.id,
38766
+ initialTags: this.conversation.tags
38767
+ });
38768
+ if (BUILT_IN_INTEGRATIONS.includes(this.conversation.integration)) {
38769
+ this.registerComponent({ component: DefaultComponents.Audio });
38770
+ this.registerComponent({ component: DefaultComponents.Image });
38771
+ this.registerComponent({ component: DefaultComponents.Video });
38772
+ this.registerComponent({ component: DefaultComponents.Carousel });
38773
+ this.registerComponent({ component: DefaultComponents.Choice });
38774
+ this.registerComponent({ component: DefaultComponents.Dropdown });
38775
+ this.registerComponent({ component: DefaultComponents.Location });
38776
+ } else {
38777
+ this.registerComponent({ component: DefaultComponents.Text });
38778
+ }
38779
+ }
38780
+ /**
38781
+ * Register a component with an optional handler
38782
+ */
38783
+ registerComponent(registration) {
38784
+ const componentName = registration.component.definition.name.toLowerCase();
38785
+ this.componentRegistry.set(componentName, registration);
38786
+ return this;
38787
+ }
38788
+ /**
38789
+ * Remove a component by name
38790
+ */
38791
+ removeComponent(name) {
38792
+ this.componentRegistry.delete(name.toLowerCase());
38793
+ return this;
38794
+ }
38795
+ /**
38796
+ * Get all registered components
38797
+ */
38798
+ async getComponents() {
38799
+ return Array.from(this.componentRegistry.values()).map((reg) => reg.component);
38800
+ }
38801
+ /**
38802
+ * Clear the entire transcript
38803
+ */
38804
+ async clearTranscript() {
38805
+ this._transcript = [];
38806
+ }
38807
+ /**
38808
+ * Prepend items to the transcript
38809
+ */
38810
+ async prependToTranscript(items) {
38811
+ if (!this._transcript) {
38812
+ throw new Error("Transcript not loaded yet \u2013 please call fetchTranscript() first");
38813
+ }
38814
+ this._transcript = [...items, ...this._transcript];
38815
+ }
38816
+ /**
38817
+ * Get a copy of the current transcript
38818
+ */
38819
+ async getTranscript() {
38820
+ if (!this._transcript) {
38821
+ await this.fetchTranscript();
38822
+ }
38823
+ return [...this._transcript];
38824
+ }
38825
+ /**
38826
+ * Replace the entire transcript
38827
+ */
38828
+ async setTranscript(items) {
38829
+ this._transcript = [...items];
38830
+ }
38831
+ async fetchTranscript() {
38832
+ if (this._transcript) return this._transcript;
38833
+ return await span(
38834
+ "chat.fetchTranscript",
38835
+ {
38836
+ conversationId: this.conversation.id
38837
+ },
38838
+ async () => {
38839
+ const since = this.trackedTags.tags["adkSyncTs"] || "";
38840
+ const fetchUnseenMessages = async () => {
38841
+ return this.client._inner.list.messages({
38842
+ conversationId: this.conversation.id,
38843
+ afterDate: since ? new Date(since).toISOString() : void 0,
38844
+ beforeDate: ""
38845
+ }).collect({ limit: 250 }).then((res) => since ? res.filter((m) => new Date(m.createdAt) > new Date(since)) : res).then((res) => res.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()));
38846
+ };
38847
+ const fetchTranscriptMessages = async () => {
38848
+ const { state } = await this.client.getOrSetState({
38849
+ id: this.conversation.id,
38850
+ type: "conversation",
38851
+ name: "conversation",
38852
+ payload: { transcript: [] }
38853
+ });
38854
+ this._transcript = state.payload.transcript;
38855
+ };
38856
+ const [unseenMessages] = await Promise.all([fetchUnseenMessages(), fetchTranscriptMessages()]);
38857
+ if (unseenMessages.length > 0) {
38858
+ await Promise.allSettled(
38859
+ unseenMessages.map(async (msg) => {
38860
+ await this.addMessage(msg);
38861
+ })
38862
+ );
38863
+ }
38864
+ return this._transcript;
38865
+ }
38866
+ );
38867
+ }
38868
+ async compactTranscript() {
38869
+ return await span(
38870
+ "chat.compactTranscript",
38871
+ {
38872
+ conversationId: this.conversation.id
38873
+ },
38874
+ async () => {
38875
+ if (!this._transcript) {
38876
+ throw new Error("Transcript not loaded yet \u2013 please call fetchTranscript() first");
38877
+ }
38878
+ const items = this._transcript?.splice(
38879
+ 0,
38880
+ // We want to keep the last N items as higher-precision context
38881
+ Math.max(this._transcript.length - Config.Transcript.SUMMARY_END_PADDING, 0)
38882
+ );
38883
+ try {
38884
+ const summary = await adk.zai.summarize(JSON.stringify(items, null, 2), {
38885
+ length: 250,
38886
+ prompt: dedent_default`
38887
+ You are a transcript summarizer tasked with compressing a long conversation between a user and an AI agent.
38888
+ Your goal is to drastically reduce the transcript length while retaining all key information, decisions, facts, user goals, and outputs.
38889
+
38890
+ Apply the following rules:
38891
+ - Collapse repetitive or verbose exchanges into concise summaries.
38892
+ - Remove fluff, filler words, greetings, small talk, and off-topic digressions.
38893
+ - If multiple turns convey the same intent or clarification, merge them.
38894
+ - Always preserve important questions, answers, decisions, tool calls, results, failures, corrections, and feedback.
38895
+ - Prefer bullet points, numbered lists, or structured sections if it increases clarity.
38896
+ - Assume the summary will be reused for resuming the session or training the assistant, so it must be precise, useful, and unambiguous.
38897
+
38898
+ The final output should feel like a high-value executive summary of a working session, not a chat log.
38899
+ `.trim()
38900
+ });
38901
+ this._transcript?.unshift({
38902
+ id: `summary-${Date.now()}`,
38903
+ role: "summary",
38904
+ content: summary
38905
+ });
38906
+ } catch (err) {
38907
+ this.logger.error("Error compacting transcript", err);
38908
+ this._transcript = [...items ?? [], ...this._transcript];
38909
+ }
38910
+ }
38911
+ );
38912
+ }
38913
+ getRemainingContextSpace() {
38914
+ if (!this._transcript) {
38915
+ throw new Error("Transcript not loaded yet \u2013 please call fetchTranscript() first");
38916
+ }
38917
+ const transcriptSize = JSON.stringify(this._transcript).length;
38918
+ return {
38919
+ messages: Math.max(0, Config.Transcript.SUMMARY_MAX_MESSAGES - this._transcript.length),
38920
+ bytes: Math.max(0, Config.Transcript.SUMMARY_MAX_BYTES - transcriptSize)
38921
+ };
38922
+ }
38923
+ shouldCompactTranscript() {
38924
+ const remaining = this.getRemainingContextSpace();
38925
+ return remaining.bytes <= 0 || remaining.messages <= 0;
38926
+ }
38927
+ async saveTranscript() {
38928
+ if (!this._transcript) return;
38929
+ return await span(
38930
+ "chat.saveTranscript",
38931
+ {
38932
+ conversationId: this.conversation.id
38933
+ },
38934
+ async () => {
38935
+ if (this.shouldCompactTranscript()) {
38936
+ await this.compactTranscript();
38937
+ }
38938
+ const truncated = truncateTranscript(this._transcript, {
38939
+ maxSize: Config.Transcript.SUMMARY_MAX_BYTES,
38940
+ maxSizePerItem: Config.Transcript.TRANSCRIPT_ITEM_MAX_BYTES
38941
+ });
38942
+ await this.client.setState({
38943
+ id: this.conversation.id,
38944
+ type: "conversation",
38945
+ name: "conversation",
38946
+ payload: { transcript: truncated }
38947
+ });
38948
+ }
38949
+ );
38950
+ }
38951
+ async sendMessage(message) {
38952
+ return await span(
38953
+ "chat.sendMessage",
38954
+ {
38955
+ conversationId: this.conversation.id,
38956
+ direction: "outgoing",
38957
+ integration: this.conversation.integration || "-",
38958
+ channel: this.conversation.channel || "-",
38959
+ userId: this.botId,
38960
+ botId: this.botId,
38961
+ "message.type": message.type
38962
+ },
38963
+ async (s) => {
38964
+ const [payload, citations] = this.citations.removeCitationsFromObject(message.payload);
38965
+ const { message: created } = await this.client.createMessage({
38966
+ conversationId: this.conversation.id,
38967
+ tags: message.tags || {},
38968
+ userId: this.botId,
38969
+ type: message.type.toLowerCase(),
38970
+ payload: {
38971
+ ...payload,
38972
+ metadata: { citations }
38973
+ }
38974
+ });
38975
+ s.setAttribute("messageId", created.id);
38976
+ await this.addMessage(created);
38977
+ return created;
38978
+ }
38979
+ );
38980
+ }
38981
+ async transformMessage(message) {
38982
+ let clone = structuredClone(message);
38983
+ clone = await this.__temporary__fixTelegramImage(clone);
38984
+ const attachments = clone.type === "image" && clone.payload.imageUrl ? [
38985
+ {
38986
+ type: "image",
38987
+ url: clone.payload.imageUrl
38988
+ }
38989
+ ] : [];
38990
+ if (clone.type === "bloc") {
38991
+ clone.payload.items = clone.payload.items.filter((item) => {
38992
+ if (item.type === "image" && item.payload.imageUrl) {
38993
+ attachments.push({
38994
+ type: "image",
38995
+ url: item.payload.imageUrl
38996
+ });
38997
+ return false;
38998
+ }
38999
+ return true;
39000
+ });
39001
+ }
39002
+ if (message.direction === "outgoing") {
39003
+ return {
39004
+ id: message.id,
39005
+ role: "assistant",
39006
+ content: JSON.stringify(
39007
+ {
39008
+ type: message.type,
39009
+ payload: message.payload
39010
+ },
39011
+ null,
39012
+ 2
39013
+ ),
39014
+ createdAt: message.createdAt
39015
+ };
39016
+ }
39017
+ if (message.direction === "incoming") {
39018
+ return {
39019
+ id: message.id,
39020
+ role: "user",
39021
+ content: JSON.stringify(
39022
+ {
39023
+ type: message.type,
39024
+ payload: message.payload
39025
+ },
39026
+ null,
39027
+ 2
39028
+ ),
39029
+ createdAt: message.createdAt,
39030
+ attachments
39031
+ };
39032
+ }
39033
+ return null;
39034
+ }
39035
+ async transformEvent(event) {
39036
+ return {
39037
+ id: event.id,
39038
+ role: "event",
39039
+ name: event.type,
39040
+ payload: event.payload,
39041
+ createdAt: event.createdAt
39042
+ };
39043
+ }
39044
+ async handle(message) {
39045
+ return await span(
39046
+ "chat.sendMessage",
39047
+ {
39048
+ conversationId: this.conversation.id,
39049
+ direction: "outgoing",
39050
+ integration: this.conversation.integration || "-",
39051
+ channel: this.conversation.channel || "-",
39052
+ userId: this.botId,
39053
+ botId: this.botId,
39054
+ "message.type": message.type
39055
+ },
39056
+ async () => {
39057
+ if (message.type === "MESSAGE") {
39058
+ message = Message.parse(message);
39059
+ if (typeof message.props.text === "string") {
39060
+ message.children.push(message.props.text);
39061
+ }
39062
+ for (const msg of message.children) {
39063
+ if (isAnyComponent2(msg)) {
39064
+ await this.handle(msg);
39065
+ } else if (typeof msg === "string" && msg.trim().length) {
39066
+ await this.sendMessage({
39067
+ type: "text",
39068
+ payload: { text: msg }
39069
+ });
39070
+ }
39071
+ }
39072
+ return;
39073
+ }
39074
+ const registration = this.componentRegistry.get(message.type.toLowerCase());
39075
+ let componentRegistration = registration;
39076
+ if (!componentRegistration) {
39077
+ for (const [_2, reg] of this.componentRegistry) {
39078
+ if (reg.component.definition.aliases?.map((x) => x.toLowerCase()).includes(message.type.toLowerCase())) {
39079
+ componentRegistration = reg;
39080
+ break;
39081
+ }
39082
+ }
39083
+ }
39084
+ if (!componentRegistration) {
39085
+ throw new Error(`Could not find component for message type "${message.type}"`);
39086
+ }
39087
+ if (componentRegistration.handler) {
39088
+ await componentRegistration.handler(message);
39089
+ return;
39090
+ }
39091
+ await this.sendMessage({
39092
+ type: componentRegistration.component.definition.name,
39093
+ payload: message.props
39094
+ });
39095
+ }
39096
+ );
39097
+ }
39098
+ /**
39099
+ * This is a temporary workaround as the Content-Type of images on Telegram are "binary/octet-stream"
39100
+ * And OpenAI integration does not support this type. It needs to be "image/jpeg" or "image/png".
39101
+ * This function fetches the image from Telegram, uploads it to the file storage, and replaces the URL in the message.
39102
+ */
39103
+ async __temporary__fixTelegramImage(message) {
39104
+ if (this.conversation.integration !== "telegram") {
39105
+ return message;
39106
+ }
39107
+ const clone = structuredClone(message);
39108
+ if (clone.type === "image" && clone.payload.imageUrl) {
39109
+ const arrayBuffer = await (await fetch(clone.payload.imageUrl)).arrayBuffer();
39110
+ const buffer = Buffer.from(arrayBuffer);
39111
+ const fileName = clone.payload.imageUrl.split("/").pop() || "image.jpg";
39112
+ const { file } = await this.client.uploadFile({
39113
+ key: `telegram/${this.conversation.id}/${clone.id}/${fileName}`,
39114
+ content: buffer,
39115
+ index: false,
39116
+ accessPolicies: ["public_content"],
39117
+ publicContentImmediatelyAccessible: true,
39118
+ expiresAt: new Date(Date.now() + 1e3 * 60 * 60 * 24).toISOString()
39119
+ // 1 day
39120
+ });
39121
+ clone.payload.imageUrl = file.url;
39122
+ }
39123
+ return clone;
39124
+ }
39125
+ async addEvent(event) {
39126
+ if (!isEvent(event)) {
39127
+ return;
39128
+ }
39129
+ const item = await this.transformEvent(event);
39130
+ if (!item || this._transcript?.find((m) => m.id === event.id)) {
39131
+ return;
39132
+ }
39133
+ let insertIndex = this._transcript?.findIndex(
39134
+ (m) => m.createdAt && new Date(m.createdAt) > new Date(event.createdAt)
39135
+ );
39136
+ this._transcript?.splice(
39137
+ insertIndex === -1 || insertIndex === void 0 ? this._transcript.length : insertIndex,
39138
+ 0,
39139
+ item
39140
+ );
39141
+ }
39142
+ async addMessage(message) {
39143
+ if (!isMessage(message)) {
39144
+ return;
39145
+ }
39146
+ const item = await this.transformMessage(message);
39147
+ if (!item || this._transcript?.find((m) => m.id === message.id)) {
39148
+ return;
39149
+ }
39150
+ let insertIndex = this._transcript?.findIndex(
39151
+ (m) => m.createdAt && item.createdAt && new Date(m.createdAt) > new Date(item.createdAt)
39152
+ );
39153
+ this._transcript?.splice(
39154
+ insertIndex === -1 || insertIndex === void 0 ? this._transcript.length : insertIndex,
39155
+ 0,
39156
+ item
39157
+ );
39158
+ this.trackedTags.tags["adkSyncTs"] = this._transcript.at(-1).createdAt;
39159
+ }
39160
+ };
38549
39161
  }
38550
39162
  });
38551
39163
 
@@ -38677,14 +39289,11 @@ var init_conversation_instance = __esm({
38677
39289
  const mapping = interfaceMappings.getIntegrationAction("typingIndicator", "startTypingIndicator", this.integration);
38678
39290
  if (mapping) {
38679
39291
  const message = context.get("message", { optional: true });
38680
- if (!message) {
38681
- return;
38682
- }
38683
39292
  await this.client.callAction({
38684
39293
  type: mapping,
38685
39294
  input: {
38686
39295
  conversationId: this.id,
38687
- messageId: message.id
39296
+ messageId: message?.id || ""
38688
39297
  }
38689
39298
  }).catch(() => {
38690
39299
  });
@@ -38697,14 +39306,11 @@ var init_conversation_instance = __esm({
38697
39306
  const mapping = interfaceMappings.getIntegrationAction("typingIndicator", "stopTypingIndicator", this.integration);
38698
39307
  if (mapping) {
38699
39308
  const message = context.get("message", { optional: true });
38700
- if (!message) {
38701
- return;
38702
- }
38703
39309
  await this.client.callAction({
38704
39310
  type: mapping,
38705
39311
  input: {
38706
39312
  conversationId: this.id,
38707
- messageId: message.id
39313
+ messageId: message?.id || ""
38708
39314
  }
38709
39315
  }).catch(() => {
38710
39316
  });
@@ -38730,6 +39336,7 @@ var init_conversation = __esm({
38730
39336
  init_events();
38731
39337
  init_conversation_instance();
38732
39338
  init_workflow_instance();
39339
+ init_chat();
38733
39340
  ConversationHandler = Symbol.for("conversation.handler");
38734
39341
  ((Typings8) => {
38735
39342
  Typings8.Primitive = "conversation";
@@ -38744,12 +39351,15 @@ var init_conversation = __esm({
38744
39351
  events;
38745
39352
  /** @internal */
38746
39353
  schema;
39354
+ /** @internal */
39355
+ chatFactory;
38747
39356
  #handler;
38748
39357
  constructor(props) {
38749
39358
  this.channel = props.channel;
38750
39359
  this.events = props.events ?? [];
38751
39360
  this.schema = props.state ?? z19.object({}).passthrough();
38752
39361
  this.#handler = props.handler;
39362
+ this.chatFactory = props.chat ?? (({ context: context3 }) => new Chat(context3));
38753
39363
  }
38754
39364
  /** @internal */
38755
39365
  getDefinition() {
@@ -38887,7 +39497,8 @@ var init_conversation = __esm({
38887
39497
  conversation: conversationInstance,
38888
39498
  state: stateProxy,
38889
39499
  client: client2,
38890
- execute
39500
+ execute,
39501
+ chat
38891
39502
  });
38892
39503
  controller.abort();
38893
39504
  void startTypingPromise.then(() => conversationInstance.stopTyping().catch(() => {
@@ -38914,7 +39525,6 @@ var init_conversation2 = __esm({
38914
39525
  init_define_PACKAGE_VERSIONS();
38915
39526
  init_runtime2();
38916
39527
  init_conversation();
38917
- init_chat2();
38918
39528
  init_tracing();
38919
39529
  init_adk();
38920
39530
  init_conversation_matching();
@@ -48966,6 +49576,7 @@ var init_library = __esm({
48966
49576
  init_runtime2();
48967
49577
  init_client();
48968
49578
  init_primitives();
49579
+ init_chat2();
48969
49580
  init_workflow_utils();
48970
49581
  init_events();
48971
49582
  init_autonomous();
@@ -48983,6 +49594,7 @@ export {
48983
49594
  Action,
48984
49595
  Autonomous,
48985
49596
  BaseConversationInstance,
49597
+ Chat,
48986
49598
  Cognitive3 as Cognitive,
48987
49599
  BaseConversation as Conversation,
48988
49600
  DataSource2 as DataSource,