@botpress/runtime 1.13.9 → 1.13.10

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.
Files changed (34) hide show
  1. package/dist/define-config.d.ts +23 -1
  2. package/dist/define-config.d.ts.map +1 -1
  3. package/dist/definition.js +400 -290
  4. package/dist/definition.js.map +4 -4
  5. package/dist/internal.js +494 -384
  6. package/dist/internal.js.map +4 -4
  7. package/dist/library.d.ts +4 -2
  8. package/dist/library.d.ts.map +1 -1
  9. package/dist/library.js +427 -284
  10. package/dist/library.js.map +4 -4
  11. package/dist/primitives/conversation.d.ts +24 -11
  12. package/dist/primitives/conversation.d.ts.map +1 -1
  13. package/dist/primitives/data-sources/source-base.d.ts +1 -1
  14. package/dist/primitives/data-sources/source-base.d.ts.map +1 -1
  15. package/dist/primitives/index.d.ts +1 -1
  16. package/dist/primitives/workflow-instance.d.ts +45 -1
  17. package/dist/primitives/workflow-instance.d.ts.map +1 -1
  18. package/dist/primitives/workflow.d.ts +3 -2
  19. package/dist/primitives/workflow.d.ts.map +1 -1
  20. package/dist/runtime/autonomous.d.ts +1 -0
  21. package/dist/runtime/autonomous.d.ts.map +1 -1
  22. package/dist/runtime/context/context.d.ts +3 -0
  23. package/dist/runtime/context/context.d.ts.map +1 -1
  24. package/dist/runtime/context/handlers.d.ts.map +1 -1
  25. package/dist/runtime/handlers/event.d.ts.map +1 -1
  26. package/dist/runtime/handlers/workflow.d.ts.map +1 -1
  27. package/dist/runtime/tracked-tags.d.ts.map +1 -1
  28. package/dist/runtime.js +451 -302
  29. package/dist/runtime.js.map +4 -4
  30. package/dist/utilities/events.d.ts +28 -0
  31. package/dist/utilities/events.d.ts.map +1 -1
  32. package/dist/utilities/validate-event-name.d.ts +20 -0
  33. package/dist/utilities/validate-event-name.d.ts.map +1 -0
  34. package/package.json +1 -1
package/dist/runtime.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.9", adk: "1.13.9", sdk: "5.0.2", llmz: "0.0.35", zai: "2.5.6", cognitive: "0.3.3" };
51
+ define_PACKAGE_VERSIONS_default = { runtime: "1.13.10", adk: "1.13.10", sdk: "5.0.2", llmz: "0.0.35", zai: "2.5.6", cognitive: "0.3.3" };
52
52
  }
53
53
  });
54
54
 
@@ -27286,41 +27286,41 @@ var require_ms = __commonJS({
27286
27286
  return void 0;
27287
27287
  }
27288
27288
  }
27289
- function fmtShort(ms3) {
27290
- var msAbs = Math.abs(ms3);
27289
+ function fmtShort(ms4) {
27290
+ var msAbs = Math.abs(ms4);
27291
27291
  if (msAbs >= d) {
27292
- return Math.round(ms3 / d) + "d";
27292
+ return Math.round(ms4 / d) + "d";
27293
27293
  }
27294
27294
  if (msAbs >= h) {
27295
- return Math.round(ms3 / h) + "h";
27295
+ return Math.round(ms4 / h) + "h";
27296
27296
  }
27297
27297
  if (msAbs >= m) {
27298
- return Math.round(ms3 / m) + "m";
27298
+ return Math.round(ms4 / m) + "m";
27299
27299
  }
27300
27300
  if (msAbs >= s) {
27301
- return Math.round(ms3 / s) + "s";
27301
+ return Math.round(ms4 / s) + "s";
27302
27302
  }
27303
- return ms3 + "ms";
27303
+ return ms4 + "ms";
27304
27304
  }
27305
- function fmtLong(ms3) {
27306
- var msAbs = Math.abs(ms3);
27305
+ function fmtLong(ms4) {
27306
+ var msAbs = Math.abs(ms4);
27307
27307
  if (msAbs >= d) {
27308
- return plural(ms3, msAbs, d, "day");
27308
+ return plural(ms4, msAbs, d, "day");
27309
27309
  }
27310
27310
  if (msAbs >= h) {
27311
- return plural(ms3, msAbs, h, "hour");
27311
+ return plural(ms4, msAbs, h, "hour");
27312
27312
  }
27313
27313
  if (msAbs >= m) {
27314
- return plural(ms3, msAbs, m, "minute");
27314
+ return plural(ms4, msAbs, m, "minute");
27315
27315
  }
27316
27316
  if (msAbs >= s) {
27317
- return plural(ms3, msAbs, s, "second");
27317
+ return plural(ms4, msAbs, s, "second");
27318
27318
  }
27319
- return ms3 + " ms";
27319
+ return ms4 + " ms";
27320
27320
  }
27321
- function plural(ms3, msAbs, n, name) {
27321
+ function plural(ms4, msAbs, n, name) {
27322
27322
  var isPlural = msAbs >= n * 1.5;
27323
- return Math.round(ms3 / n) + " " + name + (isPlural ? "s" : "");
27323
+ return Math.round(ms4 / n) + " " + name + (isPlural ? "s" : "");
27324
27324
  }
27325
27325
  }
27326
27326
  });
