@mastra/editor 0.13.1-alpha.0 → 0.13.1

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.d.cts CHANGED
@@ -96,12 +96,14 @@ declare class EditorAgentNamespace extends CrudEditorNamespace<StorageCreateAgen
96
96
  * Hydrate a stored agent config into a runtime Agent instance.
97
97
  */
98
98
  protected hydrate(storedAgent: StorageResolvedAgentType): Promise<Agent>;
99
+ update(input: StorageUpdateAgentInput): Promise<Agent>;
99
100
  /**
100
101
  * Create a new agent, applying builder defaults for fields not specified in input.
101
102
  * Also ensures the referenced workspace (if any) is persisted as a stored workspace.
102
103
  */
103
104
  create(input: StorageCreateAgentInput): Promise<Agent>;
104
105
  private getCodeDefinedAgent;
106
+ private ensureStoredWorkspaceRefs;
105
107
  /**
106
108
  * Ensure a workspace reference is persisted in the DB.
107
109
  *
@@ -267,6 +269,7 @@ declare class EditorMCPServerNamespace extends CrudEditorNamespace<StorageCreate
267
269
  declare class EditorPromptNamespace extends CrudEditorNamespace<StorageCreatePromptBlockInput, StorageUpdatePromptBlockInput, StorageListPromptBlocksInput, StorageListPromptBlocksOutput, StorageListPromptBlocksResolvedOutput, StorageResolvedPromptBlockType> {
268
270
  protected onCacheEvict(id: string): void;
269
271
  protected getStorageAdapter(): Promise<StorageAdapter<StorageCreatePromptBlockInput, StorageUpdatePromptBlockInput, StorageListPromptBlocksInput, StorageListPromptBlocksOutput, StorageListPromptBlocksResolvedOutput, StorageResolvedPromptBlockType>>;
272
+ update(input: StorageUpdatePromptBlockInput): Promise<StorageResolvedPromptBlockType>;
270
273
  preview(blocks: AgentInstructionBlock[], context: Record<string, unknown>): Promise<string>;
271
274
  }
272
275
 
package/dist/index.d.ts CHANGED
@@ -96,12 +96,14 @@ declare class EditorAgentNamespace extends CrudEditorNamespace<StorageCreateAgen
96
96
  * Hydrate a stored agent config into a runtime Agent instance.
97
97
  */
98
98
  protected hydrate(storedAgent: StorageResolvedAgentType): Promise<Agent>;
99
+ update(input: StorageUpdateAgentInput): Promise<Agent>;
99
100
  /**
100
101
  * Create a new agent, applying builder defaults for fields not specified in input.
101
102
  * Also ensures the referenced workspace (if any) is persisted as a stored workspace.
102
103
  */
103
104
  create(input: StorageCreateAgentInput): Promise<Agent>;
104
105
  private getCodeDefinedAgent;
