@axiom-lattice/core 2.1.75 → 2.1.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ __export(index_exports, {
42
42
  CompositeBackend: () => CompositeBackend,
43
43
  ConsoleLoggerClient: () => ConsoleLoggerClient,
44
44
  CustomMetricsClient: () => CustomMetricsClient,
45
+ CustomMiddlewareRegistry: () => CustomMiddlewareRegistry,
45
46
  DaytonaInstance: () => DaytonaInstance,
46
47
  DaytonaProvider: () => DaytonaProvider,
47
48
  DefaultScheduleClient: () => DefaultScheduleClient,
@@ -115,6 +116,7 @@ __export(index_exports, {
115
116
  checkEmptyContent: () => checkEmptyContent,
116
117
  clearEncryptionKeyCache: () => clearEncryptionKeyCache,
117
118
  computeSandboxName: () => computeSandboxName,
119
+ configureStores: () => configureStores,
118
120
  createAgentTeam: () => createAgentTeam,
119
121
  createExecuteSqlQueryTool: () => createExecuteSqlQueryTool,
120
122
  createFileData: () => createFileData,
@@ -2546,24 +2548,6 @@ var InMemoryThreadMessageQueueStore = class {
2546
2548
  }
2547
2549
  }
2548
2550
  }
2549
- async markCompleted(messageId) {
2550
- for (const messages of this.messages.values()) {
2551
- const message = messages.find((msg) => msg.id === messageId);
2552
- if (message) {
2553
- message.status = "completed";
2554
- return;
2555
- }
2556
- }
2557
- }
2558
- async clearCompletedMessages(threadId) {
2559
- const messages = this.messages.get(threadId);
2560
- if (messages) {
2561
- const filtered = messages.filter(
2562
- (msg) => msg.status !== "completed"
2563
- );
2564
- this.messages.set(threadId, filtered);
2565
- }
2566
- }
2567
2551
  async resetProcessingToPending(threadId) {
2568
2552
  const messages = this.messages.get(threadId);
2569
2553
  if (!messages) {
@@ -2708,14 +2692,35 @@ var InMemoryChannelInstallationStore = class {
2708
2692
  constructor() {
2709
2693
  this.installations = /* @__PURE__ */ new Map();
2710
2694
  }
2695
+ /**
2696
+ * Retrieves a channel installation by its unique ID.
2697
+ *
2698
+ * @param installationId - The installation identifier
2699
+ * @returns The {@link ChannelInstallation} or `null` if not found
2700
+ */
2711
2701
  async getInstallationById(installationId) {
2712
2702
  return this.installations.get(installationId) || null;
2713
2703
  }
2704
+ /**
2705
+ * Lists all channel installations for a tenant, optionally filtered by channel type.
2706
+ *
2707
+ * @param tenantId - Tenant identifier
2708
+ * @param channel - Optional channel type filter (`"lark"`, `"email"`, `"slack"`)
2709
+ * @returns Array of matching {@link ChannelInstallation} objects
2710
+ */
2714
2711
  async getInstallationsByTenant(tenantId, channel) {
2715
2712
  return Array.from(this.installations.values()).filter(
2716
2713
  (inst) => inst.tenantId === tenantId && (!channel || inst.channel === channel)
2717
2714
  );
2718
2715
  }
2716
+ /**
2717
+ * Creates a new channel installation for a tenant.
2718
+ *
2719
+ * @param tenantId - Tenant identifier
2720
+ * @param installationId - Unique installation ID (caller-defined)
2721
+ * @param data - {@link CreateChannelInstallationRequest} containing channel type, name, config, and fallback settings
2722
+ * @returns The created {@link ChannelInstallation}
2723
+ */
2719
2724
  async createInstallation(tenantId, installationId, data) {
2720
2725
  const now = /* @__PURE__ */ new Date();
2721
2726
  const installation = {
@@ -2733,6 +2738,14 @@ var InMemoryChannelInstallationStore = class {
2733
2738
  this.installations.set(installationId, installation);
2734
2739
  return installation;
2735
2740
  }
2741
+ /**
2742
+ * Updates an existing channel installation. Config fields are shallow-merged.
2743
+ *
2744
+ * @param tenantId - Tenant identifier (used for isolation check)
2745
+ * @param installationId - Installation ID to update
2746
+ * @param updates - {@link UpdateChannelInstallationRequest} with fields to patch
2747
+ * @returns The updated {@link ChannelInstallation} or `null` if not found or tenant mismatch
2748
+ */
2736
2749
  async updateInstallation(tenantId, installationId, updates) {
2737
2750
  const existing = this.installations.get(installationId);
2738
2751
  if (!existing || existing.tenantId !== tenantId) return null;
@@ -2748,11 +2761,21 @@ var InMemoryChannelInstallationStore = class {
2748
2761
  this.installations.set(installationId, updated);
2749
2762
  return updated;
2750
2763
  }
2764
+ /**
2765
+ * Deletes a channel installation.
2766
+ *
2767
+ * @param tenantId - Tenant identifier (used for isolation check)
2768
+ * @param installationId - Installation ID to delete
2769
+ * @returns `true` if deleted, `false` if not found or tenant mismatch
2770
+ */
2751
2771
  async deleteInstallation(tenantId, installationId) {
2752
2772
  const existing = this.installations.get(installationId);
2753
2773
  if (!existing || existing.tenantId !== tenantId) return false;
2754
2774
  return this.installations.delete(installationId);
2755
2775
  }
2776
+ /**
2777
+ * Clears all in-memory installations. Primarily used in tests.
2778
+ */
2756
2779
  clear() {
2757
2780
  this.installations.clear();
2758
2781
  }
@@ -2764,6 +2787,16 @@ var InMemoryBindingStore = class {
2764
2787
  constructor() {
2765
2788
  this.bindings = /* @__PURE__ */ new Map();
2766
2789
  }
2790
+ /**
2791
+ * Resolves a binding for the given sender across a specific channel installation.
2792
+ *
2793
+ * Called by `MessageRouter.dispatch()` during inbound message processing.
2794
+ * Matches on `channel + senderId + channelInstallationId + tenantId` and
2795
+ * requires `enabled === true`.
2796
+ *
2797
+ * @param params - Resolution parameters
2798
+ * @returns The matching {@link Binding} or `null` if none found.
2799
+ */
2767
2800
  async resolve(params) {
2768
2801
  for (const binding of this.bindings.values()) {
2769
2802
  if (binding.channel === params.channel && binding.senderId === params.senderId && binding.channelInstallationId === params.channelInstallationId && binding.tenantId === params.tenantId && binding.enabled) {
@@ -2772,6 +2805,12 @@ var InMemoryBindingStore = class {
2772
2805
  }
2773
2806
  return null;
2774
2807
  }
2808
+ /**
2809
+ * Creates a new sender-to-agent binding.
2810
+ *
2811
+ * @param input - {@link CreateBindingInput} defining channel, sender, target agent, and thread mode.
2812
+ * @returns The newly created {@link Binding}.
2813
+ */
2775
2814
  async create(input) {
2776
2815
  const now = /* @__PURE__ */ new Date();
2777
2816
  const binding = {
@@ -2794,6 +2833,14 @@ var InMemoryBindingStore = class {
2794
2833
  this.bindings.set(binding.id, binding);
2795
2834
  return binding;
2796
2835
  }
2836
+ /**
2837
+ * Updates an existing binding (e.g. changing the target agent or thread mode).
2838
+ *
2839
+ * @param id - Binding ID
2840
+ * @param patch - Partial {@link Binding} fields to update
2841
+ * @returns The updated {@link Binding}
2842
+ * @throws {Error} If the binding does not exist
2843
+ */
2797
2844
  async update(id, patch) {
2798
2845
  const existing = this.bindings.get(id);
2799
2846
  if (!existing) throw new Error(`Binding ${id} not found`);
@@ -2805,9 +2852,20 @@ var InMemoryBindingStore = class {
2805
2852
  this.bindings.set(id, updated);
2806
2853
  return updated;
2807
2854
  }
2855
+ /**
2856
+ * Deletes a binding by ID.
2857
+ *
2858
+ * @param id - Binding ID
2859
+ */
2808
2860
  async delete(id) {
2809
2861
  this.bindings.delete(id);
2810
2862
  }
2863
+ /**
2864
+ * Lists bindings filtered by tenant and optional channel/agent/installation.
2865
+ *
2866
+ * @param params - Filter and pagination parameters
2867
+ * @returns Array of matching {@link Binding} objects
2868
+ */
2811
2869
  async list(params) {
2812
2870
  let results = Array.from(this.bindings.values()).filter((b) => {
2813
2871
  if (b.tenantId !== params.tenantId) return false;
@@ -2820,6 +2878,12 @@ var InMemoryBindingStore = class {
2820
2878
  const limit = params.limit ?? 50;
2821
2879
  return results.slice(offset, offset + limit);
2822
2880
  }
2881
+ /**
2882
+ * Bulk-imports bindings.
2883
+ *
2884
+ * @param bindings - Array of {@link CreateBindingInput}
2885
+ * @returns Array of created {@link Binding} objects
2886
+ */
2823
2887
  async import(bindings) {
2824
2888
  const result = [];
2825
2889
  for (const input of bindings) {
@@ -2827,9 +2891,18 @@ var InMemoryBindingStore = class {
2827
2891
  }
2828
2892
  return result;
2829
2893
  }
2894
+ /**
2895
+ * Exports all bindings for a tenant.
2896
+ *
2897
+ * @param params - Filter by tenantId
2898
+ * @returns Array of {@link Binding} objects
2899
+ */
2830
2900
  async export(params) {
2831
2901
  return this.list({ tenantId: params.tenantId, limit: 1e4 });
2832
2902
  }
2903
+ /**
2904
+ * Clears all in-memory bindings. Primarily used in tests.
2905
+ */
2833
2906
  clear() {
2834
2907
  this.bindings.clear();
2835
2908
  }
@@ -3452,7 +3525,6 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
3452
3525
  constructor() {
3453
3526
  this.databases = /* @__PURE__ */ new Map();
3454
3527
  this.defaultDatabaseKeys = /* @__PURE__ */ new Map();
3455
- this.configStore = null;
3456
3528
  }
3457
3529
  /**
3458
3530
  * Get the singleton instance
@@ -3512,16 +3584,9 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
3512
3584
  }
3513
3585
  this.defaultDatabaseKeys.set(tenantId, key);
3514
3586
  }
3515
- /**
3516
- * Set the configuration store for on-demand database loading
3517
- * @param store - The database configuration store
3518
- */
3519
- setConfigStore(store) {
3520
- this.configStore = store;
3521
- }
3522
3587
  /**
3523
3588
  * Get a database by key for a specific tenant
3524
- * If database is not registered and configStore is set, will try to load from store
3589
+ * If database is not registered, tries to load from the store lattice
3525
3590
  * @param tenantId - Tenant identifier (required)
3526
3591
  * @param key - Database key (optional, uses default if not provided)
3527
3592
  * @returns ISqlDatabase instance
@@ -3539,8 +3604,10 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
3539
3604
  return database;
3540
3605
  }
3541
3606
  }
3542
- if (this.configStore) {
3543
- const configEntry = await this.configStore.getConfigByKey(tenantId, dbKey);
3607
+ try {
3608
+ const { store } = getStoreLattice("default", "database");
3609
+ const configStore = store;
3610
+ const configEntry = await configStore.getConfigByKey(tenantId, dbKey);
3544
3611
  if (configEntry) {
3545
3612
  this.registerDatabase(tenantId, dbKey, configEntry.config);
3546
3613
  if (!this.defaultDatabaseKeys.has(tenantId)) {
@@ -3554,6 +3621,7 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
3554
3621
  }
3555
3622
  }
3556
3623
  }
3624
+ } catch {
3557
3625
  }
3558
3626
  if (!tenantDbs) {
3559
3627
  throw new Error(`No databases registered for tenant '${tenantId}'`);
@@ -4663,6 +4731,7 @@ var MetricsServerManager = class _MetricsServerManager {
4663
4731
  this.clients = /* @__PURE__ */ new Map();
4664
4732
  this.configs = /* @__PURE__ */ new Map();
4665
4733
  this.defaultServerKeys = /* @__PURE__ */ new Map();
4734
+ this._loadingPromise = null;
4666
4735
  }
4667
4736
  /**
4668
4737
  * Get the singleton instance
@@ -4673,6 +4742,32 @@ var MetricsServerManager = class _MetricsServerManager {
4673
4742
  }
4674
4743
  return _MetricsServerManager.instance;
4675
4744
  }
4745
+ /**
4746
+ * Ensure configurations are loaded from the store lattice.
4747
+ * Uses a promise-lock to prevent concurrent loads.
4748
+ */
4749
+ async _ensureLoaded() {
4750
+ if (this.clients.size > 0) {
4751
+ return;
4752
+ }
4753
+ if (this._loadingPromise) {
4754
+ return this._loadingPromise;
4755
+ }
4756
+ this._loadingPromise = (async () => {
4757
+ try {
4758
+ const { store } = getStoreLattice("default", "metrics");
4759
+ const configStore = store;
4760
+ const configs = await configStore.getAllConfigsWithoutTenant();
4761
+ for (const entry of configs) {
4762
+ const tenantId = entry.tenantId || "default";
4763
+ this.registerServer(tenantId, entry.key, entry.config);
4764
+ }
4765
+ } finally {
4766
+ this._loadingPromise = null;
4767
+ }
4768
+ })();
4769
+ return this._loadingPromise;
4770
+ }
4676
4771
  /**
4677
4772
  * Get or create tenant clients map
4678
4773
  */
@@ -4741,7 +4836,8 @@ var MetricsServerManager = class _MetricsServerManager {
4741
4836
  * @param tenantId - Tenant identifier
4742
4837
  * @param key - Server key (optional, uses default if not provided)
4743
4838
  */
4744
- getClient(tenantId, key) {
4839
+ async getClient(tenantId, key) {
4840
+ await this._ensureLoaded();
4745
4841
  const tenantClients = this.clients.get(tenantId);
4746
4842
  if (!tenantClients) {
4747
4843
  throw new Error(`No metrics servers registered for tenant '${tenantId}'`);
@@ -4761,7 +4857,8 @@ var MetricsServerManager = class _MetricsServerManager {
4761
4857
  * @param tenantId - Tenant identifier
4762
4858
  * @param key - Server key (optional, uses default if not provided)
4763
4859
  */
4764
- getConfig(tenantId, key) {
4860
+ async getConfig(tenantId, key) {
4861
+ await this._ensureLoaded();
4765
4862
  const tenantConfigs = this.configs.get(tenantId);
4766
4863
  if (!tenantConfigs) {
4767
4864
  throw new Error(`No metrics servers registered for tenant '${tenantId}'`);
@@ -4789,7 +4886,8 @@ var MetricsServerManager = class _MetricsServerManager {
4789
4886
  * Get all registered metrics server keys with their types for a tenant
4790
4887
  * @param tenantId - Tenant identifier
4791
4888
  */
4792
- getServerKeys(tenantId) {
4889
+ async getServerKeys(tenantId) {
4890
+ await this._ensureLoaded();
4793
4891
  const tenantConfigs = this.configs.get(tenantId);
4794
4892
  if (!tenantConfigs) {
4795
4893
  return [];
@@ -4856,20 +4954,6 @@ var MetricsServerManager = class _MetricsServerManager {
4856
4954
  this.registerServer(tenantId, entry.key, entry.config);
4857
4955
  }
4858
4956
  }
4859
- /**
4860
- * Load all metrics server configurations from a store
4861
- * across all tenants and register them with this manager
4862
- *
4863
- * @param store - The metrics server configuration store
4864
- * @deprecated Use loadConfigsFromStore with specific tenant instead
4865
- */
4866
- async loadAllConfigsFromStore(store) {
4867
- const configs = await store.getAllConfigsWithoutTenant();
4868
- for (const entry of configs) {
4869
- const tenantId = entry.tenantId || "default";
4870
- this.registerServer(tenantId, entry.key, entry.config);
4871
- }
4872
- }
4873
4957
  };
4874
4958
  var metricsServerManager = MetricsServerManager.getInstance();
4875
4959
 
@@ -4888,7 +4972,7 @@ ${serverKeys.map(
4888
4972
  async (_input, _exeConfig) => {
4889
4973
  try {
4890
4974
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
4891
- const servers = metricsServerManager.getServerKeys(tenantId);
4975
+ const servers = await metricsServerManager.getServerKeys(tenantId);
4892
4976
  if (servers.length === 0) {
4893
4977
  return "No metrics servers registered.";
4894
4978
  }
@@ -4927,7 +5011,7 @@ ${serverKeys.map(
4927
5011
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
4928
5012
  let effectiveServerKeys = serverKeys;
4929
5013
  if (connectAll) {
4930
- effectiveServerKeys = metricsServerManager.getServerKeys(tenantId).map((s) => s.key);
5014
+ effectiveServerKeys = (await metricsServerManager.getServerKeys(tenantId)).map((s) => s.key);
4931
5015
  }
4932
5016
  const filteredServerKeys = filterServerKeysByTenant(effectiveServerKeys, tenantId);
4933
5017
  const runConfig = _exeConfig?.configurable?.runConfig || {};
@@ -4950,12 +5034,12 @@ To view all available data sources, please clear the current selection or reopen
4950
5034
  const allDataSources = [];
4951
5035
  for (const serverKey of filteredServerKeys) {
4952
5036
  try {
4953
- const config = metricsServerManager.getConfig(tenantId, serverKey);
5037
+ const config = await metricsServerManager.getConfig(tenantId, serverKey);
4954
5038
  if (config.type !== "semantic") {
4955
5039
  console.warn(`Server "${serverKey}" is not a semantic metrics server, skipping.`);
4956
5040
  continue;
4957
5041
  }
4958
- const client = metricsServerManager.getClient(tenantId, serverKey);
5042
+ const client = await metricsServerManager.getClient(tenantId, serverKey);
4959
5043
  const dataSources = await client.getDataSources();
4960
5044
  const selectedIds = config.selectedDataSources || [];
4961
5045
  const filteredDataSources = selectedIds.length > 0 ? dataSources.filter((ds) => selectedIds.includes(String(ds.id))) : dataSources;
@@ -5046,7 +5130,7 @@ ${serverKeys.map(
5046
5130
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
5047
5131
  let effectiveServerKeys = serverKeys;
5048
5132
  if (connectAll) {
5049
- effectiveServerKeys = metricsServerManager.getServerKeys(tenantId).map((s) => s.key);
5133
+ effectiveServerKeys = (await metricsServerManager.getServerKeys(tenantId)).map((s) => s.key);
5050
5134
  }
5051
5135
  const filteredServerKeys = filterServerKeysByTenant(effectiveServerKeys, tenantId);
5052
5136
  const runConfig = _exeConfig?.configurable?.runConfig || {};
@@ -5058,11 +5142,11 @@ ${serverKeys.map(
5058
5142
  if (!filteredServerKeys.includes(serverKey)) {
5059
5143
  return `Error: serverKey "${serverKey}" is not available for tenant "${tenantId}". Available servers: [${filteredServerKeys.join(", ")}]`;
5060
5144
  }
5061
- const config = metricsServerManager.getConfig(tenantId, serverKey);
5145
+ const config = await metricsServerManager.getConfig(tenantId, serverKey);
5062
5146
  if (config.type !== "semantic") {
5063
5147
  return `Error: Server "${serverKey}" is not a semantic metrics server. This tool only works with semantic servers.`;
5064
5148
  }
5065
- const client = metricsServerManager.getClient(tenantId, serverKey);
5149
+ const client = await metricsServerManager.getClient(tenantId, serverKey);
5066
5150
  const targetDatasourceIds = datasourceIds && datasourceIds.length > 0 ? datasourceIds : metricsDataSource?.datasourceId ? [metricsDataSource.datasourceId] : client.getSelectedDataSources();
5067
5151
  if (targetDatasourceIds.length === 0) {
5068
5152
  return `Error: No data sources specified and no default data sources configured for server "${serverKey}".`;
@@ -5204,7 +5288,7 @@ ${serverKeys.map(
5204
5288
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
5205
5289
  let effectiveServerKeys = serverKeys;
5206
5290
  if (connectAll) {
5207
- effectiveServerKeys = metricsServerManager.getServerKeys(tenantId).map((s) => s.key);
5291
+ effectiveServerKeys = (await metricsServerManager.getServerKeys(tenantId)).map((s) => s.key);
5208
5292
  }
5209
5293
  const filteredServerKeys = filterServerKeysByTenant(effectiveServerKeys, tenantId);
5210
5294
  const runConfig = _exeConfig?.configurable?.runConfig || {};
@@ -5220,11 +5304,11 @@ ${serverKeys.map(
5220
5304
  if (!metricName) {
5221
5305
  return "Error: metricName parameter is required.";
5222
5306
  }
5223
- const config = metricsServerManager.getConfig(tenantId, serverKey);
5307
+ const config = await metricsServerManager.getConfig(tenantId, serverKey);
5224
5308
  if (config.type !== "semantic") {
5225
5309
  return `Error: Server "${serverKey}" is not a semantic metrics server. This tool only works with semantic servers.`;
5226
5310
  }
5227
- const client = metricsServerManager.getClient(tenantId, serverKey);
5311
+ const client = await metricsServerManager.getClient(tenantId, serverKey);
5228
5312
  const targetDatasourceIds = datasourceId ? [datasourceId] : client.getSelectedDataSources();
5229
5313
  if (targetDatasourceIds.length === 0) {
5230
5314
  return `Error: No datasourceId specified and no default data sources configured for server "${serverKey}".`;
@@ -5440,7 +5524,7 @@ ${serverKeys.map(
5440
5524
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
5441
5525
  let effectiveServerKeys = serverKeys;
5442
5526
  if (connectAll) {
5443
- effectiveServerKeys = metricsServerManager.getServerKeys(tenantId).map((s) => s.key);
5527
+ effectiveServerKeys = (await metricsServerManager.getServerKeys(tenantId)).map((s) => s.key);
5444
5528
  }
5445
5529
  const filteredServerKeys = filterServerKeysByTenant(effectiveServerKeys, tenantId);
5446
5530
  const runConfig = _exeConfig?.configurable?.runConfig || {};
@@ -5459,11 +5543,11 @@ ${serverKeys.map(
5459
5543
  if (!metrics || metrics.length === 0) {
5460
5544
  return "Error: metrics parameter is required (at least one metric name).";
5461
5545
  }
5462
- const config = metricsServerManager.getConfig(tenantId, serverKey);
5546
+ const config = await metricsServerManager.getConfig(tenantId, serverKey);
5463
5547
  if (config.type !== "semantic") {
5464
5548
  return `Error: Server "${serverKey}" is not a semantic metrics server. This tool only works with semantic servers.`;
5465
5549
  }
5466
- const client = metricsServerManager.getClient(tenantId, serverKey);
5550
+ const client = await metricsServerManager.getClient(tenantId, serverKey);
5467
5551
  const semanticFilters = (filters || []).map((f) => ({
5468
5552
  dimension: f.dimension,
5469
5553
  operator: f.operator,
@@ -5521,7 +5605,7 @@ ${serverKeys.map(
5521
5605
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
5522
5606
  let effectiveServerKeys = serverKeys;
5523
5607
  if (connectAll) {
5524
- effectiveServerKeys = metricsServerManager.getServerKeys(tenantId).map((s) => s.key);
5608
+ effectiveServerKeys = (await metricsServerManager.getServerKeys(tenantId)).map((s) => s.key);
5525
5609
  }
5526
5610
  const filteredServerKeys = filterServerKeysByTenant(effectiveServerKeys, tenantId);
5527
5611
  const runConfig = _exeConfig?.configurable?.runConfig || {};
@@ -5533,11 +5617,11 @@ ${serverKeys.map(
5533
5617
  if (!filteredServerKeys.includes(serverKey)) {
5534
5618
  return `Error: serverKey "${serverKey}" is not available for tenant "${tenantId}". Available servers: [${filteredServerKeys.join(", ")}]`;
5535
5619
  }
5536
- const config = metricsServerManager.getConfig(tenantId, serverKey);
5620
+ const config = await metricsServerManager.getConfig(tenantId, serverKey);
5537
5621
  if (config.type !== "semantic") {
5538
5622
  return `Error: Server "${serverKey}" is not a semantic metrics server. This tool only works with semantic servers.`;
5539
5623
  }
5540
- const client = metricsServerManager.getClient(tenantId, serverKey);
5624
+ const client = await metricsServerManager.getClient(tenantId, serverKey);
5541
5625
  const targetDatasourceIds = datasourceIds && datasourceIds.length > 0 ? datasourceIds : metricsDataSource?.datasourceId ? [metricsDataSource.datasourceId] : client.getSelectedDataSources();
5542
5626
  if (targetDatasourceIds.length === 0) {
5543
5627
  return `Error: No data sources specified and no default data sources configured for server "${serverKey}".`;
@@ -5626,7 +5710,7 @@ ${serverKeys.map(
5626
5710
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
5627
5711
  let effectiveServerKeys = serverKeys;
5628
5712
  if (connectAll) {
5629
- effectiveServerKeys = metricsServerManager.getServerKeys(tenantId).map((s) => s.key);
5713
+ effectiveServerKeys = (await metricsServerManager.getServerKeys(tenantId)).map((s) => s.key);
5630
5714
  }
5631
5715
  const filteredServerKeys = filterServerKeysByTenant(effectiveServerKeys, tenantId);
5632
5716
  const runConfig = _exeConfig?.configurable?.runConfig || {};
@@ -5642,11 +5726,11 @@ ${serverKeys.map(
5642
5726
  if (!tableName) {
5643
5727
  return "Error: tableName parameter is required.";
5644
5728
  }
5645
- const config = metricsServerManager.getConfig(tenantId, serverKey);
5729
+ const config = await metricsServerManager.getConfig(tenantId, serverKey);
5646
5730
  if (config.type !== "semantic") {
5647
5731
  return `Error: Server "${serverKey}" is not a semantic metrics server. This tool only works with semantic servers.`;
5648
5732
  }
5649
- const client = metricsServerManager.getClient(tenantId, serverKey);
5733
+ const client = await metricsServerManager.getClient(tenantId, serverKey);
5650
5734
  const targetDatasourceIds = datasourceId ? [datasourceId] : client.getSelectedDataSources();
5651
5735
  if (targetDatasourceIds.length === 0) {
5652
5736
  return `Error: No datasourceId specified and no default data sources configured for server "${serverKey}".`;
@@ -5766,7 +5850,7 @@ ${serverKeys.map(
5766
5850
  const tenantId = getTenantIdFromConfig2(_exeConfig, getTenantId2);
5767
5851
  let effectiveServerKeys = serverKeys;
5768
5852
  if (connectAll) {
5769
- effectiveServerKeys = metricsServerManager.getServerKeys(tenantId).map((s) => s.key);
5853
+ effectiveServerKeys = (await metricsServerManager.getServerKeys(tenantId)).map((s) => s.key);
5770
5854
  }
5771
5855
  const filteredServerKeys = filterServerKeysByTenant(effectiveServerKeys, tenantId);
5772
5856
  const runConfig = _exeConfig?.configurable?.runConfig || {};
@@ -5785,11 +5869,11 @@ ${serverKeys.map(
5785
5869
  if (!customSql || customSql.trim().length === 0) {
5786
5870
  return "Error: customSql parameter is required and cannot be empty.";
5787
5871
  }
5788
- const config = metricsServerManager.getConfig(tenantId, serverKey);
5872
+ const config = await metricsServerManager.getConfig(tenantId, serverKey);
5789
5873
  if (config.type !== "semantic") {
5790
5874
  return `Error: Server "${serverKey}" is not a semantic metrics server. This tool only works with semantic servers.`;
5791
5875
  }
5792
- const client = metricsServerManager.getClient(tenantId, serverKey);
5876
+ const client = await metricsServerManager.getClient(tenantId, serverKey);
5793
5877
  const result = await client.executeSqlQuery({
5794
5878
  datasourceId,
5795
5879
  customSql,
@@ -12119,6 +12203,14 @@ var ThreadStatus2 = /* @__PURE__ */ ((ThreadStatus3) => {
12119
12203
  return ThreadStatus3;
12120
12204
  })(ThreadStatus2 || {});
12121
12205
  var Agent = class {
12206
+ /**
12207
+ * Constructs an Agent instance.
12208
+ *
12209
+ * Prefer {@link AgentInstanceManager.getAgent} over direct construction — it
12210
+ * ensures a single instance per thread.
12211
+ *
12212
+ * @param params - {@link AgentThreadInterface}
12213
+ */
12122
12214
  constructor({
12123
12215
  assistant_id,
12124
12216
  thread_id,
@@ -12328,9 +12420,10 @@ var Agent = class {
12328
12420
  command: p.command,
12329
12421
  custom_run_config: p.custom_run_config ?? queueMessageData.custom_run_config
12330
12422
  }, signal);
12331
- await this.queueStore?.markCompleted(p.id);
12423
+ await this.queueStore?.removeMessage(p.id);
12332
12424
  const runStatus = await this.getRunStatus();
12333
12425
  const state = await this.getCurrentState();
12426
+ const customRunConfig = p.custom_run_config ?? queueMessageData.custom_run_config;
12334
12427
  if (runStatus === "interrupted" /* INTERRUPTED */) {
12335
12428
  this.publish("message:interrupted", {
12336
12429
  type: "message:interrupted",
@@ -12355,6 +12448,12 @@ var Agent = class {
12355
12448
  state
12356
12449
  });
12357
12450
  }
12451
+ this.publish("reply:ready", {
12452
+ type: "reply:ready",
12453
+ timestamp: /* @__PURE__ */ new Date(),
12454
+ state,
12455
+ customRunConfig
12456
+ });
12358
12457
  } catch (error) {
12359
12458
  console.error(`STEER/Command message ${p.id} execution failed:`, error);
12360
12459
  this.addChunk({
@@ -12370,7 +12469,8 @@ var Agent = class {
12370
12469
  error: error instanceof Error ? error.message : String(error),
12371
12470
  timestamp: /* @__PURE__ */ new Date()
12372
12471
  });
12373
- throw error;
12472
+ await this.queueStore?.removeMessage(p.id);
12473
+ continue;
12374
12474
  }
12375
12475
  }
12376
12476
  }
@@ -12406,7 +12506,7 @@ var Agent = class {
12406
12506
  const runStatus = await this.getRunStatus();
12407
12507
  const state = await this.getCurrentState();
12408
12508
  for (const p of remainingPendings) {
12409
- await this.queueStore?.markCompleted(p.id);
12509
+ await this.queueStore?.removeMessage(p.id);
12410
12510
  if (runStatus === "interrupted" /* INTERRUPTED */) {
12411
12511
  this.publish("message:interrupted", {
12412
12512
  type: "message:interrupted",
@@ -12432,6 +12532,12 @@ var Agent = class {
12432
12532
  });
12433
12533
  }
12434
12534
  }
12535
+ this.publish("reply:ready", {
12536
+ type: "reply:ready",
12537
+ timestamp: /* @__PURE__ */ new Date(),
12538
+ state,
12539
+ customRunConfig: remainingPendings[0]?.custom_run_config ?? firstQueueMessage?.custom_run_config
12540
+ });
12435
12541
  } catch (error) {
12436
12542
  console.error(`COLLECT mode execution failed:`, error);
12437
12543
  for (const p of remainingPendings) {
@@ -12448,8 +12554,8 @@ var Agent = class {
12448
12554
  error: error instanceof Error ? error.message : String(error),
12449
12555
  timestamp: /* @__PURE__ */ new Date()
12450
12556
  });
12557
+ await this.queueStore?.removeMessage(p.id);
12451
12558
  }
12452
- throw error;
12453
12559
  }
12454
12560
  } else if (this.queueMode.mode === "followup" /* FOLLOWUP */) {
12455
12561
  for (const p of remainingPendings) {
@@ -12477,9 +12583,10 @@ var Agent = class {
12477
12583
  input,
12478
12584
  custom_run_config: p.custom_run_config ?? queueMessageData.custom_run_config
12479
12585
  }, signal);
12480
- await this.queueStore?.markCompleted(p.id);
12586
+ await this.queueStore?.removeMessage(p.id);
12481
12587
  const runStatus = await this.getRunStatus();
12482
12588
  const state = await this.getCurrentState();
12589
+ const customRunConfig = p.custom_run_config ?? queueMessageData.custom_run_config;
12483
12590
  if (runStatus === "interrupted" /* INTERRUPTED */) {
12484
12591
  this.publish("message:interrupted", {
12485
12592
  type: "message:interrupted",
@@ -12504,6 +12611,12 @@ var Agent = class {
12504
12611
  state
12505
12612
  });
12506
12613
  }
12614
+ this.publish("reply:ready", {
12615
+ type: "reply:ready",
12616
+ timestamp: /* @__PURE__ */ new Date(),
12617
+ state,
12618
+ customRunConfig
12619
+ });
12507
12620
  } catch (error) {
12508
12621
  console.error(`FOLLOWUP mode message ${p.id} execution failed:`, error);
12509
12622
  this.addChunk({
@@ -12519,7 +12632,8 @@ var Agent = class {
12519
12632
  error: error instanceof Error ? error.message : String(error),
12520
12633
  timestamp: /* @__PURE__ */ new Date()
12521
12634
  });
12522
- throw error;
12635
+ await this.queueStore?.removeMessage(p.id);
12636
+ continue;
12523
12637
  }
12524
12638
  }
12525
12639
  }
@@ -12545,9 +12659,25 @@ var Agent = class {
12545
12659
  setQueueStore(store) {
12546
12660
  this.queueStore = store;
12547
12661
  }
12662
+ /**
12663
+ * Push a chunk into the streaming buffer for this thread.
12664
+ *
12665
+ * Consumers read chunks via {@link chunkStream}.
12666
+ *
12667
+ * @param content - The message chunk to buffer
12668
+ */
12548
12669
  addChunk(content) {
12549
12670
  return this.chunkBuffer.addChunk(this.thread_id, content);
12550
12671
  }
12672
+ /**
12673
+ * Returns an async iterator over new chunks since the given message ID.
12674
+ *
12675
+ * Used by SSE endpoints to stream agent output to clients in real time.
12676
+ *
12677
+ * @param message_id - The client message ID to start streaming from
12678
+ * @param stopTypes - Optional chunk types that terminate the stream
12679
+ * @returns An async iterable yielding {@link MessageChunk} objects
12680
+ */
12551
12681
  chunkStream(message_id, stopTypes) {
12552
12682
  const stream = this.chunkBuffer.getNewChunksSinceContentIterator(
12553
12683
  this.thread_id,
@@ -12636,13 +12766,21 @@ var Agent = class {
12636
12766
  };
12637
12767
  }
12638
12768
  /**
12639
- * Set queue configuration for thread
12769
+ * Override the thread's queue processing mode.
12770
+ *
12771
+ * @param config - Partial {@link ThreadQueueConfig} (e.g. `{ mode: QueueMode.FOLLOWUP }`)
12640
12772
  */
12641
12773
  async setQueueConfig(config) {
12642
12774
  this.queueMode = { ...this.queueMode, ...config };
12643
12775
  }
12644
12776
  /**
12645
- * Stop queue processor
12777
+ * Abort any ongoing queue processing without clearing the queue.
12778
+ *
12779
+ * Used internally by STEER mode to interrupt the current execution so the
12780
+ * steer message can be processed next. For a full abort that also clears
12781
+ * pending messages, use {@link abort}.
12782
+ *
12783
+ * @see {@link abort}
12646
12784
  */
12647
12785
  stopQueueProcessor() {
12648
12786
  if (this.abortController) {
@@ -12651,14 +12789,34 @@ var Agent = class {
12651
12789
  }
12652
12790
  }
12653
12791
  /**
12654
- * Add message to queue
12655
- * All messages go to queue, processor auto-starts if not running
12656
- * STEER/Command messages are inserted at head of queue for immediate processing
12657
- *
12658
- * Supports both legacy single message format and new messages[] format:
12659
- * - Legacy: input.message (single human message)
12660
- * - New: input.messages[] (array of mixed human/system messages)
12661
- * - When input.messages is provided, it takes precedence over input.message
12792
+ * Enqueue a message for this thread.
12793
+ *
12794
+ * Messages are always queued; the queue processor starts automatically if idle.
12795
+ * Returns immediately with the message ID — execution is asynchronous.
12796
+ *
12797
+ * **Queue modes** (via the optional `mode` param):
12798
+ * - `COLLECT` (default) Batch multiple pending messages into one agent call.
12799
+ * - `FOLLOWUP` — Process messages one at a time in order.
12800
+ * - `STEER` — High-priority, inserted at queue head, interrupts current processing.
12801
+ *
12802
+ * **Format**: Supports both `input.message` (legacy string) and `input.messages[]`
12803
+ * (array of `{ role, content }` objects). The array form takes precedence.
12804
+ *
12805
+ * The `custom_run_config` field is round-tripped through the queue and emitted
12806
+ * back in {@link ReplyReadyEvent}, enabling callers to attach routing metadata
12807
+ * (e.g. `_replyTarget` for channel reply).
12808
+ *
12809
+ * @param queueMessage - The message to enqueue
12810
+ * @param mode - Optional queue mode override (defaults to thread's current mode)
12811
+ * @returns `{ queued: true, executed: false, messageId }` — execution happens asynchronously
12812
+ *
12813
+ * @example
12814
+ * ```ts
12815
+ * await agent.addMessage({
12816
+ * input: { message: "Hello" },
12817
+ * custom_run_config: { _replyTarget: { adapterChannel: "lark", rawTarget: { chatId: "xxx" } } },
12818
+ * });
12819
+ * ```
12662
12820
  */
12663
12821
  async addMessage(queueMessage, mode) {
12664
12822
  const useMode = mode ?? this.queueMode.mode;
@@ -12752,8 +12910,13 @@ var Agent = class {
12752
12910
  return { queued: true, executed: false, messageId };
12753
12911
  }
12754
12912
  /**
12755
- * Start queue processor if not already running
12756
- * Public method to allow external triggering (e.g., from recovery)
12913
+ * Start the queue processor if it is not already running.
12914
+ *
12915
+ * Called automatically by {@link addMessage} and {@link resumeTask}.
12916
+ * Safe to call externally — it is a no-op if processing is already active.
12917
+ *
12918
+ * Emits `thread:busy` before starting, and starts the {@link waitingForQueueEnd}
12919
+ * loop with a fresh {@link AbortController}.
12757
12920
  */
12758
12921
  async startQueueProcessorIfNeeded() {
12759
12922
  const store = this.getQueueStore();
@@ -12801,6 +12964,12 @@ var Agent = class {
12801
12964
  type: "system"
12802
12965
  });
12803
12966
  }
12967
+ /**
12968
+ * Returns a LangGraph StateSnapshot for this thread.
12969
+ *
12970
+ * Includes `state.values.messages` (full conversation history) and
12971
+ * `state.tasks` / `state.next` (execution progress).
12972
+ */
12804
12973
  async getCurrentState() {
12805
12974
  const { runnable_agent } = await this.getLatticeClientAndRuntimeConfig();
12806
12975
  const state = await runnable_agent.getState({
@@ -12808,6 +12977,14 @@ var Agent = class {
12808
12977
  });
12809
12978
  return state;
12810
12979
  }
12980
+ /**
12981
+ * Returns the conversation history as normalized message objects.
12982
+ *
12983
+ * Filters to `human`, `ai`, and `tool` message types. Each entry has
12984
+ * `{ id, role, content }` plus any LangChain kwargs.
12985
+ *
12986
+ * @returns Array of message objects with `id`, `role`, and `content`
12987
+ */
12811
12988
  async getCurrentMessages() {
12812
12989
  const state = await this.getCurrentState();
12813
12990
  const messages = state.values.messages || [];
@@ -12830,6 +13007,14 @@ var Agent = class {
12830
13007
  const image = await drawableGraph.drawMermaid();
12831
13008
  return image;
12832
13009
  }
13010
+ /**
13011
+ * Determine the current thread execution status.
13012
+ *
13013
+ * Checks LangGraph's `state.tasks` for interrupts and `state.next` for
13014
+ * pending steps.
13015
+ *
13016
+ * @returns {@link ThreadStatus} — `IDLE`, `BUSY`, or `INTERRUPTED`
13017
+ */
12833
13018
  async getRunStatus() {
12834
13019
  const state = await this.getCurrentState();
12835
13020
  const isInterrupted = state.tasks?.some(
@@ -12844,9 +13029,14 @@ var Agent = class {
12844
13029
  return "idle" /* IDLE */;
12845
13030
  }
12846
13031
  /**
12847
- * Resume task processing after server restart
12848
- * Resets any stuck processing messages to pending and starts queue processing
12849
- * Note: Does not rely on LangGraph state as it may be stale after crash/restart
13032
+ * Resume processing after a server restart.
13033
+ *
13034
+ * Resets any stuck "processing" messages back to "pending" and restarts the
13035
+ * queue processor. Skips threads that are in `INTERRUPTED` state (the
13036
+ * interruption was intentional).
13037
+ *
13038
+ * Called during gateway startup to recover threads that were mid-execution
13039
+ * when the server went down.
12850
13040
  */
12851
13041
  async resumeTask() {
12852
13042
  try {
@@ -12868,9 +13058,14 @@ var Agent = class {
12868
13058
  await this.startQueueProcessorIfNeeded();
12869
13059
  }
12870
13060
  /**
12871
- * Abort the current agent execution
12872
- * This will cancel any ongoing invoke or stream operations
12873
- * and clear all queued messages (pending + processing)
13061
+ * Fully abort all activity on this thread.
13062
+ *
13063
+ * Aborts any in-flight agent execution (via {@link AbortController}) and
13064
+ * clears all pending and processing messages from the queue. Also marks
13065
+ * the chunk buffer thread as aborted so streaming consumers can detect it.
13066
+ *
13067
+ * Unlike {@link stopQueueProcessor}, this is a destructive abort — the
13068
+ * queue is drained.
12874
13069
  */
12875
13070
  async abort() {
12876
13071
  if (this.abortController) {
@@ -12888,22 +13083,44 @@ var Agent = class {
12888
13083
  return this.abortController?.signal.aborted ?? false;
12889
13084
  }
12890
13085
  /**
12891
- * Subscribe to lifecycle events for this agent/thread
12892
- * Events are automatically namespaced by tenantId and threadId
13086
+ * Subscribe to a lifecycle event for this specific thread.
13087
+ *
13088
+ * Event names are namespaced as `{eventName}:{tenantId}:{threadId}` so
13089
+ * listeners only receive events for this agent instance.
13090
+ *
13091
+ * @param eventName - One of {@link AgentLifecycleEventName}
13092
+ * @param callback - Handler receiving the event data payload
13093
+ *
13094
+ * @example
13095
+ * ```ts
13096
+ * agent.subscribe("message:completed", (evt) => {
13097
+ * console.log("AI response:", evt.state?.values?.messages);
13098
+ * });
13099
+ * ```
12893
13100
  */
12894
13101
  subscribe(eventName, callback) {
12895
13102
  const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
12896
13103
  event_bus_default.subscribe(namespacedEvent, callback);
12897
13104
  }
12898
13105
  /**
12899
- * Unsubscribe from lifecycle events
13106
+ * Remove a previously registered event listener.
13107
+ *
13108
+ * @param eventName - The event that was subscribed to
13109
+ * @param callback - The same function reference used in {@link subscribe}
12900
13110
  */
12901
13111
  unsubscribe(eventName, callback) {
12902
13112
  const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
12903
13113
  event_bus_default.unsubscribe(namespacedEvent, callback);
12904
13114
  }
12905
13115
  /**
12906
- * Subscribe to lifecycle events once (auto-unsubscribe after first event)
13116
+ * Subscribe to a lifecycle event once the listener is removed after the
13117
+ * first invocation.
13118
+ *
13119
+ * Ideal for one-shot async patterns (e.g. waiting for `reply:ready` before
13120
+ * sending a channel reply).
13121
+ *
13122
+ * @param eventName - One of {@link AgentLifecycleEventName}
13123
+ * @param callback - Handler receiving the event data payload
12907
13124
  */
12908
13125
  subscribeOnce(eventName, callback) {
12909
13126
  const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
@@ -12917,6 +13134,12 @@ var Agent = class {
12917
13134
  console.log(namespacedEvent);
12918
13135
  event_bus_default.publish(namespacedEvent, data);
12919
13136
  }
13137
+ /**
13138
+ * Track a sub-agent async task spawned by this agent.
13139
+ *
13140
+ * Tasks are monitored by the agent task consumer in `packages/gateway`
13141
+ * and their status can be polled via {@link getAsyncTasks}.
13142
+ */
12920
13143
  addAsyncTask(task) {
12921
13144
  this.asyncTasks.push(task);
12922
13145
  }
@@ -12926,6 +13149,12 @@ var Agent = class {
12926
13149
  getAsyncTask(taskId) {
12927
13150
  return this.asyncTasks.find((t) => t.taskId === taskId);
12928
13151
  }
13152
+ /**
13153
+ * Update the status of a tracked async task.
13154
+ *
13155
+ * Terminal states (`completed`, `failed`, `cancelled`) automatically
13156
+ * set `completedAt` to the current timestamp.
13157
+ */
12929
13158
  updateAsyncTaskStatus(taskId, status) {
12930
13159
  const task = this.getAsyncTask(taskId);
12931
13160
  if (!task) return;
@@ -13291,6 +13520,65 @@ function createSchedulerMiddleware(options = {}) {
13291
13520
  });
13292
13521
  }
13293
13522
 
13523
+ // src/agent_lattice/builders/CustomMiddlewareRegistry.ts
13524
+ var CustomMiddlewareRegistry = class {
13525
+ /**
13526
+ * Register a custom middleware factory under the given key.
13527
+ *
13528
+ * The key is referenced by `config.key` in the database middleware configuration.
13529
+ * When an agent is built, the framework looks up this key and calls the factory
13530
+ * with the remaining config fields.
13531
+ *
13532
+ * @param key - Unique identifier, referenced in database config as `config.key`
13533
+ * @param factory - Function that receives config (minus `key`) and returns an AgentMiddleware
13534
+ *
13535
+ * @example
13536
+ * ```ts
13537
+ * CustomMiddlewareRegistry.register("my-logger", (config) =>
13538
+ * createMiddleware({ name: "Logger", beforeAgent: async () => { ... } }),
13539
+ * );
13540
+ * ```
13541
+ */
13542
+ static register(key, factory) {
13543
+ this.factories.set(key, factory);
13544
+ }
13545
+ /**
13546
+ * Remove a previously registered factory.
13547
+ *
13548
+ * @param key - The factory key to unregister
13549
+ * @returns `true` if a factory was removed, `false` if the key was not found
13550
+ */
13551
+ static unregister(key) {
13552
+ return this.factories.delete(key);
13553
+ }
13554
+ /**
13555
+ * Look up a factory by key.
13556
+ *
13557
+ * @param key - The factory key
13558
+ * @returns The factory function, or `undefined` if not registered
13559
+ */
13560
+ static get(key) {
13561
+ return this.factories.get(key);
13562
+ }
13563
+ /**
13564
+ * Check whether a factory is registered under the given key.
13565
+ *
13566
+ * @param key - The factory key to check
13567
+ */
13568
+ static has(key) {
13569
+ return this.factories.has(key);
13570
+ }
13571
+ /**
13572
+ * Get all currently registered factory keys.
13573
+ *
13574
+ * @returns Array of registered key strings
13575
+ */
13576
+ static list() {
13577
+ return Array.from(this.factories.keys());
13578
+ }
13579
+ };
13580
+ CustomMiddlewareRegistry.factories = /* @__PURE__ */ new Map();
13581
+
13294
13582
  // src/agent_lattice/builders/commonMiddleware.ts
13295
13583
  async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsIsExised) {
13296
13584
  const middlewares = [];
@@ -13367,6 +13655,21 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsI
13367
13655
  case "scheduler":
13368
13656
  middlewares.push(createSchedulerMiddleware(config.config));
13369
13657
  break;
13658
+ case "custom":
13659
+ {
13660
+ const customConfig = config.config;
13661
+ const { key, ...rest } = customConfig;
13662
+ const factory = CustomMiddlewareRegistry.get(key);
13663
+ if (factory) {
13664
+ const middleware = factory(rest);
13665
+ middlewares.push(middleware instanceof Promise ? await middleware : middleware);
13666
+ } else {
13667
+ console.warn(
13668
+ `[custom middleware] No factory registered for key "${key}". Use CustomMiddlewareRegistry.register("${key}", factory) before building the agent.`
13669
+ );
13670
+ }
13671
+ }
13672
+ break;
13370
13673
  }
13371
13674
  }
13372
13675
  return middlewares;
@@ -18110,6 +18413,75 @@ function mergeAgentConfig(parent, child) {
18110
18413
  return merged;
18111
18414
  }
18112
18415
 
18416
+ // src/store_lattice/configureStores.ts
18417
+ var _disposables = [];
18418
+ var _cleanupRegistered = false;
18419
+ function registerSignalCleanup() {
18420
+ if (_cleanupRegistered) return;
18421
+ _cleanupRegistered = true;
18422
+ for (const sig of ["SIGINT", "SIGTERM"]) {
18423
+ process.once(sig, async () => {
18424
+ for (const s of _disposables) {
18425
+ if (s.dispose) await s.dispose();
18426
+ }
18427
+ });
18428
+ }
18429
+ }
18430
+ async function initAndRegister(store, localDisposables) {
18431
+ const initStore = store;
18432
+ if (typeof initStore.initialize === "function" && initStore.initialize.length === 0) {
18433
+ await initStore.initialize();
18434
+ }
18435
+ const dispStore = store;
18436
+ if (typeof dispStore.dispose === "function") {
18437
+ localDisposables.push(dispStore);
18438
+ }
18439
+ }
18440
+ async function configureStores(stores, options = {}) {
18441
+ const localDisposables = [];
18442
+ const { schedule, checkpoint, ...regularStores } = stores;
18443
+ for (const [type, store] of Object.entries(regularStores)) {
18444
+ await initAndRegister(store, localDisposables);
18445
+ if (storeLatticeManager.hasLattice("default", type)) {
18446
+ storeLatticeManager.removeLattice("default", type);
18447
+ }
18448
+ storeLatticeManager.registerLattice("default", type, store);
18449
+ }
18450
+ if (schedule !== void 0) {
18451
+ await initAndRegister(schedule, localDisposables);
18452
+ const scheduleConfig = {
18453
+ name: "Default Scheduler",
18454
+ description: "Auto-configured schedule storage",
18455
+ type: "postgres",
18456
+ storage: schedule
18457
+ };
18458
+ registerScheduleLattice("default", scheduleConfig);
18459
+ }
18460
+ if (checkpoint !== void 0) {
18461
+ MemoryLatticeManager.getInstance().removeCheckpointSaver("default");
18462
+ registerCheckpointSaver("default", checkpoint);
18463
+ }
18464
+ if (options.customStores) {
18465
+ for (const [type, store] of Object.entries(options.customStores)) {
18466
+ await initAndRegister(store, localDisposables);
18467
+ const t = type;
18468
+ if (storeLatticeManager.hasLattice("default", t)) {
18469
+ storeLatticeManager.removeLattice("default", t);
18470
+ }
18471
+ storeLatticeManager.registerLattice("default", t, store);
18472
+ }
18473
+ }
18474
+ if (options.autoDisposeStores) {
18475
+ registerSignalCleanup();
18476
+ _disposables.push(...localDisposables);
18477
+ }
18478
+ return async () => {
18479
+ for (const s of localDisposables.reverse()) {
18480
+ if (s.dispose) await s.dispose();
18481
+ }
18482
+ };
18483
+ }
18484
+
18113
18485
  // src/store_lattice/SandboxSkillStore.ts
18114
18486
  function parseFrontmatter2(content) {
18115
18487
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
@@ -22643,6 +23015,7 @@ function clearEncryptionKeyCache() {
22643
23015
  CompositeBackend,
22644
23016
  ConsoleLoggerClient,
22645
23017
  CustomMetricsClient,
23018
+ CustomMiddlewareRegistry,
22646
23019
  DaytonaInstance,
22647
23020
  DaytonaProvider,
22648
23021
  DefaultScheduleClient,
@@ -22716,6 +23089,7 @@ function clearEncryptionKeyCache() {
22716
23089
  checkEmptyContent,
22717
23090
  clearEncryptionKeyCache,
22718
23091
  computeSandboxName,
23092
+ configureStores,
22719
23093
  createAgentTeam,
22720
23094
  createExecuteSqlQueryTool,
22721
23095
  createFileData,