@mastra/editor 0.9.1-alpha.0 → 0.10.0-alpha.2

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
@@ -518,6 +518,36 @@ var EditorMCPNamespace = class _EditorMCPNamespace extends CrudEditorNamespace {
518
518
  };
519
519
 
520
520
  // src/namespaces/agent.ts
521
+ var BUILDER_DEFAULT_FIELDS = ["memory", "workspace", "browser"];
522
+ function defaultModelToStored(entry) {
523
+ return { provider: entry.provider, name: entry.modelId };
524
+ }
525
+ var BUILDER_BASELINE_DEFAULTS = {
526
+ memory: { observationalMemory: true }
527
+ };
528
+ function applyBuilderDefaults(input, builderAgentConfig) {
529
+ const defaults = {};
530
+ for (const field of BUILDER_DEFAULT_FIELDS) {
531
+ if (input[field] !== void 0) continue;
532
+ const adminValue = builderAgentConfig?.[field];
533
+ if (adminValue !== void 0) {
534
+ defaults[field] = adminValue;
535
+ continue;
536
+ }
537
+ const baseline = BUILDER_BASELINE_DEFAULTS[field];
538
+ if (baseline !== void 0) {
539
+ defaults[field] = baseline;
540
+ }
541
+ }
542
+ if (input.model === void 0 && builderAgentConfig) {
543
+ const models = builderAgentConfig.models ?? void 0;
544
+ const adminDefault = models?.default;
545
+ if (adminDefault && typeof adminDefault.provider === "string" && typeof adminDefault.modelId === "string") {
546
+ defaults.model = defaultModelToStored(adminDefault);
547
+ }
548
+ }
549
+ return Object.keys(defaults).length > 0 ? { ...input, ...defaults } : input;
550
+ }
521
551
  var EditorAgentNamespace = class extends CrudEditorNamespace {
522
552
  async getStorageAdapter() {
523
553
  const storage = this.mastra?.getStorage();
@@ -560,6 +590,67 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
560
590
  async hydrate(storedAgent) {
561
591
  return this.createAgentFromStoredConfig(storedAgent);
562
592
  }
593
+ /**
594
+ * Create a new agent, applying builder defaults for fields not specified in input.
595
+ * Also ensures the referenced workspace (if any) is persisted as a stored workspace.
596
+ */
597
+ async create(input) {
598
+ let finalInput = input;
599
+ if (this.editor.hasEnabledBuilderConfig()) {
600
+ const builder = await this.editor.resolveBuilder();
601
+ const agentConfig = builder?.getConfiguration()?.agent;
602
+ finalInput = applyBuilderDefaults(input, agentConfig);
603
+ }
604
+ await this.ensureStoredWorkspace(finalInput.workspace);
605
+ return super.create(finalInput);
606
+ }
607
+ /**
608
+ * Ensure a workspace reference is persisted in the DB.
609
+ *
610
+ * For `type: 'id'`: looks up the runtime workspace, serializes its config,
611
+ * and creates a stored workspace record if one doesn't already exist.
612
+ *
613
+ * For `type: 'inline'`: derives a deterministic ID from the config and
614
+ * persists it as a stored workspace if one doesn't already exist.
615
+ */
616
+ async ensureStoredWorkspace(workspaceRef) {
617
+ if (!workspaceRef) return;
618
+ const workspaceNs = this.editor.workspace;
619
+ if (!workspaceNs) return;
620
+ try {
621
+ if (workspaceRef.type === "id") {
622
+ const existing = await workspaceNs.getById(workspaceRef.workspaceId);
623
+ if (existing) return;
624
+ const runtimeWorkspace = this.mastra?.getWorkspaceById(workspaceRef.workspaceId);
625
+ if (!runtimeWorkspace) {
626
+ this.logger?.warn(
627
+ `[ensureStoredWorkspace] Workspace '${workspaceRef.workspaceId}' not found in runtime registry, cannot persist`
628
+ );
629
+ return;
630
+ }
631
+ const snapshot = await workspaceNs.snapshotFromWorkspace(runtimeWorkspace);
632
+ await workspaceNs.create({
633
+ id: workspaceRef.workspaceId,
634
+ metadata: { source: "builder", builderWorkspaceId: workspaceRef.workspaceId },
635
+ ...snapshot
636
+ });
637
+ this.logger?.debug(`[ensureStoredWorkspace] Persisted runtime workspace '${workspaceRef.workspaceId}' to DB`);
638
+ } else if (workspaceRef.type === "inline") {
639
+ const configHash = createHash("sha256").update(JSON.stringify(workspaceRef.config)).digest("hex").slice(0, 12);
640
+ const workspaceId = `inline-${configHash}`;
641
+ const existing = await workspaceNs.getById(workspaceId);
642
+ if (existing) return;
643
+ await workspaceNs.create({
644
+ id: workspaceId,
645
+ metadata: { source: "builder", builderConfigHash: configHash },
646
+ ...workspaceRef.config
647
+ });
648
+ this.logger?.debug(`[ensureStoredWorkspace] Persisted inline workspace '${workspaceId}' to DB`);
649
+ }
650
+ } catch (error) {
651
+ this.logger?.warn("[ensureStoredWorkspace] Failed to persist workspace", { error });
652
+ }
653
+ }
563
654
  onCacheEvict(id) {
564
655
  try {
565
656
  const existing = this.mastra?.getAgentById(id);
@@ -749,6 +840,7 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
749
840
  const hasConditionalDefaultOptions = storedAgent.defaultOptions != null && this.isConditionalVariants(storedAgent.defaultOptions);
750
841
  const hasConditionalModel = this.isConditionalVariants(storedAgent.model);
751
842
  const hasConditionalWorkspace = storedAgent.workspace != null && this.isConditionalVariants(storedAgent.workspace);
843
+ const hasConditionalBrowser = storedAgent.browser != null && this.isConditionalVariants(storedAgent.browser);
752
844
  const hasIntegrationTools = storedAgent.integrationTools != null;
753
845
  const isDynamicTools = hasConditionalTools || hasConditionalMCPClients || hasConditionalIntegrationTools || hasIntegrationTools;
754
846
  let tools;
@@ -915,6 +1007,14 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
915
1007
  );
916
1008
  return this.resolveStoredWorkspace(resolvedRef, skillSource);
917
1009
  } : await this.resolveStoredWorkspace(storedAgent.workspace, skillSource);
1010
+ const browser = hasConditionalBrowser ? async ({ requestContext }) => {
1011
+ const ctx = requestContext.toJSON();
1012
+ const resolvedRef = this.accumulateObjectVariants(
1013
+ storedAgent.browser,
1014
+ ctx
1015
+ );
1016
+ return this.resolveStoredBrowser(resolvedRef);
1017
+ } : await this.resolveStoredBrowser(storedAgent.browser);
918
1018
  const skillsFormat = storedAgent.skillsFormat;
919
1019
  const agent = new Agent({
920
1020
  id: storedAgent.id,
@@ -935,6 +1035,7 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
935
1035
  defaultOptions,
936
1036
  requestContextSchema,
937
1037
  workspace,
1038
+ browser,
938
1039
  ...skillsFormat && { skillsFormat }
939
1040
  });
940
1041
  const existingCodeAgent = (() => {
@@ -1333,7 +1434,8 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1333
1434
  scorers: storedScorers,
1334
1435
  defaultOptions: storageDefaultOptions,
1335
1436
  metadata: resolvedMetadata,
1336
- authorId: options.authorId
1437
+ authorId: options.authorId,
1438
+ visibility: options.visibility
1337
1439
  };
1338
1440
  const adapter = await this.getStorageAdapter();
1339
1441
  await adapter.create(createInput);
@@ -1365,13 +1467,23 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1365
1467
  const hydrateOptions = skillSource ? { skillSource } : void 0;
1366
1468
  if (workspaceRef.type === "id") {
1367
1469
  const resolved = await workspaceNs.getById(workspaceRef.workspaceId);
1368
- if (!resolved) {
1369
- this.logger?.warn(
1370
- `[resolveStoredWorkspace] Workspace '${workspaceRef.workspaceId}' not found in storage, skipping`
1371
- );
1372
- return void 0;
1470
+ if (resolved) {
1471
+ return workspaceNs.hydrateSnapshotToWorkspace(workspaceRef.workspaceId, resolved, hydrateOptions);
1472
+ }
1473
+ try {
1474
+ const runtimeWorkspace = this.mastra?.getWorkspaceById(workspaceRef.workspaceId);
1475
+ if (runtimeWorkspace) {
1476
+ this.logger?.debug(
1477
+ `[resolveStoredWorkspace] Workspace '${workspaceRef.workspaceId}' found in runtime registry (not in DB)`
1478
+ );
1479
+ return runtimeWorkspace;
1480
+ }
1481
+ } catch {
1373
1482
  }
1374
- return workspaceNs.hydrateSnapshotToWorkspace(workspaceRef.workspaceId, resolved, hydrateOptions);
1483
+ this.logger?.warn(
1484
+ `[resolveStoredWorkspace] Workspace '${workspaceRef.workspaceId}' not found in storage or runtime registry, skipping`
1485
+ );
1486
+ return void 0;
1375
1487
  }
1376
1488
  if (workspaceRef.type === "inline") {
1377
1489
  const configHash = createHash("sha256").update(JSON.stringify(workspaceRef.config)).digest("hex").slice(0, 12);
@@ -1379,6 +1491,26 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1379
1491
  }
1380
1492
  return void 0;
1381
1493
  }
1494
+ /**
1495
+ * Resolve a stored browser config to a runtime MastraBrowser instance.
1496
+ * Looks up the provider by ID in the editor's browser registry.
1497
+ * Only supports `type: 'inline'` refs (config is embedded in the agent snapshot).
1498
+ */
1499
+ async resolveStoredBrowser(browserRef) {
1500
+ if (!browserRef) return void 0;
1501
+ if (browserRef.type === "inline") {
1502
+ const { provider: providerId, ...config } = browserRef.config;
1503
+ const browserProvider = this.editor.__browsers.get(providerId);
1504
+ if (!browserProvider) {
1505
+ this.logger?.warn(
1506
+ `[resolveStoredBrowser] Browser provider "${providerId}" is not registered. Register it via new MastraEditor({ browsers: { '${providerId}': yourProvider } })`
1507
+ );
1508
+ return void 0;
1509
+ }
1510
+ return await browserProvider.createBrowser(config);
1511
+ }
1512
+ return void 0;
1513
+ }
1382
1514
  /**
1383
1515
  * Resolve agent-level skill configurations into a CompositeVersionedSkillSource.
1384
1516
  *
@@ -2014,9 +2146,35 @@ var localSandboxProvider = {
2014
2146
  createSandbox: (config) => new LocalSandbox(config)
2015
2147
  };
2016
2148
 
2149
+ // src/snapshots-match.ts
2150
+ function snapshotsMatch(stored, runtime) {
2151
+ const keys = [
2152
+ "name",
2153
+ "description",
2154
+ "filesystem",
2155
+ "sandbox",
2156
+ "mounts",
2157
+ "search",
2158
+ "skills",
2159
+ "tools",
2160
+ "autoSync",
2161
+ "operationTimeout"
2162
+ ];
2163
+ const replacer = (_k, v) => v === false || v === null || v === 0 ? void 0 : v;
2164
+ for (const key of keys) {
2165
+ const storedVal = stored[key];
2166
+ const runtimeVal = runtime[key];
2167
+ const storedJSON = storedVal == null || storedVal === false ? void 0 : JSON.stringify(storedVal, replacer);
2168
+ const runtimeJSON = runtimeVal == null || runtimeVal === false ? void 0 : JSON.stringify(runtimeVal, replacer);
2169
+ if (storedJSON !== runtimeJSON) return false;
2170
+ }
2171
+ return true;
2172
+ }
2173
+
2017
2174
  // src/index.ts
2018
2175
  var MastraEditor = class {
2019
2176
  constructor(config) {
2177
+ this.__builderResolved = false;
2020
2178
  this.__logger = config?.logger;
2021
2179
  this.__toolProviders = config?.toolProviders ?? {};
2022
2180
  this.__processorProviders = { ...BUILT_IN_PROCESSOR_PROVIDERS, ...config?.processorProviders };
@@ -2034,6 +2192,10 @@ var MastraEditor = class {
2034
2192
  for (const [id, provider] of Object.entries(config?.blobStores ?? {})) {
2035
2193
  this.__blobStores.set(id, provider);
2036
2194
  }
2195
+ this.__browsers = /* @__PURE__ */ new Map();
2196
+ for (const [id, provider] of Object.entries(config?.browsers ?? {})) {
2197
+ this.__browsers.set(id, provider);
2198
+ }
2037
2199
  this.agent = new EditorAgentNamespace(this);
2038
2200
  this.mcp = new EditorMCPNamespace(this);
2039
2201
  this.mcpServer = new EditorMCPServerNamespace(this);
@@ -2042,6 +2204,7 @@ var MastraEditor = class {
2042
2204
  this.workspace = new EditorWorkspaceNamespace(this);
2043
2205
  this.skill = new EditorSkillNamespace(this);
2044
2206
  this.favorites = new EditorFavoritesNamespace(this);
2207
+ this.__builderConfig = config?.builder;
2045
2208
  }
2046
2209
  /**
2047
2210
  * Register this editor with a Mastra instance.
@@ -2052,11 +2215,171 @@ var MastraEditor = class {
2052
2215
  if (!this.__logger) {
2053
2216
  this.__logger = mastra.getLogger();
2054
2217
  }
2218
+ this.ensureBuilderWorkspaces().then(() => this.reconcileBuilderWorkspaces()).catch((err) => {
2219
+ this.__logger?.warn("[MastraEditor] Failed to persist/reconcile builder workspaces on startup", {
2220
+ error: err
2221
+ });
2222
+ });
2223
+ }
2224
+ /**
2225
+ * Ensure the builder default workspace is persisted to the DB.
2226
+ * Called automatically on startup when the editor registers with Mastra.
2227
+ * Goes through the normal create() path so hydration validates that
2228
+ * all providers (filesystem, sandbox) are properly registered.
2229
+ *
2230
+ * If the workspace already exists but its config has drifted from the
2231
+ * runtime workspace, the DB record is updated (creating a new version).
2232
+ * Builder-created workspaces are tagged with `metadata.source = 'builder'`
2233
+ * so they can be identified during reconciliation.
2234
+ */
2235
+ async ensureBuilderWorkspaces() {
2236
+ if (!this.hasEnabledBuilderConfig()) return;
2237
+ const builder = await this.resolveBuilder();
2238
+ const agentConfig = builder?.getConfiguration()?.agent;
2239
+ const workspaceRef = agentConfig?.workspace;
2240
+ if (!workspaceRef || workspaceRef.type !== "id" || !workspaceRef.workspaceId) return;
2241
+ const runtimeWorkspace = this.__mastra?.getWorkspaceById(workspaceRef.workspaceId);
2242
+ if (!runtimeWorkspace) return;
2243
+ const snapshot = await this.workspace.snapshotFromWorkspace(runtimeWorkspace);
2244
+ const builderMetadata = { source: "builder", builderWorkspaceId: workspaceRef.workspaceId };
2245
+ const existing = await this.workspace.getById(workspaceRef.workspaceId);
2246
+ if (!existing) {
2247
+ await this.workspace.create({
2248
+ id: workspaceRef.workspaceId,
2249
+ metadata: builderMetadata,
2250
+ ...snapshot
2251
+ });
2252
+ this.__logger?.info(`[MastraEditor] Persisted builder workspace '${workspaceRef.workspaceId}' to DB`);
2253
+ return;
2254
+ }
2255
+ const needsMetadataBackfill = !existing.metadata?.source;
2256
+ const configDrifted = !snapshotsMatch(existing, snapshot);
2257
+ if (needsMetadataBackfill || configDrifted) {
2258
+ const updateInput = { id: workspaceRef.workspaceId };
2259
+ if (needsMetadataBackfill) {
2260
+ updateInput.metadata = { ...existing.metadata, ...builderMetadata };
2261
+ }
2262
+ if (configDrifted) {
2263
+ Object.assign(updateInput, snapshot);
2264
+ this.__logger?.info(
2265
+ `[MastraEditor] Workspace '${workspaceRef.workspaceId}' config drifted \u2014 updating DB record`
2266
+ );
2267
+ }
2268
+ await this.workspace.update(updateInput);
2269
+ }
2270
+ }
2271
+ /**
2272
+ * Archive builder-created workspaces that no longer match the current
2273
+ * builder configuration. Called after `ensureBuilderWorkspaces()` on startup.
2274
+ *
2275
+ * Only touches workspaces tagged with `metadata.source === 'builder'`.
2276
+ * The current builder workspace (if any) is never archived.
2277
+ */
2278
+ async reconcileBuilderWorkspaces() {
2279
+ if (!this.hasEnabledBuilderConfig()) return;
2280
+ const builder = await this.resolveBuilder();
2281
+ const agentConfig = builder?.getConfiguration()?.agent;
2282
+ const workspaceRef = agentConfig?.workspace;
2283
+ let currentWorkspaceId;
2284
+ if (workspaceRef?.type === "id" && workspaceRef.workspaceId) {
2285
+ currentWorkspaceId = workspaceRef.workspaceId;
2286
+ }
2287
+ if (!currentWorkspaceId) return;
2288
+ const { workspaces: allWorkspaces } = await this.workspace.listResolved({
2289
+ perPage: false,
2290
+ // fetch all
2291
+ metadata: { source: "builder" }
2292
+ });
2293
+ for (const ws of allWorkspaces) {
2294
+ if (ws.id === currentWorkspaceId) continue;
2295
+ if (ws.status === "archived") continue;
2296
+ try {
2297
+ await this.workspace.update({ id: ws.id, status: "archived" });
2298
+ this.__logger?.info(`[MastraEditor] Archived orphaned builder workspace '${ws.id}'`);
2299
+ } catch (err) {
2300
+ this.__logger?.warn(`[MastraEditor] Failed to archive workspace '${ws.id}'`, { error: err });
2301
+ }
2302
+ }
2303
+ }
2304
+ /**
2305
+ * Sync. OSS-safe. Does NOT import @mastra/editor/ee.
2306
+ * Returns true if builder config is present and enabled.
2307
+ */
2308
+ hasEnabledBuilderConfig() {
2309
+ if (!this.__builderConfig) return false;
2310
+ return this.__builderConfig.enabled !== false;
2311
+ }
2312
+ /**
2313
+ * Async. Dynamic-imports @mastra/editor/ee on first call. Caches result.
2314
+ * Returns undefined if builder is not enabled.
2315
+ */
2316
+ async resolveBuilder() {
2317
+ if (this.__builderResolved) {
2318
+ return this.__builderInstance;
2319
+ }
2320
+ if (!this.hasEnabledBuilderConfig()) {
2321
+ this.__builderResolved = true;
2322
+ return void 0;
2323
+ }
2324
+ await this.assertAgentBuilderLicensed();
2325
+ const { EditorAgentBuilder } = await import("./ee/index.js");
2326
+ this.__builderInstance = new EditorAgentBuilder(this.__builderConfig);
2327
+ const browserRef = this.__builderInstance.getConfiguration()?.agent?.browser;
2328
+ const browserFeatureOn = this.__builderInstance.getFeatures()?.agent?.browser === true;
2329
+ if (browserFeatureOn && browserRef?.config?.provider) {
2330
+ const providerId = browserRef.config.provider;
2331
+ if (!this.__browsers.has(providerId)) {
2332
+ const warning = `Agent Builder browser config references provider "${providerId}" but no matching browser provider is registered in \`editor.browsers\`. The browser toggle will be hidden. Register the provider: \`new MastraEditor({ browsers: { "${providerId}": yourProvider } })\`.`;
2333
+ console.warn(`[mastra:editor] ${warning}`);
2334
+ const features = this.__builderInstance.getFeatures()?.agent;
2335
+ if (features) {
2336
+ features.browser = false;
2337
+ }
2338
+ }
2339
+ }
2340
+ this.__builderResolved = true;
2341
+ return this.__builderInstance;
2342
+ }
2343
+ /**
2344
+ * Defense-in-depth license guard for the Agent Builder. Mirrors the
2345
+ * startup-time check in `MastraServer.validateAgentBuilderLicense()` so the
2346
+ * builder cannot be instantiated outside the server boot path without a
2347
+ * valid EE license. Dev environments bypass via `isEEEnabled()`.
2348
+ */
2349
+ async assertAgentBuilderLicensed() {
2350
+ try {
2351
+ const { isEEEnabled } = await import("@mastra/core/auth/ee");
2352
+ if (!isEEEnabled()) {
2353
+ throw new Error(
2354
+ "[mastra/auth-ee] Agent Builder is configured but no valid EE license was found.\nAgent Builder requires a Mastra Enterprise License for production use.\nSet the MASTRA_EE_LICENSE environment variable with your license key.\nLearn more: https://github.com/mastra-ai/mastra/blob/main/ee/LICENSE"
2355
+ );
2356
+ }
2357
+ } catch (err) {
2358
+ if (err instanceof Error && err.message.startsWith("[mastra/auth-ee]")) {
2359
+ throw err;
2360
+ }
2361
+ throw new Error(
2362
+ "[mastra/auth-ee] Agent Builder is configured but the EE module (@mastra/core/auth/ee) could not be loaded.\nEnsure @mastra/core is updated to a version that includes EE support."
2363
+ );
2364
+ }
2055
2365
  }
2056
2366
  /** Registered tool providers */
2057
2367
  getToolProvider(id) {
2058
2368
  return this.__toolProviders[id];
2059
2369
  }
2370
+ /**
2371
+ * Like {@link getToolProvider}, but throws {@link UnknownToolProviderError}
2372
+ * when the id is unknown.
2373
+ */
2374
+ getToolProviderOrThrow(id) {
2375
+ const provider = this.__toolProviders[id];
2376
+ if (!provider) {
2377
+ throw new Error(
2378
+ `Unknown tool provider "${id}". Available: ${Object.keys(this.__toolProviders).join(", ") || "(none)"}`
2379
+ );
2380
+ }
2381
+ return provider;
2382
+ }
2060
2383
  /** List all registered tool providers */
2061
2384
  getToolProviders() {
2062
2385
  return this.__toolProviders;
@@ -2128,3 +2451,4 @@ export {
2128
2451
  renderTemplate,
2129
2452
  resolveInstructionBlocks
2130
2453
  };
2454
+ //# sourceMappingURL=index.js.map