106
+ private ensureStoredWorkspaceRefs;
105
107
  /**
106
108
  * Ensure a workspace reference is persisted in the DB.
107
109
  *
@@ -267,6 +269,7 @@ declare class EditorMCPServerNamespace extends CrudEditorNamespace<StorageCreate
267
269
  declare class EditorPromptNamespace extends CrudEditorNamespace<StorageCreatePromptBlockInput, StorageUpdatePromptBlockInput, StorageListPromptBlocksInput, StorageListPromptBlocksOutput, StorageListPromptBlocksResolvedOutput, StorageResolvedPromptBlockType> {
268
270
  protected onCacheEvict(id: string): void;
269
271
  protected getStorageAdapter(): Promise<StorageAdapter<StorageCreatePromptBlockInput, StorageUpdatePromptBlockInput, StorageListPromptBlocksInput, StorageListPromptBlocksOutput, StorageListPromptBlocksResolvedOutput, StorageResolvedPromptBlockType>>;
272
+ update(input: StorageUpdatePromptBlockInput): Promise<StorageResolvedPromptBlockType>;
270
273
  preview(blocks: AgentInstructionBlock[], context: Record<string, unknown>): Promise<string>;
271
274
  }
272
275
 
package/dist/index.js CHANGED
@@ -525,7 +525,106 @@ var EditorMCPNamespace = class _EditorMCPNamespace extends CrudEditorNamespace {
525
525
  }
526
526
  };
527
527
 
528
+ // src/namespaces/versioned-update.ts
529
+ import { deepEqual } from "@mastra/core/utils";
530
+ function getProvidedSnapshotFields(input, snapshotFields) {
531
+ const config = {};
532
+ for (const field of snapshotFields) {
533
+ if (input[field] !== void 0) {
534
+ config[field] = input[field];
535
+ }
536
+ }
537
+ return config;
538
+ }
539
+ function extractSnapshotConfig(version, snapshotFields) {
540
+ const record = version;
541
+ const config = {};
542
+ for (const field of snapshotFields) {
543
+ if (field in record) {
544
+ config[field] = record[field];
545
+ }
546
+ }
547
+ return config;
548
+ }
549
+ function getChangedSnapshotFields(previousConfig, providedConfig, snapshotFields) {
550
+ return snapshotFields.filter(
551
+ (field) => field in providedConfig && !deepEqual(previousConfig[field], providedConfig[field])
552
+ );
553
+ }
554
+ function isVersionNumberConflictError(error) {
555
+ if (!(error instanceof Error)) return false;
556
+ const message = error.message.toLowerCase();
557
+ return message.includes("unique") && message.includes("constraint") || message.includes("duplicate key") || message.includes("unique_violation") || message.includes("sqlite_constraint_unique") || message.includes("version number") && message.includes("already exists") || message.includes("versionnumber");
558
+ }
559
+ async function createVersionFromSnapshotUpdate({
560
+ store,
561
+ parentId,
562
+ parentIdField,
563
+ snapshotFields,
564
+ providedConfig,
565
+ changeMessage = "Auto-saved after edit",
566
+ maxRetries = 3
567
+ }) {
568
+ let lastError;
569
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
570
+ try {
571
+ const latestVersion = await store.getLatestVersion(parentId);
572
+ if (!latestVersion) {
573
+ return { versionCreated: false };
574
+ }
575
+ const previousConfig = extractSnapshotConfig(latestVersion, snapshotFields);
576
+ const changedFields = getChangedSnapshotFields(previousConfig, providedConfig, snapshotFields);
577
+ if (changedFields.length === 0) {
578
+ return { versionCreated: false };
579
+ }
580
+ const nextConfig = { ...previousConfig };
581
+ for (const [field, value] of Object.entries(providedConfig)) {
582
+ nextConfig[field] = value === null ? void 0 : value;
583
+ }
584
+ const version = await store.createVersion({
585
+ ...nextConfig,
586
+ id: crypto.randomUUID(),
587
+ [parentIdField]: parentId,
588
+ versionNumber: latestVersion.versionNumber + 1,
589
+ changedFields,
590
+ changeMessage
591
+ });
592
+ return { versionCreated: true, version, changedFields };
593
+ } catch (error) {
594
+ lastError = error;
595
+ if (isVersionNumberConflictError(error) && attempt < maxRetries - 1) {
596
+ await new Promise((resolve) => setTimeout(resolve, 10 * (attempt + 1)));
597
+ continue;
598
+ }
599
+ throw error;
600
+ }
601
+ }
602
+ throw lastError;
603
+ }
604
+
528
605
  // src/namespaces/agent.ts
606
+ var AGENT_SNAPSHOT_CONFIG_FIELDS = [
607
+ "name",
608
+ "description",
609
+ "instructions",
610
+ "model",
611
+ "tools",
612
+ "defaultOptions",
613
+ "workflows",
614
+ "agents",
615
+ "integrationTools",
616
+ "toolProviders",
617
+ "inputProcessors",
618
+ "outputProcessors",
619
+ "memory",
620
+ "scorers",
621
+ "requestContextSchema",
622
+ "mcpClients",
623
+ "skills",
624
+ "skillsFormat",
625
+ "workspace",
626
+ "browser"
627
+ ];
529
628
  var BUILDER_DEFAULT_FIELDS = ["memory", "workspace", "browser"];
530
629
  function defaultModelToStored(entry) {
531
630
  return { provider: entry.provider, name: entry.modelId };
@@ -556,6 +655,16 @@ function applyBuilderDefaults(input, builderAgentConfig) {
556
655
  }
557
656
  return Object.keys(defaults).length > 0 ? { ...input, ...defaults } : input;
558
657
  }
658
+ function getProvidedAgentRecordFields(input) {
659
+ const { id, authorId, visibility, activeVersionId, metadata, status } = input;
660
+ const recordFields = { id };
661
+ if (authorId !== void 0) recordFields.authorId = authorId;
662
+ if (visibility !== void 0) recordFields.visibility = visibility;
663
+ if (activeVersionId !== void 0) recordFields.activeVersionId = activeVersionId;
664
+ if (metadata !== void 0) recordFields.metadata = metadata;
665
+ if (status !== void 0) recordFields.status = status;
666
+ return Object.keys(recordFields).length > 1 ? recordFields : null;
667
+ }
559
668
  var EditorAgentNamespace = class extends CrudEditorNamespace {
560
669
  async getStorageAdapter() {
561
670
  const storage = this.mastra?.getStorage();
@@ -598,6 +707,53 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
598
707
  async hydrate(storedAgent) {
599
708
  return this.createAgentFromStoredConfig(storedAgent);
600
709
  }
710
+ async update(input) {
711
+ this.ensureRegistered();
712
+ const storage = this.mastra?.getStorage();
713
+ if (!storage) throw new Error("Storage is not configured");
714
+ const store = await storage.getStore("agents");
715
+ if (!store) throw new Error("Agents storage domain is not available");
716
+ const existing = await store.getById(input.id);
717
+ if (!existing) {
718
+ throw new Error(`Agent with id ${input.id} not found`);
719
+ }
720
+ const providedConfig = getProvidedSnapshotFields(
721
+ input,
722
+ AGENT_SNAPSHOT_CONFIG_FIELDS
723
+ );
724
+ if ("workspace" in providedConfig) {
725
+ await this.ensureStoredWorkspaceRefs(providedConfig.workspace);
726
+ }
727
+ const versionResult = Object.keys(providedConfig).length > 0 ? await createVersionFromSnapshotUpdate({
728
+ store,
729
+ parentId: input.id,
730
+ parentIdField: "agentId",
731
+ snapshotFields: AGENT_SNAPSHOT_CONFIG_FIELDS,
732
+ providedConfig
733
+ }) : { versionCreated: false };
734
+ const recordFields = getProvidedAgentRecordFields(input);
735
+ if (recordFields || versionResult.versionCreated) {
736
+ await store.update({
737
+ ...recordFields ?? { id: input.id },
738
+ ...versionResult.versionCreated ? { activeVersionId: versionResult.version.id } : {}
739
+ });
740
+ }
741
+ this._cache.delete(input.id);
742
+ this.onCacheEvict(input.id);
743
+ const existingCodeAgent = this.getCodeDefinedAgent(input.id);
744
+ if (existingCodeAgent) {
745
+ const hydrated2 = await this.applyStoredOverrides(existingCodeAgent, { status: "draft" });
746
+ this._cache.set(input.id, hydrated2);
747
+ return hydrated2;
748
+ }
749
+ const resolved = await store.getByIdResolved(input.id, { status: "draft" });
750
+ if (!resolved) {
751
+ throw new Error(`Failed to resolve entity ${input.id} after update`);
752
+ }
753
+ const hydrated = await this.hydrate(resolved);
754
+ this._cache.set(input.id, hydrated);
755
+ return hydrated;
756
+ }
601
757
  /**
602
758
  * Create a new agent, applying builder defaults for fields not specified in input.
603
759
  * Also ensures the referenced workspace (if any) is persisted as a stored workspace.
@@ -628,6 +784,16 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
628
784
  }
629
785
  return agent?.source === "code" ? agent : void 0;
630
786
  }
787
+ async ensureStoredWorkspaceRefs(workspace) {
788
+ if (!workspace) return;
789
+ if (this.isConditionalVariants(workspace)) {
790
+ for (const variant of workspace) {
791
+ await this.ensureStoredWorkspace(variant.value);
792
+ }
793
+ return;
794
+ }
795
+ await this.ensureStoredWorkspace(workspace);
796
+ }
631
797
  /**
632
798
  * Ensure a workspace reference is persisted in the DB.
633
799
  *
@@ -1771,6 +1937,96 @@ var EditorMCPServerNamespace = class extends CrudEditorNamespace {
1771
1937
  };
1772
1938
 
1773
1939
  // src/namespaces/prompt.ts
1940
+ var PROMPT_BLOCK_SNAPSHOT_CONFIG_FIELDS = [
1941
+ "name",
1942
+ "description",
1943
+ "content",
1944
+ "rules",
1945
+ "requestContextSchema"
1946
+ ];
1947
+ function deepEqual2(a, b) {
1948
+ if (a === b) return true;
1949
+ if (a == null || b == null) return a === b;
1950
+ if (typeof a !== typeof b) return false;
1951
+ if (Array.isArray(a) && Array.isArray(b)) {
1952
+ return a.length === b.length && a.every((item, index) => deepEqual2(item, b[index]));
1953
+ }
1954
+ if (typeof a === "object" && typeof b === "object") {
1955
+ const aObj = a;
1956
+ const bObj = b;
1957
+ const aKeys = Object.keys(aObj);
1958
+ const bKeys = Object.keys(bObj);
1959
+ return aKeys.length === bKeys.length && aKeys.every((key) => deepEqual2(aObj[key], bObj[key]));
1960
+ }
1961
+ return false;
1962
+ }
1963
+ function extractConfigFromVersion(version) {
1964
+ return {
1965
+ name: version.name,
1966
+ description: version.description,
1967
+ content: version.content,
1968
+ rules: version.rules,
1969
+ requestContextSchema: version.requestContextSchema
1970
+ };
1971
+ }
1972
+ function getProvidedConfigFields(input) {
1973
+ const config = {};
1974
+ for (const field of PROMPT_BLOCK_SNAPSHOT_CONFIG_FIELDS) {
1975
+ if (input[field] !== void 0) {
1976
+ config[field] = input[field];
1977
+ }
1978
+ }
1979
+ return config;
1980
+ }
1981
+ function getProvidedRecordFields(input) {
1982
+ const { id, authorId, activeVersionId, metadata, status } = input;
1983
+ const recordFields = { id };
1984
+ if (authorId !== void 0) recordFields.authorId = authorId;
1985
+ if (activeVersionId !== void 0) recordFields.activeVersionId = activeVersionId;
1986
+ if (metadata !== void 0) recordFields.metadata = metadata;
1987
+ if (status !== void 0) recordFields.status = status;
1988
+ return Object.keys(recordFields).length > 1 ? recordFields : null;
1989
+ }
1990
+ function getChangedFields(previousConfig, providedConfig) {
1991
+ return PROMPT_BLOCK_SNAPSHOT_CONFIG_FIELDS.filter(
1992
+ (field) => field in providedConfig && !deepEqual2(previousConfig[field], providedConfig[field])
1993
+ );
1994
+ }
1995
+ function isVersionNumberConflictError2(error) {
1996
+ if (!(error instanceof Error)) return false;
1997
+ const message = error.message.toLowerCase();
1998
+ return message.includes("unique") && message.includes("constraint") || message.includes("duplicate key") || message.includes("unique_violation") || message.includes("sqlite_constraint_unique") || message.includes("version number") && message.includes("already exists") || message.includes("versionnumber");
1999
+ }
2000
+ async function createPromptBlockVersionWithRetry(store, blockId, providedConfig, maxRetries = 3) {
2001
+ let lastError;
2002
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
2003
+ try {
2004
+ const latestVersion = await store.getLatestVersion(blockId);
2005
+ if (!latestVersion) return false;
2006
+ const previousConfig = extractConfigFromVersion(latestVersion);
2007
+ const changedFields = getChangedFields(previousConfig, providedConfig);
2008
+ if (changedFields.length === 0) return false;
2009
+ await store.createVersion({
2010
+ ...previousConfig,
2011
+ ...providedConfig,
2012
+ id: crypto.randomUUID(),
2013
+ blockId,
2014
+ versionNumber: latestVersion.versionNumber + 1,
2015
+ changedFields,
2016
+ changeMessage: "Auto-saved after edit"
2017
+ });
2018
+ return true;
2019
+ } catch (error) {
2020
+ lastError = error;
2021
+ if (isVersionNumberConflictError2(error) && attempt < maxRetries - 1) {
2022
+ await new Promise((resolve) => setTimeout(resolve, 10 * (attempt + 1)));
2023
+ continue;
2024
+ }
2025
+ throw error;
2026
+ }
2027
+ }
2028
+ throw lastError;
2029
+ }
1774
2030
  var EditorPromptNamespace = class extends CrudEditorNamespace {
1775
2031
  onCacheEvict(id) {
1776
2032
  this.mastra?.removePromptBlock(id);
@@ -1789,6 +2045,33 @@ var EditorPromptNamespace = class extends CrudEditorNamespace {
1789
2045
  listResolved: (args) => store.listResolved(args)
1790
2046
  };
1791
2047
  }
2048
+ async update(input) {
2049
+ this.ensureRegistered();
2050
+ const storage = this.mastra?.getStorage();
2051
+ if (!storage) throw new Error("Storage is not configured");
2052
+ const store = await storage.getStore("promptBlocks");
2053
+ if (!store) throw new Error("Prompt blocks storage domain is not available");
2054
+ const existing = await store.getById(input.id);
2055
+ if (!existing) {
2056
+ throw new Error(`Prompt block with id ${input.id} not found`);
2057
+ }
2058
+ const providedConfig = getProvidedConfigFields(input);
2059
+ if (Object.keys(providedConfig).length > 0) {
2060
+ await createPromptBlockVersionWithRetry(store, input.id, providedConfig);
2061
+ }
2062
+ const recordFields = getProvidedRecordFields(input);
2063
+ if (recordFields) {
2064
+ await store.update(recordFields);
2065
+ }
2066
+ this._cache.delete(input.id);
2067
+ this.onCacheEvict(input.id);
2068
+ const resolved = await store.getByIdResolved(input.id, { status: "draft" });
2069
+ if (!resolved) {
2070
+ throw new Error(`Failed to resolve entity ${input.id} after update`);
2071
+ }
2072
+ this._cache.set(input.id, resolved);
2073
+ return resolved;
2074
+ }
1792
2075
  async preview(blocks, context) {
1793
2076
  this.ensureRegistered();
1794
2077
  const storage = this.mastra?.getStorage();