@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.
- package/CHANGELOG.md +0 -180
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +0 -1
- package/dist/auth.js.map +1 -1
- package/dist/factory.d.ts +0 -8
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +6 -23
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/integrations/base.d.ts +1 -3
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/integration.d.ts +0 -14
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +1 -48
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/provenance.d.ts +2 -7
- package/dist/integrations/github/provenance.d.ts.map +1 -1
- package/dist/integrations/github/provenance.js +2 -16
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.d.ts +1 -8
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +7 -228
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.d.ts +0 -9
- package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +1 -39
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/linear/routes.d.ts.map +1 -1
- package/dist/integrations/linear/routes.js +0 -50
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts +1 -3
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +0 -33
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/routes/intake.d.ts +0 -5
- package/dist/routes/intake.d.ts.map +1 -1
- package/dist/routes/intake.js +19 -70
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/surface.d.ts +3 -4
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +12 -10
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +1 -1
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/defaults.js +5 -5
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +0 -9
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +17 -69
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +11 -28
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +5 -9
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +2 -5
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +2 -15
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +1 -4
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +1 -17
- package/dist/rules/types.js.map +1 -1
- package/dist/session/factory-session.d.ts +3 -9
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +4 -5
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/first-exec-capture.d.ts +10 -16
- package/dist/session/first-exec-capture.d.ts.map +1 -1
- package/dist/session/first-exec-capture.js +14 -74
- package/dist/session/first-exec-capture.js.map +1 -1
- package/dist/session/first-message-capture.d.ts +10 -14
- package/dist/session/first-message-capture.d.ts.map +1 -1
- package/dist/session/first-message-capture.js +11 -17
- package/dist/session/first-message-capture.js.map +1 -1
- package/dist/storage/domains/credentials/base.d.ts +1 -3
- package/dist/storage/domains/credentials/base.d.ts.map +1 -1
- package/dist/storage/domains/credentials/base.js +15 -33
- package/dist/storage/domains/credentials/base.js.map +1 -1
- package/dist/storage/domains/custom-providers/base.d.ts +1 -3
- package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
- package/dist/storage/domains/custom-providers/base.js +25 -45
- package/dist/storage/domains/custom-providers/base.js.map +1 -1
- package/dist/storage/domains/integrations/base.d.ts +1 -3
- package/dist/storage/domains/integrations/base.d.ts.map +1 -1
- package/dist/storage/domains/integrations/base.js +12 -28
- package/dist/storage/domains/integrations/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +0 -5
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +0 -6
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +0 -4
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +0 -7
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +2 -21
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +9 -65
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/factory-skills/factory-rereview/SKILL.md +0 -2
- package/factory-skills/factory-review/SKILL.md +0 -2
- package/factory-skills/factory-triage/SKILL.md +17 -7
- package/package.json +9 -9
- package/dist/secret-encryption.d.ts +0 -25
- package/dist/secret-encryption.d.ts.map +0 -1
- package/dist/secret-encryption.js +0 -96
- package/dist/secret-encryption.js.map +0 -1
- package/dist/session/session-title.d.ts +0 -7
- package/dist/session/session-title.d.ts.map +0 -1
- package/dist/session/session-title.js +0 -14
- package/dist/session/session-title.js.map +0 -1
- package/dist/session/thread-title-mirror.d.ts +0 -35
- package/dist/session/thread-title-mirror.d.ts.map +0 -1
- package/dist/session/thread-title-mirror.js +0 -51
- package/dist/session/thread-title-mirror.js.map +0 -1
- package/dist/work-item-branch.d.ts +0 -23
- package/dist/work-item-branch.d.ts.map +0 -1
- package/dist/work-item-branch.js +0 -39
- 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
|
|
103
|
-
*
|
|
104
|
-
*
|
|
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":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGrE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAG5F,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;AAE7F;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,sBAAsB,GAAG,SAAS,EAC5C,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQ7B;AAED,MAAM,WAAW,8BAA8B;IAC7C;;;;;OAKG;IACH,aAAa,EAAE,0BAA0B,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,cAAc,CAAC,EAAE,MAAM,CAAC;
|
|
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
|
|
113
|
-
*
|
|
114
|
-
*
|
|
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 =
|
|
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
|
|
16
|
-
*
|
|
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
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
|
27
|
-
*
|
|
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;
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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
|
|
43
|
-
*
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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';\
|
|
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
|
|
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
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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
|
|
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
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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 !== "
|
|
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
|
|
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
|
-
|
|
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAsC,MAAM,sBAAsB,CAAC;AAEjG,
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
149
|
+
return [...userRows.map((row) => ({
|
|
167
150
|
provider: row.provider,
|
|
168
151
|
scope: "user",
|
|
169
|
-
credential:
|
|
152
|
+
credential: row.data,
|
|
170
153
|
updatedAt: row.updated_at
|
|
171
|
-
})), ...orgRows.map(
|
|
154
|
+
})), ...orgRows.map((row) => ({
|
|
172
155
|
provider: row.provider,
|
|
173
156
|
scope: "org",
|
|
174
|
-
credential:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
222
|
-
|
|
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
|
|
208
|
+
return null;
|
|
227
209
|
}
|
|
228
210
|
const next = await refreshFn(current);
|
|
229
211
|
result = next;
|
|
230
212
|
return {
|
|
231
|
-
data:
|
|
213
|
+
data: next,
|
|
232
214
|
updated_at: /* @__PURE__ */ new Date()
|
|
233
215
|
};
|
|
234
216
|
});
|