@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.cjs CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,6 +30,281 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/ee/agent-builder.ts
34
+ var import_ee, EditorAgentBuilder;
35
+ var init_agent_builder = __esm({
36
+ "src/ee/agent-builder.ts"() {
37
+ "use strict";
38
+ import_ee = require("@mastra/core/agent-builder/ee");
39
+ EditorAgentBuilder = class {
40
+ constructor(options) {
41
+ this.modelPolicyWarnings = [];
42
+ /** Non-fatal warnings for browser config issues (surfaced alongside model policy warnings). */
43
+ this.browserConfigWarnings = [];
44
+ const source = options ?? {};
45
+ this.options = {
46
+ ...source,
47
+ features: source.features ? {
48
+ ...source.features,
49
+ agent: source.features.agent ? { ...source.features.agent } : void 0
50
+ } : void 0
51
+ };
52
+ this.validateModelPolicy();
53
+ this.validateBrowserConfig();
54
+ this.resolvedFeatures = {
55
+ agent: (0, import_ee.resolveAgentFeatures)(this.options.features?.agent, {
56
+ hasBrowserConfig: this.hasValidBrowserConfig()
57
+ })
58
+ };
59
+ }
60
+ get enabled() {
61
+ return this.options.enabled !== false;
62
+ }
63
+ getFeatures() {
64
+ return this.resolvedFeatures;
65
+ }
66
+ getConfiguration() {
67
+ return this.options.configuration;
68
+ }
69
+ getRegistries() {
70
+ return this.options.registries;
71
+ }
72
+ getModelPolicyWarnings() {
73
+ return [...this.modelPolicyWarnings, ...this.browserConfigWarnings];
74
+ }
75
+ /**
76
+ * True when `configuration.agent.browser` declares a provider. The
77
+ * EditorAgentBuilder does NOT verify the provider is registered with the
78
+ * Mastra instance — that cross-validation lives in `MastraEditor.resolveBuilder`
79
+ * because only the editor knows the registered browser providers.
80
+ */
81
+ hasValidBrowserConfig() {
82
+ const browserConfig = this.options.configuration?.agent?.browser;
83
+ return Boolean(browserConfig?.config?.provider);
84
+ }
85
+ /**
86
+ * Browser config validation only runs for **explicit** `browser: true`.
87
+ * With default-on semantics, an omitted `browser` no longer means "admin
88
+ * opted in" — it means "admin didn't opt out". The default-on path is
89
+ * resolved later by `resolveAgentFeatures`, which already gates `browser`
90
+ * on `hasValidBrowserConfig`. We don't want to spam every default-config
91
+ * deployment with warnings.
92
+ */
93
+ validateBrowserConfig() {
94
+ const explicitBrowser = this.options.features?.agent?.browser;
95
+ if (explicitBrowser !== true) return;
96
+ const browserConfig = this.options.configuration?.agent?.browser;
97
+ if (!browserConfig) {
98
+ const warning = 'Agent Builder browser feature is enabled but no default browser config was provided. Set `editor.builder.configuration.agent.browser` to a valid browser config (e.g. `{ type: "inline", config: { provider: "stagehand" } }`). The browser toggle will be hidden until a default is configured.';
99
+ this.browserConfigWarnings.push(warning);
100
+ console.warn(`[mastra:editor:builder] ${warning}`);
101
+ if (this.options.features?.agent) {
102
+ this.options.features.agent.browser = false;
103
+ }
104
+ return;
105
+ }
106
+ if (!browserConfig.config?.provider) {
107
+ const warning = 'Agent Builder browser config is missing a `provider` field. Set `editor.builder.configuration.agent.browser.config.provider` (e.g. `"stagehand"`). The browser toggle will be hidden until a provider is configured.';
108
+ this.browserConfigWarnings.push(warning);
109
+ console.warn(`[mastra:editor:builder] ${warning}`);
110
+ if (this.options.features?.agent) {
111
+ this.options.features.agent.browser = false;
112
+ }
113
+ }
114
+ }
115
+ validateModelPolicy() {
116
+ const enabled = this.options.enabled !== false;
117
+ const explicitModel = this.options.features?.agent?.model;
118
+ const pickerVisible = explicitModel !== false;
119
+ const models = this.options.configuration?.agent?.models;
120
+ const allowed = models?.allowed;
121
+ const defaultModel = models?.default;
122
+ const active = (0, import_ee.isBuilderModelPolicyActive)({
123
+ enabled,
124
+ pickerVisible,
125
+ allowed,
126
+ default: defaultModel
127
+ });
128
+ if (!active) return;
129
+ if (explicitModel === false && defaultModel === void 0) {
130
+ throw new Error(
131
+ "Agent Builder model policy is active in locked mode but no default was set. Set `editor.builder.configuration.agent.models.default`, or remove `editor.builder.features.agent.model = false` to allow end-users to pick a model."
132
+ );
133
+ }
134
+ if (defaultModel !== void 0 && allowed !== void 0 && allowed.length > 0) {
135
+ if (!(0, import_ee.isModelAllowed)(allowed, defaultModel)) {
136
+ throw new Error(
137
+ "Agent Builder default model is not in the allowlist. Either add it to `editor.builder.configuration.agent.models.allowed` or change `editor.builder.configuration.agent.models.default`."
138
+ );
139
+ }
140
+ }
141
+ }
142
+ };
143
+ }
144
+ });
145
+
146
+ // src/ee/agent-builder-agent.ts
147
+ function createBuilderAgent() {
148
+ const memory = new import_memory2.Memory();
149
+ return new import_agent3.Agent({
150
+ id: "builder-agent",
151
+ name: "Agent Builder Agent",
152
+ description: "An agent that can build agents",
153
+ instructions: `You are the Agent Builder.
154
+
155
+ Your job: turn a non-technical user's plain-language request into a fully configured, production-quality agent in a single turn.
156
+
157
+ # Non-negotiables
158
+
159
+ - Never ask the user follow-up questions. Make the most reasonable assumption and move forward.
160
+ - Never expose internal names, tool ids, file paths, schemas, code, or jargon to the user.
161
+ - Speak only in user-facing capability terms.
162
+ - Always finish the build in the same turn as the request \u2014 configure the agent end-to-end and deliver a short summary.
163
+ - Always define the new agent's name, description, model, and system prompt yourself. Do not ask the user for any of these.
164
+
165
+ Examples of communication style:
166
+ - Bad: "Added weatherTool to agent-yzx capabilities."
167
+ - Good: "Your new agent can now check the weather for you."
168
+ - Bad: "Calling set-agent-tools with [weatherTool]."
169
+ - Good: "Checking what capabilities to bring to your agent\u2026"
170
+ - Bad: "Agent created with weatherTool and recipeWorkflow attached."
171
+ - Good: "Your agent can check the weather and suggest recipes that match the day's conditions."
172
+
173
+ # Authoring loop
174
+
175
+ Follow these five steps in order, every time:
176
+
177
+ ## Step A \u2014 Understand the real outcome
178
+
179
+ Analyze what the user actually wants to achieve. Focus on the final result, not just the literal wording of the request.
180
+
181
+ Ask yourself:
182
+ - What should the agent help the user accomplish?
183
+ - Who will use this agent?
184
+ - What decisions should the agent make on its own?
185
+ - What kind of output should the agent produce?
186
+ - What recurring tasks, reasoning, or actions does the agent need to perform?
187
+
188
+ ## Step B \u2014 Define the agent's identity
189
+
190
+ Before building the agent, define:
191
+ - Agent name: short, memorable, anchored to the outcome. Never "Agent X" or generic labels
192
+ - Description: exactly one sentence in plain user-facing language explaining what the agent helps with.
193
+
194
+ Call \`set-agent-name\` and \`set-agent-description\` to set the agent's identity. Skip any whose feature is not available in the form snapshot.
195
+
196
+ ## Step C \u2014 Decide capabilities
197
+
198
+ Read the form snapshot already injected into your context. It lists the user's current selections plus the available tools, agents, workflows, stored skills, models, and workspaces.
199
+
200
+ Then:
201
+ - Pick the *minimum* set of existing tools/agents/workflows/stored skills that satisfies the outcome. Adding irrelevant capabilities makes the agent worse, not better.
202
+ - Prefer existing tools, workflows, agents, and stored skills before creating anything new.
203
+ - \`set-agent-skills\` attaches user-available stored skills from the form snapshot.
204
+ - Only call \`createSkillTool\` when (a) no existing stored skill matches reusable operating instructions the produced agent needs, AND (b) that operating instruction is genuinely needed for the outcome. Do not use stored skills as a substitute for missing integrations or tools.
205
+ - If a specific external connection is required (e.g. a sheet tool for a spreadsheet-driven outcome) and none is available, the new agent's system prompt must instruct it to refuse cleanly and explain what the user needs to connect.
206
+
207
+ ## Step D \u2014 Synthesize the run contract
208
+
209
+ Before calling \`set-agent-instructions\`, privately write a concrete run contract for the produced agent. The system prompt must instantiate each item:
210
+
211
+ 1. **Trigger / input** \u2014 what user request, schedule, event, file, row, ticket, or message starts a run.
212
+ 2. **Owned outcome** \u2014 the exact result the produced agent is responsible for finishing.
213
+ 3. **Available capabilities** \u2014 only capabilities actually attached or already available from the form snapshot, described in user-facing outcome terms.
214
+ 4. **Missing-capability fallback** \u2014 what the produced agent does when a required integration, workspace, credential, or source is absent.
215
+ 5. **Done criteria** \u2014 verifiable conditions that prove the job is finished, including tool confirmation or an explicit "not run" reason when verification is impossible.
216
+ 6. **Final response format** \u2014 the receipt, summary, draft, diff summary, report, or confirmation the user receives.
217
+
218
+ ## Step E \u2014 Write the agent
219
+
220
+ Call the capability tools. Skip any whose feature is not available in the form snapshot.
221
+
222
+ 1. \`set-agent-model\` \u2014 pick the best model for the use case from the available models list. Rules:
223
+ - Choose only a model id that appears in the available models list. Never invent, assume, or copy example model ids.
224
+ - For coding, reasoning-heavy, or planning agents, prefer the most capable available model.
225
+ - For short, simple, structured, or high-volume tasks, prefer a lower-latency/lower-cost available model when quality will not materially suffer.
226
+ - If several plausible models are available, choose the newest or strongest option based on the metadata visible in the snapshot.
227
+ 2. \`set-agent-tools\` \u2014 attach the minimum set chosen in Step C. Also use \`set-agent-skills\` and \`set-agent-browser-enabled\` only when applicable and supported by the snapshot.
228
+ 3. \`set-agent-instructions\` \u2014 write the new agent's system prompt from scratch, tailored to the user's specific outcome and the run contract from Step D.
229
+
230
+ Before calling \`set-agent-instructions\`, self-audit the draft. It must pass every check:
231
+ - No placeholders remain (no \`<...>\`, "TBD", "TODO", "your tool", or generic policy gaps).
232
+ - No internal tool ids, file paths, schemas, or builder-only terms appear.
233
+ - No generic "helpful assistant" identity remains.
234
+ - No unsupported capabilities are promised.
235
+ - Completion criteria are present, concrete, and tool-aware.
236
+ - Refusal / fallback path is present for missing integrations, credentials, permissions, workspace, or sources.
237
+ - Final response format is specified.
238
+
239
+ ## Step F \u2014 Confirm the agent configuration to the user
240
+
241
+ End your turn with one short, friendly paragraph confirming that the agent has been configured and is ready to use.
242
+
243
+ Use this shape:
244
+
245
+ "Your agent, [Agent Name], has been configured with its initial parameters. It can now [plain-language outcome]. You can adjust its instructions, inputs, or connected capabilities whenever your needs change."
246
+
247
+ Do not mention internal capability names, tools, workflows, skills, or configuration steps.
248
+
249
+ Good:
250
+ "Your agent, Sales Drop Watcher, has been configured with its initial parameters. It can now review your weekly sales sheet, flag accounts that dropped more than 10%, and prepare follow-up drafts for each one. You can adjust its instructions, thresholds, or connected data sources whenever your needs change."
251
+
252
+ Bad:
253
+ "Agent created with sheetsTool, scoringWorkflow, and emailSkill attached."
254
+
255
+ Bad:
256
+ "I configured the sheets integration and called set-agent-instructions."
257
+
258
+ # Quality bar for the produced agent's system prompt
259
+
260
+ The system prompt written into \`set-agent-instructions\` MUST include all of the following:
261
+
262
+ 1. **Role and outcome.** Define what the agent is and the concrete result it owns.
263
+ 2. **Trigger and input.** Define what starts a run and what input the agent expects.
264
+ 3. **Decision rules.** Explain how the agent resolves ambiguity, what defaults it should apply, and what it should skip without asking the user.
265
+ 4. **Capability awareness.** Describe only the tools, integrations, workspaces, or data sources the agent actually has, phrased in terms of what they let the agent accomplish.
266
+ 5. **Missing-capability fallback.** Explain what the agent should do when a required integration, credential, permission, workspace, or source is unavailable.
267
+ 6. **Completion criteria.** Define exactly when the task is done in observable, verifiable terms.
268
+ 7. **Final response format.** Specify the exact shape of the agent's final answer, report, draft, receipt, or confirmation.
269
+ 8. **Communication style.** Require plain language, short answers, no jargon, and structure only when useful.
270
+ 9. **Refusal rules.** State what the agent must refuse and how it should explain the refusal clearly.
271
+ 10. **Worked example.** Include at least one short input \u2192 output example showing a complete successful run.
272
+
273
+ # Hard rules
274
+
275
+ - If the user's request requires CLI or local-machine actions and no workspace is connected, refuse in plain language and tell the user they need to connect a workspace first.
276
+ - Never reveal that you are calling configuration tools. Describe progress only in terms of the user's intended outcome.
277
+ - Never produce a system prompt without explicit completion criteria.
278
+ - Never attach a capability "just in case." Every tool, agent, workflow, or skill must directly support the requested outcome.
279
+ - The final message to the user must be concise, friendly, and focused on what the configured agent can now do.
280
+ - The final message should make clear that the agent starts with initial parameters and can be adjusted later.`,
281
+ model: "openai/gpt-5.5",
282
+ memory
283
+ });
284
+ }
285
+ var import_agent3, import_memory2;
286
+ var init_agent_builder_agent = __esm({
287
+ "src/ee/agent-builder-agent.ts"() {
288
+ "use strict";
289
+ import_agent3 = require("@mastra/core/agent");
290
+ import_memory2 = require("@mastra/memory");
291
+ }
292
+ });
293
+
294
+ // src/ee/index.ts
295
+ var ee_exports = {};
296
+ __export(ee_exports, {
297
+ EditorAgentBuilder: () => EditorAgentBuilder,
298
+ createBuilderAgent: () => createBuilderAgent
299
+ });
300
+ var init_ee = __esm({
301
+ "src/ee/index.ts"() {
302
+ "use strict";
303
+ init_agent_builder();
304
+ init_agent_builder_agent();
305
+ }
306
+ });
307
+
30
308
  // src/index.ts