@@ -27365,8 +27365,8 @@ var require_common = __commonJS({
27365
27365
  }
27366
27366
  const self2 = debug;
27367
27367
  const curr = Number(/* @__PURE__ */ new Date());
27368
- const ms3 = curr - (prevTime || curr);
27369
- self2.diff = ms3;
27368
+ const ms4 = curr - (prevTime || curr);
27369
+ self2.diff = ms4;
27370
27370
  self2.prev = prevTime;
27371
27371
  self2.curr = curr;
27372
27372
  prevTime = curr;
@@ -34273,6 +34273,9 @@ Always prefer information from the knowledge bases over general knowledge when a
34273
34273
  ...props.hooks?.onBeforeTool && {
34274
34274
  onBeforeTool: asyncResource.bind(props.hooks.onBeforeTool)
34275
34275
  },
34276
+ ...props.hooks?.onIterationStart && {
34277
+ onIterationStart: asyncResource.bind(props.hooks.onIterationStart)
34278
+ },
34276
34279
  ...props.hooks?.onAfterTool && {
34277
34280
  onAfterTool: asyncResource.bind(props.hooks.onAfterTool)
34278
34281
  },
@@ -45123,15 +45126,51 @@ var init_validate_tag_name = __esm({
45123
45126
  }
45124
45127
  });
45125
45128
 
45129
+ // src/utilities/validate-event-name.ts
45130
+ function validateEventName(name) {
45131
+ if (!name || typeof name !== "string") {
45132
+ return { valid: false, error: "Event name must be a non-empty string" };
45133
+ }
45134
+ if (name.length > 100) {
45135
+ return { valid: false, error: `Event name "${name}" must be less than 100 characters` };
45136
+ }
45137
+ if (!/^[a-zA-Z]/.test(name)) {
45138
+ return { valid: false, error: `Event name "${name}" must start with a letter` };
45139
+ }
45140
+ if (!/^[a-zA-Z0-9_]+$/.test(name)) {
45141
+ return {
45142
+ valid: false,
45143
+ error: `Event name "${name}" can only contain letters (A-Z, a-z), numbers (0-9), and underscores (_)`
45144
+ };
45145
+ }
45146
+ return { valid: true };
45147
+ }
45148
+ function validateEventDefinitions(events, context3) {
45149
+ for (const eventName of Object.keys(events)) {
45150
+ const validation = validateEventName(eventName);
45151
+ if (!validation.valid) {
45152
+ throw new Error(`Invalid event name in ${context3}: ${validation.error}`);
45153
+ }
45154
+ }
45155
+ }
45156
+ var init_validate_event_name = __esm({
45157
+ "src/utilities/validate-event-name.ts"() {
45158
+ "use strict";
45159
+ init_define_BUILD();
45160
+ init_define_PACKAGE_VERSIONS();
45161
+ }
45162
+ });
45163
+
45126
45164
  // src/define-config.ts
45127
45165
  import { z as z17 } from "@botpress/sdk";
