@mastra/factory 0.25.0-alpha.7 → 0.25.0

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.
@@ -8,6 +8,7 @@ import { factoryMemorySettingsUserId } from "../storage/domains/memory-settings/
8
8
  import { Route } from "./route.js";
9
9
  import { RequestContext } from "@mastra/core/request-context";
10
10
  import { registerApiRoute } from "@mastra/core/server";
11
+ import { MIN_OBSERVATION_THRESHOLD } from "@mastra/code-sdk/constants";
11
12
  import { z } from "zod";
12
13
  import { MASTRA_GATEWAY_PREFIX } from "@mastra/code-sdk/providers/model-ids";
13
14
  import { MastraCodeGateway } from "@mastra/code-sdk/agents/mastracode-gateway";
@@ -306,7 +307,7 @@ function readStoredOMConfig(record, currentModelId, autoModelSource, availablePr
306
307
  reflector: roleConfig(record?.reflectorModelId ?? "auto", reflectorModelId, autoModelSource, availableProviders),
307
308
  observerModelId,
308
309
  reflectorModelId,
309
- observationThreshold: record?.observationThreshold ?? 3e4,
310
+ observationThreshold: Math.max(record?.observationThreshold ?? 3e4, MIN_OBSERVATION_THRESHOLD),
310
311
  reflectionThreshold: record?.reflectionThreshold ?? 4e4,
311
312
  observeAttachments: record?.observeAttachments ?? "auto"
312
313
  };
