@snap-agent/core 0.1.0 → 0.1.3

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
@@ -7327,12 +7327,16 @@ __export(index_exports, {
7327
7327
  Models: () => Models,
7328
7328
  MongoDBStorage: () => MongoDBStorage,
7329
7329
  PluginManager: () => PluginManager,
7330
+ PluginRegistry: () => PluginRegistry,
7330
7331
  ProviderFactory: () => ProviderFactory,
7331
7332
  ProviderNotFoundError: () => ProviderNotFoundError,
7332
7333
  Thread: () => Thread,
7333
7334
  ThreadNotFoundError: () => ThreadNotFoundError,
7334
7335
  UpstashStorage: () => UpstashStorage,
7335
- createClient: () => createClient
7336
+ createClient: () => createClient,
7337
+ envRef: () => envRef,
7338
+ pluginRegistry: () => pluginRegistry,
7339
+ resolveEnvVars: () => resolveEnvVars
7336
7340
  });
7337
7341
  module.exports = __toCommonJS(index_exports);
7338
7342
 
@@ -7551,33 +7555,88 @@ var Agent = class _Agent {
7551
7555
  }
7552
7556
  /**
7553
7557
  * Create a new agent
7558
+ *
7559
+ * If plugins are provided, their configurations will be extracted (if they implement getConfig())
7560
+ * and stored in the database for later reinstantiation.
7554
7561
  */
7555
7562
  static async create(config, storage, providerFactory) {
7556
- const agentId = await storage.createAgent(config);
7563
+ const pluginConfigs = config.pluginConfigs || [];
7564
+ if (config.plugins && config.plugins.length > 0) {
7565
+ for (const plugin of config.plugins) {
7566
+ if ("getConfig" in plugin && typeof plugin.getConfig === "function") {
7567
+ pluginConfigs.push({
7568
+ type: plugin.type,
7569
+ name: plugin.name,
7570
+ config: plugin.getConfig(),
7571
+ priority: plugin.priority,
7572
+ enabled: true
7573
+ });
7574
+ }
7575
+ }
7576
+ }
7577
+ const configWithPluginConfigs = {
7578
+ ...config,
7579
+ pluginConfigs
7580
+ };
7581
+ const agentId = await storage.createAgent(configWithPluginConfigs);
7557
7582
  const data = await storage.getAgent(agentId);
7558
7583
  if (!data) {
7559
7584
  throw new AgentNotFoundError(agentId);
7560
7585
  }
7586
+ data.plugins = config.plugins || [];
7561
7587
  return new _Agent(data, storage, providerFactory);
7562
7588
  }
7563
7589
  /**
7564
7590
  * Load an existing agent by ID
7591
+ *
7592
+ * Plugins can be attached in three ways (in order of priority):
7593
+ * 1. Direct plugins array - runtime plugin instances passed directly
7594
+ * 2. Plugin registry - reinstantiate from stored configs using registered factories
7595
+ * 3. No plugins - agent loads without plugin functionality
7596
+ *
7597
+ * @param agentId - The agent ID to load
7598
+ * @param storage - Storage adapter
7599
+ * @param providerFactory - Provider factory
7600
+ * @param options - Either:
7601
+ * - Plugin[] array (legacy, for backwards compatibility)
7602
+ * - Options object with plugins and/or registry
7565
7603
  */
