@mastra/factory 0.10.0-alpha.14 → 0.10.0-alpha.3

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 (123) hide show
  1. package/CHANGELOG.md +0 -180
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +0 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/factory.d.ts +0 -8
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +6 -23
  8. package/dist/factory.js.map +1 -1
  9. package/dist/index.d.ts +0 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -2
  12. package/dist/integrations/base.d.ts +1 -3
  13. package/dist/integrations/base.d.ts.map +1 -1
  14. package/dist/integrations/github/integration.d.ts +0 -14
  15. package/dist/integrations/github/integration.d.ts.map +1 -1
  16. package/dist/integrations/github/integration.js +1 -48
  17. package/dist/integrations/github/integration.js.map +1 -1
  18. package/dist/integrations/github/provenance.d.ts +2 -7
  19. package/dist/integrations/github/provenance.d.ts.map +1 -1
  20. package/dist/integrations/github/provenance.js +2 -16
  21. package/dist/integrations/github/provenance.js.map +1 -1
  22. package/dist/integrations/github/routes.d.ts +1 -8
  23. package/dist/integrations/github/routes.d.ts.map +1 -1
  24. package/dist/integrations/github/routes.js +7 -228
  25. package/dist/integrations/github/routes.js.map +1 -1
  26. package/dist/integrations/github/session-subscriptions.d.ts +0 -9
  27. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  28. package/dist/integrations/github/session-subscriptions.js +1 -39
  29. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  30. package/dist/integrations/linear/routes.d.ts.map +1 -1
  31. package/dist/integrations/linear/routes.js +0 -50
  32. package/dist/integrations/linear/routes.js.map +1 -1
  33. package/dist/integrations/platform/github/integration.d.ts +1 -3
  34. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  35. package/dist/integrations/platform/github/integration.js +0 -33
  36. package/dist/integrations/platform/github/integration.js.map +1 -1
  37. package/dist/routes/intake.d.ts +0 -5
  38. package/dist/routes/intake.d.ts.map +1 -1
  39. package/dist/routes/intake.js +19 -70
  40. package/dist/routes/intake.js.map +1 -1
  41. package/dist/routes/surface.d.ts +3 -4
  42. package/dist/routes/surface.d.ts.map +1 -1
  43. package/dist/routes/surface.js +12 -10
  44. package/dist/routes/surface.js.map +1 -1
  45. package/dist/routes/work-items.d.ts.map +1 -1
  46. package/dist/routes/work-items.js +1 -1
  47. package/dist/routes/work-items.js.map +1 -1
  48. package/dist/rules/defaults.js +5 -5
  49. package/dist/rules/defaults.js.map +1 -1
  50. package/dist/rules/dispatcher.d.ts +0 -9
  51. package/dist/rules/dispatcher.d.ts.map +1 -1
  52. package/dist/rules/dispatcher.js +17 -69
  53. package/dist/rules/dispatcher.js.map +1 -1
  54. package/dist/rules/processor.d.ts.map +1 -1
  55. package/dist/rules/processor.js +11 -28
  56. package/dist/rules/processor.js.map +1 -1
  57. package/dist/rules/tools.d.ts.map +1 -1
  58. package/dist/rules/tools.js +5 -9
  59. package/dist/rules/tools.js.map +1 -1
  60. package/dist/rules/transition-service.d.ts +2 -5
  61. package/dist/rules/transition-service.d.ts.map +1 -1
  62. package/dist/rules/transition-service.js +2 -15
  63. package/dist/rules/transition-service.js.map +1 -1
  64. package/dist/rules/types.d.ts +1 -4
  65. package/dist/rules/types.d.ts.map +1 -1
  66. package/dist/rules/types.js +1 -17
  67. package/dist/rules/types.js.map +1 -1
  68. package/dist/session/factory-session.d.ts +3 -9
  69. package/dist/session/factory-session.d.ts.map +1 -1
  70. package/dist/session/factory-session.js +4 -5
  71. package/dist/session/factory-session.js.map +1 -1
  72. package/dist/session/first-exec-capture.d.ts +10 -16
  73. package/dist/session/first-exec-capture.d.ts.map +1 -1
  74. package/dist/session/first-exec-capture.js +14 -74
  75. package/dist/session/first-exec-capture.js.map +1 -1
  76. package/dist/session/first-message-capture.d.ts +10 -14
  77. package/dist/session/first-message-capture.d.ts.map +1 -1
  78. package/dist/session/first-message-capture.js +11 -17
  79. package/dist/session/first-message-capture.js.map +1 -1
  80. package/dist/storage/domains/credentials/base.d.ts +1 -3
  81. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  82. package/dist/storage/domains/credentials/base.js +15 -33
  83. package/dist/storage/domains/credentials/base.js.map +1 -1
  84. package/dist/storage/domains/custom-providers/base.d.ts +1 -3
  85. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
  86. package/dist/storage/domains/custom-providers/base.js +25 -45
  87. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  88. package/dist/storage/domains/integrations/base.d.ts +1 -3
  89. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  90. package/dist/storage/domains/integrations/base.js +12 -28
  91. package/dist/storage/domains/integrations/base.js.map +1 -1
  92. package/dist/storage/domains/source-control/base.d.ts +0 -5
  93. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  94. package/dist/storage/domains/source-control/base.js +0 -6
  95. package/dist/storage/domains/source-control/base.js.map +1 -1
  96. package/dist/storage/domains/source-control/inmemory.d.ts +0 -4
  97. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  98. package/dist/storage/domains/source-control/inmemory.js +0 -7
  99. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  100. package/dist/storage/domains/work-items/base.d.ts +2 -21
  101. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  102. package/dist/storage/domains/work-items/base.js +9 -65
  103. package/dist/storage/domains/work-items/base.js.map +1 -1
  104. package/factory-skills/factory-rereview/SKILL.md +0 -2
  105. package/factory-skills/factory-review/SKILL.md +0 -2
  106. package/factory-skills/factory-triage/SKILL.md +17 -7
  107. package/package.json +9 -9
  108. package/dist/secret-encryption.d.ts +0 -25
  109. package/dist/secret-encryption.d.ts.map +0 -1
  110. package/dist/secret-encryption.js +0 -96
  111. package/dist/secret-encryption.js.map +0 -1
  112. package/dist/session/session-title.d.ts +0 -7
  113. package/dist/session/session-title.d.ts.map +0 -1
  114. package/dist/session/session-title.js +0 -14
  115. package/dist/session/session-title.js.map +0 -1
  116. package/dist/session/thread-title-mirror.d.ts +0 -35
  117. package/dist/session/thread-title-mirror.d.ts.map +0 -1
  118. package/dist/session/thread-title-mirror.js +0 -51
  119. package/dist/session/thread-title-mirror.js.map +0 -1
  120. package/dist/work-item-branch.d.ts +0 -23
  121. package/dist/work-item-branch.d.ts.map +0 -1
  122. package/dist/work-item-branch.js +0 -39
  123. package/dist/work-item-branch.js.map +0 -1