@@ -381,12 +382,87 @@ var ConfigRoutes = class extends Route {
381
382
  const { controller, authStorage, auth, deploymentProviders } = options;
382
383
  const onCredentialsChanged = options.onCredentialsChanged ?? (() => {});
383
384
  const onCustomProvidersChanged = options.onCustomProvidersChanged ?? (() => {});
384
- const resolveOMResponseContext = async (c) => {
385
- const tenantCredentials = await listTenantCredentialsForRequest({
385
+ const resolveSessionModelContext = async (c, address, includeDefaultModel = true) => {
386
+ const tenant = auth.tenant(c);
387
+ if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
388
+ if (!tenant) return void 0;
389
+ if (!options.factoryProjects || !options.sourceControlSessions || !options.workItems || !options.modelCredentials) return { response: c.json({ error: "session_model_policy_unavailable" }, 503) };
390
+ const requestContext = new RequestContext();
391
+ requestContext.set("user", {
392
+ id: tenant.userId,
393
+ organizationId: tenantOrgId(tenant)
394
+ });
395
+ requestContext.set("controller", address);
396
+ let policy;
397
+ try {
398
+ policy = await resolveFactoryModelPolicy(requestContext, {
399
+ projects: options.factoryProjects,
400
+ sourceControl: options.sourceControlSessions,
401
+ workItems: options.workItems
402
+ });
403
+ } catch {
404
+ return { response: c.json({ error: "session_model_policy_unavailable" }, 403) };
405
+ }
406
+ if (!policy) return { response: c.json({ error: "unauthorized" }, 401) };
407
+ const orgId = policy.tenant.orgId ?? tenantOrgId(tenant);
408
+ const modelScope = policy.tenant.credentialScope ?? "user";
409
+ const tenantCredentials = await listSessionScopedCredentials({
410
+ credentials: options.modelCredentials,
411
+ orgId,
412
+ userId: policy.tenant.userId,
413
+ modelScope
414
+ });
415
+ let defaultModelId = policy.factoryDefaultModelId ?? null;
416
+ let personalThinkingLevel = null;
417
+ if (modelScope === "user" && includeDefaultModel) {
418
+ if (!options.modelDefaults) return { response: c.json({ error: "model_defaults_unavailable" }, 503) };
419
+ const ownerDefaults = await routedPersonalDefaults(policy.tenant, options.modelDefaults);
420
+ defaultModelId = ownerDefaults?.modelId ?? null;
421
+ personalThinkingLevel = ownerDefaults?.thinkingLevel ?? null;
422
+ }
423
+ return {
424
+ tenantCredentials,
425
+ modelScope,
426
+ defaultModelId,
427
+ personalThinkingLevel
428
+ };
429
+ };
430
+ const resolveOMResponseContext = async (c, { sessionId, threadId, resourceId, factoryProjectId }) => {
431
+ let tenantCredentials = await listTenantCredentialsForRequest({
386
432
  c: loose(c),
387
433
  auth,
388
434
  credentials: options.modelCredentials
389
435
  });
436
+ const tenant = auth.tenant(c);
437
+ if (sessionId !== void 0 && threadId !== void 0) {
438
+ const address = z.object({
439
+ sessionId: z.string().trim().min(1),
440
+ threadId: z.string().trim().min(1),
441
+ resourceId: z.string().trim().min(1).optional()
442
+ }).transform((data) => ({
443
+ ...data,
444
+ resourceId: data.resourceId ?? data.sessionId
445
+ })).safeParse({
446
+ sessionId,
447
+ threadId,
448
+ resourceId
449
+ });
450
+ if (!address.success) return { response: c.json({ error: "invalid_session_context" }, 400) };
451
+ const sessionContext = await resolveSessionModelContext(c, address.data, false);
452
+ if (sessionContext && "response" in sessionContext) return { response: sessionContext.response };
453
+ tenantCredentials = sessionContext?.tenantCredentials;
454
+ } else if (threadId !== void 0) return { response: c.json({ error: "invalid_session_context" }, 400) };
455
+ else if (tenantCredentials && factoryProjectId && tenant) {
456
+ if (!options.factoryProjects) return { response: c.json({ error: "factory_unavailable" }, 503) };
457
+ await options.factoryProjects.ensureReady();
458
+ const project = await options.factoryProjects.get({
459
+ orgId: tenantOrgId(tenant),
460
+ id: factoryProjectId
461
+ });
462
+ if (!project) return { response: c.json({ error: "factory_project_not_found" }, 404) };
463
+ const scope = effectiveModelScope(project.modelScope, "run") === "factory" ? "org" : "user";
464
+ tenantCredentials = tenantCredentials.filter((record) => record.scope === scope);
465
+ } else if (tenantCredentials) tenantCredentials = tenantCredentials.filter((record) => record.scope === "user");
390
466
  const access = await buildProviderAccess({
391
467
  controller,
392
468
  authStorage: tenantCredentials ? void 0 : authStorage,
@@ -403,7 +479,7 @@ var ConfigRoutes = class extends Route {
403
479
  });
404
480
  if (!("response" in context)) for (const provider of await context.storage.list({ orgId: context.orgId })) availableProviders.add(provider.providerId);
405
481
  }
406
- return availableProviders;
482
+ return { availableProviders };
407
483
  };
408
484
  const factoryOmFallback = async (factoryProjectId) => {
409
485
  try {
@@ -672,46 +748,14 @@ var ConfigRoutes = class extends Route {
672
748
  sessionId
673
749
  });
674
750
  if (!address.success) return c.json({ error: "invalid_session_context" }, 400);
675
- if (!tenant && auth.enabled()) return c.json({ error: "unauthorized" }, 401);
676
- if (tenant) {
677
- if (!options.factoryProjects || !options.sourceControlSessions || !options.workItems || !options.modelCredentials) return c.json({ error: "session_model_policy_unavailable" }, 503);
678
- const requestContext = new RequestContext();
679
- requestContext.set("user", {
680
- id: tenant.userId,
681
- organizationId: tenantOrgId(tenant)
682
- });
683
- requestContext.set("controller", address.data);
684
- let policy;
685
- try {
686
- policy = await resolveFactoryModelPolicy(requestContext, {
687
- projects: options.factoryProjects,
688
- sourceControl: options.sourceControlSessions,
689
- workItems: options.workItems
690
- });
691
- } catch {
692
- return c.json({ error: "session_model_policy_unavailable" }, 403);
693
- }
694
- if (!policy) return c.json({ error: "unauthorized" }, 401);
695
- const orgId = policy.tenant.orgId ?? tenantOrgId(tenant);
696
- const modelScope = policy.tenant.credentialScope ?? "user";
697
- tenantCredentials = await listSessionScopedCredentials({
698
- credentials: options.modelCredentials,
699
- orgId,
700
- userId: policy.tenant.userId,
701
- modelScope
702
- });
703
- let defaultModelId = policy.factoryDefaultModelId ?? null;
704
- let personalThinkingLevel = null;
705
- if (modelScope === "user") {
706
- if (!options.modelDefaults) return c.json({ error: "model_defaults_unavailable" }, 503);
707
- const ownerDefaults = await routedPersonalDefaults(policy.tenant, options.modelDefaults);
708
- defaultModelId = ownerDefaults?.modelId ?? null;
709
- personalThinkingLevel = ownerDefaults?.thinkingLevel ?? null;
710
- }
751
+ const context = await resolveSessionModelContext(c, address.data);
752
+ if (context && "response" in context) return context.response;
753
+ if (context) {
754
+ tenantCredentials = context.tenantCredentials;
711
755
  sessionPolicy = {
712
- modelScope,
713
- defaultModelId,
714
- personalThinkingLevel
756
+ modelScope: context.modelScope,
757
+ defaultModelId: context.defaultModelId,
758
+ personalThinkingLevel: context.personalThinkingLevel
715
759
  };
716
760
  }
717
761
  } else if (c.req.query("scope") !== void 0) {
@@ -972,6 +1016,8 @@ var ConfigRoutes = class extends Route {
972
1016
  requiresAuth: false,
973
1017
  handler: async (c) => {
974
1018
  const sessionId = c.req.query("sessionId");
1019
+ const threadId = c.req.query("threadId");
1020
+ const resourceId = c.req.query("resourceId");
975
1021
  const factoryProjectId = c.req.query("factoryId") || void 0;
976
1022
  const context = await resolveMemorySettingsContext({
977
1023
  c: loose(c),
@@ -987,9 +1033,15 @@ var ConfigRoutes = class extends Route {
987
1033
  userId: context.userId
988
1034
  });
989
1035
  const fallback = await factoryOmFallback(factoryProjectId);
990
- const availableProviders = await resolveOMResponseContext(loose(c));
1036
+ const responseContext = await resolveOMResponseContext(loose(c), {
1037
+ sessionId,
1038
+ threadId,
1039
+ resourceId,
1040
+ factoryProjectId
1041
+ });
1042
+ if ("response" in responseContext) return responseContext.response;
991
1043
  const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
992
- return c.json({ config: readStoredOMConfig(record, sessionModelId || fallback, sessionModelId ? "live-session" : "configured-default", availableProviders) });
1044
+ return c.json({ config: readStoredOMConfig(record, sessionModelId || fallback, sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders) });
993
1045
  } catch (error) {
994
1046
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
995
1047
  }
@@ -1007,7 +1059,9 @@ var ConfigRoutes = class extends Route {
1007
1059
  } catch {
1008
1060
  return c.json({ error: "Invalid JSON body" }, 400);
1009
1061
  }
1010
- const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
1062
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
1063
+ const threadId = typeof body.threadId === "string" ? body.threadId : void 0;
1064
+ const resourceId = typeof body.resourceId === "string" ? body.resourceId : void 0;
1011
1065
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
1012
1066
  const model = typeof body.modelId === "string" ? body.modelId.trim() : "";
1013
1067
  if (!model) return c.json({ error: "Missing required field: modelId" }, 400);
@@ -1020,7 +1074,13 @@ var ConfigRoutes = class extends Route {
1020
1074
  factoryProjects: options.factoryProjects
1021
1075
  });
1022
1076
  if ("response" in context) return context.response;
1023
- const availableProviders = await resolveOMResponseContext(loose(c));
1077
+ const responseContext = await resolveOMResponseContext(loose(c), {
1078
+ sessionId,
1079
+ threadId,
1080
+ resourceId,
1081
+ factoryProjectId
1082
+ });
1083
+ if ("response" in responseContext) return responseContext.response;
1024
1084
  try {
1025
1085
  await persistMemorySettings(context, { [role === "observer" ? "observerModelId" : "reflectorModelId"]: wantsAuto ? null : model });
1026
1086
  const record = await context.storage.get({
@@ -1028,7 +1088,7 @@ var ConfigRoutes = class extends Route {
1028
1088
  userId: context.userId
1029
1089
  });
1030
1090
  const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
1031
- const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", availableProviders);
1091
+ const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders);
1032
1092
  return c.json({
1033
1093
  ok: true,
1034
1094
  config
@@ -1048,11 +1108,14 @@ var ConfigRoutes = class extends Route {
1048
1108
  } catch {
1049
1109
  return c.json({ error: "Invalid JSON body" }, 400);
1050
1110
  }
1051
- const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
1111
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
1112
+ const threadId = typeof body.threadId === "string" ? body.threadId : void 0;
1113
+ const resourceId = typeof body.resourceId === "string" ? body.resourceId : void 0;
1052
1114
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
1053
1115
  const observation = typeof body.observationThreshold === "number" && body.observationThreshold > 0 ? Math.round(body.observationThreshold) : void 0;
1054
1116
  const reflection = typeof body.reflectionThreshold === "number" && body.reflectionThreshold > 0 ? Math.round(body.reflectionThreshold) : void 0;
1055
1117
  if (observation === void 0 && reflection === void 0) return c.json({ error: "Provide observationThreshold and/or reflectionThreshold (positive numbers)" }, 400);
1118
+ if (observation !== void 0 && observation < MIN_OBSERVATION_THRESHOLD) return c.json({ error: `observationThreshold must be greater than ${MIN_OBSERVATION_THRESHOLD - 1} tokens` }, 400);
1056
1119
  const context = await resolveMemorySettingsContext({
1057
1120
  c: loose(c),
1058
1121
  auth,
@@ -1061,7 +1124,13 @@ var ConfigRoutes = class extends Route {
1061
1124
  factoryProjects: options.factoryProjects
1062
1125
  });
1063
1126
  if ("response" in context) return context.response;
1064
- const availableProviders = await resolveOMResponseContext(loose(c));
1127
+ const responseContext = await resolveOMResponseContext(loose(c), {
1128
+ sessionId,
1129
+ threadId,
1130
+ resourceId,
1131
+ factoryProjectId
1132
+ });
1133
+ if ("response" in responseContext) return responseContext.response;
1065
1134
  try {
1066
1135
  await persistMemorySettings(context, {
1067
1136
  ...observation !== void 0 ? { observationThreshold: observation } : {},
@@ -1072,7 +1141,7 @@ var ConfigRoutes = class extends Route {
1072
1141
  userId: context.userId
1073
1142
  });
1074
1143
  const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
1075
- const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", availableProviders);
1144
+ const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders);
1076
1145
  return c.json({
1077
1146
  ok: true,
1078
1147
  config
@@ -1092,7 +1161,9 @@ var ConfigRoutes = class extends Route {
1092
1161
  } catch {
1093
1162
  return c.json({ error: "Invalid JSON body" }, 400);
1094
1163
  }
1095
- const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
1164
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
1165
+ const threadId = typeof body.threadId === "string" ? body.threadId : void 0;
1166
+ const resourceId = typeof body.resourceId === "string" ? body.resourceId : void 0;
1096
1167
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
1097
1168
  const raw = body.value;
1098
1169
  const value = raw === "auto" || raw === true || raw === false ? raw : "auto";
@@ -1105,7 +1176,13 @@ var ConfigRoutes = class extends Route {
1105
1176
  factoryProjects: options.factoryProjects
1106
1177
  });
1107
1178
  if ("response" in context) return context.response;
1108
- const availableProviders = await resolveOMResponseContext(loose(c));
1179
+ const responseContext = await resolveOMResponseContext(loose(c), {
1180
+ sessionId,
1181
+ threadId,
1182
+ resourceId,
1183
+ factoryProjectId
1184
+ });
1185
+ if ("response" in responseContext) return responseContext.response;
1109
1186
  try {
1110
1187
  await persistMemorySettings(context, { observeAttachments: value });
1111
1188
  const record = await context.storage.get({
@@ -1113,7 +1190,7 @@ var ConfigRoutes = class extends Route {
1113
1190
  userId: context.userId
1114
1191
  });
1115
1192
  const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
1116
- const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", availableProviders);
1193
+ const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders);
1117
1194
  return c.json({
1118
1195
  ok: true,
1119
1196
  config