@axiom-lattice/core 2.1.76 → 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.mjs CHANGED
@@ -2463,14 +2463,35 @@ var InMemoryChannelInstallationStore = class {
2463
2463
  constructor() {
2464
2464
  this.installations = /* @__PURE__ */ new Map();
2465
2465
  }
2466
+ /**
2467
+ * Retrieves a channel installation by its unique ID.
2468
+ *
2469
+ * @param installationId - The installation identifier
2470
+ * @returns The {@link ChannelInstallation} or `null` if not found
2471
+ */
2466
2472
  async getInstallationById(installationId) {
2467
2473
  return this.installations.get(installationId) || null;
2468
2474
  }
2475
+ /**
2476
+ * Lists all channel installations for a tenant, optionally filtered by channel type.
2477
+ *
2478
+ * @param tenantId - Tenant identifier
2479
+ * @param channel - Optional channel type filter (`"lark"`, `"email"`, `"slack"`)
2480
+ * @returns Array of matching {@link ChannelInstallation} objects
2481
+ */
2469
2482
  async getInstallationsByTenant(tenantId, channel) {
2470
2483
  return Array.from(this.installations.values()).filter(
2471
2484
  (inst) => inst.tenantId === tenantId && (!channel || inst.channel === channel)
2472
2485
  );
2473
2486
  }
2487
+ /**
2488
+ * Creates a new channel installation for a tenant.
2489
+ *
2490
+ * @param tenantId - Tenant identifier
2491
+ * @param installationId - Unique installation ID (caller-defined)
2492
+ * @param data - {@link CreateChannelInstallationRequest} containing channel type, name, config, and fallback settings
2493
+ * @returns The created {@link ChannelInstallation}
2494
+ */
2474
2495
  async createInstallation(tenantId, installationId, data) {
2475
2496
  const now = /* @__PURE__ */ new Date();
2476
2497
  const installation = {
@@ -2488,6 +2509,14 @@ var InMemoryChannelInstallationStore = class {
2488
2509
  this.installations.set(installationId, installation);
2489
2510
  return installation;
2490
2511
  }
2512
+ /**
2513
+ * Updates an existing channel installation. Config fields are shallow-merged.
2514
+ *
2515
+ * @param tenantId - Tenant identifier (used for isolation check)
2516
+ * @param installationId - Installation ID to update
2517
+ * @param updates - {@link UpdateChannelInstallationRequest} with fields to patch
2518
+ * @returns The updated {@link ChannelInstallation} or `null` if not found or tenant mismatch
2519
+ */
2491
2520
  async updateInstallation(tenantId, installationId, updates) {
2492
2521
  const existing = this.installations.get(installationId);
2493
2522
  if (!existing || existing.tenantId !== tenantId) return null;
@@ -2503,11 +2532,21 @@ var InMemoryChannelInstallationStore = class {
2503
2532
  this.installations.set(installationId, updated);
2504
2533
  return updated;
2505
2534
  }
2535
+ /**
2536
+ * Deletes a channel installation.
2537
+ *
2538
+ * @param tenantId - Tenant identifier (used for isolation check)
2539
+ * @param installationId - Installation ID to delete
2540
+ * @returns `true` if deleted, `false` if not found or tenant mismatch
2541
+ */
2506
2542
  async deleteInstallation(tenantId, installationId) {
2507
2543
  const existing = this.installations.get(installationId);
2508
2544
  if (!existing || existing.tenantId !== tenantId) return false;
2509
2545
  return this.installations.delete(installationId);
2510
2546
  }
2547
+ /**
2548
+ * Clears all in-memory installations. Primarily used in tests.
2549
+ */
2511
2550
  clear() {
2512
2551
  this.installations.clear();
2513
2552
  }
@@ -2519,6 +2558,16 @@ var InMemoryBindingStore = class {
2519
2558
  constructor() {
2520
2559
  this.bindings = /* @__PURE__ */ new Map();
2521
2560
  }
2561
+ /**
2562
+ * Resolves a binding for the given sender across a specific channel installation.
2563
+ *
2564
+ * Called by `MessageRouter.dispatch()` during inbound message processing.
2565
+ * Matches on `channel + senderId + channelInstallationId + tenantId` and
2566
+ * requires `enabled === true`.
2567
+ *
2568
+ * @param params - Resolution parameters
2569
+ * @returns The matching {@link Binding} or `null` if none found.
2570
+ */
2522
2571
  async resolve(params) {
2523
2572
  for (const binding of this.bindings.values()) {
2524
2573
  if (binding.channel === params.channel && binding.senderId === params.senderId && binding.channelInstallationId === params.channelInstallationId && binding.tenantId === params.tenantId && binding.enabled) {
@@ -2527,6 +2576,12 @@ var InMemoryBindingStore = class {
2527
2576
  }
2528
2577
  return null;
2529
2578
  }
2579
+ /**
2580
+ * Creates a new sender-to-agent binding.
2581
+ *
2582
+ * @param input - {@link CreateBindingInput} defining channel, sender, target agent, and thread mode.
2583
+ * @returns The newly created {@link Binding}.
2584
+ */
2530
2585
  async create(input) {
2531
2586
  const now = /* @__PURE__ */ new Date();
2532
2587
  const binding = {
@@ -2549,6 +2604,14 @@ var InMemoryBindingStore = class {
2549
2604
  this.bindings.set(binding.id, binding);
2550
2605
  return binding;
2551
2606
  }
2607
+ /**
2608
+ * Updates an existing binding (e.g. changing the target agent or thread mode).
2609
+ *
2610
+ * @param id - Binding ID
2611
+ * @param patch - Partial {@link Binding} fields to update
2612
+ * @returns The updated {@link Binding}
2613
+ * @throws {Error} If the binding does not exist
2614
+ */
2552
2615
  async update(id, patch) {
2553
2616
  const existing = this.bindings.get(id);
2554
2617
  if (!existing) throw new Error(`Binding ${id} not found`);
@@ -2560,9 +2623,20 @@ var InMemoryBindingStore = class {
2560
2623
  this.bindings.set(id, updated);
2561
2624
  return updated;
2562
2625
  }
2626
+ /**
2627
+ * Deletes a binding by ID.
2628
+ *
2629
+ * @param id - Binding ID
2630
+ */
2563
2631
  async delete(id) {
2564
2632
  this.bindings.delete(id);
2565
2633
  }
2634
+ /**
2635
+ * Lists bindings filtered by tenant and optional channel/agent/installation.
2636
+ *
2637
+ * @param params - Filter and pagination parameters
2638
+ * @returns Array of matching {@link Binding} objects
2639
+ */
2566
2640
  async list(params) {
2567
2641
  let results = Array.from(this.bindings.values()).filter((b) => {
2568
2642
  if (b.tenantId !== params.tenantId) return false;
@@ -2575,6 +2649,12 @@ var InMemoryBindingStore = class {
2575
2649
  const limit = params.limit ?? 50;
2576
2650
  return results.slice(offset, offset + limit);
2577
2651
  }
2652
+ /**
2653
+ * Bulk-imports bindings.
2654
+ *
2655
+ * @param bindings - Array of {@link CreateBindingInput}
2656
+ * @returns Array of created {@link Binding} objects
2657
+ */
2578
2658
  async import(bindings) {
2579
2659
  const result = [];
2580
2660
  for (const input of bindings) {
@@ -2582,9 +2662,18 @@ var InMemoryBindingStore = class {
2582
2662
  }
2583
2663
  return result;
2584
2664
  }
2665
+ /**
2666
+ * Exports all bindings for a tenant.
2667
+ *
2668
+ * @param params - Filter by tenantId
2669
+ * @returns Array of {@link Binding} objects
2670
+ */
2585
2671
  async export(params) {
2586
2672
  return this.list({ tenantId: params.tenantId, limit: 1e4 });
2587
2673
  }
2674
+ /**
2675
+ * Clears all in-memory bindings. Primarily used in tests.
2676
+ */
2588
2677
  clear() {
2589
2678
  this.bindings.clear();
2590
2679
  }
@@ -11906,6 +11995,14 @@ var ThreadStatus2 = /* @__PURE__ */ ((ThreadStatus3) => {
11906
11995
  return ThreadStatus3;
11907
11996
  })(ThreadStatus2 || {});
11908
11997
  var Agent = class {
11998
+ /**
11999
+ * Constructs an Agent instance.
12000
+ *
12001
+ * Prefer {@link AgentInstanceManager.getAgent} over direct construction — it
12002
+ * ensures a single instance per thread.
12003
+ *
12004
+ * @param params - {@link AgentThreadInterface}
12005
+ */
11909
12006
  constructor({
11910
12007
  assistant_id,
11911
12008
  thread_id,
@@ -12118,6 +12215,7 @@ var Agent = class {
12118
12215
  await this.queueStore?.removeMessage(p.id);
12119
12216
  const runStatus = await this.getRunStatus();
12120
12217
  const state = await this.getCurrentState();
12218
+ const customRunConfig = p.custom_run_config ?? queueMessageData.custom_run_config;
12121
12219
  if (runStatus === "interrupted" /* INTERRUPTED */) {
12122
12220
  this.publish("message:interrupted", {
12123
12221
  type: "message:interrupted",
@@ -12142,6 +12240,12 @@ var Agent = class {
12142
12240
  state
12143
12241
  });
12144
12242
  }
12243
+ this.publish("reply:ready", {
12244
+ type: "reply:ready",
12245
+ timestamp: /* @__PURE__ */ new Date(),
12246
+ state,
12247
+ customRunConfig
12248
+ });
12145
12249
  } catch (error) {
12146
12250
  console.error(`STEER/Command message ${p.id} execution failed:`, error);
12147
12251
  this.addChunk({
@@ -12220,6 +12324,12 @@ var Agent = class {
12220
12324
  });
12221
12325
  }
12222
12326
  }
12327
+ this.publish("reply:ready", {
12328
+ type: "reply:ready",
12329
+ timestamp: /* @__PURE__ */ new Date(),
12330
+ state,
12331
+ customRunConfig: remainingPendings[0]?.custom_run_config ?? firstQueueMessage?.custom_run_config
12332
+ });
12223
12333
  } catch (error) {
12224
12334
  console.error(`COLLECT mode execution failed:`, error);
12225
12335
  for (const p of remainingPendings) {
@@ -12268,6 +12378,7 @@ var Agent = class {
12268
12378
  await this.queueStore?.removeMessage(p.id);
12269
12379
  const runStatus = await this.getRunStatus();
12270
12380
  const state = await this.getCurrentState();
12381
+ const customRunConfig = p.custom_run_config ?? queueMessageData.custom_run_config;
12271
12382
  if (runStatus === "interrupted" /* INTERRUPTED */) {
12272
12383
  this.publish("message:interrupted", {
12273
12384
  type: "message:interrupted",
@@ -12292,6 +12403,12 @@ var Agent = class {
12292
12403
  state
12293
12404
  });
12294
12405
  }
12406
+ this.publish("reply:ready", {
12407
+ type: "reply:ready",
12408
+ timestamp: /* @__PURE__ */ new Date(),
12409
+ state,
12410
+ customRunConfig
12411
+ });
12295
12412
  } catch (error) {
12296
12413
  console.error(`FOLLOWUP mode message ${p.id} execution failed:`, error);
12297
12414
  this.addChunk({
@@ -12334,9 +12451,25 @@ var Agent = class {
12334
12451
  setQueueStore(store) {
12335
12452
  this.queueStore = store;
12336
12453
  }
12454
+ /**
12455
+ * Push a chunk into the streaming buffer for this thread.
12456
+ *
12457
+ * Consumers read chunks via {@link chunkStream}.
12458
+ *
12459
+ * @param content - The message chunk to buffer
12460
+ */
12337
12461
  addChunk(content) {
12338
12462
  return this.chunkBuffer.addChunk(this.thread_id, content);
12339
12463
  }
12464
+ /**
12465
+ * Returns an async iterator over new chunks since the given message ID.
12466
+ *
12467
+ * Used by SSE endpoints to stream agent output to clients in real time.
12468
+ *
12469
+ * @param message_id - The client message ID to start streaming from
12470
+ * @param stopTypes - Optional chunk types that terminate the stream
12471
+ * @returns An async iterable yielding {@link MessageChunk} objects
12472
+ */
12340
12473
  chunkStream(message_id, stopTypes) {
12341
12474
  const stream = this.chunkBuffer.getNewChunksSinceContentIterator(
12342
12475
  this.thread_id,
@@ -12425,13 +12558,21 @@ var Agent = class {
12425
12558
  };
12426
12559
  }
12427
12560
  /**
12428
- * Set queue configuration for thread
12561
+ * Override the thread's queue processing mode.
12562
+ *
12563
+ * @param config - Partial {@link ThreadQueueConfig} (e.g. `{ mode: QueueMode.FOLLOWUP }`)
12429
12564
  */
12430
12565
  async setQueueConfig(config) {
12431
12566
  this.queueMode = { ...this.queueMode, ...config };
12432
12567
  }
12433
12568
  /**
12434
- * Stop queue processor
12569
+ * Abort any ongoing queue processing without clearing the queue.
12570
+ *
12571
+ * Used internally by STEER mode to interrupt the current execution so the
12572
+ * steer message can be processed next. For a full abort that also clears
12573
+ * pending messages, use {@link abort}.
12574
+ *
12575
+ * @see {@link abort}
12435
12576
  */
12436
12577
  stopQueueProcessor() {
12437
12578
  if (this.abortController) {
@@ -12440,14 +12581,34 @@ var Agent = class {
12440
12581
  }
12441
12582
  }
12442
12583
  /**
12443
- * Add message to queue
12444
- * All messages go to queue, processor auto-starts if not running
12445
- * STEER/Command messages are inserted at head of queue for immediate processing
12446
- *
12447
- * Supports both legacy single message format and new messages[] format:
12448
- * - Legacy: input.message (single human message)
12449
- * - New: input.messages[] (array of mixed human/system messages)
12450
- * - When input.messages is provided, it takes precedence over input.message
12584
+ * Enqueue a message for this thread.
12585
+ *
12586
+ * Messages are always queued; the queue processor starts automatically if idle.
12587
+ * Returns immediately with the message ID — execution is asynchronous.
12588
+ *
12589
+ * **Queue modes** (via the optional `mode` param):
12590
+ * - `COLLECT` (default) Batch multiple pending messages into one agent call.
12591
+ * - `FOLLOWUP` — Process messages one at a time in order.
12592
+ * - `STEER` — High-priority, inserted at queue head, interrupts current processing.
12593
+ *
12594
+ * **Format**: Supports both `input.message` (legacy string) and `input.messages[]`
12595
+ * (array of `{ role, content }` objects). The array form takes precedence.
12596
+ *
12597
+ * The `custom_run_config` field is round-tripped through the queue and emitted
12598
+ * back in {@link ReplyReadyEvent}, enabling callers to attach routing metadata
12599
+ * (e.g. `_replyTarget` for channel reply).
12600
+ *
12601
+ * @param queueMessage - The message to enqueue
12602
+ * @param mode - Optional queue mode override (defaults to thread's current mode)
12603
+ * @returns `{ queued: true, executed: false, messageId }` — execution happens asynchronously
12604
+ *
12605
+ * @example
12606
+ * ```ts
12607
+ * await agent.addMessage({
12608
+ * input: { message: "Hello" },
12609
+ * custom_run_config: { _replyTarget: { adapterChannel: "lark", rawTarget: { chatId: "xxx" } } },
12610
+ * });
12611
+ * ```
12451
12612
  */
12452
12613
  async addMessage(queueMessage, mode) {
12453
12614
  const useMode = mode ?? this.queueMode.mode;
@@ -12541,8 +12702,13 @@ var Agent = class {
12541
12702
  return { queued: true, executed: false, messageId };
12542
12703
  }
12543
12704
  /**
12544
- * Start queue processor if not already running
12545
- * Public method to allow external triggering (e.g., from recovery)
12705
+ * Start the queue processor if it is not already running.
12706
+ *
12707
+ * Called automatically by {@link addMessage} and {@link resumeTask}.
12708
+ * Safe to call externally — it is a no-op if processing is already active.
12709
+ *
12710
+ * Emits `thread:busy` before starting, and starts the {@link waitingForQueueEnd}
12711
+ * loop with a fresh {@link AbortController}.
12546
12712
  */
12547
12713
  async startQueueProcessorIfNeeded() {
12548
12714
  const store = this.getQueueStore();
@@ -12590,6 +12756,12 @@ var Agent = class {
12590
12756
  type: "system"
12591
12757
  });
12592
12758
  }
12759
+ /**
12760
+ * Returns a LangGraph StateSnapshot for this thread.
12761
+ *
12762
+ * Includes `state.values.messages` (full conversation history) and
12763
+ * `state.tasks` / `state.next` (execution progress).
12764
+ */
12593
12765
  async getCurrentState() {
12594
12766
  const { runnable_agent } = await this.getLatticeClientAndRuntimeConfig();
12595
12767
  const state = await runnable_agent.getState({
@@ -12597,6 +12769,14 @@ var Agent = class {
12597
12769
  });
12598
12770
  return state;
12599
12771
  }
12772
+ /**
12773
+ * Returns the conversation history as normalized message objects.
12774
+ *
12775
+ * Filters to `human`, `ai`, and `tool` message types. Each entry has
12776
+ * `{ id, role, content }` plus any LangChain kwargs.
12777
+ *
12778
+ * @returns Array of message objects with `id`, `role`, and `content`
12779
+ */
12600
12780
  async getCurrentMessages() {
12601
12781
  const state = await this.getCurrentState();
12602
12782
  const messages = state.values.messages || [];
@@ -12619,6 +12799,14 @@ var Agent = class {
12619
12799
  const image = await drawableGraph.drawMermaid();
12620
12800
  return image;
12621
12801
  }
12802
+ /**
12803
+ * Determine the current thread execution status.
12804
+ *
12805
+ * Checks LangGraph's `state.tasks` for interrupts and `state.next` for
12806
+ * pending steps.
12807
+ *
12808
+ * @returns {@link ThreadStatus} — `IDLE`, `BUSY`, or `INTERRUPTED`
12809
+ */
12622
12810
  async getRunStatus() {
12623
12811
  const state = await this.getCurrentState();
12624
12812
  const isInterrupted = state.tasks?.some(
@@ -12633,9 +12821,14 @@ var Agent = class {
12633
12821
  return "idle" /* IDLE */;
12634
12822
  }
12635
12823
  /**
12636
- * Resume task processing after server restart
12637
- * Resets any stuck processing messages to pending and starts queue processing
12638
- * Note: Does not rely on LangGraph state as it may be stale after crash/restart
12824
+ * Resume processing after a server restart.
12825
+ *
12826
+ * Resets any stuck "processing" messages back to "pending" and restarts the
12827
+ * queue processor. Skips threads that are in `INTERRUPTED` state (the
12828
+ * interruption was intentional).
12829
+ *
12830
+ * Called during gateway startup to recover threads that were mid-execution
12831
+ * when the server went down.
12639
12832
  */
12640
12833
  async resumeTask() {
12641
12834
  try {
@@ -12657,9 +12850,14 @@ var Agent = class {
12657
12850
  await this.startQueueProcessorIfNeeded();
12658
12851
  }
12659
12852
  /**
12660
- * Abort the current agent execution
12661
- * This will cancel any ongoing invoke or stream operations
12662
- * and clear all queued messages (pending + processing)
12853
+ * Fully abort all activity on this thread.
12854
+ *
12855
+ * Aborts any in-flight agent execution (via {@link AbortController}) and
12856
+ * clears all pending and processing messages from the queue. Also marks
12857
+ * the chunk buffer thread as aborted so streaming consumers can detect it.
12858
+ *
12859
+ * Unlike {@link stopQueueProcessor}, this is a destructive abort — the
12860
+ * queue is drained.
12663
12861
  */
12664
12862
  async abort() {
12665
12863
  if (this.abortController) {
@@ -12677,22 +12875,44 @@ var Agent = class {
12677
12875
  return this.abortController?.signal.aborted ?? false;
12678
12876
  }
12679
12877
  /**
12680
- * Subscribe to lifecycle events for this agent/thread
12681
- * Events are automatically namespaced by tenantId and threadId
12878
+ * Subscribe to a lifecycle event for this specific thread.
12879
+ *
12880
+ * Event names are namespaced as `{eventName}:{tenantId}:{threadId}` so
12881
+ * listeners only receive events for this agent instance.
12882
+ *
12883
+ * @param eventName - One of {@link AgentLifecycleEventName}
12884
+ * @param callback - Handler receiving the event data payload
12885
+ *
12886
+ * @example
12887
+ * ```ts
12888
+ * agent.subscribe("message:completed", (evt) => {
12889
+ * console.log("AI response:", evt.state?.values?.messages);
12890
+ * });
12891
+ * ```
12682
12892
  */
12683
12893
  subscribe(eventName, callback) {
12684
12894
  const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
12685
12895
  event_bus_default.subscribe(namespacedEvent, callback);
12686
12896
  }
12687
12897
  /**
12688
- * Unsubscribe from lifecycle events
12898
+ * Remove a previously registered event listener.
12899
+ *
12900
+ * @param eventName - The event that was subscribed to
12901
+ * @param callback - The same function reference used in {@link subscribe}
12689
12902
  */
12690
12903
  unsubscribe(eventName, callback) {
12691
12904
  const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
12692
12905
  event_bus_default.unsubscribe(namespacedEvent, callback);
12693
12906
  }
12694
12907
  /**
12695
- * Subscribe to lifecycle events once (auto-unsubscribe after first event)
12908
+ * Subscribe to a lifecycle event once the listener is removed after the
12909
+ * first invocation.
12910
+ *
12911
+ * Ideal for one-shot async patterns (e.g. waiting for `reply:ready` before
12912
+ * sending a channel reply).
12913
+ *
12914
+ * @param eventName - One of {@link AgentLifecycleEventName}
12915
+ * @param callback - Handler receiving the event data payload
12696
12916
  */
12697
12917
  subscribeOnce(eventName, callback) {
12698
12918
  const namespacedEvent = `${eventName}:${this.tenant_id}:${this.thread_id}`;
@@ -12706,6 +12926,12 @@ var Agent = class {
12706
12926
  console.log(namespacedEvent);
12707
12927
  event_bus_default.publish(namespacedEvent, data);
12708
12928
  }
12929
+ /**
12930
+ * Track a sub-agent async task spawned by this agent.
12931
+ *
12932
+ * Tasks are monitored by the agent task consumer in `packages/gateway`
12933
+ * and their status can be polled via {@link getAsyncTasks}.
12934
+ */
12709
12935
  addAsyncTask(task) {
12710
12936
  this.asyncTasks.push(task);
12711
12937
  }
@@ -12715,6 +12941,12 @@ var Agent = class {
12715
12941
  getAsyncTask(taskId) {
12716
12942
  return this.asyncTasks.find((t) => t.taskId === taskId);
12717
12943
  }
12944
+ /**
12945
+ * Update the status of a tracked async task.
12946
+ *
12947
+ * Terminal states (`completed`, `failed`, `cancelled`) automatically
12948
+ * set `completedAt` to the current timestamp.
12949
+ */
12718
12950
  updateAsyncTaskStatus(taskId, status) {
12719
12951
  const task = this.getAsyncTask(taskId);
12720
12952
  if (!task) return;
@@ -13080,6 +13312,65 @@ function createSchedulerMiddleware(options = {}) {
13080
13312
  });
13081
13313
  }
13082
13314
 
13315
+ // src/agent_lattice/builders/CustomMiddlewareRegistry.ts
13316
+ var CustomMiddlewareRegistry = class {
13317
+ /**
13318
+ * Register a custom middleware factory under the given key.
13319
+ *
13320
+ * The key is referenced by `config.key` in the database middleware configuration.
13321
+ * When an agent is built, the framework looks up this key and calls the factory
13322
+ * with the remaining config fields.
13323
+ *
13324
+ * @param key - Unique identifier, referenced in database config as `config.key`
13325
+ * @param factory - Function that receives config (minus `key`) and returns an AgentMiddleware
13326
+ *
13327
+ * @example
13328
+ * ```ts
13329
+ * CustomMiddlewareRegistry.register("my-logger", (config) =>
13330
+ * createMiddleware({ name: "Logger", beforeAgent: async () => { ... } }),
13331
+ * );
13332
+ * ```
13333
+ */
13334
+ static register(key, factory) {
13335
+ this.factories.set(key, factory);
13336
+ }
13337
+ /**
13338
+ * Remove a previously registered factory.
13339
+ *
13340
+ * @param key - The factory key to unregister
13341
+ * @returns `true` if a factory was removed, `false` if the key was not found
13342
+ */
13343
+ static unregister(key) {
13344
+ return this.factories.delete(key);
13345
+ }
13346
+ /**
13347
+ * Look up a factory by key.
13348
+ *
13349
+ * @param key - The factory key
13350
+ * @returns The factory function, or `undefined` if not registered
13351
+ */
13352
+ static get(key) {
13353
+ return this.factories.get(key);
13354
+ }
13355
+ /**
13356
+ * Check whether a factory is registered under the given key.
13357
+ *
13358
+ * @param key - The factory key to check
13359
+ */
13360
+ static has(key) {
13361
+ return this.factories.has(key);
13362
+ }
13363
+ /**
13364
+ * Get all currently registered factory keys.
13365
+ *
13366
+ * @returns Array of registered key strings
13367
+ */
13368
+ static list() {
13369
+ return Array.from(this.factories.keys());
13370
+ }
13371
+ };
13372
+ CustomMiddlewareRegistry.factories = /* @__PURE__ */ new Map();
13373
+
13083
13374
  // src/agent_lattice/builders/commonMiddleware.ts
13084
13375
  async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsIsExised) {
13085
13376
  const middlewares = [];
@@ -13156,6 +13447,21 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsI
13156
13447
  case "scheduler":
13157
13448
  middlewares.push(createSchedulerMiddleware(config.config));
13158
13449
  break;
13450
+ case "custom":
13451
+ {
13452
+ const customConfig = config.config;
13453
+ const { key, ...rest } = customConfig;
13454
+ const factory = CustomMiddlewareRegistry.get(key);
13455
+ if (factory) {
13456
+ const middleware = factory(rest);
13457
+ middlewares.push(middleware instanceof Promise ? await middleware : middleware);
13458
+ } else {
13459
+ console.warn(
13460
+ `[custom middleware] No factory registered for key "${key}". Use CustomMiddlewareRegistry.register("${key}", factory) before building the agent.`
13461
+ );
13462
+ }
13463
+ }
13464
+ break;
13159
13465
  }
13160
13466
  }
13161
13467
  return middlewares;
@@ -22532,6 +22838,7 @@ export {
22532
22838
  CompositeBackend,
22533
22839
  ConsoleLoggerClient,
22534
22840
  CustomMetricsClient,
22841
+ CustomMiddlewareRegistry,
22535
22842
  DaytonaInstance,
22536
22843
  DaytonaProvider,
22537
22844
  DefaultScheduleClient,