7566
- static async load(agentId, storage, providerFactory) {
7604
+ static async load(agentId, storage, providerFactory, options) {
7567
7605
  const data = await storage.getAgent(agentId);
7568
7606
  if (!data) {
7569
7607
  return null;
7570
7608
  }
7609
+ if (Array.isArray(options)) {
7610
+ data.plugins = options;
7611
+ return new _Agent(data, storage, providerFactory);
7612
+ }
7613
+ if (options?.plugins && options.plugins.length > 0) {
7614
+ data.plugins = options.plugins;
7615
+ } else if (options?.registry && data.pluginConfigs && data.pluginConfigs.length > 0) {
7616
+ try {
7617
+ data.plugins = await options.registry.instantiateAll(data.pluginConfigs);
7618
+ } catch (error) {
7619
+ console.error("Failed to reinstantiate plugins from stored configs:", error);
7620
+ throw error;
7621
+ }
7622
+ } else {
7623
+ data.plugins = [];
7624
+ }
7571
7625
  return new _Agent(data, storage, providerFactory);
7572
7626
  }
7573
7627
  /**
7574
7628
  * Update agent properties
7575
7629
  */
7576
7630
  async update(updates) {
7631
+ const currentPlugins = this.data.plugins || [];
7577
7632
  await this.storage.updateAgent(this.data.id, updates);
7578
7633
  const updatedData = await this.storage.getAgent(this.data.id);
7579
7634
  if (updatedData) {
7635
+ updatedData.plugins = updates.plugins || currentPlugins;
7580
7636
  this.data = updatedData;
7637
+ if (updates.plugins) {
7638
+ this.pluginManager = new PluginManager(updatedData.plugins);
7639
+ }
7581
7640
  }
7582
7641
  }
7583
7642
  /**
@@ -7629,11 +7688,37 @@ var Agent = class _Agent {
7629
7688
  ragMetadata = allMetadata;
7630
7689
  }
7631
7690
  const model = await this.providerFactory.getModel(this.data.provider, this.data.model);
7632
- const { text } = await (0, import_ai.generateText)({
7633
- model,
7634
- messages: beforeResult.messages,
7635
- system: systemPrompt
7636
- });
7691
+ let text;
7692
+ let parsed;
7693
+ if (options?.output?.mode === "object") {
7694
+ const result = await (0, import_ai.generateText)({
7695
+ model,
7696
+ messages: beforeResult.messages,
7697
+ system: systemPrompt,
7698
+ experimental_output: import_ai.Output.object({ schema: options.output.schema })
7699
+ });
7700
+ text = JSON.stringify(result.experimental_output);
7701
+ parsed = result.experimental_output;
7702
+ } else if (options?.output?.mode === "json") {
7703
+ const jsonSystemPrompt = systemPrompt + "\n\n---\nOUTPUT FORMAT: You MUST respond with valid JSON only. No markdown code blocks, no explanations, no additional text - just raw JSON that can be parsed directly.";
7704
+ const result = await (0, import_ai.generateText)({
7705
+ model,
7706
+ messages: beforeResult.messages,
7707
+ system: jsonSystemPrompt
7708
+ });
7709
+ text = result.text;
7710
+ try {
7711
+ parsed = JSON.parse(text);
7712
+ } catch {
7713
+ }
7714
+ } else {
7715
+ const result = await (0, import_ai.generateText)({
7716
+ model,
7717
+ messages: beforeResult.messages,
7718
+ system: systemPrompt
7719
+ });
7720
+ text = result.text;
7721
+ }
7637
7722
  const afterResult = await this.pluginManager.executeAfterResponse(text, {
7638
7723
  agentId: this.data.id,
7639
7724
  threadId: options?.threadId,
@@ -7649,6 +7734,7 @@ var Agent = class _Agent {
7649
7734
  });
7650
7735
  return {
7651
7736
  text: afterResult.response,
7737
+ ...parsed !== void 0 && { parsed },
7652
7738
  metadata: {
7653
7739
  ...afterResult.metadata,
7654
7740
  ragMetadata,
@@ -8390,6 +8476,7 @@ var AgentClient = class {
8390
8476
  this.storage = config.storage;
8391
8477
  this.providers = config.providers;
8392
8478
  this.providerFactory = new ProviderFactory(config.providers);
8479
+ this.pluginRegistry = config.pluginRegistry;
8393
8480
  }
8394
8481
  validateConfig(config) {
8395
8482
  if (!config.storage) {
@@ -8432,9 +8519,28 @@ var AgentClient = class {
8432
8519
  }
8433
8520
  /**
8434
8521
  * Get an agent by ID
8522
+ *
8523
+ * Plugin loading priority:
8524
+ * 1. Direct plugins array passed to this method
8525
+ * 2. Plugin registry (if configured) - reinstantiates from stored configs
8526
+ * 3. No plugins
8527
+ *
8528
+ * @param agentId - The agent ID to load
8529
+ * @param options - Either Plugin[] for backwards compatibility, or options object
8435
8530
  */
8436
- async getAgent(agentId) {
8437
- const agent = await Agent.load(agentId, this.storage, this.providerFactory);
8531
+ async getAgent(agentId, options) {
8532
+ if (Array.isArray(options)) {
8533
+ const agent2 = await Agent.load(agentId, this.storage, this.providerFactory, options);
8534
+ if (!agent2) {
8535
+ throw new AgentNotFoundError(agentId);
8536
+ }
8537
+ return agent2;
8538
+ }
8539
+ const registry = options?.registry || this.pluginRegistry;
8540
+ const agent = await Agent.load(agentId, this.storage, this.providerFactory, {
8541
+ plugins: options?.plugins,
8542
+ registry
8543
+ });
8438
8544
  if (!agent) {
8439
8545
  throw new AgentNotFoundError(agentId);
8440
8546
  }
@@ -8592,6 +8698,149 @@ Return only the title without additional explanations.`
8592
8698
  }
8593
8699
  };
8594
8700
 
8701
+ // src/core/PluginRegistry.ts
8702
+ function resolveEnvVars(config) {
8703
+ const resolved = {};
8704
+ for (const [key, value] of Object.entries(config)) {
8705
+ if (typeof value === "string") {
8706
+ const envMatch = value.match(/^\$\{([^}:]+)(?::([^}]*))?\}$/);
8707
+ if (envMatch) {
8708
+ const [, envVar, defaultValue] = envMatch;
8709
+ const envValue = process.env[envVar];
8710
+ if (envValue !== void 0) {
8711
+ resolved[key] = envValue;
8712
+ } else if (defaultValue !== void 0) {
8713
+ resolved[key] = defaultValue;
8714
+ } else {
8715
+ throw new Error(
8716
+ `Environment variable ${envVar} is required for plugin config but not set`
8717
+ );
8718
+ }
8719
+ } else {
8720
+ resolved[key] = value;
8721
+ }
8722
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
8723
+ resolved[key] = resolveEnvVars(value);
8724
+ } else {
8725
+ resolved[key] = value;
8726
+ }
8727
+ }
8728
+ return resolved;
8729
+ }
8730
+ var PluginRegistry = class {
8731
+ constructor() {
8732
+ this.registrations = /* @__PURE__ */ new Map();
8733
+ }
8734
+ /**
8735
+ * Register a plugin factory
8736
+ *
8737
+ * @param name - Unique plugin identifier (e.g., "@snap-agent/rag-ecommerce")
8738
+ * @param factory - Function that creates plugin instance from config
8739
+ * @param defaultConfig - Optional default configuration values
8740
+ */
8741
+ register(name14, factory6, defaultConfig) {
8742
+ if (this.registrations.has(name14)) {
8743
+ console.warn(`Plugin "${name14}" is already registered. Overwriting.`);
8744
+ }
8745
+ this.registrations.set(name14, { factory: factory6, defaultConfig });
8746
+ }
8747
+ /**
8748
+ * Unregister a plugin factory
8749
+ */
8750
+ unregister(name14) {
8751
+ return this.registrations.delete(name14);
8752
+ }
8753
+ /**
8754
+ * Check if a plugin is registered
8755
+ */
8756
+ isRegistered(name14) {
8757
+ return this.registrations.has(name14);
8758
+ }
8759
+ /**
8760
+ * Get all registered plugin names
8761
+ */
8762
+ getRegisteredPlugins() {
8763
+ return Array.from(this.registrations.keys());
8764
+ }
8765
+ /**
8766
+ * Instantiate a plugin from stored configuration
8767
+ *
8768
+ * @param storedConfig - Serialized plugin configuration from database
8769
+ * @returns Plugin instance
8770
+ * @throws Error if plugin is not registered
8771
+ */
8772
+ async instantiate(storedConfig) {
8773
+ const registration = this.registrations.get(storedConfig.name);
8774
+ if (!registration) {
8775
+ throw new Error(
8776
+ `Plugin "${storedConfig.name}" is not registered. Available plugins: ${this.getRegisteredPlugins().join(", ") || "none"}. Make sure to register the plugin before loading the agent.`
8777
+ );
8778
+ }
8779
+ const mergedConfig = {
8780
+ ...registration.defaultConfig,
8781
+ ...storedConfig.config
8782
+ };
8783
+ const resolvedConfig = resolveEnvVars(mergedConfig);
8784
+ const plugin = await registration.factory(resolvedConfig);
8785
+ if (storedConfig.priority !== void 0) {
8786
+ plugin.priority = storedConfig.priority;
8787
+ }
8788
+ return plugin;
8789
+ }
8790
+ /**
8791
+ * Instantiate multiple plugins from stored configurations
8792
+ *
8793
+ * @param storedConfigs - Array of serialized plugin configurations
8794
+ * @returns Array of plugin instances (skips disabled plugins)
8795
+ */
8796
+ async instantiateAll(storedConfigs) {
8797
+ const plugins = [];
8798
+ for (const config of storedConfigs) {
8799
+ if (config.enabled === false) {
8800
+ continue;
8801
+ }
8802
+ try {
8803
+ const plugin = await this.instantiate(config);
8804
+ plugins.push(plugin);
8805
+ } catch (error) {
8806
+ console.error(`Failed to instantiate plugin "${config.name}":`, error);
8807
+ throw error;
8808
+ }
8809
+ }
8810
+ return plugins;
8811
+ }
8812
+ /**
8813
+ * Extract serializable configuration from a plugin instance
8814
+ * Requires the plugin to implement getConfig() method
8815
+ *
8816
+ * @param plugin - Plugin instance
8817
+ * @returns Stored plugin configuration
8818
+ */
8819
+ extractConfig(plugin) {
8820
+ const config = plugin.getConfig?.() ?? {};
8821
+ return {
8822
+ type: plugin.type,
8823
+ name: plugin.name,
8824
+ config,
8825
+ priority: plugin.priority,
8826
+ enabled: true
8827
+ };
8828
+ }
8829
+ /**
8830
+ * Extract configurations from multiple plugins
8831
+ */
8832
+ extractAllConfigs(plugins) {
8833
+ return plugins.map((plugin) => this.extractConfig(plugin));
8834
+ }
8835
+ };
8836
+ var pluginRegistry = new PluginRegistry();
8837
+ function envRef(envVarName, defaultValue) {
8838
+ if (defaultValue !== void 0) {
8839
+ return `\${${envVarName}:${defaultValue}}`;
8840
+ }
8841
+ return `\${${envVarName}}`;
8842
+ }
8843
+
8595
8844
  // src/storage/MongoDBStorage.ts
8596
8845
  var import_mongodb = require("mongodb");
8597
8846
  var MongoDBStorage = class {
@@ -8645,7 +8894,8 @@ var MongoDBStorage = class {
8645
8894
  createdAt: /* @__PURE__ */ new Date(),
8646
8895
  updatedAt: /* @__PURE__ */ new Date(),
8647
8896
  files: [],
8648
- metadata: config.metadata || {}
8897
+ metadata: config.metadata || {},
8898
+ pluginConfigs: config.pluginConfigs || []
8649
8899
  };
8650
8900
  const result = await collection.insertOne(doc);
8651
8901
  return result.insertedId.toString();
@@ -8828,7 +9078,8 @@ var MongoDBStorage = class {
8828
9078
  createdAt: doc.createdAt,
8829
9079
  updatedAt: doc.updatedAt,
8830
9080
  files: doc.files,
8831
- metadata: doc.metadata
9081
+ metadata: doc.metadata,
9082
+ pluginConfigs: doc.pluginConfigs
8832
9083
  };
8833
9084
  }
8834
9085
  threadDocToData(doc) {
@@ -9404,10 +9655,14 @@ function createClient(config) {
9404
9655
  Models,
9405
9656
  MongoDBStorage,
9406
9657
  PluginManager,
9658
+ PluginRegistry,
9407
9659
  ProviderFactory,
9408
9660
  ProviderNotFoundError,
9409
9661
  Thread,
9410
9662
  ThreadNotFoundError,
9411
9663
  UpstashStorage,
9412
- createClient
9664
+ createClient,
9665
+ envRef,
9666
+ pluginRegistry,
9667
+ resolveEnvVars
9413
9668
  });