@mastra/factory 0.25.0-alpha.8 → 0.26.0-alpha.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.
Files changed (34) hide show
  1. package/dist/factory.d.ts +1 -1
  2. package/dist/factory.d.ts.map +1 -1
  3. package/dist/factory.js +27 -3
  4. package/dist/factory.js.map +1 -1
  5. package/dist/integrations/slack/integration.d.ts +7 -8
  6. package/dist/integrations/slack/integration.d.ts.map +1 -1
  7. package/dist/integrations/slack/integration.js +11 -3
  8. package/dist/integrations/slack/integration.js.map +1 -1
  9. package/dist/integrations/slack/run-error-card.d.ts.map +1 -1
  10. package/dist/integrations/slack/run-error-card.js +25 -8
  11. package/dist/integrations/slack/run-error-card.js.map +1 -1
  12. package/dist/integrations/slack/slack.d.ts +3 -1
  13. package/dist/integrations/slack/slack.d.ts.map +1 -1
  14. package/dist/integrations/slack/slack.js.map +1 -1
  15. package/dist/routes/config.d.ts +4 -1
  16. package/dist/routes/config.d.ts.map +1 -1
  17. package/dist/routes/config.js +153 -56
  18. package/dist/routes/config.js.map +1 -1
  19. package/dist/routes/provider-credentials.d.ts +6 -1
  20. package/dist/routes/provider-credentials.d.ts.map +1 -1
  21. package/dist/routes/provider-credentials.js +2 -2
  22. package/dist/routes/provider-credentials.js.map +1 -1
  23. package/dist/run-error.d.ts +1 -1
  24. package/dist/run-error.d.ts.map +1 -1
  25. package/dist/run-error.js +16 -11
  26. package/dist/run-error.js.map +1 -1
  27. package/dist/session/reply-credential-processor.d.ts +8 -0
  28. package/dist/session/reply-credential-processor.d.ts.map +1 -1
  29. package/dist/session/reply-credential-processor.js +20 -11
  30. package/dist/session/reply-credential-processor.js.map +1 -1
  31. package/dist/storage/domains/audit/domain.d.ts.map +1 -1
  32. package/dist/storage/domains/audit/domain.js +10 -3
  33. package/dist/storage/domains/audit/domain.js.map +1 -1
  34. package/package.json +15 -15
@@ -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";
@@ -29,7 +30,7 @@ function loose(c) {
29
30
  * *caller's* tenant rows with user > org precedence and the server-global
30
31
  * `authStorage` is ignored; otherwise the local `auth.json` view is reported.
31
32
  */
32
- async function listProviders({ controller, authStorage, tenantCredentials, deploymentProviders }) {
33
+ async function listProviders({ controller, authStorage, tenantCredentials, deploymentProviders, listCustomProviders }) {
33
34
  const models = await controller.listAvailableModels();
34
35
  const seen = /* @__PURE__ */ new Map();
35
36
  for (const model of models) {
@@ -80,6 +81,16 @@ async function listProviders({ controller, authStorage, tenantCredentials, deplo
80
81
  } } : {}
81
82
  });
82
83
  }
84
+ if (listCustomProviders) try {
85
+ for (const record of await listCustomProviders()) {
86
+ if (seen.has(record.providerId)) continue;
87
+ seen.set(record.providerId, {
88
+ provider: record.providerId,
89
+ source: tenantCredentials ? "stored-org" : "stored",
90
+ custom: true
91
+ });
92
+ }
93
+ } catch {}
83
94
  return Array.from(seen.values()).sort((a, b) => a.provider.localeCompare(b.provider));
84
95
  }
85
96
  /** Redact a stored custom-provider row for the client (key presence only). */
@@ -306,7 +317,7 @@ function readStoredOMConfig(record, currentModelId, autoModelSource, availablePr
306
317
  reflector: roleConfig(record?.reflectorModelId ?? "auto", reflectorModelId, autoModelSource, availableProviders),
307
318
  observerModelId,
308
319
  reflectorModelId,
309
- observationThreshold: record?.observationThreshold ?? 3e4,
320
+ observationThreshold: Math.max(record?.observationThreshold ?? 3e4, MIN_OBSERVATION_THRESHOLD),
310
321
  reflectionThreshold: record?.reflectionThreshold ?? 4e4,
311
322
  observeAttachments: record?.observeAttachments ?? "auto"
312
323
  };
