@mastra/factory 0.21.0-alpha.2 → 0.21.0-alpha.4

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 (85) hide show
  1. package/dist/auth.d.ts +7 -12
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +20 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/factory.d.ts +9 -0
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +56 -23
  8. package/dist/factory.js.map +1 -1
  9. package/dist/integrations/github/routes.js +1 -0
  10. package/dist/integrations/github/routes.js.map +1 -1
  11. package/dist/integrations/linear/issue-reconciler.d.ts +3 -1
  12. package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -1
  13. package/dist/integrations/linear/issue-reconciler.js +1 -1
  14. package/dist/integrations/linear/issue-reconciler.js.map +1 -1
  15. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  16. package/dist/integrations/platform/linear/integration.js +10 -9
  17. package/dist/integrations/platform/linear/integration.js.map +1 -1
  18. package/dist/integrations/slack/integration.d.ts.map +1 -1
  19. package/dist/integrations/slack/integration.js +0 -1
  20. package/dist/integrations/slack/integration.js.map +1 -1
  21. package/dist/integrations/slack/slack.d.ts +0 -17
  22. package/dist/integrations/slack/slack.d.ts.map +1 -1
  23. package/dist/integrations/slack/slack.js +8 -40
  24. package/dist/integrations/slack/slack.js.map +1 -1
  25. package/dist/packages/_internals/workspace/dist/index.js +3 -0
  26. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  27. package/dist/routes/config.d.ts +15 -36
  28. package/dist/routes/config.d.ts.map +1 -1
  29. package/dist/routes/config.js +72 -123
  30. package/dist/routes/config.js.map +1 -1
  31. package/dist/routes/knowledge.d.ts +115 -21
  32. package/dist/routes/knowledge.d.ts.map +1 -1
  33. package/dist/routes/knowledge.js +715 -30
  34. package/dist/routes/knowledge.js.map +1 -1
  35. package/dist/routes/oauth.d.ts +0 -3
  36. package/dist/routes/oauth.d.ts.map +1 -1
  37. package/dist/routes/oauth.js +2 -10
  38. package/dist/routes/oauth.js.map +1 -1
  39. package/dist/routes/provider-credentials.d.ts.map +1 -1
  40. package/dist/routes/provider-credentials.js +2 -1
  41. package/dist/routes/provider-credentials.js.map +1 -1
  42. package/dist/routes/surface.d.ts +1 -0
  43. package/dist/routes/surface.d.ts.map +1 -1
  44. package/dist/routes/surface.js +6 -3
  45. package/dist/routes/surface.js.map +1 -1
  46. package/dist/rules/binding-context.d.ts +19 -0
  47. package/dist/rules/binding-context.d.ts.map +1 -1
  48. package/dist/rules/binding-context.js +39 -1
  49. package/dist/rules/binding-context.js.map +1 -1
  50. package/dist/rules/dispatcher.d.ts +2 -13
  51. package/dist/rules/dispatcher.d.ts.map +1 -1
  52. package/dist/rules/dispatcher.js +0 -6
  53. package/dist/rules/dispatcher.js.map +1 -1
  54. package/dist/rules/processor.d.ts +35 -1
  55. package/dist/rules/processor.d.ts.map +1 -1
  56. package/dist/rules/processor.js +80 -4
  57. package/dist/rules/processor.js.map +1 -1
  58. package/dist/rules/start-coordinator.d.ts +1 -2
  59. package/dist/rules/start-coordinator.d.ts.map +1 -1
  60. package/dist/rules/start-coordinator.js +2 -6
  61. package/dist/rules/start-coordinator.js.map +1 -1
  62. package/dist/session/factory-session.d.ts +5 -43
  63. package/dist/session/factory-session.d.ts.map +1 -1
  64. package/dist/session/factory-session.js +6 -47
  65. package/dist/session/factory-session.js.map +1 -1
  66. package/dist/session/memory-settings-hydration.d.ts +17 -89
  67. package/dist/session/memory-settings-hydration.d.ts.map +1 -1
  68. package/dist/session/memory-settings-hydration.js +25 -111
  69. package/dist/session/memory-settings-hydration.js.map +1 -1
  70. package/dist/storage/domains/memory-settings/base.d.ts +5 -17
  71. package/dist/storage/domains/memory-settings/base.d.ts.map +1 -1
  72. package/dist/storage/domains/memory-settings/base.js +8 -15
  73. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  74. package/dist/supervisor/session.d.ts +2 -4
  75. package/dist/supervisor/session.d.ts.map +1 -1
  76. package/dist/supervisor/session.js +3 -5
  77. package/dist/supervisor/session.js.map +1 -1
  78. package/dist/workspace.d.ts.map +1 -1
  79. package/dist/workspace.js +2 -2
  80. package/dist/workspace.js.map +1 -1
  81. package/package.json +8 -8
  82. package/dist/routes/om-seed.d.ts +0 -22
  83. package/dist/routes/om-seed.d.ts.map +0 -1
  84. package/dist/routes/om-seed.js +0 -40
  85. package/dist/routes/om-seed.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import { resolveProviderOMDefault } from '@mastra/code-sdk/onboarding/packs';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\n\nimport { factoryMemorySettingsUserId } from '../storage/domains/memory-settings/base.js';\nimport type { MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport {\n SourceControlConnectionNotFoundError,\n type SourceControlSession,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { applyStoredMemorySettings, type OMConfigurableSession } from './memory-settings-hydration.js';\nimport { seedSessionOrg } from './org-seed.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\n\nexport class FactorySourceControlConflictError extends Error {}\n\n/**\n * Read the factory project's default model. Best-effort: a missing project or an\n * uninitialized storage domain means \"no default\", never a failed run.\n */\nexport async function resolveFactoryDefaultModelId(\n projects: FactoryProjectsStorage | undefined,\n factoryProjectId: string | undefined,\n): Promise<string | undefined> {\n if (!projects || !factoryProjectId) return undefined;\n try {\n const project = await projects.getById({ id: factoryProjectId });\n return project?.defaultModelId ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface SourceControlSessionLookup {\n getBySessionId(sessionId: string): Promise<SourceControlSession | null>;\n getSourceControlBySessionId(sessionId: string): Promise<SourceControlStorageHandle | null>;\n rename(args: { sessionId: string; title: string }): Promise<void>;\n markFirstMessage(args: { sessionId: string }): Promise<void>;\n markFirstMeaningfulExec(args: { sessionId: string }): Promise<void>;\n}\n\n/**\n * Read or update a session across every registered source-control partition.\n * Session ids are globally generated, so more than one match is invalid and\n * fails closed instead of mutating an arbitrary provider.\n */\nexport function createSourceControlSessionLookup(\n sourceControls: readonly SourceControlStorageHandle[],\n): SourceControlSessionLookup {\n const resolve = async (\n sessionId: string,\n ): Promise<{ sourceControl: SourceControlStorageHandle; session: SourceControlSession } | null> => {\n const matches = (\n await Promise.all(\n sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(sessionId),\n })),\n )\n ).filter(\n (match): match is { sourceControl: SourceControlStorageHandle; session: SourceControlSession } =>\n match.session !== null,\n );\n if (matches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n return matches[0] ?? null;\n };\n\n return {\n getBySessionId: async sessionId => (await resolve(sessionId))?.session ?? null,\n getSourceControlBySessionId: async sessionId => (await resolve(sessionId))?.sourceControl ?? null,\n rename: async args => (await resolve(args.sessionId))?.sourceControl.sessions.rename(args),\n markFirstMessage: async args => (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMessage(args),\n markFirstMeaningfulExec: async args =>\n (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMeaningfulExec(args),\n };\n}\n\n/**\n * Resolve the source-control provider from durable Factory repository links,\n * never from the issue tracker that happened to create the work item.\n */\nexport async function resolveFactorySourceControl(args: {\n sourceControls: readonly SourceControlStorageHandle[];\n orgId: string;\n factoryProjectId: string;\n sessionId?: string;\n}): Promise<SourceControlStorageHandle | undefined> {\n if (args.sessionId) {\n const sessionMatches = (\n await Promise.all(\n args.sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(args.sessionId!),\n })),\n )\n ).filter(match => match.session !== null);\n if (sessionMatches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n if (sessionMatches[0]) return sessionMatches[0].sourceControl;\n }\n\n const linked = [];\n for (const sourceControl of args.sourceControls) {\n const connections = await sourceControl.connections.list({\n orgId: args.orgId,\n factoryProjectId: args.factoryProjectId,\n });\n let hasLinkedRepository = false;\n for (const connection of connections) {\n try {\n if (\n (await sourceControl.projectRepositories.list({ orgId: args.orgId, connectionId: connection.id })).length > 0\n ) {\n hasLinkedRepository = true;\n break;\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n }\n }\n if (hasLinkedRepository) linked.push(sourceControl);\n }\n if (linked.length > 1)\n throw new FactorySourceControlConflictError(\n 'Factory project has repositories linked through multiple source-control providers.',\n );\n return linked[0];\n}\n\nexport interface EnsureFactorySourceSessionArgs {\n /**\n * Storage handle of the integration that owns source control. Nothing here is\n * provider-specific: the connection is matched by the handle's own\n * `integrationId`, so GitHub, Slack-on-behalf-of-GitHub, or any future owner\n * all resolve through the same traversal.\n */\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n branch: string;\n /** Pick the linked repository that the work item targets. */\n repositorySlug: string;\n /**\n * Attribute the run to this user instead of the repo connector. Set when the\n * run has an interactive user — e.g. the person who approved a proposed run.\n */\n attributeToUserId?: string;\n}\n\nexport interface EnsuredFactorySourceSession {\n sessionId: string;\n userId: string;\n projectRepositoryId: string;\n branch: string;\n baseBranch: string;\n}\n\nexport class FactorySourceSessionResolutionError extends Error {\n constructor(readonly reason: 'connection' | 'repository') {\n super(\n reason === 'connection'\n ? 'Factory source-control connection not found.'\n : 'Factory source-control repository not found.',\n );\n this.name = 'FactorySourceSessionResolutionError';\n }\n}\n\nexport interface ResolvedFactorySourceRepository {\n projectRepositoryId: string;\n /** The repository's pinned branch, else its default branch. */\n baseBranch: string;\n /** Who connected the repository. The attribution for runs with no interactive user. */\n connectedByUserId: string;\n}\n\n/**\n * Outcome of {@link resolveFactorySourceRepository}. A miss carries which step\n * failed: callers differ on whether that is an error (an autonomous run cannot\n * proceed) or a routine fallback (a chat integration drops to a chat-only\n * session), and the two steps fail for different reasons worth reporting apart.\n */\nexport type FactorySourceRepositoryResult =\n | ({ found: true } & ResolvedFactorySourceRepository)\n | { found: false; reason: 'connection' | 'repository' };\n\n/**\n * Resolve which repository a factory project's source-control runs act on: the\n * owner's connection on the project, then one of its linked repositories.\n *\n * The owner is whichever integration owns source control, matched by the\n * handle's own `integrationId` — nothing here is provider-specific.\n */\nexport async function resolveFactorySourceRepository(\n args:\n | {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n repositorySlug: string;\n }\n | {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n firstLinkedRepository: true;\n },\n): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId } = args;\n const repositorySlug = 'repositorySlug' in args ? args.repositorySlug : undefined;\n const firstLinkedRepository = 'firstLinkedRepository' in args;\n\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n const candidates = connections.filter(candidate => candidate.integrationId === sourceControl.integrationId);\n if (candidates.length === 0) return { found: false, reason: 'connection' };\n\n // A project can carry stale connections: a provider-app reinstall leaves the\n // old connection pointing at an installation that no longer exists, and that\n // row can sit ahead of the healthy one. Try every candidate and skip the ones\n // that no longer resolve rather than failing on the first.\n for (const connection of candidates) {\n let resolved;\n try {\n const projectRepositories = await sourceControl.projectRepositories.list({ orgId, connectionId: connection.id });\n const resolvedRepositories = await Promise.all(\n projectRepositories.map(async projectRepository => ({\n projectRepository,\n repository: await sourceControl.repositories.get({ orgId, id: projectRepository.repositoryId }),\n })),\n );\n resolved = resolvedRepositories.find(\n candidate => candidate.repository && (firstLinkedRepository || candidate.repository.slug === repositorySlug),\n );\n } catch (error) {\n // A deleted installation invalidates a stale connection; storage failures must propagate.\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n continue;\n }\n if (!resolved?.repository) continue;\n\n return {\n found: true,\n projectRepositoryId: resolved.projectRepository.id,\n baseBranch: resolved.projectRepository.branch ?? resolved.repository.defaultBranch,\n connectedByUserId: connection.createdByUserId,\n };\n }\n\n return { found: false, reason: 'repository' };\n}\n\n/**\n * Walk a Factory user-session id back to the project it belongs to.\n *\n * Repo-backed channel threads are keyed by their Factory session id, which is\n * the only handle a session-start hook gets. This turns that id back into the\n * project whose configuration the session should adopt. Durable by\n * construction — it reads the same rows the session was created from, so it\n * survives restarts without any in-memory mapping.\n */\nexport async function resolveFactoryProjectForSession(args: {\n sourceControl: SourceControlStorageHandle;\n sessionId: string;\n}): Promise<{ factoryProjectId: string; orgId: string; userId: string } | null> {\n const { sourceControl, sessionId } = args;\n\n const session = await sourceControl.sessions.getBySessionId(sessionId);\n if (!session) return null;\n const projectRepository = await sourceControl.projectRepositories.get({\n orgId: session.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) return null;\n const connection = await sourceControl.connections.get({ orgId: session.orgId, id: projectRepository.connectionId });\n if (!connection) return null;\n\n return { factoryProjectId: connection.factoryProjectId, orgId: session.orgId, userId: session.userId };\n}\n\n/**\n * Create the source-control session a repo-backed factory run needs.\n *\n * `FactoryStartCoordinator.prepare` requires this record to already exist —\n * `resolveSourceSession` throws `Factory session not found` otherwise — so every\n * autonomous entry point has to produce one before it can start a run. This is\n * that step, in one place: the owner's connection on the factory project, one of\n * its linked repositories, and a session on the requested branch with the\n * repository's pinned or default branch as the base.\n *\n * The run is attributed to `attributeToUserId` when the caller has an\n * interactive user (e.g. the approver of a proposed run), and otherwise falls\n * back to whoever connected the repository (`connection.createdByUserId`),\n * because a genuinely autonomous run has no interactive user of its own.\n */\nexport async function ensureFactorySourceSession(\n args: EnsureFactorySourceSessionArgs,\n): Promise<EnsuredFactorySourceSession> {\n const { sourceControl, orgId, factoryProjectId, branch, repositorySlug } = args;\n\n const resolved = await resolveFactorySourceRepository({ sourceControl, orgId, factoryProjectId, repositorySlug });\n if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);\n\n const userId = args.attributeToUserId ?? resolved.connectedByUserId;\n const session = await sourceControl.sessions.create({\n sessionId: globalThis.crypto.randomUUID(),\n projectRepositoryId: resolved.projectRepositoryId,\n orgId,\n userId,\n branch,\n baseBranch: resolved.baseBranch,\n visibility: 'org',\n });\n return {\n sessionId: session.sessionId,\n userId,\n projectRepositoryId: resolved.projectRepositoryId,\n branch: session.branch,\n baseBranch: resolved.baseBranch,\n };\n}\n\nexport interface HydrateFactorySessionArgs {\n orgId: string;\n /**\n * The factory project whose shared memory settings apply. Factory sessions\n * never read an individual user's personal memory settings — the project's\n * own row (or the built-in defaults) is what they run with.\n */\n factoryProjectId?: string;\n /** The factory project's default model. Without it the session keeps the SDK's built-in mode default. */\n defaultModelId?: string;\n /**\n * Model whose provider supplies the observational-memory fallback. Defaults\n * to the factory model, but channel sessions can use the sender's model so OM\n * resolves against that sender's credentials.\n */\n observationalMemoryModelId?: string;\n /**\n * When provided, the factory project's stored memory-settings row is\n * applied. When omitted (or no row exists) the session is reset to the\n * built-in memory defaults.\n */\n memorySettings?: MemorySettingsStorage;\n}\n\n/**\n * Apply a factory project's configuration to a freshly created session:\n * observational-memory settings, then the project's default model.\n *\n * Both steps are best-effort. A retired model id or an unreachable settings row\n * must not sink a run that is otherwise ready — the session simply keeps the\n * default it was created with, and the reason is logged.\n */\nexport async function hydrateFactorySession(session: FactorySession, args: HydrateFactorySessionArgs): Promise<void> {\n // The org rung knowledge curation scopes on. Seeded first so it lands even if\n // a later best-effort step fails; an empty org marks the session unresolved.\n await seedSessionOrg(session, args.orgId);\n try {\n const record =\n args.memorySettings && args.factoryProjectId\n ? await args.memorySettings.get({\n orgId: args.orgId,\n userId: factoryMemorySettingsUserId(args.factoryProjectId),\n })\n : null;\n // Without a stored row, fall back to the low-cost OM model of the factory\n // default model's provider — a factory connected only to Anthropic should\n // not observe with the (uncredentialed) built-in Google default.\n const observationalMemoryModelId = args.observationalMemoryModelId ?? args.defaultModelId;\n const provider = observationalMemoryModelId?.split('/')[0];\n const fallbackOmModelId = provider\n ? resolveProviderOMDefault(provider, observationalMemoryModelId).modelId\n : undefined;\n await applyStoredMemorySettings(session, record, fallbackOmModelId);\n } catch (error) {\n console.warn('[Factory Start] Failed to apply observational-memory settings', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n if (args.defaultModelId) {\n try {\n await session.model.switch(args.defaultModelId);\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default model', {\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n // Subagents otherwise keep the server-wide settings (or the SDK's built-in\n // default), which may name a provider this factory has no credentials for.\n // Each role is independent, so one failure doesn't strand the others.\n for (const agentType of ['explore', 'plan', 'execute']) {\n try {\n await session.subagents.model.set({ modelId: args.defaultModelId, agentType });\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default subagent model', {\n agentType,\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n}\n\nexport interface RefreshFactorySessionMemorySettingsArgs {\n orgId: string;\n factoryProjectId: string;\n projects: Pick<FactoryProjectsStorage, 'get'>;\n memorySettings: Pick<MemorySettingsStorage, 'get'>;\n}\n\n/**\n * Re-apply a factory project's stored observational-memory settings to an\n * already-running session that automation is about to reuse. Session creation\n * hydrates these settings once (`hydrateFactorySession`), but a reused binding\n * keeps whatever observer/reflector models it was created with — so a project\n * whose OM models changed since would keep observing with the stale (and\n * possibly since-rejected) models. This reads the project's current row with the\n * same provider-aware fallback as initial hydration and applies it, mirroring\n * the `GET /web/config/om` refresh. Best-effort: a settings lookup failure must\n * never sink an otherwise-ready run, so it is logged and swallowed.\n */\nexport async function refreshFactorySessionMemorySettings(\n session: OMConfigurableSession,\n args: RefreshFactorySessionMemorySettingsArgs,\n): Promise<void> {\n try {\n const record = await args.memorySettings.get({\n orgId: args.orgId,\n userId: factoryMemorySettingsUserId(args.factoryProjectId),\n });\n const project = await args.projects.get({ orgId: args.orgId, id: args.factoryProjectId });\n const provider = project?.defaultModelId?.split('/')[0];\n const fallbackOmModelId = provider\n ? resolveProviderOMDefault(provider, project?.defaultModelId ?? undefined).modelId\n : undefined;\n await applyStoredMemorySettings(session, record, fallbackOmModelId);\n } catch (error) {\n console.warn('[Factory dispatch] Failed to reapply observational-memory settings on session reuse', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\n\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport {\n SourceControlConnectionNotFoundError,\n type SourceControlSession,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { seedSessionOrg } from './org-seed.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\n\nexport class FactorySourceControlConflictError extends Error {}\n\n/**\n * Read the factory project's default model. Best-effort: a missing project or an\n * uninitialized storage domain means \"no default\", never a failed run.\n */\nexport async function resolveFactoryDefaultModelId(\n projects: FactoryProjectsStorage | undefined,\n factoryProjectId: string | undefined,\n): Promise<string | undefined> {\n if (!projects || !factoryProjectId) return undefined;\n try {\n const project = await projects.getById({ id: factoryProjectId });\n return project?.defaultModelId ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface SourceControlSessionLookup {\n getBySessionId(sessionId: string): Promise<SourceControlSession | null>;\n getSourceControlBySessionId(sessionId: string): Promise<SourceControlStorageHandle | null>;\n rename(args: { sessionId: string; title: string }): Promise<void>;\n markFirstMessage(args: { sessionId: string }): Promise<void>;\n markFirstMeaningfulExec(args: { sessionId: string }): Promise<void>;\n}\n\n/**\n * Read or update a session across every registered source-control partition.\n * Session ids are globally generated, so more than one match is invalid and\n * fails closed instead of mutating an arbitrary provider.\n */\nexport function createSourceControlSessionLookup(\n sourceControls: readonly SourceControlStorageHandle[],\n): SourceControlSessionLookup {\n const resolve = async (\n sessionId: string,\n ): Promise<{ sourceControl: SourceControlStorageHandle; session: SourceControlSession } | null> => {\n const matches = (\n await Promise.all(\n sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(sessionId),\n })),\n )\n ).filter(\n (match): match is { sourceControl: SourceControlStorageHandle; session: SourceControlSession } =>\n match.session !== null,\n );\n if (matches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n return matches[0] ?? null;\n };\n\n return {\n getBySessionId: async sessionId => (await resolve(sessionId))?.session ?? null,\n getSourceControlBySessionId: async sessionId => (await resolve(sessionId))?.sourceControl ?? null,\n rename: async args => (await resolve(args.sessionId))?.sourceControl.sessions.rename(args),\n markFirstMessage: async args => (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMessage(args),\n markFirstMeaningfulExec: async args =>\n (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMeaningfulExec(args),\n };\n}\n\n/**\n * Resolve the source-control provider from durable Factory repository links,\n * never from the issue tracker that happened to create the work item.\n */\nexport async function resolveFactorySourceControl(args: {\n sourceControls: readonly SourceControlStorageHandle[];\n orgId: string;\n factoryProjectId: string;\n sessionId?: string;\n}): Promise<SourceControlStorageHandle | undefined> {\n if (args.sessionId) {\n const sessionMatches = (\n await Promise.all(\n args.sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(args.sessionId!),\n })),\n )\n ).filter(match => match.session !== null);\n if (sessionMatches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n if (sessionMatches[0]) return sessionMatches[0].sourceControl;\n }\n\n const linked = [];\n for (const sourceControl of args.sourceControls) {\n const connections = await sourceControl.connections.list({\n orgId: args.orgId,\n factoryProjectId: args.factoryProjectId,\n });\n let hasLinkedRepository = false;\n for (const connection of connections) {\n try {\n if (\n (await sourceControl.projectRepositories.list({ orgId: args.orgId, connectionId: connection.id })).length > 0\n ) {\n hasLinkedRepository = true;\n break;\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n }\n }\n if (hasLinkedRepository) linked.push(sourceControl);\n }\n if (linked.length > 1)\n throw new FactorySourceControlConflictError(\n 'Factory project has repositories linked through multiple source-control providers.',\n );\n return linked[0];\n}\n\nexport interface EnsureFactorySourceSessionArgs {\n /**\n * Storage handle of the integration that owns source control. Nothing here is\n * provider-specific: the connection is matched by the handle's own\n * `integrationId`, so GitHub, Slack-on-behalf-of-GitHub, or any future owner\n * all resolve through the same traversal.\n */\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n branch: string;\n /** Pick the linked repository that the work item targets. */\n repositorySlug: string;\n /**\n * Attribute the run to this user instead of the repo connector. Set when the\n * run has an interactive user — e.g. the person who approved a proposed run.\n */\n attributeToUserId?: string;\n}\n\nexport interface EnsuredFactorySourceSession {\n sessionId: string;\n userId: string;\n projectRepositoryId: string;\n branch: string;\n baseBranch: string;\n}\n\nexport class FactorySourceSessionResolutionError extends Error {\n constructor(readonly reason: 'connection' | 'repository') {\n super(\n reason === 'connection'\n ? 'Factory source-control connection not found.'\n : 'Factory source-control repository not found.',\n );\n this.name = 'FactorySourceSessionResolutionError';\n }\n}\n\nexport interface ResolvedFactorySourceRepository {\n projectRepositoryId: string;\n /** The repository's pinned branch, else its default branch. */\n baseBranch: string;\n /** Who connected the repository. The attribution for runs with no interactive user. */\n connectedByUserId: string;\n}\n\n/**\n * Outcome of {@link resolveFactorySourceRepository}. A miss carries which step\n * failed: callers differ on whether that is an error (an autonomous run cannot\n * proceed) or a routine fallback (a chat integration drops to a chat-only\n * session), and the two steps fail for different reasons worth reporting apart.\n */\nexport type FactorySourceRepositoryResult =\n | ({ found: true } & ResolvedFactorySourceRepository)\n | { found: false; reason: 'connection' | 'repository' };\n\n/**\n * Resolve which repository a factory project's source-control runs act on: the\n * owner's connection on the project, then one of its linked repositories.\n *\n * The owner is whichever integration owns source control, matched by the\n * handle's own `integrationId` — nothing here is provider-specific.\n */\nexport async function resolveFactorySourceRepository(\n args:\n | {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n repositorySlug: string;\n }\n | {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n firstLinkedRepository: true;\n },\n): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId } = args;\n const repositorySlug = 'repositorySlug' in args ? args.repositorySlug : undefined;\n const firstLinkedRepository = 'firstLinkedRepository' in args;\n\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n const candidates = connections.filter(candidate => candidate.integrationId === sourceControl.integrationId);\n if (candidates.length === 0) return { found: false, reason: 'connection' };\n\n // A project can carry stale connections: a provider-app reinstall leaves the\n // old connection pointing at an installation that no longer exists, and that\n // row can sit ahead of the healthy one. Try every candidate and skip the ones\n // that no longer resolve rather than failing on the first.\n for (const connection of candidates) {\n let resolved;\n try {\n const projectRepositories = await sourceControl.projectRepositories.list({ orgId, connectionId: connection.id });\n const resolvedRepositories = await Promise.all(\n projectRepositories.map(async projectRepository => ({\n projectRepository,\n repository: await sourceControl.repositories.get({ orgId, id: projectRepository.repositoryId }),\n })),\n );\n resolved = resolvedRepositories.find(\n candidate => candidate.repository && (firstLinkedRepository || candidate.repository.slug === repositorySlug),\n );\n } catch (error) {\n // A deleted installation invalidates a stale connection; storage failures must propagate.\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n continue;\n }\n if (!resolved?.repository) continue;\n\n return {\n found: true,\n projectRepositoryId: resolved.projectRepository.id,\n baseBranch: resolved.projectRepository.branch ?? resolved.repository.defaultBranch,\n connectedByUserId: connection.createdByUserId,\n };\n }\n\n return { found: false, reason: 'repository' };\n}\n\n/**\n * Walk a Factory user-session id back to the project it belongs to.\n *\n * Repo-backed channel threads are keyed by their Factory session id, which is\n * the only handle a session-start hook gets. This turns that id back into the\n * project whose configuration the session should adopt. Durable by\n * construction — it reads the same rows the session was created from, so it\n * survives restarts without any in-memory mapping.\n */\nexport async function resolveFactoryProjectForSession(args: {\n sourceControl: SourceControlStorageHandle;\n sessionId: string;\n}): Promise<{ factoryProjectId: string; orgId: string; userId: string } | null> {\n const { sourceControl, sessionId } = args;\n\n const session = await sourceControl.sessions.getBySessionId(sessionId);\n if (!session) return null;\n const projectRepository = await sourceControl.projectRepositories.get({\n orgId: session.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) return null;\n const connection = await sourceControl.connections.get({ orgId: session.orgId, id: projectRepository.connectionId });\n if (!connection) return null;\n\n return { factoryProjectId: connection.factoryProjectId, orgId: session.orgId, userId: session.userId };\n}\n\n/**\n * Create the source-control session a repo-backed factory run needs.\n *\n * `FactoryStartCoordinator.prepare` requires this record to already exist —\n * `resolveSourceSession` throws `Factory session not found` otherwise — so every\n * autonomous entry point has to produce one before it can start a run. This is\n * that step, in one place: the owner's connection on the factory project, one of\n * its linked repositories, and a session on the requested branch with the\n * repository's pinned or default branch as the base.\n *\n * The run is attributed to `attributeToUserId` when the caller has an\n * interactive user (e.g. the approver of a proposed run), and otherwise falls\n * back to whoever connected the repository (`connection.createdByUserId`),\n * because a genuinely autonomous run has no interactive user of its own.\n */\nexport async function ensureFactorySourceSession(\n args: EnsureFactorySourceSessionArgs,\n): Promise<EnsuredFactorySourceSession> {\n const { sourceControl, orgId, factoryProjectId, branch, repositorySlug } = args;\n\n const resolved = await resolveFactorySourceRepository({ sourceControl, orgId, factoryProjectId, repositorySlug });\n if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);\n\n const userId = args.attributeToUserId ?? resolved.connectedByUserId;\n const session = await sourceControl.sessions.create({\n sessionId: globalThis.crypto.randomUUID(),\n projectRepositoryId: resolved.projectRepositoryId,\n orgId,\n userId,\n branch,\n baseBranch: resolved.baseBranch,\n visibility: 'org',\n });\n return {\n sessionId: session.sessionId,\n userId,\n projectRepositoryId: resolved.projectRepositoryId,\n branch: session.branch,\n baseBranch: resolved.baseBranch,\n };\n}\n\nexport interface HydrateFactorySessionArgs {\n orgId: string;\n /** The factory project's default model. Without it the session keeps the SDK's built-in mode default. */\n defaultModelId?: string;\n}\n\n/**\n * Apply a factory project's main model to a session on every run.\n *\n * The model switch is best-effort. A retired model id must not sink a run that\n * is otherwise ready — the session simply keeps the default it was created with,\n * and the reason is logged. Observational-memory settings are resolved from the\n * authoritative row per invocation before memory processors run.\n */\nexport async function hydrateFactorySession(session: FactorySession, args: HydrateFactorySessionArgs): Promise<void> {\n // The org rung knowledge curation scopes on. Seeded first so it lands even if\n // a later best-effort step fails; an empty org marks the session unresolved.\n await seedSessionOrg(session, args.orgId);\n\n if (args.defaultModelId) {\n try {\n await session.model.switch(args.defaultModelId);\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default model', {\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n // Subagents otherwise keep the server-wide settings (or the SDK's built-in\n // default), which may name a provider this factory has no credentials for.\n // Each role is independent, so one failure doesn't strand the others.\n for (const agentType of ['explore', 'plan', 'execute']) {\n try {\n await session.subagents.model.set({ modelId: args.defaultModelId, agentType });\n } catch (error) {\n console.warn('[Factory Start] Failed to apply factory default subagent model', {\n agentType,\n modelId: args.defaultModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,108 +1,36 @@
1
- import { type MemorySettingsRecord, type MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';
2
- import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
1
+ import type { MastraCodeState } from '@mastra/code-sdk/schema';
2
+ import type { MemorySettingsRecord } from '../storage/domains/memory-settings/base.js';
3
3
  import type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';
4
4
  /** Default thresholds mirror the TUI `/om` fallbacks. */
5
5
  export declare const DEFAULT_OBSERVATION_THRESHOLD = 30000;
6
6
  export declare const DEFAULT_REFLECTION_THRESHOLD = 40000;
7
- /** One observational-memory role's read/switch surface. */
8
- interface OMRoleSlice {
9
- modelId: () => string | undefined;
10
- switchModel: (args: {
11
- modelId: string;
12
- }) => Promise<unknown>;
13
- }
14
7
  /**
15
- * Session-state fields memory-settings hydration writes. The index signatures
16
- * mirror `MastraCodeState` so the concrete `Session.state.set(Partial<MastraCodeState>)`
17
- * stays assignable to this minimal surface (contravariant parameter check).
8
+ * Layer a channel sender's own row over the project's shared row. The sender
9
+ * wins only for knobs they saved; an unsaved (`null`) knob keeps the project's
10
+ * value rather than resetting to Auto or the built-in default, because the
11
+ * project's choice may be the only credentialed one.
18
12
  */
19
- interface OMStateWrites {
20
- [key: string]: unknown;
21
- [key: `subagentModelId_${string}`]: string | undefined;
22
- observationThreshold?: number;
23
- reflectionThreshold?: number;
24
- observeAttachments?: 'auto' | boolean;
25
- factoryOrgId?: string;
26
- }
27
- /** The slice of a session needed to apply stored observational-memory settings. */
28
- export interface OMConfigurableSession {
29
- om: {
30
- observer: OMRoleSlice;
31
- reflector: OMRoleSlice;
32
- };
33
- state: {
34
- get: () => Record<string, unknown> | undefined;
35
- set: (updates: OMStateWrites) => Promise<void> | void;
36
- };
37
- }
38
- /**
39
- * Apply a stored memory-settings row onto a session, so the DB — not whatever
40
- * happens to sit in persisted session state (e.g. a stale boot-time seed from
41
- * before memory settings moved to the DB) — is what the web surface reads and
42
- * what the session's OM actually runs with. The row is authoritative: knobs
43
- * without a stored value reset to the built-in defaults. This is the single
44
- * application path shared by the settings routes, coordinator hydration, and
45
- * the web session boot seed.
46
- */
47
- export declare function applyStoredMemorySettings(session: OMConfigurableSession, record: MemorySettingsRecord | null, fallbackOmModelId?: string): Promise<void>;
48
- export interface PersonalMemorySettingsArgs {
49
- /** Without the domain there is no personal preference to read. */
50
- memorySettings: Pick<MemorySettingsStorage, 'get'> | undefined;
51
- orgId: string;
52
- /** The human whose settings apply — for a channel session, the linked sender. */
53
- userId: string;
54
- }
55
- /**
56
- * Layer a user's own memory-settings row over whatever the session already runs
57
- * with, so a personal preference beats the one the project path resolved.
58
- *
59
- * The row wins only for the knobs the user has actually saved: an unsaved knob
60
- * keeps the value already on the session (the project's row, or the
61
- * provider-aware fallback resolved from the factory default model). That
62
- * distinction matters because `applyStoredMemorySettings` treats a null knob as
63
- * "reset to the built-in default", and switching observation onto an
64
- * uncredentialed provider would fail every observe cycle.
65
- *
66
- * Best-effort: a missing row, an uninitialized domain, or a read failure all
67
- * mean "no personal preference" and leave the session exactly as it was.
68
- */
69
- export declare function applyPersonalMemorySettings(session: OMConfigurableSession, { memorySettings, orgId, userId }: PersonalMemorySettingsArgs): Promise<void>;
70
- export interface MemorySettingsHydrationSession extends OMConfigurableSession {
13
+ export declare function layerPersonalMemorySettings(project: MemorySettingsRecord | null, personal: MemorySettingsRecord | null): MemorySettingsRecord | null;
14
+ export interface MemorySettingsHydrationSession {
71
15
  readonly identity: {
72
16
  getResourceId(): string;
73
17
  };
18
+ state: {
19
+ get: () => MastraCodeState | undefined;
20
+ set: (updates: Partial<MastraCodeState>) => Promise<void> | void;
21
+ };
74
22
  }
75
23
  export interface MemorySettingsHydrationDependencies {
76
24
  /** GitHub-integration source-control rows — the only creator of web user sessions today. */
77
25
  sourceControl: {
78
26
  sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;
79
27
  };
80
- projects: Pick<FactoryProjectsStorage, 'get'>;
81
- memorySettings: Pick<MemorySettingsStorage, 'get'>;
82
28
  }
83
29
  /**
84
- * Seed a freshly created controller session's tenant org and its
85
- * observational-memory settings from the owner's source-control row. Registered
86
- * as a blocking session-created listener so the seed lands before the caller can
87
- * start a run.
88
- *
89
- * The org seed matters beyond settings. Subconscious knowledge curation scopes
90
- * every node and record on `factoryOrgId`; before the SDK refusal guard,
91
- * missing it made curation substitute the session owner id. For web chat sessions
92
- * that is the agent controller's own id rather than a tenant, so curated
93
- * knowledge landed under an org rung no reader ever queries. Same rule as the
94
- * start coordinator: the org
95
- * comes from the row the session was created from, never improvised from an
96
- * owner id.
97
- *
98
- * Tagged sessions may be coordinator-owned runs or web sessions that only carry
99
- * browser-seeded Factory state. This path resolves their source-control row and
100
- * reapplies a stored project settings row when one exists. If no project row
101
- * exists, it preserves the coordinator's provider-aware fallback.
102
- * Sessions without a GitHub source-control row (e.g. chat-only channel sessions)
103
- * hydrate through `hydrateFactorySession` with their own resolved tenant.
104
- * Best-effort: failures are logged, never thrown.
30
+ * Seed a freshly created controller session's tenant org from its source-control
31
+ * row. Memory settings remain DB-authoritative and are loaded into request
32
+ * context for each invocation instead of being copied into mutable session
33
+ * state.
105
34
  */
106
- export declare function hydrateSessionMemorySettings(session: MemorySettingsHydrationSession, { sourceControl, projects, memorySettings }: MemorySettingsHydrationDependencies): Promise<void>;
107
- export {};
35
+ export declare function hydrateSessionMemorySettings(session: MemorySettingsHydrationSession, { sourceControl }: MemorySettingsHydrationDependencies): Promise<void>;
108
36
  //# sourceMappingURL=memory-settings-hydration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-settings-hydration.d.ts","sourceRoot":"","sources":["../../src/session/memory-settings-hydration.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC3B,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAG5F,yDAAyD;AACzD,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,2DAA2D;AAC3D,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClC,WAAW,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAED;;;;GAIG;AACH,UAAU,aAAa;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,CAAC,GAAG,EAAE,mBAAmB,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IACvD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,mFAAmF;AACnF,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE;QAAE,QAAQ,EAAE,WAAW,CAAC;QAAC,SAAS,EAAE,WAAW,CAAA;KAAE,CAAC;IACtD,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;QAC/C,GAAG,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KACvD,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,oBAAoB,GAAG,IAAI,EACnC,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED,MAAM,WAAW,0BAA0B;IACzC,kEAAkE;IAClE,cAAc,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,qBAAqB,EAC9B,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,0BAA0B,GAC5D,OAAO,CAAC,IAAI,CAAC,CAiBf;AAUD,MAAM,WAAW,8BAA+B,SAAQ,qBAAqB;IAC3E,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;CAChD;AAED,MAAM,WAAW,mCAAmC;IAClD,4FAA4F;IAC5F,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;IACF,QAAQ,EAAE,IAAI,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;IAC9C,cAAc,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;CACpD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,8BAA8B,EACvC,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,mCAAmC,GAC/E,OAAO,CAAC,IAAI,CAAC,CAyCf"}
1
+ {"version":3,"file":"memory-settings-hydration.d.ts","sourceRoot":"","sources":["../../src/session/memory-settings-hydration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAG5F,yDAAyD;AACzD,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,oBAAoB,GAAG,IAAI,EACpC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,GACpC,oBAAoB,GAAG,IAAI,CAW7B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,eAAe,GAAG,SAAS,CAAC;QACvC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAClE,CAAC;CACH;AAED,MAAM,WAAW,mCAAmC;IAClD,4FAA4F;IAC5F,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,8BAA8B,EACvC,EAAE,aAAa,EAAE,EAAE,mCAAmC,GACrD,OAAO,CAAC,IAAI,CAAC,CAYf"}
@@ -1,129 +1,43 @@
1
- import { seedSessionOrg } from "./org-seed.js";
2
- import { factoryMemorySettingsUserId } from "../storage/domains/memory-settings/base.js";
3
- import { DEFAULT_OM_MODEL_ID } from "@mastra/code-sdk/constants";
4
- import { resolveProviderOMDefault } from "@mastra/code-sdk/onboarding/packs";
1
+ import { hasResolvedOrg, seedSessionOrg } from "./org-seed.js";
5
2
  //#region src/session/memory-settings-hydration.ts
6
3
  /** Default thresholds mirror the TUI `/om` fallbacks. */
7
4
  const DEFAULT_OBSERVATION_THRESHOLD = 3e4;
8
5
  const DEFAULT_REFLECTION_THRESHOLD = 4e4;
9
6
  /**
10
- * Apply a stored memory-settings row onto a session, so the DB — not whatever
11
- * happens to sit in persisted session state (e.g. a stale boot-time seed from
12
- * before memory settings moved to the DB) — is what the web surface reads and
13
- * what the session's OM actually runs with. The row is authoritative: knobs
14
- * without a stored value reset to the built-in defaults. This is the single
15
- * application path shared by the settings routes, coordinator hydration, and
16
- * the web session boot seed.
7
+ * Layer a channel sender's own row over the project's shared row. The sender
8
+ * wins only for knobs they saved; an unsaved (`null`) knob keeps the project's
9
+ * value rather than resetting to Auto or the built-in default, because the
10
+ * project's choice may be the only credentialed one.
17
11
  */
18
- async function applyStoredMemorySettings(session, record, fallbackOmModelId) {
19
- for (const role of ["observer", "reflector"]) {
20
- const target = (role === "observer" ? record?.observerModelId : record?.reflectorModelId) ?? fallbackOmModelId ?? DEFAULT_OM_MODEL_ID;
21
- if (session.om[role].modelId() !== target) await session.om[role].switchModel({ modelId: target });
22
- }
23
- const state = session.state.get() ?? {};
24
- const updates = {};
25
- const observationThreshold = record?.observationThreshold ?? 3e4;
26
- if (state.observationThreshold !== observationThreshold) updates.observationThreshold = observationThreshold;
27
- const reflectionThreshold = record?.reflectionThreshold ?? 4e4;
28
- if (state.reflectionThreshold !== reflectionThreshold) updates.reflectionThreshold = reflectionThreshold;
29
- const observeAttachments = record?.observeAttachments ?? "auto";
30
- if ((state.observeAttachments ?? "auto") !== observeAttachments) updates.observeAttachments = observeAttachments;
31
- if (Object.keys(updates).length > 0) await session.state.set(updates);
32
- }
33
- /**
34
- * Layer a user's own memory-settings row over whatever the session already runs
35
- * with, so a personal preference beats the one the project path resolved.
36
- *
37
- * The row wins only for the knobs the user has actually saved: an unsaved knob
38
- * keeps the value already on the session (the project's row, or the
39
- * provider-aware fallback resolved from the factory default model). That
40
- * distinction matters because `applyStoredMemorySettings` treats a null knob as
41
- * "reset to the built-in default", and switching observation onto an
42
- * uncredentialed provider would fail every observe cycle.
43
- *
44
- * Best-effort: a missing row, an uninitialized domain, or a read failure all
45
- * mean "no personal preference" and leave the session exactly as it was.
46
- */
47
- async function applyPersonalMemorySettings(session, { memorySettings, orgId, userId }) {
48
- if (!memorySettings) return;
49
- try {
50
- const record = await memorySettings.get({
51
- orgId,
52
- userId
53
- });
54
- if (!record) return;
55
- const state = session.state.get() ?? {};
56
- await applyStoredMemorySettings(session, {
57
- ...record,
58
- observerModelId: record.observerModelId ?? session.om.observer.modelId() ?? null,
59
- reflectorModelId: record.reflectorModelId ?? session.om.reflector.modelId() ?? null,
60
- observationThreshold: record.observationThreshold ?? storedNumber(state.observationThreshold),
61
- reflectionThreshold: record.reflectionThreshold ?? storedNumber(state.reflectionThreshold),
62
- observeAttachments: record.observeAttachments ?? storedObserveAttachments(state.observeAttachments)
63
- });
64
- } catch (error) {
65
- console.warn("[Factory memory-settings hydration] Unable to apply the user's memory settings.", error);
66
- }
67
- }
68
- function storedNumber(value) {
69
- return typeof value === "number" && Number.isFinite(value) ? value : null;
70
- }
71
- function storedObserveAttachments(value) {
72
- return value === "auto" || typeof value === "boolean" ? value : null;
12
+ function layerPersonalMemorySettings(project, personal) {
13
+ if (!personal) return project;
14
+ if (!project) return personal;
15
+ return {
16
+ ...project,
17
+ observerModelId: personal.observerModelId ?? project.observerModelId,
18
+ reflectorModelId: personal.reflectorModelId ?? project.reflectorModelId,
19
+ observationThreshold: personal.observationThreshold ?? project.observationThreshold,
20
+ reflectionThreshold: personal.reflectionThreshold ?? project.reflectionThreshold,
21
+ observeAttachments: personal.observeAttachments ?? project.observeAttachments
22
+ };
73
23
  }
74
24
  /**
75
- * Seed a freshly created controller session's tenant org and its
76
- * observational-memory settings from the owner's source-control row. Registered
77
- * as a blocking session-created listener so the seed lands before the caller can
78
- * start a run.
79
- *
80
- * The org seed matters beyond settings. Subconscious knowledge curation scopes
81
- * every node and record on `factoryOrgId`; before the SDK refusal guard,
82
- * missing it made curation substitute the session owner id. For web chat sessions
83
- * that is the agent controller's own id rather than a tenant, so curated
84
- * knowledge landed under an org rung no reader ever queries. Same rule as the
85
- * start coordinator: the org
86
- * comes from the row the session was created from, never improvised from an
87
- * owner id.
88
- *
89
- * Tagged sessions may be coordinator-owned runs or web sessions that only carry
90
- * browser-seeded Factory state. This path resolves their source-control row and
91
- * reapplies a stored project settings row when one exists. If no project row
92
- * exists, it preserves the coordinator's provider-aware fallback.
93
- * Sessions without a GitHub source-control row (e.g. chat-only channel sessions)
94
- * hydrate through `hydrateFactorySession` with their own resolved tenant.
95
- * Best-effort: failures are logged, never thrown.
25
+ * Seed a freshly created controller session's tenant org from its source-control
26
+ * row. Memory settings remain DB-authoritative and are loaded into request
27
+ * context for each invocation instead of being copied into mutable session
28
+ * state.
96
29
  */
97
- async function hydrateSessionMemorySettings(session, { sourceControl, projects, memorySettings }) {
30
+ async function hydrateSessionMemorySettings(session, { sourceControl }) {
98
31
  const state = session.state.get() ?? {};
99
- const isFactoryRun = Boolean(state.factoryProjectId);
32
+ if (Boolean(state.factoryProjectId) && hasResolvedOrg(state.factoryOrgId)) return;
100
33
  try {
101
- const record = await sourceControl.sessions.getBySessionId(session.identity.getResourceId());
102
- await seedSessionOrg(session, record?.orgId);
103
- if (!record) return;
104
- const factoryProjectId = isFactoryRun ? String(state.factoryProjectId) : void 0;
105
- const settings = await memorySettings.get({
106
- orgId: record.orgId,
107
- userId: factoryProjectId ? factoryMemorySettingsUserId(factoryProjectId) : record.userId
108
- });
109
- if (factoryProjectId && !settings) return;
110
- const project = factoryProjectId ? await projects.get({
111
- orgId: record.orgId,
112
- id: factoryProjectId
113
- }) : null;
114
- const provider = project?.defaultModelId?.split("/")[0];
115
- const fallbackOmModelId = provider ? resolveProviderOMDefault(provider, project.defaultModelId ?? void 0).modelId : void 0;
116
- await applyStoredMemorySettings(session, factoryProjectId && settings && !fallbackOmModelId ? {
117
- ...settings,
118
- observerModelId: settings?.observerModelId ?? session.om.observer.modelId() ?? null,
119
- reflectorModelId: settings?.reflectorModelId ?? session.om.reflector.modelId() ?? null
120
- } : settings, fallbackOmModelId);
34
+ await seedSessionOrg(session, (await sourceControl.sessions.getBySessionId(session.identity.getResourceId()))?.orgId);
121
35
  } catch (error) {
122
- console.warn("[Factory memory-settings hydration] Unable to apply stored memory settings.", error);
36
+ console.warn("[Factory session hydration] Unable to resolve the session organization.", error);
123
37
  if (!session.state.get()?.factoryOrgId) await seedSessionOrg(session, void 0);
124
38
  }
125
39
  }
126
40
  //#endregion
127
- export { DEFAULT_OBSERVATION_THRESHOLD, DEFAULT_REFLECTION_THRESHOLD, applyPersonalMemorySettings, applyStoredMemorySettings, hydrateSessionMemorySettings };
41
+ export { DEFAULT_OBSERVATION_THRESHOLD, DEFAULT_REFLECTION_THRESHOLD, hydrateSessionMemorySettings, layerPersonalMemorySettings };
128
42
 
129
43
  //# sourceMappingURL=memory-settings-hydration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-settings-hydration.js","names":[],"sources":["../../src/session/memory-settings-hydration.ts"],"sourcesContent":["import { DEFAULT_OM_MODEL_ID } from '@mastra/code-sdk/constants';\nimport { resolveProviderOMDefault } from '@mastra/code-sdk/onboarding/packs';\n\nimport {\n factoryMemorySettingsUserId,\n type MemorySettingsRecord,\n type MemorySettingsStorage,\n} from '../storage/domains/memory-settings/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { seedSessionOrg } from './org-seed.js';\n\n/** Default thresholds mirror the TUI `/om` fallbacks. */\nexport const DEFAULT_OBSERVATION_THRESHOLD = 30_000;\nexport const DEFAULT_REFLECTION_THRESHOLD = 40_000;\n\n/** One observational-memory role's read/switch surface. */\ninterface OMRoleSlice {\n modelId: () => string | undefined;\n switchModel: (args: { modelId: string }) => Promise<unknown>;\n}\n\n/**\n * Session-state fields memory-settings hydration writes. The index signatures\n * mirror `MastraCodeState` so the concrete `Session.state.set(Partial<MastraCodeState>)`\n * stays assignable to this minimal surface (contravariant parameter check).\n */\ninterface OMStateWrites {\n [key: string]: unknown;\n [key: `subagentModelId_${string}`]: string | undefined;\n observationThreshold?: number;\n reflectionThreshold?: number;\n observeAttachments?: 'auto' | boolean;\n factoryOrgId?: string;\n}\n\n/** The slice of a session needed to apply stored observational-memory settings. */\nexport interface OMConfigurableSession {\n om: { observer: OMRoleSlice; reflector: OMRoleSlice };\n state: {\n get: () => Record<string, unknown> | undefined;\n set: (updates: OMStateWrites) => Promise<void> | void;\n };\n}\n\n/**\n * Apply a stored memory-settings row onto a session, so the DB — not whatever\n * happens to sit in persisted session state (e.g. a stale boot-time seed from\n * before memory settings moved to the DB) — is what the web surface reads and\n * what the session's OM actually runs with. The row is authoritative: knobs\n * without a stored value reset to the built-in defaults. This is the single\n * application path shared by the settings routes, coordinator hydration, and\n * the web session boot seed.\n */\nexport async function applyStoredMemorySettings(\n session: OMConfigurableSession,\n record: MemorySettingsRecord | null,\n fallbackOmModelId?: string,\n): Promise<void> {\n for (const role of ['observer', 'reflector'] as const) {\n const stored = role === 'observer' ? record?.observerModelId : record?.reflectorModelId;\n const target = stored ?? fallbackOmModelId ?? DEFAULT_OM_MODEL_ID;\n if (session.om[role].modelId() !== target) {\n await session.om[role].switchModel({ modelId: target });\n }\n }\n const state = session.state.get() ?? {};\n const updates: OMStateWrites = {};\n const observationThreshold = record?.observationThreshold ?? DEFAULT_OBSERVATION_THRESHOLD;\n if (state.observationThreshold !== observationThreshold) {\n updates.observationThreshold = observationThreshold;\n }\n const reflectionThreshold = record?.reflectionThreshold ?? DEFAULT_REFLECTION_THRESHOLD;\n if (state.reflectionThreshold !== reflectionThreshold) {\n updates.reflectionThreshold = reflectionThreshold;\n }\n const observeAttachments = record?.observeAttachments ?? 'auto';\n if ((state.observeAttachments ?? 'auto') !== observeAttachments) {\n updates.observeAttachments = observeAttachments;\n }\n if (Object.keys(updates).length > 0) await session.state.set(updates);\n}\n\nexport interface PersonalMemorySettingsArgs {\n /** Without the domain there is no personal preference to read. */\n memorySettings: Pick<MemorySettingsStorage, 'get'> | undefined;\n orgId: string;\n /** The human whose settings apply — for a channel session, the linked sender. */\n userId: string;\n}\n\n/**\n * Layer a user's own memory-settings row over whatever the session already runs\n * with, so a personal preference beats the one the project path resolved.\n *\n * The row wins only for the knobs the user has actually saved: an unsaved knob\n * keeps the value already on the session (the project's row, or the\n * provider-aware fallback resolved from the factory default model). That\n * distinction matters because `applyStoredMemorySettings` treats a null knob as\n * \"reset to the built-in default\", and switching observation onto an\n * uncredentialed provider would fail every observe cycle.\n *\n * Best-effort: a missing row, an uninitialized domain, or a read failure all\n * mean \"no personal preference\" and leave the session exactly as it was.\n */\nexport async function applyPersonalMemorySettings(\n session: OMConfigurableSession,\n { memorySettings, orgId, userId }: PersonalMemorySettingsArgs,\n): Promise<void> {\n if (!memorySettings) return;\n try {\n const record = await memorySettings.get({ orgId, userId });\n if (!record) return;\n const state = session.state.get() ?? {};\n await applyStoredMemorySettings(session, {\n ...record,\n observerModelId: record.observerModelId ?? session.om.observer.modelId() ?? null,\n reflectorModelId: record.reflectorModelId ?? session.om.reflector.modelId() ?? null,\n observationThreshold: record.observationThreshold ?? storedNumber(state.observationThreshold),\n reflectionThreshold: record.reflectionThreshold ?? storedNumber(state.reflectionThreshold),\n observeAttachments: record.observeAttachments ?? storedObserveAttachments(state.observeAttachments),\n });\n } catch (error) {\n console.warn(\"[Factory memory-settings hydration] Unable to apply the user's memory settings.\", error);\n }\n}\n\nfunction storedNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n\nfunction storedObserveAttachments(value: unknown): 'auto' | boolean | null {\n return value === 'auto' || typeof value === 'boolean' ? value : null;\n}\n\nexport interface MemorySettingsHydrationSession extends OMConfigurableSession {\n readonly identity: { getResourceId(): string };\n}\n\nexport interface MemorySettingsHydrationDependencies {\n /** GitHub-integration source-control rows — the only creator of web user sessions today. */\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n projects: Pick<FactoryProjectsStorage, 'get'>;\n memorySettings: Pick<MemorySettingsStorage, 'get'>;\n}\n\n/**\n * Seed a freshly created controller session's tenant org and its\n * observational-memory settings from the owner's source-control row. Registered\n * as a blocking session-created listener so the seed lands before the caller can\n * start a run.\n *\n * The org seed matters beyond settings. Subconscious knowledge curation scopes\n * every node and record on `factoryOrgId`; before the SDK refusal guard,\n * missing it made curation substitute the session owner id. For web chat sessions\n * that is the agent controller's own id rather than a tenant, so curated\n * knowledge landed under an org rung no reader ever queries. Same rule as the\n * start coordinator: the org\n * comes from the row the session was created from, never improvised from an\n * owner id.\n *\n * Tagged sessions may be coordinator-owned runs or web sessions that only carry\n * browser-seeded Factory state. This path resolves their source-control row and\n * reapplies a stored project settings row when one exists. If no project row\n * exists, it preserves the coordinator's provider-aware fallback.\n * Sessions without a GitHub source-control row (e.g. chat-only channel sessions)\n * hydrate through `hydrateFactorySession` with their own resolved tenant.\n * Best-effort: failures are logged, never thrown.\n */\nexport async function hydrateSessionMemorySettings(\n session: MemorySettingsHydrationSession,\n { sourceControl, projects, memorySettings }: MemorySettingsHydrationDependencies,\n): Promise<void> {\n const state = session.state.get() ?? {};\n const isFactoryRun = Boolean(state.factoryProjectId);\n try {\n const record = await sourceControl.sessions.getBySessionId(session.identity.getResourceId());\n // No row, or a row whose org is blank, leaves the session with no tenant.\n // Mark it rather than returning silently: an unmarked projectless factory\n // session is indistinguishable from a local one, and curation would file it\n // under the local scope — the same bug wearing a different rung.\n await seedSessionOrg(session, record?.orgId);\n if (!record) return;\n const factoryProjectId = isFactoryRun ? String(state.factoryProjectId) : undefined;\n const settings = await memorySettings.get({\n orgId: record.orgId,\n userId: factoryProjectId ? factoryMemorySettingsUserId(factoryProjectId) : record.userId,\n });\n // Coordinator hydration applies a provider-aware fallback when no project row\n // exists. Do not replace that fallback with the generic OM default here.\n if (factoryProjectId && !settings) return;\n const project = factoryProjectId ? await projects.get({ orgId: record.orgId, id: factoryProjectId }) : null;\n const provider = project?.defaultModelId?.split('/')[0];\n const fallbackOmModelId = provider\n ? resolveProviderOMDefault(provider, project.defaultModelId ?? undefined).modelId\n : undefined;\n await applyStoredMemorySettings(\n session,\n factoryProjectId && settings && !fallbackOmModelId\n ? {\n ...settings,\n observerModelId: settings?.observerModelId ?? session.om.observer.modelId() ?? null,\n reflectorModelId: settings?.reflectorModelId ?? session.om.reflector.modelId() ?? null,\n }\n : settings,\n fallbackOmModelId,\n );\n } catch (error) {\n console.warn('[Factory memory-settings hydration] Unable to apply stored memory settings.', error);\n // A failed lookup is an unresolved org, not an absent one — unless the seed\n // already landed and a later step is what threw.\n if (!session.state.get()?.factoryOrgId) await seedSessionOrg(session, undefined);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"memory-settings-hydration.js","names":[],"sources":["../../src/session/memory-settings-hydration.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\n\nimport type { MemorySettingsRecord } from '../storage/domains/memory-settings/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { hasResolvedOrg, seedSessionOrg } from './org-seed.js';\n\n/** Default thresholds mirror the TUI `/om` fallbacks. */\nexport const DEFAULT_OBSERVATION_THRESHOLD = 30_000;\nexport const DEFAULT_REFLECTION_THRESHOLD = 40_000;\n\n/**\n * Layer a channel sender's own row over the project's shared row. The sender\n * wins only for knobs they saved; an unsaved (`null`) knob keeps the project's\n * value rather than resetting to Auto or the built-in default, because the\n * project's choice may be the only credentialed one.\n */\nexport function layerPersonalMemorySettings(\n project: MemorySettingsRecord | null,\n personal: MemorySettingsRecord | null,\n): MemorySettingsRecord | null {\n if (!personal) return project;\n if (!project) return personal;\n return {\n ...project,\n observerModelId: personal.observerModelId ?? project.observerModelId,\n reflectorModelId: personal.reflectorModelId ?? project.reflectorModelId,\n observationThreshold: personal.observationThreshold ?? project.observationThreshold,\n reflectionThreshold: personal.reflectionThreshold ?? project.reflectionThreshold,\n observeAttachments: personal.observeAttachments ?? project.observeAttachments,\n };\n}\n\nexport interface MemorySettingsHydrationSession {\n readonly identity: { getResourceId(): string };\n state: {\n get: () => MastraCodeState | undefined;\n set: (updates: Partial<MastraCodeState>) => Promise<void> | void;\n };\n}\n\nexport interface MemorySettingsHydrationDependencies {\n /** GitHub-integration source-control rows — the only creator of web user sessions today. */\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n}\n\n/**\n * Seed a freshly created controller session's tenant org from its source-control\n * row. Memory settings remain DB-authoritative and are loaded into request\n * context for each invocation instead of being copied into mutable session\n * state.\n */\nexport async function hydrateSessionMemorySettings(\n session: MemorySettingsHydrationSession,\n { sourceControl }: MemorySettingsHydrationDependencies,\n): Promise<void> {\n const state: Partial<MastraCodeState> = session.state.get() ?? {};\n const isFactoryRun = Boolean(state.factoryProjectId);\n if (isFactoryRun && hasResolvedOrg(state.factoryOrgId)) return;\n\n try {\n const record = await sourceControl.sessions.getBySessionId(session.identity.getResourceId());\n await seedSessionOrg(session, record?.orgId);\n } catch (error) {\n console.warn('[Factory session hydration] Unable to resolve the session organization.', error);\n if (!session.state.get()?.factoryOrgId) await seedSessionOrg(session, undefined);\n }\n}\n"],"mappings":";;;AAOA,MAAa,gCAAgC;AAC7C,MAAa,+BAA+B;;;;;;;AAQ5C,SAAgB,4BACd,SACA,UAC6B;CAC7B,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL,GAAG;EACH,iBAAiB,SAAS,mBAAmB,QAAQ;EACrD,kBAAkB,SAAS,oBAAoB,QAAQ;EACvD,sBAAsB,SAAS,wBAAwB,QAAQ;EAC/D,qBAAqB,SAAS,uBAAuB,QAAQ;EAC7D,oBAAoB,SAAS,sBAAsB,QAAQ;CAC7D;AACF;;;;;;;AAuBA,eAAsB,6BACpB,SACA,EAAE,iBACa;CACf,MAAM,QAAkC,QAAQ,MAAM,IAAI,KAAK,CAAC;CAEhE,IADqB,QAAQ,MAAM,gBACpB,KAAK,eAAe,MAAM,YAAY,GAAG;CAExD,IAAI;EAEF,MAAM,eAAe,UAAS,MADT,cAAc,SAAS,eAAe,QAAQ,SAAS,cAAc,CAAC,EAAA,EACrD,KAAK;CAC7C,SAAS,OAAO;EACd,QAAQ,KAAK,2EAA2E,KAAK;EAC7F,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,EAAE,cAAc,MAAM,eAAe,SAAS,KAAA,CAAS;CACjF;AACF"}
@@ -18,23 +18,14 @@ export interface MemorySettingsRecord {
18
18
  createdAt: Date;
19
19
  updatedAt: Date;
20
20
  }
21
- /** Partial update — only the provided knobs are written. */
21
+ /** Partial update — only the provided knobs are written. `null` resets a model role to auto. */
22
22
  export interface MemorySettingsPatch {
23
- observerModelId?: string;
24
- reflectorModelId?: string;
23
+ observerModelId?: string | null;
24
+ reflectorModelId?: string | null;
25
25
  observationThreshold?: number;
26
26
  reflectionThreshold?: number;
27
27
  observeAttachments?: 'auto' | boolean;
28
28
  }
29
- /**
30
- * Knobs written only when the stored value is still unset (`NULL`). The check
31
- * happens inside the backend's atomic-update primitive, so a concurrent
32
- * explicit write to the same knob is never clobbered by a fill.
33
- */
34
- export interface MemorySettingsFillIfUnset {
35
- observerModelId?: string;
36
- reflectorModelId?: string;
37
- }
38
29
  /**
39
30
  * Sentinel `user_id` for a factory project's shared memory settings. Factory
40
31
  * runs are org-shared, so their OM configuration lives on the project — this
@@ -55,16 +46,13 @@ export declare class MemorySettingsStorage extends FactoryStorageDomain {
55
46
  }): Promise<MemorySettingsRecord | null>;
56
47
  /**
57
48
  * Upsert the user's row, writing only the knobs present in `patch`.
58
- * `fillIfUnset` knobs are written only where the stored value is still
59
- * `NULL`, decided inside the atomic update so concurrent explicit writes
60
- * win over fills. Concurrent first writes are resolved via the shared
49
+ * Concurrent first writes are resolved via the shared
61
50
  * insert-then-catch-unique-violation pattern (see `queue_health`).
62
51
  */
63
- patch({ orgId, userId, patch, fillIfUnset, }: {
52
+ patch({ orgId, userId, patch, }: {
64
53
  orgId: string;
65
54
  userId: string;
66
55
  patch: MemorySettingsPatch;
67
- fillIfUnset?: MemorySettingsFillIfUnset;
68
56
  }): Promise<MemorySettingsRecord>;
69
57
  }
70
58
  //# sourceMappingURL=base.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/memory-settings/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,kBAAkB,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC5C,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,eAAO,MAAM,sBAAsB,EAAE,gBAgBpC,CAAC;AAuCF,qBAAa,qBAAsB,SAAQ,oBAAoB;;;IAKvD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQpC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAKrG;;;;;;OAMG;IACG,KAAK,CAAC,EACV,KAAK,EACL,MAAM,EACN,KAAK,EACL,WAAW,GACZ,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,mBAAmB,CAAC;QAC3B,WAAW,CAAC,EAAE,yBAAyB,CAAC;KACzC,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAuClC"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/memory-settings/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,kBAAkB,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC5C,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,gGAAgG;AAChG,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,eAAO,MAAM,sBAAsB,EAAE,gBAgBpC,CAAC;AAuCF,qBAAa,qBAAsB,SAAQ,oBAAoB;;;IAKvD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQpC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAKrG;;;;OAIG;IACG,KAAK,CAAC,EACV,KAAK,EACL,MAAM,EACN,KAAK,GACN,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,mBAAmB,CAAC;KAC5B,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAiClC"}
@@ -88,33 +88,26 @@ var MemorySettingsStorage = class extends FactoryStorageDomain {
88
88
  }
89
89
  /**
90
90
  * Upsert the user's row, writing only the knobs present in `patch`.
91
- * `fillIfUnset` knobs are written only where the stored value is still
92
- * `NULL`, decided inside the atomic update so concurrent explicit writes
93
- * win over fills. Concurrent first writes are resolved via the shared
91
+ * Concurrent first writes are resolved via the shared
94
92
  * insert-then-catch-unique-violation pattern (see `queue_health`).
95
93
  */
96
- async patch({ orgId, userId, patch, fillIfUnset }) {
94
+ async patch({ orgId, userId, patch }) {
97
95
  const now = /* @__PURE__ */ new Date();
98
96
  const updateExisting = () => this.#db.updateAtomic("memory_settings", {
99
97
  org_id: orgId,
100
98
  user_id: userId
101
- }, (row) => {
102
- const columns = {
103
- ...patchToColumns(patch),
104
- updated_at: now
105
- };
106
- if (fillIfUnset?.observerModelId !== void 0 && row.observer_model_id == null) columns.observer_model_id = patch.observerModelId ?? fillIfUnset.observerModelId;
107
- if (fillIfUnset?.reflectorModelId !== void 0 && row.reflector_model_id == null) columns.reflector_model_id = patch.reflectorModelId ?? fillIfUnset.reflectorModelId;
108
- return columns;
109
- });
99
+ }, () => ({
100
+ ...patchToColumns(patch),
101
+ updated_at: now
102
+ }));
110
103
  const updated = await updateExisting();
111
104
  if (updated) return toRecord(updated);
112
105
  try {
113
106
  return toRecord(await this.#db.insertOne("memory_settings", {
114
107
  org_id: orgId,
115
108
  user_id: userId,
116
- observer_model_id: fillIfUnset?.observerModelId ?? null,
117
- reflector_model_id: fillIfUnset?.reflectorModelId ?? null,
109
+ observer_model_id: null,
110
+ reflector_model_id: null,
118
111
  observation_threshold: null,
119
112
  reflection_threshold: null,
120
113
  observe_attachments: null,
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/memory-settings/base.ts"],"sourcesContent":["import { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\n/**\n * A user's persisted memory (observational-memory) configuration. The\n * DB-backed tenant counterpart of the local `settings.json` OM fields\n * (`observerModelOverride`, `reflectorModelOverride`, `omObservationThreshold`,\n * `omReflectionThreshold`, `omObserveAttachments`): one row per `(org, user)`,\n * every knob nullable so only explicitly-changed values are stored.\n */\nexport interface MemorySettingsRecord {\n orgId: string;\n userId: string;\n observerModelId: string | null;\n reflectorModelId: string | null;\n observationThreshold: number | null;\n reflectionThreshold: number | null;\n observeAttachments: 'auto' | boolean | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/** Partial update — only the provided knobs are written. */\nexport interface MemorySettingsPatch {\n observerModelId?: string;\n reflectorModelId?: string;\n observationThreshold?: number;\n reflectionThreshold?: number;\n observeAttachments?: 'auto' | boolean;\n}\n\n/**\n * Knobs written only when the stored value is still unset (`NULL`). The check\n * happens inside the backend's atomic-update primitive, so a concurrent\n * explicit write to the same knob is never clobbered by a fill.\n */\nexport interface MemorySettingsFillIfUnset {\n observerModelId?: string;\n reflectorModelId?: string;\n}\n\n/**\n * Sentinel `user_id` for a factory project's shared memory settings. Factory\n * runs are org-shared, so their OM configuration lives on the project — this\n * key reuses the same `(org, user)` table without a schema change. Real user\n * ids are WorkOS ids (`user_…`) or the `local` sentinel, so the prefix cannot\n * collide.\n */\nexport function factoryMemorySettingsUserId(factoryProjectId: string): string {\n return `factory-project:${factoryProjectId}`;\n}\n\nexport const MEMORY_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'memory_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n observer_model_id: { type: 'text', nullable: true },\n reflector_model_id: { type: 'text', nullable: true },\n observation_threshold: { type: 'integer', nullable: true },\n reflection_threshold: { type: 'integer', nullable: true },\n // 'auto' | true | false — json keeps the tri-state without string encoding.\n observe_attachments: { type: 'json', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'memory_settings_org_user_key', columns: ['org_id', 'user_id'] }],\n};\n\ninterface MemorySettingsDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n user_id: string;\n observer_model_id: string | null;\n reflector_model_id: string | null;\n observation_threshold: number | null;\n reflection_threshold: number | null;\n observe_attachments: 'auto' | boolean | null;\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toRecord(row: MemorySettingsDbRow): MemorySettingsRecord {\n return {\n orgId: row.org_id,\n userId: row.user_id,\n observerModelId: row.observer_model_id,\n reflectorModelId: row.reflector_model_id,\n observationThreshold: row.observation_threshold,\n reflectionThreshold: row.reflection_threshold,\n observeAttachments: row.observe_attachments,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction patchToColumns(patch: MemorySettingsPatch): Partial<MemorySettingsDbRow> {\n const columns: Partial<MemorySettingsDbRow> = {};\n if (patch.observerModelId !== undefined) columns.observer_model_id = patch.observerModelId;\n if (patch.reflectorModelId !== undefined) columns.reflector_model_id = patch.reflectorModelId;\n if (patch.observationThreshold !== undefined) columns.observation_threshold = patch.observationThreshold;\n if (patch.reflectionThreshold !== undefined) columns.reflection_threshold = patch.reflectionThreshold;\n if (patch.observeAttachments !== undefined) columns.observe_attachments = patch.observeAttachments;\n return columns;\n}\n\nexport class MemorySettingsStorage extends FactoryStorageDomain {\n constructor() {\n super('memory-settings');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([MEMORY_SETTINGS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('memory_settings', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async get({ orgId, userId }: { orgId: string; userId: string }): Promise<MemorySettingsRecord | null> {\n const row = await this.#db.findOne<MemorySettingsDbRow>('memory_settings', { org_id: orgId, user_id: userId });\n return row ? toRecord(row) : null;\n }\n\n /**\n * Upsert the user's row, writing only the knobs present in `patch`.\n * `fillIfUnset` knobs are written only where the stored value is still\n * `NULL`, decided inside the atomic update so concurrent explicit writes\n * win over fills. Concurrent first writes are resolved via the shared\n * insert-then-catch-unique-violation pattern (see `queue_health`).\n */\n async patch({\n orgId,\n userId,\n patch,\n fillIfUnset,\n }: {\n orgId: string;\n userId: string;\n patch: MemorySettingsPatch;\n fillIfUnset?: MemorySettingsFillIfUnset;\n }): Promise<MemorySettingsRecord> {\n const now = new Date();\n const updateExisting = () =>\n this.#db.updateAtomic<MemorySettingsDbRow>('memory_settings', { org_id: orgId, user_id: userId }, row => {\n const columns: Partial<MemorySettingsDbRow> = { ...patchToColumns(patch), updated_at: now };\n if (fillIfUnset?.observerModelId !== undefined && row.observer_model_id == null) {\n columns.observer_model_id = patch.observerModelId ?? fillIfUnset.observerModelId;\n }\n if (fillIfUnset?.reflectorModelId !== undefined && row.reflector_model_id == null) {\n columns.reflector_model_id = patch.reflectorModelId ?? fillIfUnset.reflectorModelId;\n }\n return columns;\n });\n\n const updated = await updateExisting();\n if (updated) return toRecord(updated);\n\n try {\n const row = await this.#db.insertOne<MemorySettingsDbRow>('memory_settings', {\n org_id: orgId,\n user_id: userId,\n observer_model_id: fillIfUnset?.observerModelId ?? null,\n reflector_model_id: fillIfUnset?.reflectorModelId ?? null,\n observation_threshold: null,\n reflection_threshold: null,\n observe_attachments: null,\n ...patchToColumns(patch),\n created_at: now,\n updated_at: now,\n });\n return toRecord(row);\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the first-write race — apply the patch to the winning row.\n const row = await updateExisting();\n if (!row) throw error;\n return toRecord(row);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/memory-settings/base.ts"],"sourcesContent":["import { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\n/**\n * A user's persisted memory (observational-memory) configuration. The\n * DB-backed tenant counterpart of the local `settings.json` OM fields\n * (`observerModelOverride`, `reflectorModelOverride`, `omObservationThreshold`,\n * `omReflectionThreshold`, `omObserveAttachments`): one row per `(org, user)`,\n * every knob nullable so only explicitly-changed values are stored.\n */\nexport interface MemorySettingsRecord {\n orgId: string;\n userId: string;\n observerModelId: string | null;\n reflectorModelId: string | null;\n observationThreshold: number | null;\n reflectionThreshold: number | null;\n observeAttachments: 'auto' | boolean | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/** Partial update — only the provided knobs are written. `null` resets a model role to auto. */\nexport interface MemorySettingsPatch {\n observerModelId?: string | null;\n reflectorModelId?: string | null;\n observationThreshold?: number;\n reflectionThreshold?: number;\n observeAttachments?: 'auto' | boolean;\n}\n\n/**\n * Sentinel `user_id` for a factory project's shared memory settings. Factory\n * runs are org-shared, so their OM configuration lives on the project — this\n * key reuses the same `(org, user)` table without a schema change. Real user\n * ids are WorkOS ids (`user_…`) or the `local` sentinel, so the prefix cannot\n * collide.\n */\nexport function factoryMemorySettingsUserId(factoryProjectId: string): string {\n return `factory-project:${factoryProjectId}`;\n}\n\nexport const MEMORY_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'memory_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n observer_model_id: { type: 'text', nullable: true },\n reflector_model_id: { type: 'text', nullable: true },\n observation_threshold: { type: 'integer', nullable: true },\n reflection_threshold: { type: 'integer', nullable: true },\n // 'auto' | true | false — json keeps the tri-state without string encoding.\n observe_attachments: { type: 'json', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'memory_settings_org_user_key', columns: ['org_id', 'user_id'] }],\n};\n\ninterface MemorySettingsDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n user_id: string;\n observer_model_id: string | null;\n reflector_model_id: string | null;\n observation_threshold: number | null;\n reflection_threshold: number | null;\n observe_attachments: 'auto' | boolean | null;\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toRecord(row: MemorySettingsDbRow): MemorySettingsRecord {\n return {\n orgId: row.org_id,\n userId: row.user_id,\n observerModelId: row.observer_model_id,\n reflectorModelId: row.reflector_model_id,\n observationThreshold: row.observation_threshold,\n reflectionThreshold: row.reflection_threshold,\n observeAttachments: row.observe_attachments,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction patchToColumns(patch: MemorySettingsPatch): Partial<MemorySettingsDbRow> {\n const columns: Partial<MemorySettingsDbRow> = {};\n if (patch.observerModelId !== undefined) columns.observer_model_id = patch.observerModelId;\n if (patch.reflectorModelId !== undefined) columns.reflector_model_id = patch.reflectorModelId;\n if (patch.observationThreshold !== undefined) columns.observation_threshold = patch.observationThreshold;\n if (patch.reflectionThreshold !== undefined) columns.reflection_threshold = patch.reflectionThreshold;\n if (patch.observeAttachments !== undefined) columns.observe_attachments = patch.observeAttachments;\n return columns;\n}\n\nexport class MemorySettingsStorage extends FactoryStorageDomain {\n constructor() {\n super('memory-settings');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([MEMORY_SETTINGS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('memory_settings', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async get({ orgId, userId }: { orgId: string; userId: string }): Promise<MemorySettingsRecord | null> {\n const row = await this.#db.findOne<MemorySettingsDbRow>('memory_settings', { org_id: orgId, user_id: userId });\n return row ? toRecord(row) : null;\n }\n\n /**\n * Upsert the user's row, writing only the knobs present in `patch`.\n * Concurrent first writes are resolved via the shared\n * insert-then-catch-unique-violation pattern (see `queue_health`).\n */\n async patch({\n orgId,\n userId,\n patch,\n }: {\n orgId: string;\n userId: string;\n patch: MemorySettingsPatch;\n }): Promise<MemorySettingsRecord> {\n const now = new Date();\n const updateExisting = () =>\n this.#db.updateAtomic<MemorySettingsDbRow>('memory_settings', { org_id: orgId, user_id: userId }, () => ({\n ...patchToColumns(patch),\n updated_at: now,\n }));\n\n const updated = await updateExisting();\n if (updated) return toRecord(updated);\n\n try {\n const row = await this.#db.insertOne<MemorySettingsDbRow>('memory_settings', {\n org_id: orgId,\n user_id: userId,\n observer_model_id: null,\n reflector_model_id: null,\n observation_threshold: null,\n reflection_threshold: null,\n observe_attachments: null,\n ...patchToColumns(patch),\n created_at: now,\n updated_at: now,\n });\n return toRecord(row);\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the first-write race — apply the patch to the winning row.\n const row = await updateExisting();\n if (!row) throw error;\n return toRecord(row);\n }\n }\n}\n"],"mappings":";;;;;;;;;AAsCA,SAAgB,4BAA4B,kBAAkC;CAC5E,OAAO,mBAAmB;AAC5B;AAEA,MAAa,yBAA2C;CACtD,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,SAAS,EAAE,MAAM,OAAO;EACxB,mBAAmB;GAAE,MAAM;GAAQ,UAAU;EAAK;EAClD,oBAAoB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACnD,uBAAuB;GAAE,MAAM;GAAW,UAAU;EAAK;EACzD,sBAAsB;GAAE,MAAM;GAAW,UAAU;EAAK;EAExD,qBAAqB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACpD,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CAAC;EAAE,MAAM;EAAgC,SAAS,CAAC,UAAU,SAAS;CAAE,CAAC;AAC1F;AAeA,SAAS,SAAS,KAAgD;CAChE,OAAO;EACL,OAAO,IAAI;EACX,QAAQ,IAAI;EACZ,iBAAiB,IAAI;EACrB,kBAAkB,IAAI;EACtB,sBAAsB,IAAI;EAC1B,qBAAqB,IAAI;EACzB,oBAAoB,IAAI;EACxB,WAAW,IAAI;EACf,WAAW,IAAI;CACjB;AACF;AAEA,SAAS,eAAe,OAA0D;CAChF,MAAM,UAAwC,CAAC;CAC/C,IAAI,MAAM,oBAAoB,KAAA,GAAW,QAAQ,oBAAoB,MAAM;CAC3E,IAAI,MAAM,qBAAqB,KAAA,GAAW,QAAQ,qBAAqB,MAAM;CAC7E,IAAI,MAAM,yBAAyB,KAAA,GAAW,QAAQ,wBAAwB,MAAM;CACpF,IAAI,MAAM,wBAAwB,KAAA,GAAW,QAAQ,uBAAuB,MAAM;CAClF,IAAI,MAAM,uBAAuB,KAAA,GAAW,QAAQ,sBAAsB,MAAM;CAChF,OAAO;AACT;AAEA,IAAa,wBAAb,cAA2C,qBAAqB;CAC9D,cAAc;EACZ,MAAM,iBAAiB;CACzB;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,sBAAsB,CAAC;CACvD;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,mBAAmB,CAAC,CAAC;CACjD;CAEA,IAAIA,MAAyB;EAC3B,OAAO,KAAK;CACd;CAEA,MAAM,IAAI,EAAE,OAAO,UAAmF;EACpG,MAAM,MAAM,MAAM,KAAKA,IAAI,QAA6B,mBAAmB;GAAE,QAAQ;GAAO,SAAS;EAAO,CAAC;EAC7G,OAAO,MAAM,SAAS,GAAG,IAAI;CAC/B;;;;;;CAOA,MAAM,MAAM,EACV,OACA,QACA,SAKgC;EAChC,MAAM,sBAAM,IAAI,KAAK;EACrB,MAAM,uBACJ,KAAKA,IAAI,aAAkC,mBAAmB;GAAE,QAAQ;GAAO,SAAS;EAAO,UAAU;GACvG,GAAG,eAAe,KAAK;GACvB,YAAY;EACd,EAAE;EAEJ,MAAM,UAAU,MAAM,eAAe;EACrC,IAAI,SAAS,OAAO,SAAS,OAAO;EAEpC,IAAI;GAaF,OAAO,SAAS,MAZE,KAAKA,IAAI,UAA+B,mBAAmB;IAC3E,QAAQ;IACR,SAAS;IACT,mBAAmB;IACnB,oBAAoB;IACpB,uBAAuB;IACvB,sBAAsB;IACtB,qBAAqB;IACrB,GAAG,eAAe,KAAK;IACvB,YAAY;IACZ,YAAY;GACd,CAAC,CACkB;EACrB,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;GAEpD,MAAM,MAAM,MAAM,eAAe;GACjC,IAAI,CAAC,KAAK,MAAM;GAChB,OAAO,SAAS,GAAG;EACrB;CACF;AACF"}
@@ -7,7 +7,6 @@
7
7
  import type { MastraCodeState } from '@mastra/code-sdk/schema';
8
8
  import type { AgentController } from '@mastra/core/agent-controller';
9
9
  import type { RequestContext } from '@mastra/core/request-context';
10
- import type { MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';
11
10
  import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
12
11
  import type { SupervisorScope } from './read-tools.js';
13
12
  type FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;
@@ -26,13 +25,12 @@ export declare function resolveSupervisorScope(options: {
26
25
  }): Promise<SupervisorScope | null>;
27
26
  /**
28
27
  * Session-start hook: stamp the owning project onto a supervisor session and
29
- * apply the factory's default model and memory settings. Runs on every
30
- * (re)creation so a restarted server heals the in-memory state. Sessions
28
+ * apply the factory's default model. Runs on every (re)creation so a restarted
29
+ * server heals the in-memory state. Sessions
31
30
  * that are not supervisors are left untouched.
32
31
  */
33
32
  export declare function hydrateSupervisorSession(session: FactorySession, deps: {
34
33
  projects: Pick<FactoryProjectsStorage, 'getById'>;
35
- memorySettings?: MemorySettingsStorage;
36
34
  }): Promise<void>;
37
35
  export {};
38
36
  //# sourceMappingURL=session.d.ts.map