@@ -22,11 +22,6 @@ export interface EnsureFactorySourceSessionArgs {
22
22
  branch: string;
23
23
  /** Pick a specific linked repository by slug. Defaults to the first linked repository. */
24
24
  repositorySlug?: string;
25
- /**
26
- * Attribute the run to this user instead of the repo connector. Set when the
27
- * run has an interactive user — e.g. the person who approved a proposed run.
28
- */
29
- attributeToUserId?: string;
30
25
  }
31
26
  export interface EnsuredFactorySourceSession {
32
27
  sessionId: string;
@@ -99,10 +94,9 @@ export declare function resolveFactoryProjectForSession(args: {
99
94
  * its linked repositories, and a session on the requested branch with the
100
95
  * repository's pinned or default branch as the base.
101
96
  *
102
- * The run is attributed to `attributeToUserId` when the caller has an
103
- * interactive user (e.g. the approver of a proposed run), and otherwise falls
104
- * back to whoever connected the repository (`connection.createdByUserId`),
105
- * because a genuinely autonomous run has no interactive user of its own.
97
+ * The run is attributed to whoever connected the repository
98
+ * (`connection.createdByUserId`), because an autonomous run has no interactive
99
+ * user of its own.
106
100
  */
107
101
  export declare function ensureFactorySourceSession(args: EnsureFactorySourceSessionArgs): Promise<EnsuredFactorySourceSession>;
108
102
  export interface HydrateFactorySessionArgs {
@@ -1 +1 @@
1
- {"version":3,"file":"factory-session.d.ts","sourceRoot":"","sources":["../../src/session/factory-session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory-session.d.ts","sourceRoot":"","sources":["../../src/session/factory-session.ts"],"names":[],"mappings":"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"}
@@ -109,10 +109,9 @@ async function resolveFactoryProjectForSession(args) {
109
109
  * its linked repositories, and a session on the requested branch with the
110
110
  * repository's pinned or default branch as the base.
111
111
  *
112
- * The run is attributed to `attributeToUserId` when the caller has an
113
- * interactive user (e.g. the approver of a proposed run), and otherwise falls
114
- * back to whoever connected the repository (`connection.createdByUserId`),
115
- * because a genuinely autonomous run has no interactive user of its own.
112
+ * The run is attributed to whoever connected the repository
113
+ * (`connection.createdByUserId`), because an autonomous run has no interactive
114
+ * user of its own.
116
115
  */
117
116
  async function ensureFactorySourceSession(args) {
118
117
  const { sourceControl, orgId, factoryProjectId, branch, repositorySlug } = args;
@@ -123,7 +122,7 @@ async function ensureFactorySourceSession(args) {
123
122
  repositorySlug
124
123
  });
125
124
  if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);
126
- const userId = args.attributeToUserId ?? resolved.connectedByUserId;
125
+ const userId = resolved.connectedByUserId;
127
126
  const session = await sourceControl.sessions.create({
128
127
  sessionId: randomUUID(),
129
128
  projectRepositoryId: resolved.projectRepositoryId,
@@ -1 +1 @@
1
- {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { 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 type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { applyStoredMemorySettings } from './memory-settings-hydration.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\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 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 a specific linked repository by slug. Defaults to the first linked repository. */\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(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n /** Pick a specific linked repository by slug. Defaults to the first linked repository. */\n repositorySlug?: string;\n}): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId, repositorySlug } = 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 && (!repositorySlug || candidate.repository.slug === repositorySlug),\n );\n } catch {\n // The connection no longer resolves (e.g. its installation was deleted).\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: 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 * 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 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 provider = args.defaultModelId?.split('/')[0];\n const fallbackOmModelId = provider ? resolveProviderOMDefault(provider, args.defaultModelId).modelId : 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({ modelId: 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 }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { 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 type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { applyStoredMemorySettings } from './memory-settings-hydration.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\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 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 a specific linked repository by slug. Defaults to the first linked repository. */\n repositorySlug?: 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(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n /** Pick a specific linked repository by slug. Defaults to the first linked repository. */\n repositorySlug?: string;\n}): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId, repositorySlug } = 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 && (!repositorySlug || candidate.repository.slug === repositorySlug),\n );\n } catch {\n // The connection no longer resolves (e.g. its installation was deleted).\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 whoever connected the repository\n * (`connection.createdByUserId`), because an autonomous run has no interactive\n * 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 = resolved.connectedByUserId;\n const session = await sourceControl.sessions.create({\n sessionId: 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 * 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 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 provider = args.defaultModelId?.split('/')[0];\n const fallbackOmModelId = provider ? resolveProviderOMDefault(provider, args.defaultModelId).modelId : 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({ modelId: 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 }\n}\n"],"mappings":"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"}
@@ -12,24 +12,18 @@ export interface FirstExecCaptureDependencies {
12
12
  };
13
13
  }
14
14
  /**
15
- * Record when a session's agent completed its first successful meaningful
16
- * tool call (the TTFME anchor — "time to first meaningful exec").
15
+ * Record when a session's agent completed its first successful sandbox exec
16
+ * (the TTFME anchor — "time to first meaningful exec").
17
17
  *
18
- * Meaningful = a workspace tool the agent invoked itself (filesystem,
19
- * search, code intelligence, sandbox exec, process control). We accept any
20
- * tool whose name starts with the workspace prefix `mastra_workspace_` or
21
- * matches the post-remap mastracode tool names. Non-workspace tools (memory,
22
- * notification inbox, subagent, etc.) and skill-loader / preflight /
23
- * materializer `sandbox.executeCommand()` calls that never flow through the
24
- * tool layer are intentionally excluded.
18
+ * `command_exit` is emitted only for the agent's own foreground
19
+ * `execute_command` tool calls: direct `sandbox.executeCommand()` calls from
20
+ * the skill loader / preflight / materializer never flow through the tool, so
21
+ * the first successful exit event is definitionally the agent's first
22
+ * meaningful exec — no command classifier needed. Background spawns emit no
23
+ * exit event at spawn time and are intentionally excluded.
25
24
  *
26
- * Failed tool calls (`isError === true`) don't count. Approval-denied and
27
- * abort-while-parked tool completions (`denied === true`) also don't count:
28
- * the run-engine emits `tool_end` with `isError: false` for those paths
29
- * because the tool didn't fail, but it also never ran, so treating them as
30
- * "meaningful exec" would re-introduce the same contamination this fix is
31
- * removing on the message side. The listener stays subscribed until a
32
- * successful qualifying end, then unsubscribes. The storage write is guarded
25
+ * Failed commands (nonzero exit) don't count; the listener stays subscribed
26
+ * until a successful exit, then unsubscribes. The storage write is guarded
33
27
  * (`first_meaningful_exec_at IS NULL`), so restarts, re-materialized
34
28
  * sessions, and sessions without a source-control row are no-ops.
35
29
  */
@@ -1 +1 @@
1
- {"version":3,"file":"first-exec-capture.d.ts","sourceRoot":"","sources":["../../src/session/first-exec-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAG1E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAE5F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,4BAA4B;IAC3C,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,yBAAyB,CAAC,CAAC;KACnF,CAAC;CACH;AAmCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,uBAAuB,EAChC,EAAE,aAAa,EAAE,EAAE,4BAA4B,GAC9C,MAAM,IAAI,CAoDZ"}
1
+ {"version":3,"file":"first-exec-capture.d.ts","sourceRoot":"","sources":["../../src/session/first-exec-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAE1E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAE5F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,4BAA4B;IAC3C,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,yBAAyB,CAAC,CAAC;KACnF,CAAC;CACH;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,uBAAuB,EAChC,EAAE,aAAa,EAAE,EAAE,4BAA4B,GAC9C,MAAM,IAAI,CAWZ"}
@@ -1,87 +1,27 @@
1
- import { WORKSPACE_TOOLS_PREFIX } from "@mastra/core/workspace";
2
1
  //#region src/session/first-exec-capture.ts
3
2
  /**
4
- * Tool names that count as "meaningful exec" independent of the workspace
5
- * prefix. The workspace tools get remapped from `mastra_workspace_*` to
6
- * mastracode-friendly names (see `mastracode/sdk/src/tool-names.ts`), and
7
- * this listener runs on the emitted tool name after remap, so both spellings
8
- * need to match.
9
- */
10
- const MEANINGFUL_TOOL_NAMES = /* @__PURE__ */ new Set([
11
- "view",
12
- "write_file",
13
- "string_replace_lsp",
14
- "find_files",
15
- "delete_file",
16
- "file_stat",
17
- "mkdir",
18
- "search_content",
19
- "ast_smart_edit",
20
- "lsp_inspect",
21
- "execute_command",
22
- "get_process_output",
23
- "kill_process"
24
- ]);
25
- function isMeaningfulToolName(name) {
26
- if (!name) return false;
27
- if (name.startsWith(`${WORKSPACE_TOOLS_PREFIX}_`)) return true;
28
- return MEANINGFUL_TOOL_NAMES.has(name);
29
- }
30
- /**
31
- * Record when a session's agent completed its first successful meaningful
32
- * tool call (the TTFME anchor — "time to first meaningful exec").
3
+ * Record when a session's agent completed its first successful sandbox exec
4
+ * (the TTFME anchor — "time to first meaningful exec").
33
5
  *
34
- * Meaningful = a workspace tool the agent invoked itself (filesystem,
35
- * search, code intelligence, sandbox exec, process control). We accept any
36
- * tool whose name starts with the workspace prefix `mastra_workspace_` or
37
- * matches the post-remap mastracode tool names. Non-workspace tools (memory,
38
- * notification inbox, subagent, etc.) and skill-loader / preflight /
39
- * materializer `sandbox.executeCommand()` calls that never flow through the
40
- * tool layer are intentionally excluded.
6
+ * `command_exit` is emitted only for the agent's own foreground
7
+ * `execute_command` tool calls: direct `sandbox.executeCommand()` calls from
8
+ * the skill loader / preflight / materializer never flow through the tool, so
9
+ * the first successful exit event is definitionally the agent's first
10
+ * meaningful exec — no command classifier needed. Background spawns emit no
11
+ * exit event at spawn time and are intentionally excluded.
41
12
  *
42
- * Failed tool calls (`isError === true`) don't count. Approval-denied and
43
- * abort-while-parked tool completions (`denied === true`) also don't count:
44
- * the run-engine emits `tool_end` with `isError: false` for those paths
45
- * because the tool didn't fail, but it also never ran, so treating them as
46
- * "meaningful exec" would re-introduce the same contamination this fix is
47
- * removing on the message side. The listener stays subscribed until a
48
- * successful qualifying end, then unsubscribes. The storage write is guarded
13
+ * Failed commands (nonzero exit) don't count; the listener stays subscribed
14
+ * until a successful exit, then unsubscribes. The storage write is guarded
49
15
  * (`first_meaningful_exec_at IS NULL`), so restarts, re-materialized
50
16
  * sessions, and sessions without a source-control row are no-ops.
51
17
  */
52
18
  function observeSessionFirstExec(session, { sourceControl }) {
53
19
  let seen = false;
54
- const toolNames = /* @__PURE__ */ new Map();
55
- const suspended = /* @__PURE__ */ new Set();
56
20
  const unsubscribe = session.subscribe((event) => {
57
- if (seen) return;
58
- switch (event.type) {
59
- case "tool_start":
60
- toolNames.set(event.toolCallId, event.toolName);
61
- return;
62
- case "tool_suspended":
63
- suspended.add(event.toolCallId);
64
- return;
65
- case "tool_suspension_cancelled":
66
- toolNames.delete(event.toolCallId);
67
- suspended.delete(event.toolCallId);
68
- return;
69
- case "agent_end":
70
- for (const id of toolNames.keys()) if (!suspended.has(id)) toolNames.delete(id);
71
- return;
72
- case "tool_end": {
73
- const toolName = toolNames.get(event.toolCallId);
74
- toolNames.delete(event.toolCallId);
75
- suspended.delete(event.toolCallId);
76
- if (event.isError) return;
77
- if (event.denied) return;
78
- if (!isMeaningfulToolName(toolName)) return;
79
- seen = true;
80
- unsubscribe();
81
- sourceControl.sessions.markFirstMeaningfulExec({ sessionId: session.identity.getResourceId() }).catch((error) => console.warn("[Factory first-exec capture] Unable to persist first exec time.", error));
82
- return;
83
- }
84
- }
21
+ if (seen || event.type !== "command_exit" || !event.success) return;
22
+ seen = true;
23
+ unsubscribe();
24
+ sourceControl.sessions.markFirstMeaningfulExec({ sessionId: session.identity.getResourceId() }).catch((error) => console.warn("[Factory first-exec capture] Unable to persist first exec time.", error));
85
25
  });
86
26
  return unsubscribe;
87
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"first-exec-capture.js","names":[],"sources":["../../src/session/first-exec-capture.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\nimport { WORKSPACE_TOOLS_PREFIX } from '@mastra/core/workspace';\n\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\n\nexport interface FirstExecCaptureSession {\n readonly identity: { getResourceId(): string };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\nexport interface FirstExecCaptureDependencies {\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'markFirstMeaningfulExec'>;\n };\n}\n\n/**\n * Tool names that count as \"meaningful exec\" independent of the workspace\n * prefix. The workspace tools get remapped from `mastra_workspace_*` to\n * mastracode-friendly names (see `mastracode/sdk/src/tool-names.ts`), and\n * this listener runs on the emitted tool name after remap, so both spellings\n * need to match.\n */\nconst MEANINGFUL_TOOL_NAMES: ReadonlySet<string> = new Set([\n // Filesystem (post-remap)\n 'view',\n 'write_file',\n 'string_replace_lsp',\n 'find_files',\n 'delete_file',\n 'file_stat',\n 'mkdir',\n // Search\n 'search_content',\n // Code intelligence\n 'ast_smart_edit',\n 'lsp_inspect',\n // Sandbox\n 'execute_command',\n 'get_process_output',\n 'kill_process',\n]);\n\nfunction isMeaningfulToolName(name: string | undefined): boolean {\n if (!name) return false;\n if (name.startsWith(`${WORKSPACE_TOOLS_PREFIX}_`)) return true;\n return MEANINGFUL_TOOL_NAMES.has(name);\n}\n\n/**\n * Record when a session's agent completed its first successful meaningful\n * tool call (the TTFME anchor — \"time to first meaningful exec\").\n *\n * Meaningful = a workspace tool the agent invoked itself (filesystem,\n * search, code intelligence, sandbox exec, process control). We accept any\n * tool whose name starts with the workspace prefix `mastra_workspace_` or\n * matches the post-remap mastracode tool names. Non-workspace tools (memory,\n * notification inbox, subagent, etc.) and skill-loader / preflight /\n * materializer `sandbox.executeCommand()` calls that never flow through the\n * tool layer are intentionally excluded.\n *\n * Failed tool calls (`isError === true`) don't count. Approval-denied and\n * abort-while-parked tool completions (`denied === true`) also don't count:\n * the run-engine emits `tool_end` with `isError: false` for those paths\n * because the tool didn't fail, but it also never ran, so treating them as\n * \"meaningful exec\" would re-introduce the same contamination this fix is\n * removing on the message side. The listener stays subscribed until a\n * successful qualifying end, then unsubscribes. The storage write is guarded\n * (`first_meaningful_exec_at IS NULL`), so restarts, re-materialized\n * sessions, and sessions without a source-control row are no-ops.\n */\nexport function observeSessionFirstExec(\n session: FirstExecCaptureSession,\n { sourceControl }: FirstExecCaptureDependencies,\n): () => void {\n let seen = false;\n // toolCallId -> toolName. Populated on `tool_start`, consumed on `tool_end`.\n // Suspended entries survive `agent_end` so the resumed run can still resolve\n // its tool name when the eventual `tool_end` arrives.\n const toolNames = new Map<string, string>();\n const suspended = new Set<string>();\n const unsubscribe = session.subscribe(event => {\n if (seen) return;\n switch (event.type) {\n case 'tool_start': {\n toolNames.set(event.toolCallId, event.toolName);\n return;\n }\n case 'tool_suspended': {\n suspended.add(event.toolCallId);\n return;\n }\n case 'tool_suspension_cancelled': {\n toolNames.delete(event.toolCallId);\n suspended.delete(event.toolCallId);\n return;\n }\n case 'agent_end': {\n // Drop any in-flight starts that will never see a matching `tool_end`\n // on this run. Genuinely suspended calls stay so their eventual\n // resume-time `tool_end` can still map back to a tool name. Without\n // this, sessions that repeatedly abort or error before a qualifying\n // completion would accumulate stale entries for the entire lifetime\n // of the subscription.\n for (const id of toolNames.keys()) {\n if (!suspended.has(id)) toolNames.delete(id);\n }\n return;\n }\n case 'tool_end': {\n const toolName = toolNames.get(event.toolCallId);\n toolNames.delete(event.toolCallId);\n suspended.delete(event.toolCallId);\n if (event.isError) return;\n if (event.denied) return;\n if (!isMeaningfulToolName(toolName)) return;\n seen = true;\n unsubscribe();\n void sourceControl.sessions\n .markFirstMeaningfulExec({ sessionId: session.identity.getResourceId() })\n .catch(error => console.warn('[Factory first-exec capture] Unable to persist first exec time.', error));\n return;\n }\n }\n });\n return unsubscribe;\n}\n"],"mappings":";;;;;;;;;AAuBA,MAAM,wCAA6C,IAAI,IAAI;CAEzD;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA;CACA;CAEA;CACA;CACA;AACF,CAAC;AAED,SAAS,qBAAqB,MAAmC;CAC/D,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI,KAAK,WAAW,GAAG,uBAAuB,EAAE,GAAG,OAAO;CAC1D,OAAO,sBAAsB,IAAI,IAAI;AACvC;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,wBACd,SACA,EAAE,iBACU;CACZ,IAAI,OAAO;CAIX,MAAM,4BAAY,IAAI,IAAoB;CAC1C,MAAM,4BAAY,IAAI,IAAY;CAClC,MAAM,cAAc,QAAQ,WAAU,UAAS;EAC7C,IAAI,MAAM;EACV,QAAQ,MAAM,MAAd;GACE,KAAK;IACH,UAAU,IAAI,MAAM,YAAY,MAAM,QAAQ;IAC9C;GAEF,KAAK;IACH,UAAU,IAAI,MAAM,UAAU;IAC9B;GAEF,KAAK;IACH,UAAU,OAAO,MAAM,UAAU;IACjC,UAAU,OAAO,MAAM,UAAU;IACjC;GAEF,KAAK;IAOH,KAAK,MAAM,MAAM,UAAU,KAAK,GAC9B,IAAI,CAAC,UAAU,IAAI,EAAE,GAAG,UAAU,OAAO,EAAE;IAE7C;GAEF,KAAK,YAAY;IACf,MAAM,WAAW,UAAU,IAAI,MAAM,UAAU;IAC/C,UAAU,OAAO,MAAM,UAAU;IACjC,UAAU,OAAO,MAAM,UAAU;IACjC,IAAI,MAAM,SAAS;IACnB,IAAI,MAAM,QAAQ;IAClB,IAAI,CAAC,qBAAqB,QAAQ,GAAG;IACrC,OAAO;IACP,YAAY;IACZ,cAAmB,SAChB,wBAAwB,EAAE,WAAW,QAAQ,SAAS,cAAc,EAAE,CAAC,CAAC,CACxE,OAAM,UAAS,QAAQ,KAAK,mEAAmE,KAAK,CAAC;IACxG;GACF;EACF;CACF,CAAC;CACD,OAAO;AACT"}
1
+ {"version":3,"file":"first-exec-capture.js","names":[],"sources":["../../src/session/first-exec-capture.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\n\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\n\nexport interface FirstExecCaptureSession {\n readonly identity: { getResourceId(): string };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\nexport interface FirstExecCaptureDependencies {\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'markFirstMeaningfulExec'>;\n };\n}\n\n/**\n * Record when a session's agent completed its first successful sandbox exec\n * (the TTFME anchor — \"time to first meaningful exec\").\n *\n * `command_exit` is emitted only for the agent's own foreground\n * `execute_command` tool calls: direct `sandbox.executeCommand()` calls from\n * the skill loader / preflight / materializer never flow through the tool, so\n * the first successful exit event is definitionally the agent's first\n * meaningful exec — no command classifier needed. Background spawns emit no\n * exit event at spawn time and are intentionally excluded.\n *\n * Failed commands (nonzero exit) don't count; the listener stays subscribed\n * until a successful exit, then unsubscribes. The storage write is guarded\n * (`first_meaningful_exec_at IS NULL`), so restarts, re-materialized\n * sessions, and sessions without a source-control row are no-ops.\n */\nexport function observeSessionFirstExec(\n session: FirstExecCaptureSession,\n { sourceControl }: FirstExecCaptureDependencies,\n): () => void {\n let seen = false;\n const unsubscribe = session.subscribe(event => {\n if (seen || event.type !== 'command_exit' || !event.success) return;\n seen = true;\n unsubscribe();\n void sourceControl.sessions\n .markFirstMeaningfulExec({ sessionId: session.identity.getResourceId() })\n .catch(error => console.warn('[Factory first-exec capture] Unable to persist first exec time.', error));\n });\n return unsubscribe;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA+BA,SAAgB,wBACd,SACA,EAAE,iBACU;CACZ,IAAI,OAAO;CACX,MAAM,cAAc,QAAQ,WAAU,UAAS;EAC7C,IAAI,QAAQ,MAAM,SAAS,kBAAkB,CAAC,MAAM,SAAS;EAC7D,OAAO;EACP,YAAY;EACZ,cAAmB,SAChB,wBAAwB,EAAE,WAAW,QAAQ,SAAS,cAAc,EAAE,CAAC,CAAC,CACxE,OAAM,UAAS,QAAQ,KAAK,mEAAmE,KAAK,CAAC;CAC1G,CAAC;CACD,OAAO;AACT"}
@@ -12,22 +12,18 @@ export interface FirstMessageCaptureDependencies {
12
12
  };
13
13
  }
14
14
  /**
15
- * Record when a session's agent first produced an actual conversational
16
- * message (the TTFI anchor — "time to first interaction").
15
+ * Record when a session's agent first started working on a message.
17
16
  *
18
- * We stamp on the first `message_start` whose role is `user` or `assistant`.
19
- * The coordinator writes `role='signal'` rows into the thread (skill loads,
20
- * phase markers, memory reminders) before the model is ever invoked; those
21
- * signals used to trigger a stamp via `agent_start`, giving every session —
22
- * including zero-message model-init failures — a false TTFI of a few seconds.
23
- * Gating on real conversational roles keeps the TTFI column honest: sessions
24
- * that never produced a user or assistant message stay NULL and drop out of
25
- * TTFI percentiles instead of contaminating them.
17
+ * `agent_start` is the earliest reliable session event for "a message reached
18
+ * the agent": it fires when the run engine begins processing an accepted
19
+ * message — a user's chat message, a factory run's kickoff prompt, or a
20
+ * channel message — for every session kind (user, work, review). The plain
21
+ * user message itself is never emitted as a `message_start` event, so the run
22
+ * start is the observable proxy, milliseconds after arrival.
26
23
  *
27
- * The listener unsubscribes after the first qualifying message; the storage
28
- * write is guarded (`first_message_at IS NULL`), so restarts, re-materialized
29
- * sessions, and sessions without a source-control row (chat-only channels)
30
- * are no-ops.
24
+ * The listener unsubscribes after the first event; the storage write is
25
+ * guarded (`first_message_at IS NULL`), so restarts, re-materialized sessions,
26
+ * and sessions without a source-control row (chat-only channels) are no-ops.
31
27
  */
32
28
  export declare function observeSessionFirstMessage(session: FirstMessageCaptureSession, { sourceControl }: FirstMessageCaptureDependencies): () => void;
33
29
  //# sourceMappingURL=first-message-capture.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"first-message-capture.d.ts","sourceRoot":"","sources":["../../src/session/first-message-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAE1E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAE5F,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,+BAA+B;IAC9C,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC,CAAC;KAC5E,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,0BAA0B,EACnC,EAAE,aAAa,EAAE,EAAE,+BAA+B,GACjD,MAAM,IAAI,CAaZ"}
1
+ {"version":3,"file":"first-message-capture.d.ts","sourceRoot":"","sources":["../../src/session/first-message-capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAE1E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAE5F,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,+BAA+B;IAC9C,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC,CAAC;KAC5E,CAAC;CACH;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,0BAA0B,EACnC,EAAE,aAAa,EAAE,EAAE,+BAA+B,GACjD,MAAM,IAAI,CAWZ"}
@@ -1,28 +1,22 @@
1
1
  //#region src/session/first-message-capture.ts
2
2
  /**
3
- * Record when a session's agent first produced an actual conversational
4
- * message (the TTFI anchor — "time to first interaction").
3
+ * Record when a session's agent first started working on a message.
5
4
  *
6
- * We stamp on the first `message_start` whose role is `user` or `assistant`.
7
- * The coordinator writes `role='signal'` rows into the thread (skill loads,
8
- * phase markers, memory reminders) before the model is ever invoked; those
9
- * signals used to trigger a stamp via `agent_start`, giving every session —
10
- * including zero-message model-init failures — a false TTFI of a few seconds.
11
- * Gating on real conversational roles keeps the TTFI column honest: sessions
12
- * that never produced a user or assistant message stay NULL and drop out of
13
- * TTFI percentiles instead of contaminating them.
5
+ * `agent_start` is the earliest reliable session event for "a message reached
6
+ * the agent": it fires when the run engine begins processing an accepted
7
+ * message — a user's chat message, a factory run's kickoff prompt, or a
8
+ * channel message — for every session kind (user, work, review). The plain
9
+ * user message itself is never emitted as a `message_start` event, so the run
10
+ * start is the observable proxy, milliseconds after arrival.
14
11
  *
15
- * The listener unsubscribes after the first qualifying message; the storage
16
- * write is guarded (`first_message_at IS NULL`), so restarts, re-materialized
17
- * sessions, and sessions without a source-control row (chat-only channels)
18
- * are no-ops.
12
+ * The listener unsubscribes after the first event; the storage write is
13
+ * guarded (`first_message_at IS NULL`), so restarts, re-materialized sessions,
14
+ * and sessions without a source-control row (chat-only channels) are no-ops.
19
15
  */
20
16
  function observeSessionFirstMessage(session, { sourceControl }) {
21
17
  let seen = false;
22
18
  const unsubscribe = session.subscribe((event) => {
23
- if (seen || event.type !== "message_start") return;
24
- const role = event.message.role;
25
- if (role !== "user" && role !== "assistant") return;
19
+ if (seen || event.type !== "agent_start") return;
26
20
  seen = true;
27
21
  unsubscribe();
28
22
  sourceControl.sessions.markFirstMessage({ sessionId: session.identity.getResourceId() }).catch((error) => console.warn("[Factory first-message capture] Unable to persist first message time.", error));
@@ -1 +1 @@
1
- {"version":3,"file":"first-message-capture.js","names":[],"sources":["../../src/session/first-message-capture.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\n\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\n\nexport interface FirstMessageCaptureSession {\n readonly identity: { getResourceId(): string };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\nexport interface FirstMessageCaptureDependencies {\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'markFirstMessage'>;\n };\n}\n\n/**\n * Record when a session's agent first produced an actual conversational\n * message (the TTFI anchor — \"time to first interaction\").\n *\n * We stamp on the first `message_start` whose role is `user` or `assistant`.\n * The coordinator writes `role='signal'` rows into the thread (skill loads,\n * phase markers, memory reminders) before the model is ever invoked; those\n * signals used to trigger a stamp via `agent_start`, giving every session —\n * including zero-message model-init failures — a false TTFI of a few seconds.\n * Gating on real conversational roles keeps the TTFI column honest: sessions\n * that never produced a user or assistant message stay NULL and drop out of\n * TTFI percentiles instead of contaminating them.\n *\n * The listener unsubscribes after the first qualifying message; the storage\n * write is guarded (`first_message_at IS NULL`), so restarts, re-materialized\n * sessions, and sessions without a source-control row (chat-only channels)\n * are no-ops.\n */\nexport function observeSessionFirstMessage(\n session: FirstMessageCaptureSession,\n { sourceControl }: FirstMessageCaptureDependencies,\n): () => void {\n let seen = false;\n const unsubscribe = session.subscribe(event => {\n if (seen || event.type !== 'message_start') return;\n const role = event.message.role;\n if (role !== 'user' && role !== 'assistant') return;\n seen = true;\n unsubscribe();\n void sourceControl.sessions\n .markFirstMessage({ sessionId: session.identity.getResourceId() })\n .catch(error => console.warn('[Factory first-message capture] Unable to persist first message time.', error));\n });\n return unsubscribe;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,2BACd,SACA,EAAE,iBACU;CACZ,IAAI,OAAO;CACX,MAAM,cAAc,QAAQ,WAAU,UAAS;EAC7C,IAAI,QAAQ,MAAM,SAAS,iBAAiB;EAC5C,MAAM,OAAO,MAAM,QAAQ;EAC3B,IAAI,SAAS,UAAU,SAAS,aAAa;EAC7C,OAAO;EACP,YAAY;EACZ,cAAmB,SAChB,iBAAiB,EAAE,WAAW,QAAQ,SAAS,cAAc,EAAE,CAAC,CAAC,CACjE,OAAM,UAAS,QAAQ,KAAK,yEAAyE,KAAK,CAAC;CAChH,CAAC;CACD,OAAO;AACT"}
1
+ {"version":3,"file":"first-message-capture.js","names":[],"sources":["../../src/session/first-message-capture.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\n\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\n\nexport interface FirstMessageCaptureSession {\n readonly identity: { getResourceId(): string };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\nexport interface FirstMessageCaptureDependencies {\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'markFirstMessage'>;\n };\n}\n\n/**\n * Record when a session's agent first started working on a message.\n *\n * `agent_start` is the earliest reliable session event for \"a message reached\n * the agent\": it fires when the run engine begins processing an accepted\n * message — a user's chat message, a factory run's kickoff prompt, or a\n * channel message — for every session kind (user, work, review). The plain\n * user message itself is never emitted as a `message_start` event, so the run\n * start is the observable proxy, milliseconds after arrival.\n *\n * The listener unsubscribes after the first event; the storage write is\n * guarded (`first_message_at IS NULL`), so restarts, re-materialized sessions,\n * and sessions without a source-control row (chat-only channels) are no-ops.\n */\nexport function observeSessionFirstMessage(\n session: FirstMessageCaptureSession,\n { sourceControl }: FirstMessageCaptureDependencies,\n): () => void {\n let seen = false;\n const unsubscribe = session.subscribe(event => {\n if (seen || event.type !== 'agent_start') return;\n seen = true;\n unsubscribe();\n void sourceControl.sessions\n .markFirstMessage({ sessionId: session.identity.getResourceId() })\n .catch(error => console.warn('[Factory first-message capture] Unable to persist first message time.', error));\n });\n return unsubscribe;\n}\n"],"mappings":";;;;;;;;;;;;;;;AA6BA,SAAgB,2BACd,SACA,EAAE,iBACU;CACZ,IAAI,OAAO;CACX,MAAM,cAAc,QAAQ,WAAU,UAAS;EAC7C,IAAI,QAAQ,MAAM,SAAS,eAAe;EAC1C,OAAO;EACP,YAAY;EACZ,cAAmB,SAChB,iBAAiB,EAAE,WAAW,QAAQ,SAAS,cAAc,EAAE,CAAC,CAAC,CACjE,OAAM,UAAS,QAAQ,KAAK,yEAAyE,KAAK,CAAC;CAChH,CAAC;CACD,OAAO;AACT"}
@@ -25,7 +25,6 @@
25
25
  import type { AuthCredential, OAuthCredential } from '@mastra/code-sdk/auth/types';
26
26
  import { FactoryStorageDomain } from '@mastra/core/storage';
27
27
  import type { CollectionSchema } from '@mastra/core/storage';
28
- import type { FactorySecretEncryption } from '../../../secret-encryption.js';
29
28
  /** Owning tenant of a credential row. `userId` absent = org-scoped row. */
30
29
  export interface CredentialTenant {
31
30
  orgId: string;
@@ -89,8 +88,7 @@ export declare const OAUTH_LOGIN_SESSIONS_SCHEMA: CollectionSchema;
89
88
  */
90
89
  export declare class ModelCredentialsStorage extends FactoryStorageDomain {
91
90
  #private;
92
- private readonly encryption;
93
- constructor(encryption?: FactorySecretEncryption);
91
+ constructor();
94
92
  init(): Promise<void>;
95
93
  dangerouslyClearAll(): Promise<void>;
96
94
  /** Read the tenant's credential for a provider at exactly that scope. */
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/credentials/base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/credentials/base.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
2
1
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
3
2
  //#region src/storage/domains/credentials/base.ts
4
3
  /** Mirror of `AuthStorage.getApiKey()`'s expiry check. */
@@ -87,15 +86,11 @@ function tenantWhere(tenant, provider) {
87
86
  * invalidating each other's rotating tokens / double-claiming a flow.
88
87
  */
89
88
  var ModelCredentialsStorage = class extends FactoryStorageDomain {
90
- encryption;
91
- constructor(encryption = createPlaintextFactorySecretEncryption()) {
89
+ constructor() {
92
90
  super("model-credentials");
93
- this.encryption = encryption;
94
91
  }
95
92
  async init() {
96
93
  await this.ensureCollections([MODEL_CREDENTIALS_SCHEMA, OAUTH_LOGIN_SESSIONS_SCHEMA]);
97
- const rows = await this.ops.findMany("model_provider_credentials", {});
98
- await Promise.all(rows.map((row) => this.#migrateCredential(row.id)));
99
94
  }
100
95
  async dangerouslyClearAll() {
101
96
  await this.ops.deleteMany("oauth_login_sessions", {});
@@ -104,28 +99,16 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
104
99
  get #db() {
105
100
  return this.ops;
106
101
  }
107
- async #decryptCredential(value) {
108
- return (await this.encryption.decrypt(value)).value;
109
- }
110
- async #migrateCredential(id) {
111
- await this.#db.updateAtomic("model_provider_credentials", { id }, async (row) => {
112
- const decrypted = await this.encryption.decrypt(row.data);
113
- if (!decrypted.needsReencryption) return null;
114
- return { data: await this.encryption.encrypt(decrypted.value) };
115
- });
116
- }
117
102
  /** Read the tenant's credential for a provider at exactly that scope. */
118
103
  async getCredential(tenant, provider) {
119
- const row = await this.#db.findOne("model_provider_credentials", tenantWhere(tenant, provider));
120
- return row ? this.#decryptCredential(row.data) : void 0;
104
+ return (await this.#db.findOne("model_provider_credentials", tenantWhere(tenant, provider)))?.data;
121
105
  }
122
106
  /** Upsert the tenant's credential (`created_at` is preserved on update). */
123
107
  async setCredential(tenant, provider, credential) {
124
108
  const where = tenantWhere(tenant, provider);
125
- const encrypted = await this.encryption.encrypt(credential);
126
109
  const update = async () => this.#db.updateMany("model_provider_credentials", where, {
127
110
  type: credential.type,
128
- data: encrypted,
111
+ data: credential,
129
112
  updated_at: /* @__PURE__ */ new Date()
130
113
  });
131
114
  let lastError;
@@ -138,7 +121,7 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
138
121
  user_id: tenant.userId ?? null,
139
122
  provider,
140
123
  type: credential.type,
141
- data: encrypted,
124
+ data: credential,
142
125
  created_at: now,
143
126
  updated_at: now
144
127
  });
@@ -163,17 +146,17 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
163
146
  org_id: orgId,
164
147
  user_id: null
165
148
  })]);
166
- return Promise.all([...userRows.map(async (row) => ({
149
+ return [...userRows.map((row) => ({
167
150
  provider: row.provider,
168
151
  scope: "user",
169
- credential: await this.#decryptCredential(row.data),
152
+ credential: row.data,
170
153
  updatedAt: row.updated_at
171
- })), ...orgRows.map(async (row) => ({
154
+ })), ...orgRows.map((row) => ({
172
155
  provider: row.provider,
173
156
  scope: "org",
174
- credential: await this.#decryptCredential(row.data),
157
+ credential: row.data,
175
158
  updatedAt: row.updated_at
176
- }))]);
159
+ }))];
177
160
  }
178
161
  /**
179
162
  * Resolve the credential a caller should use for a provider. By default the
@@ -190,7 +173,7 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
190
173
  return row ? {
191
174
  provider: row.provider,
192
175
  scope: "user",
193
- credential: await this.#decryptCredential(row.data)
176
+ credential: row.data
194
177
  } : void 0;
195
178
  };
196
179
  const readOrg = async () => {
@@ -202,7 +185,7 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
202
185
  return row ? {
203
186
  provider: row.provider,
204
187
  scope: "org",
205
- credential: await this.#decryptCredential(row.data)
188
+ credential: row.data
206
189
  } : void 0;
207
190
  };
208
191
  return precedence === "org" ? await readOrg() ?? await readUser() : await readUser() ?? await readOrg();
@@ -218,17 +201,16 @@ var ModelCredentialsStorage = class extends FactoryStorageDomain {
218
201
  async refreshOAuth(tenant, provider, refreshFn) {
219
202
  let result;
220
203
  await this.#db.updateAtomic("model_provider_credentials", tenantWhere(tenant, provider), async (row) => {
221
- const decrypted = await this.encryption.decrypt(row.data);
222
- if (decrypted.value.type !== "oauth") return null;
223
- const current = decrypted.value;
204
+ if (row.data.type !== "oauth") return null;
205
+ const current = row.data;
224
206
  if (!isOAuthCredentialExpired(current)) {
225
207
  result = current;
226
- return decrypted.needsReencryption ? { data: await this.encryption.encrypt(current) } : null;
208
+ return null;
227
209
  }
228
210
  const next = await refreshFn(current);
229
211
  result = next;
230
212
  return {
231
- data: await this.encryption.encrypt(next),
213
+ data: next,
232
214
  updated_at: /* @__PURE__ */ new Date()
233
215
  };
234
216
  });