@@ -381,12 +392,87 @@ var ConfigRoutes = class extends Route {
381
392
  const { controller, authStorage, auth, deploymentProviders } = options;
382
393
  const onCredentialsChanged = options.onCredentialsChanged ?? (() => {});
383
394
  const onCustomProvidersChanged = options.onCustomProvidersChanged ?? (() => {});
384
- const resolveOMResponseContext = async (c) => {
385
- const tenantCredentials = await listTenantCredentialsForRequest({
395
+ const resolveSessionModelContext = async (c, address, includeDefaultModel = true) => {
396
+ const tenant = auth.tenant(c);
397
+ if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
398
+ if (!tenant) return void 0;
399
+ if (!options.factoryProjects || !options.sourceControlSessions || !options.workItems || !options.modelCredentials) return { response: c.json({ error: "session_model_policy_unavailable" }, 503) };
400
+ const requestContext = new RequestContext();
401
+ requestContext.set("user", {
402
+ id: tenant.userId,
403
+ organizationId: tenantOrgId(tenant)
404
+ });
405
+ requestContext.set("controller", address);
406
+ let policy;
407
+ try {
408
+ policy = await resolveFactoryModelPolicy(requestContext, {
409
+ projects: options.factoryProjects,
410
+ sourceControl: options.sourceControlSessions,
411
+ workItems: options.workItems
412
+ });
413
+ } catch {
414
+ return { response: c.json({ error: "session_model_policy_unavailable" }, 403) };
415
+ }
416
+ if (!policy) return { response: c.json({ error: "unauthorized" }, 401) };
417
+ const orgId = policy.tenant.orgId ?? tenantOrgId(tenant);
418
+ const modelScope = policy.tenant.credentialScope ?? "user";
419
+ const tenantCredentials = await listSessionScopedCredentials({
420
+ credentials: options.modelCredentials,
421
+ orgId,
422
+ userId: policy.tenant.userId,
423
+ modelScope
424
+ });
425
+ let defaultModelId = policy.factoryDefaultModelId ?? null;
426
+ let personalThinkingLevel = null;
427
+ if (modelScope === "user" && includeDefaultModel) {
428
+ if (!options.modelDefaults) return { response: c.json({ error: "model_defaults_unavailable" }, 503) };
429
+ const ownerDefaults = await routedPersonalDefaults(policy.tenant, options.modelDefaults);
430
+ defaultModelId = ownerDefaults?.modelId ?? null;
431
+ personalThinkingLevel = ownerDefaults?.thinkingLevel ?? null;
432
+ }
433
+ return {
434
+ tenantCredentials,
435
+ modelScope,
436
+ defaultModelId,
437
+ personalThinkingLevel
438
+ };
439
+ };
440
+ const resolveOMResponseContext = async (c, { sessionId, threadId, resourceId, factoryProjectId }) => {
441
+ let tenantCredentials = await listTenantCredentialsForRequest({
386
442
  c: loose(c),
387
443
  auth,
388
444
  credentials: options.modelCredentials
389
445
  });
446
+ const tenant = auth.tenant(c);
447
+ if (sessionId !== void 0 && threadId !== void 0) {
448
+ const address = z.object({
449
+ sessionId: z.string().trim().min(1),
450
+ threadId: z.string().trim().min(1),
451
+ resourceId: z.string().trim().min(1).optional()
452
+ }).transform((data) => ({
453
+ ...data,
454
+ resourceId: data.resourceId ?? data.sessionId
455
+ })).safeParse({
456
+ sessionId,
457
+ threadId,
458
+ resourceId
459
+ });
460
+ if (!address.success) return { response: c.json({ error: "invalid_session_context" }, 400) };
461
+ const sessionContext = await resolveSessionModelContext(c, address.data, false);
462
+ if (sessionContext && "response" in sessionContext) return { response: sessionContext.response };
463
+ tenantCredentials = sessionContext?.tenantCredentials;
464
+ } else if (threadId !== void 0) return { response: c.json({ error: "invalid_session_context" }, 400) };
465
+ else if (tenantCredentials && factoryProjectId && tenant) {
466
+ if (!options.factoryProjects) return { response: c.json({ error: "factory_unavailable" }, 503) };
467
+ await options.factoryProjects.ensureReady();
468
+ const project = await options.factoryProjects.get({
469
+ orgId: tenantOrgId(tenant),
470
+ id: factoryProjectId
471
+ });
472
+ if (!project) return { response: c.json({ error: "factory_project_not_found" }, 404) };
473
+ const scope = effectiveModelScope(project.modelScope, "run") === "factory" ? "org" : "user";
474
+ tenantCredentials = tenantCredentials.filter((record) => record.scope === scope);
475
+ } else if (tenantCredentials) tenantCredentials = tenantCredentials.filter((record) => record.scope === "user");
390
476
  const access = await buildProviderAccess({
391
477
  controller,
392
478
  authStorage: tenantCredentials ? void 0 : authStorage,
@@ -403,7 +489,7 @@ var ConfigRoutes = class extends Route {
403
489
  });
404
490
  if (!("response" in context)) for (const provider of await context.storage.list({ orgId: context.orgId })) availableProviders.add(provider.providerId);
405
491
  }
406
- return availableProviders;
492
+ return { availableProviders };
407
493
  };
408
494
  const factoryOmFallback = async (factoryProjectId) => {
409
495
  try {
@@ -433,12 +519,22 @@ var ConfigRoutes = class extends Route {
433
519
  });
434
520
  const tenant = auth.tenant(loose(c));
435
521
  const orgKeyAdmin = tenant ? await auth.isOrganizationAdmin(loose(c), tenantOrgId(tenant)) : void 0;
522
+ const customProviders = options.customProviders;
523
+ const listCustomProviders = customProviders ? async () => {
524
+ const ctx = await resolveCustomProvidersContext({
525
+ c: loose(c),
526
+ auth,
527
+ customProviders
528
+ });
529
+ return "response" in ctx ? [] : ctx.storage.list({ orgId: ctx.orgId });
530
+ } : void 0;
436
531
  return c.json({
437
532
  providers: await listProviders({
438
533
  controller,
439
534
  authStorage: tenantCredentials ? void 0 : authStorage,
440
535
  tenantCredentials,
441
- deploymentProviders
536
+ deploymentProviders,
537
+ listCustomProviders
442
538
  }),
443
539
  ...orgKeyAdmin !== void 0 ? { orgKeyAdmin } : {}
444
540
  });
@@ -672,46 +768,14 @@ var ConfigRoutes = class extends Route {
672
768
  sessionId
673
769
  });
674
770
  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
- }
771
+ const context = await resolveSessionModelContext(c, address.data);
772
+ if (context && "response" in context) return context.response;
773
+ if (context) {
774
+ tenantCredentials = context.tenantCredentials;
711
775
  sessionPolicy = {
712
- modelScope,
713
- defaultModelId,
714
- personalThinkingLevel
776
+ modelScope: context.modelScope,
777
+ defaultModelId: context.defaultModelId,
778
+ personalThinkingLevel: context.personalThinkingLevel
715
779
  };
716
780
  }
717
781
  } else if (c.req.query("scope") !== void 0) {
@@ -972,6 +1036,8 @@ var ConfigRoutes = class extends Route {
972
1036
  requiresAuth: false,
973
1037
  handler: async (c) => {
974
1038
  const sessionId = c.req.query("sessionId");
1039
+ const threadId = c.req.query("threadId");
1040
+ const resourceId = c.req.query("resourceId");
975
1041
  const factoryProjectId = c.req.query("factoryId") || void 0;
976
1042
  const context = await resolveMemorySettingsContext({
977
1043
  c: loose(c),
@@ -987,9 +1053,15 @@ var ConfigRoutes = class extends Route {
987
1053
  userId: context.userId
988
1054
  });
989
1055
  const fallback = await factoryOmFallback(factoryProjectId);
990
- const availableProviders = await resolveOMResponseContext(loose(c));
1056
+ const responseContext = await resolveOMResponseContext(loose(c), {
1057
+ sessionId,
1058
+ threadId,
1059
+ resourceId,
1060
+ factoryProjectId
1061
+ });
1062
+ if ("response" in responseContext) return responseContext.response;
991
1063
  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) });
1064
+ return c.json({ config: readStoredOMConfig(record, sessionModelId || fallback, sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders) });
993
1065
  } catch (error) {
994
1066
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
995
1067
  }
@@ -1007,7 +1079,9 @@ var ConfigRoutes = class extends Route {
1007
1079
  } catch {
1008
1080
  return c.json({ error: "Invalid JSON body" }, 400);
1009
1081
  }
1010
- const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
1082
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
1083
+ const threadId = typeof body.threadId === "string" ? body.threadId : void 0;
1084
+ const resourceId = typeof body.resourceId === "string" ? body.resourceId : void 0;
1011
1085
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
1012
1086
  const model = typeof body.modelId === "string" ? body.modelId.trim() : "";
1013
1087
  if (!model) return c.json({ error: "Missing required field: modelId" }, 400);
@@ -1020,7 +1094,13 @@ var ConfigRoutes = class extends Route {
1020
1094
  factoryProjects: options.factoryProjects
1021
1095
  });
1022
1096
  if ("response" in context) return context.response;
1023
- const availableProviders = await resolveOMResponseContext(loose(c));
1097
+ const responseContext = await resolveOMResponseContext(loose(c), {
1098
+ sessionId,
1099
+ threadId,
1100
+ resourceId,
1101
+ factoryProjectId
1102
+ });
1103
+ if ("response" in responseContext) return responseContext.response;
1024
1104
  try {
1025
1105
  await persistMemorySettings(context, { [role === "observer" ? "observerModelId" : "reflectorModelId"]: wantsAuto ? null : model });
1026
1106
  const record = await context.storage.get({
@@ -1028,7 +1108,7 @@ var ConfigRoutes = class extends Route {
1028
1108
  userId: context.userId
1029
1109
  });
1030
1110
  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);
1111
+ const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders);
1032
1112
  return c.json({
1033
1113
  ok: true,
1034
1114
  config
@@ -1048,11 +1128,14 @@ var ConfigRoutes = class extends Route {
1048
1128
  } catch {
1049
1129
  return c.json({ error: "Invalid JSON body" }, 400);
1050
1130
  }
1051
- const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
1131
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
1132
+ const threadId = typeof body.threadId === "string" ? body.threadId : void 0;
1133
+ const resourceId = typeof body.resourceId === "string" ? body.resourceId : void 0;
1052
1134
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
1053
1135
  const observation = typeof body.observationThreshold === "number" && body.observationThreshold > 0 ? Math.round(body.observationThreshold) : void 0;
1054
1136
  const reflection = typeof body.reflectionThreshold === "number" && body.reflectionThreshold > 0 ? Math.round(body.reflectionThreshold) : void 0;
1055
1137
  if (observation === void 0 && reflection === void 0) return c.json({ error: "Provide observationThreshold and/or reflectionThreshold (positive numbers)" }, 400);
1138
+ if (observation !== void 0 && observation < MIN_OBSERVATION_THRESHOLD) return c.json({ error: `observationThreshold must be greater than ${MIN_OBSERVATION_THRESHOLD - 1} tokens` }, 400);
1056
1139
  const context = await resolveMemorySettingsContext({
1057
1140
  c: loose(c),
1058
1141
  auth,
@@ -1061,7 +1144,13 @@ var ConfigRoutes = class extends Route {
1061
1144
  factoryProjects: options.factoryProjects
1062
1145
  });
1063
1146
  if ("response" in context) return context.response;
1064
- const availableProviders = await resolveOMResponseContext(loose(c));
1147
+ const responseContext = await resolveOMResponseContext(loose(c), {
1148
+ sessionId,
1149
+ threadId,
1150
+ resourceId,
1151
+ factoryProjectId
1152
+ });
1153
+ if ("response" in responseContext) return responseContext.response;
1065
1154
  try {
1066
1155
  await persistMemorySettings(context, {
1067
1156
  ...observation !== void 0 ? { observationThreshold: observation } : {},
@@ -1072,7 +1161,7 @@ var ConfigRoutes = class extends Route {
1072
1161
  userId: context.userId
1073
1162
  });
1074
1163
  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);
1164
+ const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders);
1076
1165
  return c.json({
1077
1166
  ok: true,
1078
1167
  config
@@ -1092,7 +1181,9 @@ var ConfigRoutes = class extends Route {
1092
1181
  } catch {
1093
1182
  return c.json({ error: "Invalid JSON body" }, 400);
1094
1183
  }
1095
- const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
1184
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
1185
+ const threadId = typeof body.threadId === "string" ? body.threadId : void 0;
1186
+ const resourceId = typeof body.resourceId === "string" ? body.resourceId : void 0;
1096
1187
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
1097
1188
  const raw = body.value;
1098
1189
  const value = raw === "auto" || raw === true || raw === false ? raw : "auto";
@@ -1105,7 +1196,13 @@ var ConfigRoutes = class extends Route {
1105
1196
  factoryProjects: options.factoryProjects
1106
1197
  });
1107
1198
  if ("response" in context) return context.response;
1108
- const availableProviders = await resolveOMResponseContext(loose(c));
1199
+ const responseContext = await resolveOMResponseContext(loose(c), {
1200
+ sessionId,
1201
+ threadId,
1202
+ resourceId,
1203
+ factoryProjectId
1204
+ });
1205
+ if ("response" in responseContext) return responseContext.response;
1109
1206
  try {
1110
1207
  await persistMemorySettings(context, { observeAttachments: value });
1111
1208
  const record = await context.storage.get({
@@ -1113,7 +1210,7 @@ var ConfigRoutes = class extends Route {
1113
1210
  userId: context.userId
1114
1211
  });
1115
1212
  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);
1213
+ const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", responseContext.availableProviders);
1117
1214
  return c.json({
1118
1215
  ok: true,
1119
1216
  config