45128
- var zuiSchema, modelSchema, tagDefinitionSchema, configSchema, AGENT_CONFIG_BRAND, defineConfig;
45166
+ var zuiSchema, modelSchema, tagDefinitionSchema, eventDefinitionSchema, configSchema, AGENT_CONFIG_BRAND, defineConfig;
45129
45167
  var init_define_config = __esm({
45130
45168
  "src/define-config.ts"() {
45131
45169
  "use strict";
45132
45170
  init_define_BUILD();
45133
45171
  init_define_PACKAGE_VERSIONS();
45134
45172
  init_validate_tag_name();
45173
+ init_validate_event_name();
45135
45174
  zuiSchema = z17.custom(
45136
45175
  (val) => {
45137
45176
  if (typeof val === "object" && val !== null && "parse" in val) {
@@ -45156,6 +45195,13 @@ var init_define_config = __esm({
45156
45195
  description: z17.string().optional()
45157
45196
  })
45158
45197
  );
45198
+ eventDefinitionSchema = z17.record(
45199
+ z17.string(),
45200
+ z17.object({
45201
+ schema: zuiSchema.optional(),
45202
+ description: z17.string().optional()
45203
+ })
45204
+ );
45159
45205
  configSchema = z17.object({
45160
45206
  name: z17.string().optional(),
45161
45207
  description: z17.string().optional(),
@@ -45196,7 +45242,8 @@ var init_define_config = __esm({
45196
45242
  zai: val?.zai ?? "openai:gpt-4.1-2025-04-14",
45197
45243
  autonomous: val?.autonomous ?? "openai:gpt-4.1-mini-2025-04-14"
45198
45244
  })),
45199
- dependencies: z17.custom()
45245
+ dependencies: z17.custom(),
45246
+ events: eventDefinitionSchema.optional()
45200
45247
  });
45201
45248
  AGENT_CONFIG_BRAND = Symbol.for("@botpress/runtime/AgentConfig");
45202
45249
  defineConfig = (config) => {
@@ -45216,6 +45263,9 @@ var init_define_config = __esm({
45216
45263
  if (parsed.workflow?.tags) {
45217
45264
  validateTagDefinitions(parsed.workflow.tags, "workflow.tags");
45218
45265
  }
45266
+ if (parsed.events) {
45267
+ validateEventDefinitions(parsed.events, "events");
45268
+ }
45219
45269
  return {
45220
45270
  ...parsed,
45221
45271
  __brand: "AgentConfig",
@@ -45473,12 +45523,12 @@ var init_workflow_step = __esm({
45473
45523
  workflowControlContext.abort();
45474
45524
  throw createStepSignal();
45475
45525
  };
45476
- step.sleep = async (name, ms3) => {
45526
+ step.sleep = async (name, ms4) => {
45477
45527
  await _step(
45478
45528
  name,
45479
45529
  async () => {
45480
45530
  const remainingTime = context2.get("runtime").getRemainingExecutionTimeInMs();
45481
- if (remainingTime - MIN_STEP_REMAINING_TIME_MS <= ms3 || ms3 >= 1e4) {
45531
+ if (remainingTime - MIN_STEP_REMAINING_TIME_MS <= ms4 || ms4 >= 1e4) {
45482
45532
  const client2 = context2.get("client");
45483
45533
  const workflowControlContext = context2.get("workflowControlContext");
45484
45534
  await client2.createEvent({
@@ -45486,7 +45536,7 @@ var init_workflow_step = __esm({
45486
45536
  payload: {},
45487
45537
  workflowId: workflowControlContext.workflow.id,
45488
45538
  schedule: {
45489
- delay: ms3
45539
+ delay: ms4
45490
45540
  }
45491
45541
  });
45492
45542
  await updateWorkflow({
@@ -45495,7 +45545,7 @@ var init_workflow_step = __esm({
45495
45545
  });
45496
45546
  workflowControlContext.abort();
45497
45547
  } else {
45498
- await new Promise((resolve) => void setTimeout(resolve, ms3));
45548
+ await new Promise((resolve) => void setTimeout(resolve, ms4));
45499
45549
  context2.get("workflowControlContext").signal.throwIfAborted();
45500
45550
  }
45501
45551
  },
@@ -45506,8 +45556,8 @@ var init_workflow_step = __esm({
45506
45556
  );
45507
45557
  };
45508
45558
  step.sleepUntil = async (name, date) => {
45509
- const ms3 = Math.max(0, new Date(date).getTime() - Date.now() - MIN_STEP_REMAINING_TIME_MS);
45510
- await step.sleep(name, ms3);
45559
+ const ms4 = Math.max(0, new Date(date).getTime() - Date.now() - MIN_STEP_REMAINING_TIME_MS);
45560
+ await step.sleep(name, ms4);
45511
45561
  };
45512
45562
  step.waitForWorkflow = async (name, workflowId) => {
45513
45563
  const workflowControlContext = context2.get("workflowControlContext");
@@ -45726,12 +45776,13 @@ function createWorkflowExecutionState(client2, workflowId) {
45726
45776
  name: BUILT_IN_STATES.workflowSteps
45727
45777
  });
45728
45778
  }
45729
- var workflowStepContextSchema, workflowExecutionContextSchema, StepSymbol, BaseWorkflowInstance;
45779
+ var import_ms2, workflowStepContextSchema, workflowExecutionContextSchema, StepSymbol, BaseWorkflowInstance;
45730
45780
  var init_workflow_instance = __esm({
45731
45781
  "src/primitives/workflow-instance.ts"() {
45732
45782
  "use strict";
45733
45783
  init_define_BUILD();
45734
45784
  init_define_PACKAGE_VERSIONS();
45785
+ import_ms2 = __toESM(require_ms(), 1);
45735
45786
  init_errors();
45736
45787
  init_library();
45737
45788
  init_autonomous();
@@ -45858,6 +45909,81 @@ var init_workflow_instance = __esm({
45858
45909
  });
45859
45910
  Object.assign(this.workflow, workflow);
45860
45911
  }
45912
+ /**
45913
+ * Extend the workflow timeout by setting a new timeout.
45914
+ * This is useful for long-running workflows that need more time to complete.
45915
+ *
45916
+ * @param options - Either `{ in: string }` for relative duration or `{ at: string }` for absolute ISO timestamp
45917
+ * @returns A promise that resolves when the timeout is updated (can be awaited or not)
45918
+ * @example
45919
+ * // Relative timeout (duration from now):
45920
+ * workflow.setTimeout({ in: '30m' }) // Timeout in 30 minutes
45921
+ * workflow.setTimeout({ in: '6 hours' }) // Timeout in 6 hours
45922
+ *
45923
+ * // Absolute timeout (ISO timestamp):
45924
+ * workflow.setTimeout({ at: '2024-12-25T00:00:00Z' })
45925
+ *
45926
+ * // Optionally await if you need to ensure the update completes:
45927
+ * await workflow.setTimeout({ in: '1h' })
45928
+ */
45929
+ setTimeout(options) {
45930
+ let newTimeoutAt;
45931
+ if ("in" in options) {
45932
+ const durationMs = (0, import_ms2.default)(options.in);
45933
+ if (!durationMs) {
45934
+ throw new Error(`Invalid duration format: "${options.in}". Use formats like "30m", "1h", "6 hours".`);
45935
+ }
45936
+ newTimeoutAt = new Date(Date.now() + durationMs).toISOString();
45937
+ } else {
45938
+ const date = new Date(options.at);
45939
+ if (isNaN(date.getTime())) {
45940
+ throw new Error(`Invalid ISO date format: "${options.at}".`);
45941
+ }
45942
+ newTimeoutAt = date.toISOString();
45943
+ }
45944
+ return updateWorkflow({
45945
+ id: this.id,
45946
+ timeoutAt: newTimeoutAt
45947
+ }).then(({ workflow }) => {
45948
+ Object.assign(this.workflow, workflow);
45949
+ });
45950
+ }
45951
+ /**
45952
+ * Fail the workflow with an error reason.
45953
+ * This immediately interrupts the workflow handler and marks the workflow as failed.
45954
+ * Can only be called from within a workflow handler.
45955
+ *
45956
+ * @param reason - The error reason for the failure
45957
+ * @throws Never returns - always throws to interrupt the handler
45958
+ * @example
45959
+ * workflow.fail('Invalid input data')
45960
+ */
45961
+ fail(reason) {
45962
+ const controlContext = context2.get("workflowControlContext", { optional: true });
45963
+ if (!controlContext || controlContext.workflow.id !== this.id) {
45964
+ throw new Error("workflow.fail() can only be called from within the workflow handler");
45965
+ }
45966
+ controlContext.fail(reason);
45967
+ throw createStepSignal();
45968
+ }
45969
+ /**
45970
+ * Complete the workflow early with the given output.
45971
+ * This immediately interrupts the workflow handler and marks the workflow as completed.
45972
+ * Can only be called from within a workflow handler.
45973
+ *
45974
+ * @param output - The workflow output (typed according to workflow definition)
45975
+ * @throws Never returns - always throws to interrupt the handler
45976
+ * @example
45977
+ * workflow.complete({ result: 'success', data: processedData })
45978
+ */
45979
+ complete(output2) {
45980
+ const controlContext = context2.get("workflowControlContext", { optional: true });
45981
+ if (!controlContext || controlContext.workflow.id !== this.id) {
45982
+ throw new Error("workflow.complete() can only be called from within the workflow handler");
45983
+ }
45984
+ controlContext.complete(output2);
45985
+ throw createStepSignal();
45986
+ }
45861
45987
  /**
45862
45988
  * Provide data in response to a workflow data request (instance method).
45863
45989
  * Call this method from a conversation handler when you receive a WorkflowDataRequestEvent.
@@ -45908,6 +46034,7 @@ var init_workflow_instance = __esm({
45908
46034
  workflow: this.workflow,
45909
46035
  aborted: false,
45910
46036
  failed: false,
46037
+ completed: false,
45911
46038
  acked: false,
45912
46039
  restarted: false,
45913
46040
  signal: abortSignal,
@@ -45922,6 +46049,10 @@ var init_workflow_instance = __esm({
45922
46049
  workflowControlContext.failed = true;
45923
46050
  workflowControlContext.failedReason = reason;
45924
46051
  },
46052
+ complete: (result) => {
46053
+ workflowControlContext.completed = true;
46054
+ workflowControlContext.completedResult = result;
46055
+ },
45925
46056
  ack: async () => {
45926
46057
  if (workflowControlContext.acked) {
45927
46058
  return;
@@ -45975,7 +46106,8 @@ var init_workflow_instance = __esm({
45975
46106
  step,
45976
46107
  client: this.client,
45977
46108
  execute: this.execute.bind(this),
45978
- signal: abortSignal
46109
+ signal: abortSignal,
46110
+ workflow: this
45979
46111
  });
45980
46112
  return {
45981
46113
  status: "done",
@@ -45986,6 +46118,12 @@ var init_workflow_instance = __esm({
45986
46118
  }
45987
46119
  } catch (err) {
45988
46120
  if (isStepSignal(err)) {
46121
+ if (workflowControlContext.completed) {
46122
+ return {
46123
+ status: "done",
46124
+ result: workflowControlContext.completedResult
46125
+ };
46126
+ }
45989
46127
  if (workflowControlContext.failed) {
45990
46128
  return {
45991
46129
  status: "error",
@@ -46601,6 +46739,259 @@ var init_tracked_state = __esm({
46601
46739
  }
46602
46740
  });
46603
46741
 
46742
+ // src/runtime/tracked-tags.ts
46743
+ var TrackedTags;
46744
+ var init_tracked_tags = __esm({
46745
+ "src/runtime/tracked-tags.ts"() {
46746
+ "use strict";
46747
+ init_define_BUILD();
46748
+ init_define_PACKAGE_VERSIONS();
46749
+ init_context3();
46750
+ init_tracing();
46751
+ TrackedTags = class _TrackedTags {
46752
+ type;
46753
+ id;
46754
+ client;
46755
+ _tags = {};
46756
+ _initialTags = {};
46757
+ _loaded = false;
46758
+ _saving = false;
46759
+ _saveAgain = false;
46760
+ _saveAgainCount = 0;
46761
+ static _savingAll = false;
46762
+ static _saveAllAgain = false;
46763
+ static _saveAllCount = 0;
46764
+ constructor(props) {
46765
+ this.type = props.type;
46766
+ this.id = props.id;
46767
+ this.client = props.client;
46768
+ }
46769
+ static create(props) {
46770
+ const tags = context2.get("tags", { optional: true });
46771
+ const executionFinished = context2.get("executionFinished", { optional: true });
46772
+ if (executionFinished) {
46773
+ throw new Error(`Cannot create new TrackedTags "${props.type}/${props.id}" after execution has finished.`);
46774
+ }
46775
+ const match2 = tags?.find((x) => x.id === props.id && x.type === props.type);
46776
+ if (match2) {
46777
+ return match2;
46778
+ }
46779
+ const instance = new _TrackedTags(props);
46780
+ if (props.initialTags) {
46781
+ instance._tags = { ...props.initialTags };
46782
+ instance._initialTags = { ...props.initialTags };
46783
+ instance._loaded = true;
46784
+ }
46785
+ tags?.push(instance);
46786
+ return instance;
46787
+ }
46788
+ static async saveAllDirty() {
46789
+ if (this._savingAll) {
46790
+ this._saveAllAgain = true;
46791
+ return;
46792
+ }
46793
+ try {
46794
+ this._savingAll = true;
46795
+ const tags = context2.get("tags", { optional: true });
46796
+ const dirtyTags = tags?.filter((t) => t.isDirty()) || [];
46797
+ if (!dirtyTags.length) {
46798
+ return;
46799
+ }
46800
+ await span(
46801
+ "tags.saveAllDirty",
46802
+ {
46803
+ tags_count: tags?.length || 0,
46804
+ tags: tags.map((t) => `${t.type}/${t.id}`)
46805
+ },
46806
+ () => Promise.allSettled(dirtyTags.map((t) => t.save()))
46807
+ );
46808
+ } finally {
46809
+ this._savingAll = false;
46810
+ if (this._saveAllAgain && this._saveAllCount++ <= 5) {
46811
+ this._saveAllAgain = false;
46812
+ await this.saveAllDirty();
46813
+ } else {
46814
+ this._saveAllCount = 0;
46815
+ }
46816
+ }
46817
+ }
46818
+ static async loadAll() {
46819
+ await span("tags.loadAll", {}, async () => {
46820
+ const client2 = context2.get("client")._inner;
46821
+ const bot2 = context2.get("bot", { optional: true });
46822
+ const user2 = context2.get("user", { optional: true });
46823
+ const conversation = context2.get("conversation", { optional: true });
46824
+ const workflow = context2.get("workflow", { optional: true });
46825
+ if (bot2) {
46826
+ const botTags = bot2.tags;
46827
+ _TrackedTags.create({
46828
+ client: client2,
46829
+ type: "bot",
46830
+ id: bot2.id,
46831
+ ...botTags && { initialTags: botTags }
46832
+ });
46833
+ }
46834
+ if (user2) {
46835
+ const userTags = user2.tags;
46836
+ _TrackedTags.create({
46837
+ client: client2,
46838
+ type: "user",
46839
+ id: user2.id,
46840
+ ...userTags && { initialTags: userTags }
46841
+ });
46842
+ }
46843
+ if (conversation) {
46844
+ const conversationTags = conversation.tags;
46845
+ _TrackedTags.create({
46846
+ client: client2,
46847
+ type: "conversation",
46848
+ id: conversation.id,
46849
+ ...conversationTags && { initialTags: conversationTags }
46850
+ });
46851
+ }
46852
+ if (workflow) {
46853
+ const workflowTags = workflow.tags;
46854
+ _TrackedTags.create({
46855
+ client: client2,
46856
+ type: "workflow",
46857
+ id: workflow.id,
46858
+ ...workflowTags && { initialTags: workflowTags }
46859
+ });
46860
+ }
46861
+ const tags = context2.get("tags", { optional: true });
46862
+ const unloadedTags = tags?.filter((tag) => !tag._loaded) ?? [];
46863
+ if (unloadedTags.length > 0) {
46864
+ await Promise.allSettled(unloadedTags.map((tag) => tag.load()));
46865
+ }
46866
+ });
46867
+ }
46868
+ static unloadAll() {
46869
+ context2.get("tags", { optional: true })?.splice(0);
46870
+ }
46871
+ async load(force = false) {
46872
+ if (this._loaded && !force) {
46873
+ return;
46874
+ }
46875
+ await span(
46876
+ "tags.load",
46877
+ {
46878
+ type: this.type,
46879
+ id: this.id
46880
+ },
46881
+ async () => {
46882
+ const tags = await this.fetchTags();
46883
+ this._tags = { ...tags };
46884
+ this._initialTags = { ...tags };
46885
+ this._loaded = true;
46886
+ }
46887
+ );
46888
+ }
46889
+ async save() {
46890
+ if (this._saving) {
46891
+ this._saveAgain = true;
46892
+ return;
46893
+ }
46894
+ const executionFinished = context2.get("executionFinished", { optional: true });
46895
+ if (executionFinished) {
46896
+ throw new Error(`Cannot save TrackedTags "${this.type}/${this.id}" after execution has finished.`);
46897
+ }
46898
+ try {
46899
+ this._saving = true;
46900
+ await span(
46901
+ "tags.save",
46902
+ {
46903
+ type: this.type,
46904
+ id: this.id
46905
+ },
46906
+ async () => {
46907
+ await this.persistTags(this._tags);
46908
+ this._initialTags = { ...this._tags };
46909
+ }
46910
+ );
46911
+ } finally {
46912
+ this._saving = false;
46913
+ if (this._saveAgain && this._saveAgainCount++ <= 5) {
46914
+ this._saveAgain = false;
46915
+ await this.save();
46916
+ } else {
46917
+ this._saveAgainCount = 0;
46918
+ }
46919
+ }
46920
+ }
46921
+ isDirty() {
46922
+ const currentKeys = Object.keys(this._tags).filter((k) => !k.includes(":")).sort();
46923
+ const initialKeys = Object.keys(this._initialTags).filter((k) => !k.includes(":")).sort();
46924
+ if (currentKeys.length !== initialKeys.length) {
46925
+ return true;
46926
+ }
46927
+ for (const key of currentKeys) {
46928
+ if (this._tags[key] !== this._initialTags[key]) {
46929
+ return true;
46930
+ }
46931
+ }
46932
+ return false;
46933
+ }
46934
+ get tags() {
46935
+ return new Proxy(this._tags, {
46936
+ set: (target, prop, value) => {
46937
+ target[prop] = value;
46938
+ return true;
46939
+ },
46940
+ deleteProperty: (target, prop) => {
46941
+ target[prop] = void 0;
46942
+ return true;
46943
+ }
46944
+ });
46945
+ }
46946
+ set tags(value) {
46947
+ this._tags = { ...value };
46948
+ }
46949
+ async fetchTags() {
46950
+ try {
46951
+ if (this.type === "bot") {
46952
+ const { bot: bot2 } = await this.client.getBot({ id: this.id });
46953
+ return bot2.tags || {};
46954
+ } else if (this.type === "user") {
46955
+ const { user: user2 } = await this.client.getUser({ id: this.id });
46956
+ return user2.tags || {};
46957
+ } else if (this.type === "conversation") {
46958
+ const { conversation } = await this.client.getConversation({ id: this.id });
46959
+ return conversation.tags || {};
46960
+ } else if (this.type === "workflow") {
46961
+ const { workflow } = await this.client.getWorkflow({ id: this.id });
46962
+ return workflow.tags || {};
46963
+ }
46964
+ } catch (err) {
46965
+ console.error(`Failed to fetch tags for ${this.type}/${this.id}:`, err);
46966
+ }
46967
+ return {};
46968
+ }
46969
+ async persistTags(tags) {
46970
+ const tagsForApi = {};
46971
+ for (const [key, value] of Object.entries(tags)) {
46972
+ if (value !== void 0 && !key.includes(":")) {
46973
+ tagsForApi[key] = value;
46974
+ }
46975
+ }
46976
+ try {
46977
+ if (this.type === "bot") {
46978
+ await this.client.updateBot({ id: this.id, tags: tagsForApi });
46979
+ } else if (this.type === "user") {
46980
+ await this.client.updateUser({ id: this.id, tags: tagsForApi });
46981
+ } else if (this.type === "conversation") {
46982
+ await this.client.updateConversation({ id: this.id, tags: tagsForApi });
46983
+ } else if (this.type === "workflow") {
46984
+ await this.client.updateWorkflow({ id: this.id, tags: tagsForApi });
46985
+ }
46986
+ } catch (err) {
46987
+ console.error(`Failed to persist tags for ${this.type}/${this.id}:`, err);
46988
+ throw err;
46989
+ }
46990
+ }
46991
+ };
46992
+ }
46993
+ });
46994
+
46604
46995
  // src/runtime/context/inspector-handler.ts
46605
46996
  async function handleInspectorDisable() {
46606
46997
  const inspector = await import("inspector");
@@ -46743,6 +47134,7 @@ var init_handlers = __esm({
46743
47134
  init_http();
46744
47135
  init_agent_registry();
46745
47136
  init_tracked_state();
47137
+ init_tracked_tags();
46746
47138
  init_tracing();
46747
47139
  init_heavy_imports();
46748
47140
  init_environment();
@@ -46870,12 +47262,13 @@ var init_handlers = __esm({
46870
47262
  reject(error);
46871
47263
  } finally {
46872
47264
  clearTimeout(timeout);
46873
- await TrackedState2.saveAllDirty();
47265
+ await Promise.all([TrackedState2.saveAllDirty(), TrackedTags.saveAllDirty()]);
46874
47266
  }
46875
47267
  });
46876
47268
  } finally {
46877
- await TrackedState2.saveAllDirty();
47269
+ await Promise.all([TrackedState2.saveAllDirty(), TrackedTags.saveAllDirty()]);
46878
47270
  TrackedState2.unloadAll();
47271
+ TrackedTags.unloadAll();
46879
47272
  context2.set("executionFinished", true);
46880
47273
  await shutdownPromiseTracker();
46881
47274
  }
@@ -48389,7 +48782,7 @@ var workflow_exports = {};
48389
48782
  __export(workflow_exports, {
48390
48783
  setup: () => setup3
48391
48784
  });
48392
- var import_ms2, setup3;
48785
+ var import_ms3, setup3;
48393
48786
  var init_workflow2 = __esm({
48394
48787
  "src/runtime/handlers/workflow.ts"() {
48395
48788
  "use strict";
@@ -48402,7 +48795,7 @@ var init_workflow2 = __esm({
48402
48795
  init_esm();
48403
48796
  init_adk();
48404
48797
  init_workflow_utils();
48405
- import_ms2 = __toESM(require_ms(), 1);
48798
+ import_ms3 = __toESM(require_ms(), 1);
48406
48799
  setup3 = (bot2) => {
48407
48800
  const handler = async function({ workflow, event, client: client2, ctx, conversation }) {
48408
48801
  await span(
@@ -48430,7 +48823,7 @@ var init_workflow2 = __esm({
48430
48823
  });
48431
48824
  return;
48432
48825
  }
48433
- const DEFAULT_TIMEOUT_MS = (0, import_ms2.default)("5m");
48826
+ const DEFAULT_TIMEOUT_MS = (0, import_ms3.default)("5m");
48434
48827
  const configuredTimeout = workflowDefinition.timeout;
48435
48828
  if (workflow.timeoutAt && configuredTimeout !== DEFAULT_TIMEOUT_MS) {
48436
48829
  const workflowStartedAt = new Date(workflow.createdAt).getTime();
@@ -48448,10 +48841,13 @@ var init_workflow2 = __esm({
48448
48841
  s.setAttribute("workflow.timeout.to", configuredTimeout);
48449
48842
  }
48450
48843
  }
48451
- const instance = await BaseWorkflowInstance.load({
48452
- id: workflow.id,
48453
- workflow
48454
- });
48844
+ const [instance] = await Promise.all([
48845
+ BaseWorkflowInstance.load({
48846
+ id: workflow.id,
48847
+ workflow
48848
+ }),
48849
+ TrackedState2.loadAll()
48850
+ ]);
48455
48851
  void workflow.acknowledgeStartOfProcessing();
48456
48852
  try {
48457
48853
  const interval = setInterval(async () => {
@@ -48606,15 +49002,18 @@ var init_event = __esm({
48606
49002
  logger.warn(`Skipping ${WorkflowCallbackEvent.name} event, conversation not found in context`);
48607
49003
  return;
48608
49004
  }
48609
- let alias = conversation.integration;
48610
- const colonIndex = event.type.indexOf(":");
48611
- if (colonIndex > 0) {
48612
- alias = event.type.substring(0, colonIndex);
48613
- }
48614
- const handlerName = alias + "." + conversation.channel;
48615
- const handler = findMatchingHandler(adk.project.conversations, handlerName);
49005
+ const handlerName = conversation.integration + "." + conversation.channel;
49006
+ const isBuiltInEvent = event.type === WorkflowCallbackEvent.name || event.type === WorkflowDataRequestEvent2.name;
49007
+ const matchingHandlers = adk.project.conversations.filter(
49008
+ (h) => matchesChannel(h.channel, handlerName) && (isBuiltInEvent || h.events.includes(event.type))
49009
+ );
49010
+ const handler = matchingHandlers.sort((a, b) => {
49011
+ const aScore = a.channel === "*" ? 0 : Array.isArray(a.channel) ? 1 : 2;
49012
+ const bScore = b.channel === "*" ? 0 : Array.isArray(b.channel) ? 1 : 2;
49013
+ return bScore - aScore;
49014
+ })[0];
48616
49015
  if (!handler) {
48617
- logger.debug(`Skipping message, no ADK Conversation defined for "${handlerName}"`);
49016
+ logger.debug(`Skipping event "${event.type}", no ADK Conversation listening to this event for channel "${handlerName}"`);
48618
49017
  return;
48619
49018
  }
48620
49019
  await span(
@@ -48632,15 +49031,18 @@ var init_event = __esm({
48632
49031
  async () => {
48633
49032
  const chat = new BotpressChat(context2.getAll());
48634
49033
  context2.set("chat", chat);
48635
- const transcript = await chat.fetchTranscript();
49034
+ const [transcript, _statesLoaded, _tagsLoaded] = await Promise.all([
49035
+ chat.fetchTranscript(),
49036
+ TrackedState2.loadAll(),
49037
+ TrackedTags.loadAll()
49038
+ ]);
48636
49039
  if (transcript.find((x) => x.id === event.id)) {
48637
49040
  logger.debug(`Message ${event.id} already processed`);
48638
49041
  return;
48639
49042
  }
48640
49043
  await chat.addEvent(event);
48641
- await TrackedState2.loadAll();
48642
49044
  await handler[ConversationHandler]();
48643
- await TrackedState2.saveAllDirty();
49045
+ await Promise.all([TrackedState2.saveAllDirty(), TrackedTags.saveAllDirty()]);
48644
49046
  await chat.saveTranscript();
48645
49047
  }
48646
49048
  );
@@ -48733,259 +49135,6 @@ var init_handlers2 = __esm({
48733
49135
  }
48734
49136
  });
48735
49137
 
48736
- // src/runtime/tracked-tags.ts
48737
- var TrackedTags;
48738
- var init_tracked_tags = __esm({
48739
- "src/runtime/tracked-tags.ts"() {
48740
- "use strict";
48741
- init_define_BUILD();
48742
- init_define_PACKAGE_VERSIONS();
48743
- init_context3();
48744
- init_tracing();
48745
- TrackedTags = class _TrackedTags {
48746
- type;
48747
- id;
48748
- client;
48749
- _tags = {};
48750
- _initialTags = {};
48751
- _loaded = false;
48752
- _saving = false;
48753
- _saveAgain = false;
48754
- _saveAgainCount = 0;
48755
- static _savingAll = false;
48756
- static _saveAllAgain = false;
48757
- static _saveAllCount = 0;
48758
- constructor(props) {
48759
- this.type = props.type;
48760
- this.id = props.id;
48761
- this.client = props.client;
48762
- }
48763
- static create(props) {
48764
- const tags = context2.get("tags", { optional: true });
48765
- const executionFinished = context2.get("executionFinished", { optional: true });
48766
- if (executionFinished) {
48767
- throw new Error(`Cannot create new TrackedTags "${props.type}/${props.id}" after execution has finished.`);
48768
- }
48769
- const match2 = tags?.find((x) => x.id === props.id && x.type === props.type);
48770
- if (match2) {
48771
- return match2;
48772
- }
48773
- const instance = new _TrackedTags(props);
48774
- if (props.initialTags) {
48775
- instance._tags = { ...props.initialTags };
48776
- instance._initialTags = { ...props.initialTags };
48777
- instance._loaded = true;
48778
- }
48779
- tags?.push(instance);
48780
- return instance;
48781
- }
48782
- static async saveAllDirty() {
48783
- if (this._savingAll) {
48784
- this._saveAllAgain = true;
48785
- return;
48786
- }
48787
- try {
48788
- this._savingAll = true;
48789
- const tags = context2.get("tags", { optional: true });
48790
- const dirtyTags = tags?.filter((t) => t.isDirty()) || [];
48791
- if (!dirtyTags.length) {
48792
- return;
48793
- }
48794
- await span(
48795
- "tags.saveAllDirty",
48796
- {
48797
- tags_count: tags?.length || 0,
48798
- tags: tags.map((t) => `${t.type}/${t.id}`)
48799
- },
48800
- () => Promise.allSettled(dirtyTags.map((t) => t.save()))
48801
- );
48802
- } finally {
48803
- this._savingAll = false;
48804
- if (this._saveAllAgain && this._saveAllCount++ <= 5) {
48805
- this._saveAllAgain = false;
48806
- await this.saveAllDirty();
48807
- } else {
48808
- this._saveAllCount = 0;
48809
- }
48810
- }
48811
- }
48812
- static async loadAll() {
48813
- await span("tags.loadAll", {}, async () => {
48814
- const client2 = context2.get("client")._inner;
48815
- const bot2 = context2.get("bot", { optional: true });
48816
- const user2 = context2.get("user", { optional: true });
48817
- const conversation = context2.get("conversation", { optional: true });
48818
- const workflow = context2.get("workflow", { optional: true });
48819
- if (bot2) {
48820
- const botTags = bot2.tags;
48821
- _TrackedTags.create({
48822
- client: client2,
48823
- type: "bot",
48824
- id: bot2.id,
48825
- ...botTags && { initialTags: botTags }
48826
- });
48827
- }
48828
- if (user2) {
48829
- const userTags = user2.tags;
48830
- _TrackedTags.create({
48831
- client: client2,
48832
- type: "user",
48833
- id: user2.id,
48834
- ...userTags && { initialTags: userTags }
48835
- });
48836
- }
48837
- if (conversation) {
48838
- const conversationTags = conversation.tags;
48839
- _TrackedTags.create({
48840
- client: client2,
48841
- type: "conversation",
48842
- id: conversation.id,
48843
- ...conversationTags && { initialTags: conversationTags }
48844
- });
48845
- }
48846
- if (workflow) {
48847
- const workflowTags = workflow.tags;
48848
- _TrackedTags.create({
48849
- client: client2,
48850
- type: "workflow",
48851
- id: workflow.id,
48852
- ...workflowTags && { initialTags: workflowTags }
48853
- });
48854
- }
48855
- const tags = context2.get("tags", { optional: true });
48856
- const unloadedTags = tags?.filter((tag) => !tag._loaded) ?? [];
48857
- if (unloadedTags.length > 0) {
48858
- await Promise.allSettled(unloadedTags.map((tag) => tag.load()));
48859
- }
48860
- });
48861
- }
48862
- static unloadAll() {
48863
- context2.get("tags", { optional: true })?.splice(0);
48864
- }
48865
- async load(force = false) {
48866
- if (this._loaded && !force) {
48867
- return;
48868
- }
48869
- await span(
48870
- "tags.load",
48871
- {
48872
- type: this.type,
48873
- id: this.id
48874
- },
48875
- async () => {
48876
- const tags = await this.fetchTags();
48877
- this._tags = { ...tags };
48878
- this._initialTags = { ...tags };
48879
- this._loaded = true;
48880
- }
48881
- );
48882
- }
48883
- async save() {
48884
- if (this._saving) {
48885
- this._saveAgain = true;
48886
- return;
48887
- }
48888
- const executionFinished = context2.get("executionFinished", { optional: true });
48889
- if (executionFinished) {
48890
- throw new Error(`Cannot save TrackedTags "${this.type}/${this.id}" after execution has finished.`);
48891
- }
48892
- try {
48893
- this._saving = true;
48894
- await span(
48895
- "tags.save",
48896
- {
48897
- type: this.type,
48898
- id: this.id
48899
- },
48900
- async () => {
48901
- await this.persistTags(this._tags);
48902
- this._initialTags = { ...this._tags };
48903
- }
48904
- );
48905
- } finally {
48906
- this._saving = false;
48907
- if (this._saveAgain && this._saveAgainCount++ <= 5) {
48908
- this._saveAgain = false;
48909
- await this.save();
48910
- } else {
48911
- this._saveAgainCount = 0;
48912
- }
48913
- }
48914
- }
48915
- isDirty() {
48916
- const currentKeys = Object.keys(this._tags).sort();
48917
- const initialKeys = Object.keys(this._initialTags).sort();
48918
- if (currentKeys.length !== initialKeys.length) {
48919
- return true;
48920
- }
48921
- for (const key of currentKeys) {
48922
- if (this._tags[key] !== this._initialTags[key]) {
48923
- return true;
48924
- }
48925
- }
48926
- return false;
48927
- }
48928
- get tags() {
48929
- return new Proxy(this._tags, {
48930
- set: (target, prop, value) => {
48931
- target[prop] = value;
48932
- return true;
48933
- },
48934
- deleteProperty: (target, prop) => {
48935
- target[prop] = void 0;
48936
- return true;
48937
- }
48938
- });
48939
- }
48940
- set tags(value) {
48941
- this._tags = { ...value };
48942
- }
48943
- async fetchTags() {
48944
- try {
48945
- if (this.type === "bot") {
48946
- const { bot: bot2 } = await this.client.getBot({ id: this.id });
48947
- return bot2.tags || {};
48948
- } else if (this.type === "user") {
48949
- const { user: user2 } = await this.client.getUser({ id: this.id });
48950
- return user2.tags || {};
48951
- } else if (this.type === "conversation") {
48952
- const { conversation } = await this.client.getConversation({ id: this.id });
48953
- return conversation.tags || {};
48954
- } else if (this.type === "workflow") {
48955
- const { workflow } = await this.client.getWorkflow({ id: this.id });
48956
- return workflow.tags || {};
48957
- }
48958
- } catch (err) {
48959
- console.error(`Failed to fetch tags for ${this.type}/${this.id}:`, err);
48960
- }
48961
- return {};
48962
- }
48963
- async persistTags(tags) {
48964
- const tagsForApi = {};
48965
- for (const [key, value] of Object.entries(tags)) {
48966
- if (value !== void 0) {
48967
- tagsForApi[key] = value;
48968
- }
48969
- }
48970
- try {
48971
- if (this.type === "bot") {
48972
- await this.client.updateBot({ id: this.id, tags: tagsForApi });
48973
- } else if (this.type === "user") {
48974
- await this.client.updateUser({ id: this.id, tags: tagsForApi });
48975
- } else if (this.type === "conversation") {
48976
- await this.client.updateConversation({ id: this.id, tags: tagsForApi });
48977
- } else if (this.type === "workflow") {
48978
- await this.client.updateWorkflow({ id: this.id, tags: tagsForApi });
48979
- }
48980
- } catch (err) {
48981
- console.error(`Failed to persist tags for ${this.type}/${this.id}:`, err);
48982
- throw err;
48983
- }
48984
- }
48985
- };
48986
- }
48987
- });
48988
-
48989
49138
  // src/runtime/index.ts
48990
49139
  var init_runtime = __esm({
48991
49140
  "src/runtime/index.ts"() {