31
309
  var index_exports = {};
32
310
  __export(index_exports, {
@@ -567,6 +845,36 @@ var EditorMCPNamespace = class _EditorMCPNamespace extends CrudEditorNamespace {
567
845
  };
568
846
 
569
847
  // src/namespaces/agent.ts
848
+ var BUILDER_DEFAULT_FIELDS = ["memory", "workspace", "browser"];
849
+ function defaultModelToStored(entry) {
850
+ return { provider: entry.provider, name: entry.modelId };
851
+ }
852
+ var BUILDER_BASELINE_DEFAULTS = {
853
+ memory: { observationalMemory: true }
854
+ };
855
+ function applyBuilderDefaults(input, builderAgentConfig) {
856
+ const defaults = {};
857
+ for (const field of BUILDER_DEFAULT_FIELDS) {
858
+ if (input[field] !== void 0) continue;
859
+ const adminValue = builderAgentConfig?.[field];
860
+ if (adminValue !== void 0) {
861
+ defaults[field] = adminValue;
862
+ continue;
863
+ }
864
+ const baseline = BUILDER_BASELINE_DEFAULTS[field];
865
+ if (baseline !== void 0) {
866
+ defaults[field] = baseline;
867
+ }
868
+ }
869
+ if (input.model === void 0 && builderAgentConfig) {
870
+ const models = builderAgentConfig.models ?? void 0;
871
+ const adminDefault = models?.default;
872
+ if (adminDefault && typeof adminDefault.provider === "string" && typeof adminDefault.modelId === "string") {
873
+ defaults.model = defaultModelToStored(adminDefault);
874
+ }
875
+ }
876
+ return Object.keys(defaults).length > 0 ? { ...input, ...defaults } : input;
877
+ }
570
878
  var EditorAgentNamespace = class extends CrudEditorNamespace {
571
879
  async getStorageAdapter() {
572
880
  const storage = this.mastra?.getStorage();
@@ -609,6 +917,67 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
609
917
  async hydrate(storedAgent) {
610
918
  return this.createAgentFromStoredConfig(storedAgent);
611
919
  }
920
+ /**
921
+ * Create a new agent, applying builder defaults for fields not specified in input.
922
+ * Also ensures the referenced workspace (if any) is persisted as a stored workspace.
923
+ */
924
+ async create(input) {
925
+ let finalInput = input;
926
+ if (this.editor.hasEnabledBuilderConfig()) {
927
+ const builder = await this.editor.resolveBuilder();
928
+ const agentConfig = builder?.getConfiguration()?.agent;
929
+ finalInput = applyBuilderDefaults(input, agentConfig);
930
+ }
931
+ await this.ensureStoredWorkspace(finalInput.workspace);
932
+ return super.create(finalInput);
933
+ }
934
+ /**
935
+ * Ensure a workspace reference is persisted in the DB.
936
+ *
937
+ * For `type: 'id'`: looks up the runtime workspace, serializes its config,
938
+ * and creates a stored workspace record if one doesn't already exist.
939
+ *
940
+ * For `type: 'inline'`: derives a deterministic ID from the config and
941
+ * persists it as a stored workspace if one doesn't already exist.
942
+ */
943
+ async ensureStoredWorkspace(workspaceRef) {
944
+ if (!workspaceRef) return;
945
+ const workspaceNs = this.editor.workspace;
946
+ if (!workspaceNs) return;
947
+ try {
948
+ if (workspaceRef.type === "id") {
949
+ const existing = await workspaceNs.getById(workspaceRef.workspaceId);
950
+ if (existing) return;
951
+ const runtimeWorkspace = this.mastra?.getWorkspaceById(workspaceRef.workspaceId);
952
+ if (!runtimeWorkspace) {
953
+ this.logger?.warn(
954
+ `[ensureStoredWorkspace] Workspace '${workspaceRef.workspaceId}' not found in runtime registry, cannot persist`
955
+ );
956
+ return;
957
+ }
958
+ const snapshot = await workspaceNs.snapshotFromWorkspace(runtimeWorkspace);
959
+ await workspaceNs.create({
960
+ id: workspaceRef.workspaceId,
961
+ metadata: { source: "builder", builderWorkspaceId: workspaceRef.workspaceId },
962
+ ...snapshot
963
+ });
964
+ this.logger?.debug(`[ensureStoredWorkspace] Persisted runtime workspace '${workspaceRef.workspaceId}' to DB`);
965
+ } else if (workspaceRef.type === "inline") {
966
+ const configHash = (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(workspaceRef.config)).digest("hex").slice(0, 12);
967
+ const workspaceId = `inline-${configHash}`;
968
+ const existing = await workspaceNs.getById(workspaceId);
969
+ if (existing) return;
970
+ await workspaceNs.create({
971
+ id: workspaceId,
972
+ metadata: { source: "builder", builderConfigHash: configHash },
973
+ ...workspaceRef.config
974
+ });
975
+ this.logger?.debug(`[ensureStoredWorkspace] Persisted inline workspace '${workspaceId}' to DB`);
976
+ }
977
+ } catch (error) {
978
+ this.logger?.warn("[ensureStoredWorkspace] Failed to persist workspace", { error });
979
+ }
980
+ }
612
981
  onCacheEvict(id) {
613
982
  try {
614
983
  const existing = this.mastra?.getAgentById(id);
@@ -798,6 +1167,7 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
798
1167
  const hasConditionalDefaultOptions = storedAgent.defaultOptions != null && this.isConditionalVariants(storedAgent.defaultOptions);
799
1168
  const hasConditionalModel = this.isConditionalVariants(storedAgent.model);
800
1169
  const hasConditionalWorkspace = storedAgent.workspace != null && this.isConditionalVariants(storedAgent.workspace);
1170
+ const hasConditionalBrowser = storedAgent.browser != null && this.isConditionalVariants(storedAgent.browser);
801
1171
  const hasIntegrationTools = storedAgent.integrationTools != null;
802
1172
  const isDynamicTools = hasConditionalTools || hasConditionalMCPClients || hasConditionalIntegrationTools || hasIntegrationTools;
803
1173
  let tools;
@@ -964,6 +1334,14 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
964
1334
  );
965
1335
  return this.resolveStoredWorkspace(resolvedRef, skillSource);
966
1336
  } : await this.resolveStoredWorkspace(storedAgent.workspace, skillSource);
1337
+ const browser = hasConditionalBrowser ? async ({ requestContext }) => {
1338
+ const ctx = requestContext.toJSON();
1339
+ const resolvedRef = this.accumulateObjectVariants(
1340
+ storedAgent.browser,
1341
+ ctx
1342
+ );
1343
+ return this.resolveStoredBrowser(resolvedRef);
1344
+ } : await this.resolveStoredBrowser(storedAgent.browser);
967
1345
  const skillsFormat = storedAgent.skillsFormat;
968
1346
  const agent = new import_agent.Agent({
969
1347
  id: storedAgent.id,
@@ -984,6 +1362,7 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
984
1362
  defaultOptions,
985
1363
  requestContextSchema,
986
1364
  workspace,
1365
+ browser,
987
1366
  ...skillsFormat && { skillsFormat }
988
1367
  });
989
1368
  const existingCodeAgent = (() => {
@@ -1382,7 +1761,8 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1382
1761
  scorers: storedScorers,
1383
1762
  defaultOptions: storageDefaultOptions,
1384
1763
  metadata: resolvedMetadata,
1385
- authorId: options.authorId
1764
+ authorId: options.authorId,
1765
+ visibility: options.visibility
1386
1766
  };
1387
1767
  const adapter = await this.getStorageAdapter();
1388
1768
  await adapter.create(createInput);
@@ -1414,13 +1794,23 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1414
1794
  const hydrateOptions = skillSource ? { skillSource } : void 0;
1415
1795
  if (workspaceRef.type === "id") {
1416
1796
  const resolved = await workspaceNs.getById(workspaceRef.workspaceId);
1417
- if (!resolved) {
1418
- this.logger?.warn(
1419
- `[resolveStoredWorkspace] Workspace '${workspaceRef.workspaceId}' not found in storage, skipping`
1420
- );
1421
- return void 0;
1797
+ if (resolved) {
1798
+ return workspaceNs.hydrateSnapshotToWorkspace(workspaceRef.workspaceId, resolved, hydrateOptions);
1799
+ }
1800
+ try {
1801
+ const runtimeWorkspace = this.mastra?.getWorkspaceById(workspaceRef.workspaceId);
1802
+ if (runtimeWorkspace) {
1803
+ this.logger?.debug(
1804
+ `[resolveStoredWorkspace] Workspace '${workspaceRef.workspaceId}' found in runtime registry (not in DB)`
1805
+ );
1806
+ return runtimeWorkspace;
1807
+ }
1808
+ } catch {
1422
1809
  }
1423
- return workspaceNs.hydrateSnapshotToWorkspace(workspaceRef.workspaceId, resolved, hydrateOptions);
1810
+ this.logger?.warn(
1811
+ `[resolveStoredWorkspace] Workspace '${workspaceRef.workspaceId}' not found in storage or runtime registry, skipping`
1812
+ );
1813
+ return void 0;
1424
1814
  }
1425
1815
  if (workspaceRef.type === "inline") {
1426
1816
  const configHash = (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(workspaceRef.config)).digest("hex").slice(0, 12);
@@ -1428,6 +1818,26 @@ var EditorAgentNamespace = class extends CrudEditorNamespace {
1428
1818
  }
1429
1819
  return void 0;
1430
1820
  }
1821
+ /**
1822
+ * Resolve a stored browser config to a runtime MastraBrowser instance.
1823
+ * Looks up the provider by ID in the editor's browser registry.
1824
+ * Only supports `type: 'inline'` refs (config is embedded in the agent snapshot).
1825
+ */
1826
+ async resolveStoredBrowser(browserRef) {
1827
+ if (!browserRef) return void 0;
1828
+ if (browserRef.type === "inline") {
1829
+ const { provider: providerId, ...config } = browserRef.config;
1830
+ const browserProvider = this.editor.__browsers.get(providerId);
1831
+ if (!browserProvider) {
1832
+ this.logger?.warn(
1833
+ `[resolveStoredBrowser] Browser provider "${providerId}" is not registered. Register it via new MastraEditor({ browsers: { '${providerId}': yourProvider } })`
1834
+ );
1835
+ return void 0;
1836
+ }
1837
+ return await browserProvider.createBrowser(config);
1838
+ }
1839
+ return void 0;
1840
+ }
1431
1841
  /**
1432
1842
  * Resolve agent-level skill configurations into a CompositeVersionedSkillSource.
1433
1843
  *
@@ -2063,9 +2473,35 @@ var localSandboxProvider = {
2063
2473
  createSandbox: (config) => new import_workspace6.LocalSandbox(config)
2064
2474
  };
2065
2475
 
2476
+ // src/snapshots-match.ts
2477
+ function snapshotsMatch(stored, runtime) {
2478
+ const keys = [
2479
+ "name",
2480
+ "description",
2481
+ "filesystem",
2482
+ "sandbox",
2483
+ "mounts",
2484
+ "search",
2485
+ "skills",
2486
+ "tools",
2487
+ "autoSync",
2488
+ "operationTimeout"
2489
+ ];
2490
+ const replacer = (_k, v) => v === false || v === null || v === 0 ? void 0 : v;
2491
+ for (const key of keys) {
2492
+ const storedVal = stored[key];
2493
+ const runtimeVal = runtime[key];
2494
+ const storedJSON = storedVal == null || storedVal === false ? void 0 : JSON.stringify(storedVal, replacer);
2495
+ const runtimeJSON = runtimeVal == null || runtimeVal === false ? void 0 : JSON.stringify(runtimeVal, replacer);
2496
+ if (storedJSON !== runtimeJSON) return false;
2497
+ }
2498
+ return true;
2499
+ }
2500
+
2066
2501
  // src/index.ts
2067
2502
  var MastraEditor = class {
2068
2503
  constructor(config) {
2504
+ this.__builderResolved = false;
2069
2505
  this.__logger = config?.logger;
2070
2506
  this.__toolProviders = config?.toolProviders ?? {};
2071
2507
  this.__processorProviders = { ...import_processor_provider2.BUILT_IN_PROCESSOR_PROVIDERS, ...config?.processorProviders };
@@ -2083,6 +2519,10 @@ var MastraEditor = class {
2083
2519
  for (const [id, provider] of Object.entries(config?.blobStores ?? {})) {
2084
2520
  this.__blobStores.set(id, provider);
2085
2521
  }
2522
+ this.__browsers = /* @__PURE__ */ new Map();
2523
+ for (const [id, provider] of Object.entries(config?.browsers ?? {})) {
2524
+ this.__browsers.set(id, provider);
2525
+ }
2086
2526
  this.agent = new EditorAgentNamespace(this);
2087
2527
  this.mcp = new EditorMCPNamespace(this);
2088
2528
  this.mcpServer = new EditorMCPServerNamespace(this);
@@ -2091,6 +2531,7 @@ var MastraEditor = class {
2091
2531
  this.workspace = new EditorWorkspaceNamespace(this);
2092
2532
  this.skill = new EditorSkillNamespace(this);
2093
2533
  this.favorites = new EditorFavoritesNamespace(this);
2534
+ this.__builderConfig = config?.builder;
2094
2535
  }
2095
2536
  /**
2096
2537
  * Register this editor with a Mastra instance.
@@ -2101,11 +2542,171 @@ var MastraEditor = class {
2101
2542
  if (!this.__logger) {
2102
2543
  this.__logger = mastra.getLogger();
2103
2544
  }
2545
+ this.ensureBuilderWorkspaces().then(() => this.reconcileBuilderWorkspaces()).catch((err) => {
2546
+ this.__logger?.warn("[MastraEditor] Failed to persist/reconcile builder workspaces on startup", {
2547
+ error: err
2548
+ });
2549
+ });
2550
+ }
2551
+ /**
2552
+ * Ensure the builder default workspace is persisted to the DB.
2553
+ * Called automatically on startup when the editor registers with Mastra.
2554
+ * Goes through the normal create() path so hydration validates that
2555
+ * all providers (filesystem, sandbox) are properly registered.
2556
+ *
2557
+ * If the workspace already exists but its config has drifted from the
2558
+ * runtime workspace, the DB record is updated (creating a new version).
2559
+ * Builder-created workspaces are tagged with `metadata.source = 'builder'`
2560
+ * so they can be identified during reconciliation.
2561
+ */
2562
+ async ensureBuilderWorkspaces() {
2563
+ if (!this.hasEnabledBuilderConfig()) return;
2564
+ const builder = await this.resolveBuilder();
2565
+ const agentConfig = builder?.getConfiguration()?.agent;
2566
+ const workspaceRef = agentConfig?.workspace;
2567
+ if (!workspaceRef || workspaceRef.type !== "id" || !workspaceRef.workspaceId) return;
2568
+ const runtimeWorkspace = this.__mastra?.getWorkspaceById(workspaceRef.workspaceId);
2569
+ if (!runtimeWorkspace) return;
2570
+ const snapshot = await this.workspace.snapshotFromWorkspace(runtimeWorkspace);
2571
+ const builderMetadata = { source: "builder", builderWorkspaceId: workspaceRef.workspaceId };
2572
+ const existing = await this.workspace.getById(workspaceRef.workspaceId);
2573
+ if (!existing) {
2574
+ await this.workspace.create({
2575
+ id: workspaceRef.workspaceId,
2576
+ metadata: builderMetadata,
2577
+ ...snapshot
2578
+ });
2579
+ this.__logger?.info(`[MastraEditor] Persisted builder workspace '${workspaceRef.workspaceId}' to DB`);
2580
+ return;
2581
+ }
2582
+ const needsMetadataBackfill = !existing.metadata?.source;
2583
+ const configDrifted = !snapshotsMatch(existing, snapshot);
2584
+ if (needsMetadataBackfill || configDrifted) {
2585
+ const updateInput = { id: workspaceRef.workspaceId };
2586
+ if (needsMetadataBackfill) {
2587
+ updateInput.metadata = { ...existing.metadata, ...builderMetadata };
2588
+ }
2589
+ if (configDrifted) {
2590
+ Object.assign(updateInput, snapshot);
2591
+ this.__logger?.info(
2592
+ `[MastraEditor] Workspace '${workspaceRef.workspaceId}' config drifted \u2014 updating DB record`
2593
+ );
2594
+ }
2595
+ await this.workspace.update(updateInput);
2596
+ }
2597
+ }
2598
+ /**
2599
+ * Archive builder-created workspaces that no longer match the current
2600
+ * builder configuration. Called after `ensureBuilderWorkspaces()` on startup.
2601
+ *
2602
+ * Only touches workspaces tagged with `metadata.source === 'builder'`.
2603
+ * The current builder workspace (if any) is never archived.
2604
+ */
2605
+ async reconcileBuilderWorkspaces() {
2606
+ if (!this.hasEnabledBuilderConfig()) return;
2607
+ const builder = await this.resolveBuilder();
2608
+ const agentConfig = builder?.getConfiguration()?.agent;
2609
+ const workspaceRef = agentConfig?.workspace;
2610
+ let currentWorkspaceId;
2611
+ if (workspaceRef?.type === "id" && workspaceRef.workspaceId) {
2612
+ currentWorkspaceId = workspaceRef.workspaceId;
2613
+ }
2614
+ if (!currentWorkspaceId) return;
2615
+ const { workspaces: allWorkspaces } = await this.workspace.listResolved({
2616
+ perPage: false,
2617
+ // fetch all
2618
+ metadata: { source: "builder" }
2619
+ });
2620
+ for (const ws of allWorkspaces) {
2621
+ if (ws.id === currentWorkspaceId) continue;
2622
+ if (ws.status === "archived") continue;
2623
+ try {
2624
+ await this.workspace.update({ id: ws.id, status: "archived" });
2625
+ this.__logger?.info(`[MastraEditor] Archived orphaned builder workspace '${ws.id}'`);
2626
+ } catch (err) {
2627
+ this.__logger?.warn(`[MastraEditor] Failed to archive workspace '${ws.id}'`, { error: err });
2628
+ }
2629
+ }
2630
+ }
2631
+ /**
2632
+ * Sync. OSS-safe. Does NOT import @mastra/editor/ee.
2633
+ * Returns true if builder config is present and enabled.
2634
+ */
2635
+ hasEnabledBuilderConfig() {
2636
+ if (!this.__builderConfig) return false;
2637
+ return this.__builderConfig.enabled !== false;
2638
+ }
2639
+ /**
2640
+ * Async. Dynamic-imports @mastra/editor/ee on first call. Caches result.
2641
+ * Returns undefined if builder is not enabled.
2642
+ */
2643
+ async resolveBuilder() {
2644
+ if (this.__builderResolved) {
2645
+ return this.__builderInstance;
2646
+ }
2647
+ if (!this.hasEnabledBuilderConfig()) {
2648
+ this.__builderResolved = true;
2649
+ return void 0;
2650
+ }
2651
+ await this.assertAgentBuilderLicensed();
2652
+ const { EditorAgentBuilder: EditorAgentBuilder2 } = await Promise.resolve().then(() => (init_ee(), ee_exports));
2653
+ this.__builderInstance = new EditorAgentBuilder2(this.__builderConfig);
2654
+ const browserRef = this.__builderInstance.getConfiguration()?.agent?.browser;
2655
+ const browserFeatureOn = this.__builderInstance.getFeatures()?.agent?.browser === true;
2656
+ if (browserFeatureOn && browserRef?.config?.provider) {
2657
+ const providerId = browserRef.config.provider;
2658
+ if (!this.__browsers.has(providerId)) {
2659
+ 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 } })\`.`;
2660
+ console.warn(`[mastra:editor] ${warning}`);
2661
+ const features = this.__builderInstance.getFeatures()?.agent;
2662
+ if (features) {
2663
+ features.browser = false;
2664
+ }
2665
+ }
2666
+ }
2667
+ this.__builderResolved = true;
2668
+ return this.__builderInstance;
2669
+ }
2670
+ /**
2671
+ * Defense-in-depth license guard for the Agent Builder. Mirrors the
2672
+ * startup-time check in `MastraServer.validateAgentBuilderLicense()` so the
2673
+ * builder cannot be instantiated outside the server boot path without a
2674
+ * valid EE license. Dev environments bypass via `isEEEnabled()`.
2675
+ */
2676
+ async assertAgentBuilderLicensed() {
2677
+ try {
2678
+ const { isEEEnabled } = await import("@mastra/core/auth/ee");
2679
+ if (!isEEEnabled()) {
2680
+ throw new Error(
2681
+ "[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"
2682
+ );
2683
+ }
2684
+ } catch (err) {
2685
+ if (err instanceof Error && err.message.startsWith("[mastra/auth-ee]")) {
2686
+ throw err;
2687
+ }
2688
+ throw new Error(
2689
+ "[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."
2690
+ );
2691
+ }
2104
2692
  }
2105
2693
  /** Registered tool providers */
2106
2694
  getToolProvider(id) {
2107
2695
  return this.__toolProviders[id];
2108
2696
  }
2697
+ /**
2698
+ * Like {@link getToolProvider}, but throws {@link UnknownToolProviderError}
2699
+ * when the id is unknown.
2700
+ */
2701
+ getToolProviderOrThrow(id) {
2702
+ const provider = this.__toolProviders[id];
2703
+ if (!provider) {
2704
+ throw new Error(
2705
+ `Unknown tool provider "${id}". Available: ${Object.keys(this.__toolProviders).join(", ") || "(none)"}`
2706
+ );
2707
+ }
2708
+ return provider;
2709
+ }
2109
2710
  /** List all registered tool providers */
2110
2711
  getToolProviders() {
2111
2712
  return this.__toolProviders;
@@ -2178,3 +2779,4 @@ var MastraEditor = class {
2178
2779
  renderTemplate,
2179
2780
  resolveInstructionBlocks
2180
2781
  });
2782
+ //# sourceMappingURL=index.cjs.map