@mastra/factory 0.18.0-alpha.9 → 0.18.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/factory.d.ts.map +1 -1
  2. package/dist/factory.js +5 -2
  3. package/dist/factory.js.map +1 -1
  4. package/dist/integrations/github/default-rules.d.ts.map +1 -1
  5. package/dist/integrations/github/default-rules.js +3 -3
  6. package/dist/integrations/github/default-rules.js.map +1 -1
  7. package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
  8. package/dist/integrations/github/reconcile-worker.js +1 -2
  9. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  10. package/dist/integrations/github/routes.d.ts.map +1 -1
  11. package/dist/integrations/github/routes.js +1 -14
  12. package/dist/integrations/github/routes.js.map +1 -1
  13. package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
  14. package/dist/integrations/gitlab/version-control.js +4 -5
  15. package/dist/integrations/gitlab/version-control.js.map +1 -1
  16. package/dist/integrations/gitlab/webhook.d.ts.map +1 -1
  17. package/dist/integrations/gitlab/webhook.js +2 -2
  18. package/dist/integrations/gitlab/webhook.js.map +1 -1
  19. package/dist/integrations/issue-reconcile-worker.d.ts.map +1 -1
  20. package/dist/integrations/issue-reconcile-worker.js +1 -2
  21. package/dist/integrations/issue-reconcile-worker.js.map +1 -1
  22. package/dist/integrations/linear/integration.d.ts.map +1 -1
  23. package/dist/integrations/linear/integration.js +10 -30
  24. package/dist/integrations/linear/integration.js.map +1 -1
  25. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
  26. package/dist/integrations/platform/github/event-worker.js +1 -2
  27. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  28. package/dist/integrations/platform/gitlab/event-worker.d.ts.map +1 -1
  29. package/dist/integrations/platform/gitlab/event-worker.js +1 -2
  30. package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
  31. package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -1
  32. package/dist/integrations/platform/linear/event-worker.js +1 -2
  33. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  34. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  35. package/dist/integrations/platform/linear/integration.js +9 -9
  36. package/dist/integrations/platform/linear/integration.js.map +1 -1
  37. package/dist/integrations/slack/slack.d.ts.map +1 -1
  38. package/dist/integrations/slack/slack.js +137 -19
  39. package/dist/integrations/slack/slack.js.map +1 -1
  40. package/dist/integrations/subscription-session.d.ts +3 -1
  41. package/dist/integrations/subscription-session.d.ts.map +1 -1
  42. package/dist/integrations/subscription-session.js +4 -2
  43. package/dist/integrations/subscription-session.js.map +1 -1
  44. package/dist/review-verdict.d.ts +6 -0
  45. package/dist/review-verdict.d.ts.map +1 -0
  46. package/dist/review-verdict.js +14 -0
  47. package/dist/review-verdict.js.map +1 -0
  48. package/dist/routes/contracts.d.ts +23 -0
  49. package/dist/routes/contracts.d.ts.map +1 -1
  50. package/dist/routes/contracts.js +21 -0
  51. package/dist/routes/contracts.js.map +1 -1
  52. package/dist/routes/oauth.d.ts.map +1 -1
  53. package/dist/routes/oauth.js +3 -3
  54. package/dist/routes/oauth.js.map +1 -1
  55. package/dist/routes/projects.d.ts +22 -2
  56. package/dist/routes/projects.d.ts.map +1 -1
  57. package/dist/routes/projects.js +101 -0
  58. package/dist/routes/projects.js.map +1 -1
  59. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  60. package/dist/routes/source-control-sessions.js +1 -2
  61. package/dist/routes/source-control-sessions.js.map +1 -1
  62. package/dist/rules/dispatcher.d.ts.map +1 -1
  63. package/dist/rules/dispatcher.js +1 -2
  64. package/dist/rules/dispatcher.js.map +1 -1
  65. package/dist/rules/processor.d.ts.map +1 -1
  66. package/dist/rules/processor.js +5 -5
  67. package/dist/rules/processor.js.map +1 -1
  68. package/dist/rules/transition-service.d.ts.map +1 -1
  69. package/dist/rules/transition-service.js +1 -2
  70. package/dist/rules/transition-service.js.map +1 -1
  71. package/dist/sandbox/materialization.d.ts +5 -1
  72. package/dist/sandbox/materialization.d.ts.map +1 -1
  73. package/dist/sandbox/materialization.js.map +1 -1
  74. package/dist/sandbox/session-sandbox.d.ts +13 -1
  75. package/dist/sandbox/session-sandbox.d.ts.map +1 -1
  76. package/dist/sandbox/session-sandbox.js.map +1 -1
  77. package/dist/secret-encryption.js +2 -2
  78. package/dist/secret-encryption.js.map +1 -1
  79. package/dist/session/factory-session.d.ts.map +1 -1
  80. package/dist/session/factory-session.js +26 -9
  81. package/dist/session/factory-session.js.map +1 -1
  82. package/dist/session/source-control-tools.d.ts +9 -0
  83. package/dist/session/source-control-tools.d.ts.map +1 -1
  84. package/dist/session/source-control-tools.js +51 -2
  85. package/dist/session/source-control-tools.js.map +1 -1
  86. package/dist/state-signing.js +3 -3
  87. package/dist/state-signing.js.map +1 -1
  88. package/dist/storage/domains/audit/base.d.ts.map +1 -1
  89. package/dist/storage/domains/audit/base.js +2 -3
  90. package/dist/storage/domains/audit/base.js.map +1 -1
  91. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  92. package/dist/storage/domains/source-control/inmemory.js +5 -6
  93. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  94. package/dist/storage/domains/work-items/base.d.ts +2 -0
  95. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  96. package/dist/storage/domains/work-items/base.js +9 -1
  97. package/dist/storage/domains/work-items/base.js.map +1 -1
  98. package/dist/workspace.d.ts.map +1 -1
  99. package/dist/workspace.js +8 -7
  100. package/dist/workspace.js.map +1 -1
  101. package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
  102. package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
  103. package/factory-skills/factory-rereview/SKILL.md +12 -3
  104. package/factory-skills/factory-review/SKILL.md +12 -3
  105. package/package.json +10 -10
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#client","#endpointHost","#workflowStatesByTeam","#resolveIntakeDispatch","#listProjectSources","#listTeamSources","#listIssues","#findIssue","#loadComments","#resolveWorkspaceForIssue","#listWorkflowStates","#rules","#projects","#auth","#listWorkspaces","#connectRoute","#candidateWorkspaceIds"],"sources":["../../../../src/integrations/platform/linear/integration.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\nimport type { Context } from 'hono';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type { Intake, IntakeIssue, IntakeIssueDetail, UpdateIntakeIssueInput } from '../../../capabilities/intake.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport { buildLinearAgentTools } from '../../linear/agent-tools.js';\nimport type { LinearEventRules, LinearRuleOverrides } from '../../linear/default-rules.js';\nimport { resolveLinearRules } from '../../linear/default-rules.js';\nimport type {\n LinearConnectionCheck,\n LinearIntegration,\n LinearIssueDetail as LinearRouteIssueDetail,\n} from '../../linear/integration.js';\nimport { attachLinearIssueReconciler } from '../../linear/issue-reconciler.js';\nimport {\n linearIssueReconciliationEnabled,\n linearIssueReconciliationInterval,\n} from '../../linear/reconciliation-config.js';\nimport { buildLinearRoutes } from '../../linear/routes.js';\nimport { attachLinearRules } from '../../linear/rules.js';\nimport type { LinearConnectionData, LinearConnectionRow, LinearStorageHandle } from '../../linear/storage.js';\nimport {\n logPlatformInfo,\n logPlatformWarn,\n PlatformApiClient,\n PlatformApiError,\n platformApiClientConfigFromEnv,\n} from '../api-client.js';\nimport { PlatformLinearEventWorker } from './event-worker.js';\nimport type { PlatformLinearEventStorage } from './event-worker.js';\n\ntype PageInfo = { hasNextPage: boolean; endCursor: string | null };\ntype LinearUser = {\n id: string;\n name: string;\n displayName: string;\n email: string | null;\n avatarUrl: string | null;\n};\ntype LinearIssue = {\n id: string;\n identifier: string;\n number: number;\n title: string;\n description: string | null;\n url: string;\n priority: number;\n priorityLabel: string;\n labels: Array<{ id: string; name: string }>;\n state: { id: string; name: string; type: string };\n team: { id: string; key: string; name: string };\n project: { id: string } | null;\n assignee: LinearUser | null;\n creator: LinearUser | null;\n createdAt: string;\n updatedAt: string;\n archivedAt: string | null;\n};\ntype LinearComment = {\n id: string;\n body: string;\n url: string;\n issue: { id: string; identifier: string };\n user: LinearUser | null;\n parent: { id: string } | null;\n createdAt: string;\n updatedAt: string;\n};\ntype LinearWorkspace = {\n linearWorkspaceId: string;\n linearWorkspaceName: string;\n urlKey: string | null;\n connected: boolean;\n};\ntype LinearProject = {\n id: string;\n name: string;\n state: string;\n teams: Array<{ id: string; key: string; name: string }>;\n};\ntype LinearTeam = { id: string; key: string; name: string };\ntype ProjectSource = { workspace: LinearWorkspace; project: LinearProject };\ntype TeamSource = { workspace: LinearWorkspace; team: LinearTeam };\ntype LinearWorkflowState = {\n id: string;\n name: string;\n type: string;\n position: number;\n teamId: string;\n};\n\nconst API_PREFIX = '/v1/server/linear';\nconst PAGE_SIZE = 30;\nconst MAX_REFERENCE_PAGES = 20;\nconst MAX_COMMENT_PAGES = 20;\nconst PLATFORM_MANAGED_CONNECTION_TOKEN = 'platform-managed';\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction routeBaseUrl(ctx: IntegrationContext, requestUrl: string): string {\n return (ctx.baseUrl || new URL(requestUrl).origin).replace(/\\/+$/, '');\n}\n\nexport class PlatformLinearIntegration implements FactoryIntegration {\n readonly id = 'linear';\n readonly #client: PlatformApiClient;\n readonly #endpointHost: string;\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n /**\n * Per-instance workflow-state cache keyed by `${workspaceId}:${teamId}`. Scoped to the process\n * lifetime; not shared across requests intentionally to keep failure modes simple (stale states\n * clear on process restart). Populated lazily on first `updateIssue` per team.\n */\n readonly #workflowStatesByTeam = new Map<string, LinearWorkflowState[]>();\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: async () => {\n const [projectSources, teamSources] = await Promise.all([this.#listProjectSources(), this.#listTeamSources()]);\n const projects = projectSources.map(({ workspace, project }) => ({\n id: encodeSourceId(workspace.linearWorkspaceId, project.id),\n name: project.name,\n type: 'project' as const,\n metadata: {\n workspaceId: workspace.linearWorkspaceId,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n state: project.state,\n teams: project.teams,\n },\n }));\n const teams = teamSources.map(({ workspace, team }) => ({\n id: encodeTeamSourceId(workspace.linearWorkspaceId, team.id),\n name: team.name,\n type: 'team' as const,\n metadata: {\n workspaceId: workspace.linearWorkspaceId,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n teamKey: team.key,\n },\n }));\n return [...projects, ...teams];\n },\n listItems: async ({ sourceIds, attributionSourceIds, cursor }) => {\n const result = await this.#listIssues(sourceIds, cursor, undefined, attributionSourceIds);\n return {\n items: result.issues.map(({ issue, sourceId, workspace }) => ({\n source: { type: 'issue', externalId: issue.id, url: issue.url },\n sourceId,\n title: issue.title,\n status: issue.state.name,\n labels: issue.labels.map(label => label.name),\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata: {\n identifier: issue.identifier,\n workspaceId: workspace.linearWorkspaceId,\n workspaceName: workspace.linearWorkspaceName,\n projectId: issue.project?.id ?? null,\n team: issue.team.key,\n priority: issue.priorityLabel,\n },\n })),\n nextCursor: result.nextCursor,\n };\n },\n listIssues: async ({ connection, sourceIds, attributionSourceIds, labels, cursor }) => {\n requireLinearConnection(connection);\n const result = await this.#listIssues(sourceIds, cursor, labels, attributionSourceIds);\n return {\n issues: result.issues.map(({ issue, sourceId }) => ({\n ...parseIssue(issue),\n sourceId,\n })),\n nextCursor: result.nextCursor,\n };\n },\n getIssue: async ({ connection, sourceId, issueId }) => {\n requireLinearConnection(connection);\n const located = await this.#findIssue(sourceId, issueId);\n if (!located) return null;\n const comments = await this.#loadComments(located.workspaceId, issueId, located.issue.comments);\n return parseIssueDetail(located.issue, comments);\n },\n createComment: async ({ connection, sourceId, issueId, body }) => {\n requireLinearConnection(connection);\n const workspaceId = await this.#resolveWorkspaceForIssue(sourceId, issueId);\n if (!workspaceId) return null;\n try {\n const comment = await this.#client.request<LinearComment>(\n 'POST',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments`,\n { body },\n );\n return { id: comment.id, url: comment.url };\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n },\n updateIssue: async (input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> => {\n requireLinearConnection(input.connection);\n const located = await this.#findIssue(input.sourceId, input.issueId);\n if (!located) return null;\n const { workspaceId, issue } = located;\n\n const target = input.state;\n // Idempotency: skip the write entirely if the issue is already at the target state.\n if (target.kind === 'byType' && issue.state.type === target.stateType) {\n return parseIssue(issue);\n }\n if (target.kind === 'byName' && issue.state.name.toLowerCase() === target.name.toLowerCase()) {\n return parseIssue(issue);\n }\n\n let states: LinearWorkflowState[];\n try {\n states = await this.#listWorkflowStates(workspaceId, issue.team.id);\n } catch (error) {\n if (isNotFound(error)) {\n // Platform companion endpoint not deployed yet — degrade to policy skip so PR 1\n // is standalone-mergeable ahead of the platform-side workflow-states route landing.\n logPlatformWarn('Platform Linear: workflow-states endpoint not available; skipping updateIssue.', {\n workspaceId,\n teamId: issue.team.id,\n });\n return null;\n }\n throw error;\n }\n let matched: LinearWorkflowState | undefined;\n if (target.kind === 'byType') {\n matched = states\n .slice()\n .sort((a, b) => a.position - b.position)\n .find(s => s.type === target.stateType);\n } else {\n const wanted = target.name.toLowerCase();\n matched = states.find(s => s.name.toLowerCase() === wanted);\n }\n if (!matched) {\n logPlatformWarn('Platform Linear: no workflow state matched target; skipping updateIssue.', {\n workspaceId,\n teamId: issue.team.id,\n target,\n });\n return null;\n }\n\n try {\n await this.#client.request<LinearIssue>(\n 'PATCH',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(input.issueId)}`,\n { stateId: matched.id },\n );\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n\n const refreshed = await this.#findIssue(input.sourceId, input.issueId);\n return refreshed ? parseIssue(refreshed.issue) : null;\n },\n };\n\n readonly #rules: LinearEventRules;\n\n get rules(): LinearEventRules {\n return this.#rules;\n }\n\n constructor({ rules }: { rules?: LinearRuleOverrides } = {}) {\n this.#rules = resolveLinearRules(rules);\n const config = platformApiClientConfigFromEnv();\n this.#client = new PlatformApiClient(config);\n this.#endpointHost = new URL(config.baseUrl).host;\n }\n\n get storage(): LinearStorageHandle {\n const now = new Date();\n return {\n integrationId: this.id,\n connections: {\n get: async (orgId: string) => ({\n id: `platform-linear:${orgId}`,\n orgId,\n userId: null,\n data: {\n accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,\n refreshToken: null,\n expiresAtMs: null,\n scope: 'read,comments:create',\n workspaceName: null,\n workspaceUrlKey: null,\n } satisfies LinearConnectionData,\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }),\n },\n } as unknown as LinearStorageHandle;\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) throw new Error('PlatformLinearIntegration projects storage has not been initialized.');\n return this.#projects;\n }\n\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth?: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\n logPlatformInfo('Platform Linear integration initialized', { endpointHost: this.#endpointHost });\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n try {\n const project = await this.projects.getById({ id: resourceId });\n return project?.orgId ?? null;\n } catch {\n return null;\n }\n }\n\n async loadConnection(orgId: string): Promise<LinearConnectionRow | null> {\n const workspace = (await this.#listWorkspaces())[0];\n if (!workspace) return null;\n const now = new Date();\n return {\n id: `platform-linear:${orgId}`,\n orgId,\n userId: null,\n accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,\n scope: 'read,comments:create',\n refreshToken: null,\n expiresAt: null,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n async getFreshAccessToken(_connection: LinearConnectionRow): Promise<string> {\n return PLATFORM_MANAGED_CONNECTION_TOKEN;\n }\n\n /**\n * Background-dispatch context: platform-managed connection token when the\n * org has a connected workspace. Linear work items store the issue UUID\n * directly as `externalId`.\n */\n async #resolveIntakeDispatch({\n orgId,\n externalSource,\n }: {\n orgId: string;\n externalSource: { type: string; externalId: string };\n }): Promise<{ connection: IntegrationConnection; issueId: string } | null> {\n if (externalSource.type !== 'issue') return null;\n const connection = await this.loadConnection(orgId);\n if (!connection) return null;\n return {\n connection: { type: 'oauth', accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN },\n issueId: externalSource.externalId,\n };\n }\n\n canPostComments(connection: LinearConnectionRow): boolean {\n const scopes = (connection.scope ?? '').split(/[\\s,]+/).filter(Boolean);\n return scopes.some(scope => scope === 'comments:create' || scope === 'write' || scope === 'admin');\n }\n\n async checkConnection(orgId: string): Promise<LinearConnectionCheck> {\n const connection = await this.loadConnection(orgId);\n return {\n connected: connection !== null,\n canComment: connection !== null && this.canPostComments(connection),\n checkedAt: Date.now(),\n };\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n const pollingEnabled = process.env.MASTRACODE_PLATFORM_LINEAR_POLLING_ENABLED?.trim().toLowerCase() !== 'false';\n const reconcileEnabled = linearIssueReconciliationEnabled();\n if (!pollingEnabled && !reconcileEnabled) return [];\n\n const ingest = attachLinearRules(this, ctx);\n const reconcile = reconcileEnabled ? attachLinearIssueReconciler(this, ctx) : undefined;\n const workItems = ctx.runtime?.workItems;\n if (!workItems) return [];\n if (!ingest && !reconcile) return [];\n\n const pollIntervalMs = optionalPositiveIntegerEnv('MASTRACODE_PLATFORM_LINEAR_POLLING_INTERVAL_MS');\n const reconcileIntervalMs = linearIssueReconciliationInterval();\n\n return [\n new PlatformLinearEventWorker({\n client: this.#client,\n linear: { listWorkspaces: () => this.listWorkspaces() },\n storage: ctx.storage.generic as unknown as PlatformLinearEventStorage,\n projects: ctx.storage.projects,\n workItems,\n ...(ingest ? { ingestFactoryIssue: ingest } : {}),\n ...(reconcile ? { reconcileFactoryState: reconcile } : {}),\n pollEventsEnabled: pollingEnabled,\n ...(pollIntervalMs !== undefined ? { intervalMs: pollIntervalMs } : {}),\n ...(reconcileIntervalMs !== undefined ? { reconcileIntervalMs } : {}),\n }),\n ];\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n return [\n this.#connectRoute(ctx),\n ...buildLinearRoutes({\n auth: ctx.auth,\n linear: this as unknown as LinearIntegration,\n stateSigner: ctx.stateSigner,\n baseUrl: ctx.baseUrl,\n intake: ctx.storage.intake,\n projects: ctx.storage.projects,\n ingestFactoryIssues: attachLinearRules(this, ctx),\n workItems: ctx.runtime?.workItems,\n boards: ctx.runtime?.boards,\n }).filter(route => !route.path.startsWith('/auth/linear/')),\n ];\n }\n\n #connectRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/auth/linear/connect', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n const returnTo = c.req.query('return_to') || '/';\n const originator = routeBaseUrl(ctx, c.req.url);\n logPlatformInfo('Starting Platform Linear connect flow', {\n orgId: tenant.orgId,\n returnTo,\n originator,\n });\n const query = new URLSearchParams({ return_to: returnTo, originator });\n const location = await this.#client.requestRedirect('GET', `${API_PREFIX}/authorize?${query}`);\n return c.redirect(location);\n },\n });\n }\n\n async agentTools({ requestContext }: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildLinearAgentTools({ requestContext, linear: this as unknown as LinearIntegration });\n }\n\n diagnostics(): Record<string, unknown> {\n return { mode: 'platform', endpointHost: this.#endpointHost };\n }\n\n async listProjects(): Promise<Array<LinearProject & { workspaceId: string }>> {\n return (await this.#listProjectSources()).map(({ workspace, project }) => ({\n ...project,\n id: encodeSourceId(workspace.linearWorkspaceId, project.id),\n workspaceId: workspace.linearWorkspaceId,\n }));\n }\n\n async listTeams(_accessToken: string): Promise<Array<LinearTeam & { workspaceId: string; sourceId: string }>> {\n return (await this.#listTeamSources()).map(({ workspace, team }) => ({\n ...team,\n workspaceId: workspace.linearWorkspaceId,\n sourceId: encodeTeamSourceId(workspace.linearWorkspaceId, team.id),\n }));\n }\n\n async fetchIssueDetail(\n _accessToken: string,\n idOrIdentifier: string,\n sourceIds?: string[],\n routedSourceIds?: string[],\n ): Promise<LinearRouteIssueDetail | null> {\n const located = await this.#findIssue(undefined, idOrIdentifier, sourceIds, routedSourceIds);\n if (!located) return null;\n const comments = await this.#loadComments(located.workspaceId, located.issue.id, located.issue.comments);\n const issue = located.issue;\n return {\n id: issue.id,\n projectId: issue.project?.id ?? null,\n workspaceId: located.workspaceId,\n teamId: issue.team.id,\n identifier: issue.identifier,\n title: issue.title,\n description: issue.description?.trim() ? issue.description : null,\n url: issue.url,\n state: issue.state.name,\n stateType: issue.state.type,\n priorityLabel: issue.priorityLabel,\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n creator: issue.creator?.displayName ?? issue.creator?.name ?? null,\n team: issue.team.key,\n labels: issue.labels.map(label => label.name),\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n comments: comments.map(comment => ({\n author: comment.user?.displayName ?? comment.user?.name ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n })),\n };\n }\n\n sourceMatchesIssue(\n sourceId: string,\n issue: Pick<LinearRouteIssueDetail, 'workspaceId' | 'projectId' | 'teamId'>,\n ): boolean {\n const source = parseSourceId(sourceId);\n if (issue.workspaceId !== source.workspaceId) return false;\n return source.kind === 'team' ? issue.teamId === source.teamId : issue.projectId === source.projectId;\n }\n\n async #listProjectSources(workspaceIds?: ReadonlySet<string>): Promise<ProjectSource[]> {\n const workspaces = await this.#listWorkspaces();\n const scopedWorkspaces = workspaceIds\n ? workspaces.filter(workspace => workspaceIds.has(workspace.linearWorkspaceId))\n : workspaces;\n const projectGroups = await Promise.all(\n scopedWorkspaces.map(async workspace => {\n const projects: LinearProject[] = [];\n let after: string | undefined;\n for (let page = 0; page < MAX_REFERENCE_PAGES; page += 1) {\n const query = new URLSearchParams({ first: '200' });\n if (after) query.set('after', after);\n const result = await this.#client.request<{ projects: LinearProject[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/projects?${query}`,\n );\n projects.push(...result.projects);\n if (!result.pageInfo.hasNextPage || !result.pageInfo.endCursor) break;\n // A page that hands back the cursor it was asked for would replay forever.\n if (result.pageInfo.endCursor === after) throw invalidLinearCursor();\n after = result.pageInfo.endCursor;\n }\n return projects.map(project => ({ workspace, project }));\n }),\n );\n return projectGroups.flat();\n }\n\n async #listTeamSources(workspaceIds?: ReadonlySet<string>): Promise<TeamSource[]> {\n const workspaces = await this.#listWorkspaces();\n const scopedWorkspaces = workspaceIds\n ? workspaces.filter(workspace => workspaceIds.has(workspace.linearWorkspaceId))\n : workspaces;\n const teamGroups = await Promise.all(\n scopedWorkspaces.map(async workspace => {\n const teams: LinearTeam[] = [];\n let after: string | undefined;\n for (let page = 0; page < MAX_REFERENCE_PAGES; page += 1) {\n const query = new URLSearchParams({ first: '200' });\n if (after) query.set('after', after);\n const result = await this.#client.request<{ teams: LinearTeam[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/teams?${query}`,\n );\n teams.push(...result.teams);\n if (!result.pageInfo.hasNextPage || !result.pageInfo.endCursor) break;\n // A page that hands back the cursor it was asked for would replay forever.\n if (result.pageInfo.endCursor === after) throw invalidLinearCursor();\n after = result.pageInfo.endCursor;\n }\n return teams.map(team => ({ workspace, team }));\n }),\n );\n return teamGroups.flat();\n }\n\n async listWorkspaces(): Promise<LinearWorkspace[]> {\n return this.#listWorkspaces();\n }\n\n async #listWorkspaces(): Promise<LinearWorkspace[]> {\n const result = await this.#client.request<{ workspaces: LinearWorkspace[] }>('GET', `${API_PREFIX}/workspaces`);\n return result.workspaces.filter(workspace => workspace.connected);\n }\n\n async #listIssues(sourceIds: string[], cursor?: string, labels?: string[], attributionSourceIds = sourceIds) {\n type ListedIssue = { issue: LinearIssue; sourceId: string; workspace: LinearWorkspace };\n if (sourceIds.length === 0) return { issues: [] as ListedIssue[], nextCursor: null };\n\n // Resolve every selected source to a concrete listing descriptor. Project\n // and team sources are listed separately: a project source filters by\n // `projectIds`, a team source filters by `teamId` (and returns projectless\n // issues too). Filters are never combined on one request.\n const parsedSources = sourceIds.map(sourceId => parseSourceId(sourceId));\n const projectWorkspaceIds = new Set(\n parsedSources.flatMap(source => (source.kind === 'project' ? [source.workspaceId] : [])),\n );\n const teamWorkspaceIds = new Set(\n parsedSources.flatMap(source => (source.kind === 'team' ? [source.workspaceId] : [])),\n );\n const [projectSources, teamSources] = await Promise.all([\n projectWorkspaceIds.size > 0\n ? this.#listProjectSources(projectWorkspaceIds)\n : Promise.resolve([] as ProjectSource[]),\n teamWorkspaceIds.size > 0 ? this.#listTeamSources(teamWorkspaceIds) : Promise.resolve([] as TeamSource[]),\n ]);\n const projectMap = new Map(\n projectSources.map(source => [encodeSourceId(source.workspace.linearWorkspaceId, source.project.id), source]),\n );\n const teamMap = new Map(\n teamSources.map(source => [encodeTeamSourceId(source.workspace.linearWorkspaceId, source.team.id), source]),\n );\n\n type Descriptor = {\n kind: 'project' | 'team';\n sourceId: string;\n workspace: LinearWorkspace;\n query: URLSearchParams;\n };\n const descriptors: Descriptor[] = [];\n const selectedProjectIdsByWorkspace = new Map<string, Set<string>>();\n for (const attributionSourceId of attributionSourceIds) {\n const source = parseSourceId(attributionSourceId);\n if (source.kind !== 'project') continue;\n const selectedProjectIds = selectedProjectIdsByWorkspace.get(source.workspaceId) ?? new Set<string>();\n selectedProjectIds.add(source.projectId);\n selectedProjectIdsByWorkspace.set(source.workspaceId, selectedProjectIds);\n }\n for (const sourceId of sourceIds) {\n const projectSource = projectMap.get(sourceId);\n if (projectSource) {\n const query = new URLSearchParams({\n first: String(PAGE_SIZE),\n projectIds: projectSource.project.id,\n stateType: 'triage,backlog,unstarted,started',\n orderBy: 'updatedAt',\n });\n const workspaceId = projectSource.workspace.linearWorkspaceId;\n const selectedProjectIds = selectedProjectIdsByWorkspace.get(workspaceId) ?? new Set<string>();\n selectedProjectIds.add(projectSource.project.id);\n selectedProjectIdsByWorkspace.set(workspaceId, selectedProjectIds);\n descriptors.push({ kind: 'project', sourceId, workspace: projectSource.workspace, query });\n continue;\n }\n const teamSource = teamMap.get(sourceId);\n if (teamSource) {\n const query = new URLSearchParams({\n first: String(PAGE_SIZE),\n teamId: teamSource.team.id,\n stateType: 'triage,backlog,unstarted,started',\n orderBy: 'updatedAt',\n });\n descriptors.push({ kind: 'team', sourceId, workspace: teamSource.workspace, query });\n }\n }\n\n const cursors = decodeCursor(cursor, sourceIds, attributionSourceIds);\n const normalizedLabels = normalizeLabels(labels);\n const nextState: Record<string, string | null> = {};\n let hasNextPage = false;\n const pages = await Promise.all(\n descriptors.map(async ({ kind, sourceId, workspace, query }) => {\n if (cursors[sourceId] === null) {\n nextState[sourceId] = null;\n return [] as ListedIssue[];\n }\n const after = cursors[sourceId];\n if (after) query.set('after', after);\n const result = await this.#client.request<{ issues: LinearIssue[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/issues?${query}`,\n );\n const next = result.pageInfo.hasNextPage ? result.pageInfo.endCursor : null;\n // A page that hands back the cursor it was asked for would replay forever.\n if (next !== null && next === after) throw invalidLinearCursor();\n nextState[sourceId] = next;\n hasNextPage ||= next !== null;\n return result.issues\n .filter(\n issue => normalizedLabels.length === 0 || issue.labels.some(label => normalizedLabels.includes(label.name)),\n )\n .filter(\n issue =>\n kind === 'project' ||\n issue.project === null ||\n !selectedProjectIdsByWorkspace.get(workspace.linearWorkspaceId)?.has(issue.project.id),\n )\n .map(issue => ({ issue, sourceId, workspace }));\n }),\n );\n return {\n issues: dedupeIssuesBySource(pages.flat(), sourceIds),\n nextCursor: hasNextPage ? encodeCursor(nextState, sourceIds, attributionSourceIds) : null,\n };\n }\n\n async #findIssue(\n sourceId: string | undefined,\n issueId: string,\n sourceIds?: string[],\n routedSourceIds?: string[],\n ): Promise<{\n workspaceId: string;\n issue: LinearIssue & { comments?: { nodes: LinearComment[]; pageInfo: PageInfo } };\n } | null> {\n const scopedSourceIds = sourceIds ?? (sourceId ? [sourceId] : undefined);\n const scopedSources = scopedSourceIds?.map(sourceKey => ({\n sourceKey,\n source: parseSourceId(sourceKey),\n }));\n const routedSourceSet = routedSourceIds ? new Set(routedSourceIds) : undefined;\n const workspaceIds = routedSourceIds\n ? [...new Set(routedSourceIds.map(sourceKey => parseSourceId(sourceKey).workspaceId))]\n : scopedSources\n ? [...new Set(scopedSources.map(({ source }) => source.workspaceId))]\n : await this.#candidateWorkspaceIds(undefined);\n for (const workspaceId of workspaceIds) {\n try {\n const issue = await this.#client.request<\n LinearIssue & { comments?: { nodes: LinearComment[]; pageInfo: PageInfo } }\n >(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}?include=comments`,\n );\n if (scopedSources) {\n const matching = scopedSources.filter(\n ({ source }) =>\n source.workspaceId === workspaceId &&\n (source.kind === 'team' ? issue.team.id === source.teamId : issue.project?.id === source.projectId),\n );\n const winner = matching.find(({ source }) => source.kind === 'project') ?? matching[0];\n if (!winner || (routedSourceSet && !routedSourceSet.has(winner.sourceKey))) continue;\n }\n return { workspaceId, issue };\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n return null;\n }\n\n async #resolveWorkspaceForIssue(sourceId: string | undefined, issueId: string): Promise<string | null> {\n const workspaceIds = await this.#candidateWorkspaceIds(sourceId);\n if (workspaceIds.length === 1) return workspaceIds[0]!;\n for (const workspaceId of workspaceIds) {\n try {\n await this.#client.request<LinearIssue>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}`,\n );\n return workspaceId;\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n return null;\n }\n\n async #listWorkflowStates(workspaceId: string, teamId: string): Promise<LinearWorkflowState[]> {\n const cacheKey = `${workspaceId}:${teamId}`;\n const cached = this.#workflowStatesByTeam.get(cacheKey);\n if (cached) return cached;\n const result = await this.#client.request<{ workflowStates: LinearWorkflowState[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/workflow-states?teamId=${encodeURIComponent(teamId)}&first=100`,\n );\n this.#workflowStatesByTeam.set(cacheKey, result.workflowStates);\n return result.workflowStates;\n }\n\n async #candidateWorkspaceIds(sourceId: string | undefined): Promise<string[]> {\n if (sourceId) return [parseSourceId(sourceId).workspaceId];\n return (await this.#listWorkspaces()).map(workspace => workspace.linearWorkspaceId);\n }\n\n async #loadComments(\n workspaceId: string,\n issueId: string,\n embedded: { nodes: LinearComment[]; pageInfo: PageInfo } | undefined,\n ): Promise<LinearComment[]> {\n const comments = [...(embedded?.nodes ?? [])];\n let pageInfo = embedded?.pageInfo;\n let page = 0;\n while (pageInfo?.hasNextPage && pageInfo.endCursor && page < MAX_COMMENT_PAGES) {\n const result = await this.#client.request<{ comments: LinearComment[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments?first=200&after=${encodeURIComponent(pageInfo.endCursor)}`,\n );\n comments.push(...result.comments);\n if (result.pageInfo.hasNextPage && result.pageInfo.endCursor === pageInfo.endCursor) throw invalidLinearCursor();\n pageInfo = result.pageInfo;\n page += 1;\n }\n return comments;\n }\n}\n\nfunction parseIssue(issue: LinearIssue): IntakeIssue {\n return {\n id: issue.id,\n identifier: issue.identifier,\n title: issue.title,\n url: issue.url,\n author: issue.creator?.displayName ?? issue.creator?.name ?? null,\n state: issue.state.name,\n stateType: issue.state.type,\n priority: issue.priorityLabel,\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n source: issue.team.key,\n labels: issue.labels.map(label => label.name),\n commentCount: null,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n };\n}\n\nfunction parseIssueDetail(issue: LinearIssue, comments: LinearComment[]): IntakeIssueDetail {\n return {\n ...parseIssue(issue),\n commentCount: comments.length,\n description: issue.description?.trim() ? issue.description : null,\n comments: comments.map(comment => ({\n author: comment.user?.displayName ?? comment.user?.name ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n })),\n };\n}\n\n/**\n * Deduplicate issues that surfaced from more than one selected source (a team\n * source and one of its projects both select the same issue). Keeps one entry\n * per Linear issue UUID.\n *\n * Tie-breaker — MOST-SPECIFIC-WINS: a project source beats a team source, so an\n * issue that belongs to a selected project is attributed to that project's\n * source (and therefore routes to the project's board). The team source covers\n * only the remainder — projectless issues and issues in projects that were not\n * separately selected. `selectionOrder` breaks a same-kind tie deterministically\n * by the order the sources were selected.\n *\n * This function is the single precedence point; change the comparison here to\n * change the routing policy.\n */\nfunction dedupeIssuesBySource<T extends { issue: { id: string }; sourceId: string }>(\n entries: T[],\n selectionOrder: string[],\n): T[] {\n const rank = new Map(selectionOrder.map((id, index) => [id, index]));\n const specificity = (sourceId: string): number => (parseSourceId(sourceId).kind === 'project' ? 0 : 1);\n const winners = new Map<string, T>();\n for (const entry of entries) {\n const existing = winners.get(entry.issue.id);\n if (!existing) {\n winners.set(entry.issue.id, entry);\n continue;\n }\n const bySpecificity = specificity(entry.sourceId) - specificity(existing.sourceId);\n const better =\n bySpecificity < 0 ||\n (bySpecificity === 0 &&\n (rank.get(entry.sourceId) ?? Number.MAX_SAFE_INTEGER) <\n (rank.get(existing.sourceId) ?? Number.MAX_SAFE_INTEGER));\n if (better) winners.set(entry.issue.id, entry);\n }\n // Preserve first-seen order of the surviving issues.\n const seen = new Set<string>();\n const ordered: T[] = [];\n for (const entry of entries) {\n if (seen.has(entry.issue.id)) continue;\n seen.add(entry.issue.id);\n ordered.push(winners.get(entry.issue.id)!);\n }\n return ordered;\n}\n\nexport function encodeSourceId(workspaceId: string, projectId: string): string {\n return `linear-project:${Buffer.from(JSON.stringify({ workspaceId, projectId })).toString('base64url')}`;\n}\n\nfunction decodeSourceId(sourceId: string): { workspaceId: string; projectId: string } {\n if (!sourceId.startsWith('linear-project:')) throw new Error('Linear project source id is invalid.');\n try {\n const parsed = JSON.parse(Buffer.from(sourceId.slice('linear-project:'.length), 'base64url').toString('utf8')) as {\n workspaceId?: unknown;\n projectId?: unknown;\n };\n if (typeof parsed.workspaceId !== 'string' || !parsed.workspaceId) throw new Error();\n if (typeof parsed.projectId !== 'string' || !parsed.projectId) throw new Error();\n return { workspaceId: parsed.workspaceId, projectId: parsed.projectId };\n } catch {\n throw new Error('Linear project source id is invalid.');\n }\n}\n\nconst TEAM_SOURCE_PREFIX = 'linear-team:';\n\nexport function encodeTeamSourceId(workspaceId: string, teamId: string): string {\n return `${TEAM_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ workspaceId, teamId })).toString('base64url')}`;\n}\n\nexport function decodeTeamSourceId(sourceId: string): { workspaceId: string; teamId: string } {\n if (!sourceId.startsWith(TEAM_SOURCE_PREFIX)) throw new Error('Linear team source id is invalid.');\n try {\n const parsed = JSON.parse(Buffer.from(sourceId.slice(TEAM_SOURCE_PREFIX.length), 'base64url').toString('utf8')) as {\n workspaceId?: unknown;\n teamId?: unknown;\n };\n if (typeof parsed.workspaceId !== 'string' || !parsed.workspaceId) throw new Error();\n if (typeof parsed.teamId !== 'string' || !parsed.teamId) throw new Error();\n return { workspaceId: parsed.workspaceId, teamId: parsed.teamId };\n } catch {\n throw new Error('Linear team source id is invalid.');\n }\n}\n\n/**\n * A decoded intake source: either a Linear project or a whole Linear team.\n * Downstream code discriminates on `kind` to build the right issue-listing\n * filter and to apply most-specific-wins routing (project beats team).\n */\nexport type DecodedSource =\n { kind: 'project'; workspaceId: string; projectId: string } | { kind: 'team'; workspaceId: string; teamId: string };\n\n/** Discriminate any Linear source id by its prefix and decode it. */\nexport function parseSourceId(sourceId: string): DecodedSource {\n if (sourceId.startsWith(TEAM_SOURCE_PREFIX)) {\n return { kind: 'team', ...decodeTeamSourceId(sourceId) };\n }\n return { kind: 'project', ...decodeSourceId(sourceId) };\n}\n\nfunction normalizeLabels(labels: string[] | undefined): string[] {\n return [...new Set((labels ?? []).map(label => label.trim()).filter(Boolean))];\n}\n\ntype PlatformListCursor = { v: 1; sourceSet: string; cursors: Array<string | null> };\n\nfunction canonicalSourceIds(sourceIds: string[]): string[] {\n return [...new Set(sourceIds)].sort();\n}\n\nfunction linearSourceSetFingerprint(sourceIds: string[], attributionSourceIds: string[]): string {\n const scope = {\n sourceIds: canonicalSourceIds(sourceIds),\n attributionSourceIds: canonicalSourceIds(attributionSourceIds),\n };\n return createHash('sha256').update(JSON.stringify(scope)).digest('base64url');\n}\n\nfunction invalidLinearCursor(): Error {\n return Object.assign(new Error('Linear cursor is invalid or stale.'), { code: 'invalid_cursor' as const });\n}\n\nfunction decodeCursor(\n cursor: string | undefined,\n sourceIds: string[],\n attributionSourceIds: string[],\n): Record<string, string | null | undefined> {\n if (!cursor) return {};\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Partial<PlatformListCursor>;\n const canonical = canonicalSourceIds(sourceIds);\n if (\n parsed.v !== 1 ||\n parsed.sourceSet !== linearSourceSetFingerprint(sourceIds, attributionSourceIds) ||\n !Array.isArray(parsed.cursors) ||\n parsed.cursors.length !== canonical.length ||\n !parsed.cursors.every(value => value === null || typeof value === 'string')\n ) {\n throw invalidLinearCursor();\n }\n return Object.fromEntries(canonical.map((sourceId, index) => [sourceId, parsed.cursors![index]]));\n } catch {\n throw invalidLinearCursor();\n }\n}\n\nfunction encodeCursor(\n state: Record<string, string | null>,\n sourceIds: string[],\n attributionSourceIds: string[],\n): string {\n const canonical = canonicalSourceIds(sourceIds);\n const cursor: PlatformListCursor = {\n v: 1,\n sourceSet: linearSourceSetFingerprint(sourceIds, attributionSourceIds),\n cursors: canonical.map(sourceId => state[sourceId] ?? null),\n };\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction requireLinearConnection(connection: IntegrationConnection): void {\n if (connection.type !== 'oauth') {\n throw new Error('Linear capabilities require an OAuth connection.');\n }\n}\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof PlatformApiError && error.status === 404;\n}\n\nfunction optionalPositiveIntegerEnv(name: string): number | undefined {\n const value = process.env[name]?.trim();\n if (!value) return undefined;\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed) || parsed <= 0) {\n throw new Error(`${name} must be a positive integer.`);\n }\n return parsed;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","names":["#client","#endpointHost","#workflowStatesByTeam","#resolveIntakeDispatch","#listProjectSources","#listTeamSources","#listIssues","#findIssue","#loadComments","#resolveWorkspaceForIssue","#listWorkflowStates","#rules","#projects","#auth","#listWorkspaces","#connectRoute","#candidateWorkspaceIds"],"sources":["../../../../src/integrations/platform/linear/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\nimport type { Context } from 'hono';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type { Intake, IntakeIssue, IntakeIssueDetail, UpdateIntakeIssueInput } from '../../../capabilities/intake.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport { buildLinearAgentTools } from '../../linear/agent-tools.js';\nimport type { LinearEventRules, LinearRuleOverrides } from '../../linear/default-rules.js';\nimport { resolveLinearRules } from '../../linear/default-rules.js';\nimport type {\n LinearConnectionCheck,\n LinearIntegration,\n LinearIssueDetail as LinearRouteIssueDetail,\n} from '../../linear/integration.js';\nimport { attachLinearIssueReconciler } from '../../linear/issue-reconciler.js';\nimport {\n linearIssueReconciliationEnabled,\n linearIssueReconciliationInterval,\n} from '../../linear/reconciliation-config.js';\nimport { buildLinearRoutes } from '../../linear/routes.js';\nimport { attachLinearRules } from '../../linear/rules.js';\nimport type { LinearConnectionData, LinearConnectionRow, LinearStorageHandle } from '../../linear/storage.js';\nimport {\n logPlatformInfo,\n logPlatformWarn,\n PlatformApiClient,\n PlatformApiError,\n platformApiClientConfigFromEnv,\n} from '../api-client.js';\nimport { PlatformLinearEventWorker } from './event-worker.js';\nimport type { PlatformLinearEventStorage } from './event-worker.js';\n\ntype PageInfo = { hasNextPage: boolean; endCursor: string | null };\ntype LinearUser = {\n id: string;\n name: string;\n displayName: string;\n email: string | null;\n avatarUrl: string | null;\n};\ntype LinearIssue = {\n id: string;\n identifier: string;\n number: number;\n title: string;\n description: string | null;\n url: string;\n priority: number;\n priorityLabel: string;\n labels: Array<{ id: string; name: string }>;\n state: { id: string; name: string; type: string };\n team: { id: string; key: string; name: string };\n project: { id: string } | null;\n assignee: LinearUser | null;\n creator: LinearUser | null;\n createdAt: string;\n updatedAt: string;\n archivedAt: string | null;\n};\ntype LinearComment = {\n id: string;\n body: string;\n url: string;\n issue: { id: string; identifier: string };\n user: LinearUser | null;\n parent: { id: string } | null;\n createdAt: string;\n updatedAt: string;\n};\ntype LinearWorkspace = {\n linearWorkspaceId: string;\n linearWorkspaceName: string;\n urlKey: string | null;\n connected: boolean;\n};\ntype LinearProject = {\n id: string;\n name: string;\n state: string;\n teams: Array<{ id: string; key: string; name: string }>;\n};\ntype LinearTeam = { id: string; key: string; name: string };\ntype ProjectSource = { workspace: LinearWorkspace; project: LinearProject };\ntype TeamSource = { workspace: LinearWorkspace; team: LinearTeam };\ntype LinearWorkflowState = {\n id: string;\n name: string;\n type: string;\n position: number;\n teamId: string;\n};\n\nconst API_PREFIX = '/v1/server/linear';\nconst PAGE_SIZE = 30;\nconst MAX_REFERENCE_PAGES = 20;\nconst MAX_COMMENT_PAGES = 20;\nconst PLATFORM_MANAGED_CONNECTION_TOKEN = 'platform-managed';\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction routeBaseUrl(ctx: IntegrationContext, requestUrl: string): string {\n return (ctx.baseUrl || new URL(requestUrl).origin).replace(/\\/+$/, '');\n}\n\nexport class PlatformLinearIntegration implements FactoryIntegration {\n readonly id = 'linear';\n readonly #client: PlatformApiClient;\n readonly #endpointHost: string;\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n /**\n * Per-instance workflow-state cache keyed by `${workspaceId}:${teamId}`. Scoped to the process\n * lifetime; not shared across requests intentionally to keep failure modes simple (stale states\n * clear on process restart). Populated lazily on first `updateIssue` per team.\n */\n readonly #workflowStatesByTeam = new Map<string, LinearWorkflowState[]>();\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: async () => {\n const [projectSources, teamSources] = await Promise.all([this.#listProjectSources(), this.#listTeamSources()]);\n const projects = projectSources.map(({ workspace, project }) => ({\n id: encodeSourceId(workspace.linearWorkspaceId, project.id),\n name: project.name,\n type: 'project' as const,\n metadata: {\n workspaceId: workspace.linearWorkspaceId,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n state: project.state,\n teams: project.teams,\n },\n }));\n const teams = teamSources.map(({ workspace, team }) => ({\n id: encodeTeamSourceId(workspace.linearWorkspaceId, team.id),\n name: team.name,\n type: 'team' as const,\n metadata: {\n workspaceId: workspace.linearWorkspaceId,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n teamKey: team.key,\n },\n }));\n return [...projects, ...teams];\n },\n listItems: async ({ sourceIds, attributionSourceIds, cursor }) => {\n const result = await this.#listIssues(sourceIds, cursor, undefined, attributionSourceIds);\n return {\n items: result.issues.map(({ issue, sourceId, workspace }) => ({\n source: { type: 'issue', externalId: issue.id, url: issue.url },\n sourceId,\n title: issue.title,\n status: issue.state.name,\n labels: issue.labels.map(label => label.name),\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata: {\n identifier: issue.identifier,\n workspaceId: workspace.linearWorkspaceId,\n workspaceName: workspace.linearWorkspaceName,\n projectId: issue.project?.id ?? null,\n team: issue.team.key,\n priority: issue.priorityLabel,\n },\n })),\n nextCursor: result.nextCursor,\n };\n },\n listIssues: async ({ connection, sourceIds, attributionSourceIds, labels, cursor }) => {\n requireLinearConnection(connection);\n const result = await this.#listIssues(sourceIds, cursor, labels, attributionSourceIds);\n return {\n issues: result.issues.map(({ issue, sourceId }) => ({\n ...parseIssue(issue),\n sourceId,\n })),\n nextCursor: result.nextCursor,\n };\n },\n getIssue: async ({ connection, sourceId, issueId }) => {\n requireLinearConnection(connection);\n const located = await this.#findIssue(sourceId, issueId);\n if (!located) return null;\n const comments = await this.#loadComments(located.workspaceId, issueId, located.issue.comments);\n return parseIssueDetail(located.issue, comments);\n },\n createComment: async ({ connection, sourceId, issueId, body }) => {\n requireLinearConnection(connection);\n const workspaceId = await this.#resolveWorkspaceForIssue(sourceId, issueId);\n if (!workspaceId) return null;\n try {\n const comment = await this.#client.request<LinearComment>(\n 'POST',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments`,\n { body },\n );\n return { id: comment.id, url: comment.url };\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n },\n updateIssue: async (input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> => {\n requireLinearConnection(input.connection);\n const located = await this.#findIssue(input.sourceId, input.issueId);\n if (!located) return null;\n const { workspaceId, issue } = located;\n\n const target = input.state;\n // Idempotency: skip the write entirely if the issue is already at the target state.\n if (target.kind === 'byType' && issue.state.type === target.stateType) {\n return parseIssue(issue);\n }\n if (target.kind === 'byName' && issue.state.name.toLowerCase() === target.name.toLowerCase()) {\n return parseIssue(issue);\n }\n\n let states: LinearWorkflowState[];\n try {\n states = await this.#listWorkflowStates(workspaceId, issue.team.id);\n } catch (error) {\n if (isNotFound(error)) {\n // Platform companion endpoint not deployed yet — degrade to policy skip so PR 1\n // is standalone-mergeable ahead of the platform-side workflow-states route landing.\n logPlatformWarn('Platform Linear: workflow-states endpoint not available; skipping updateIssue.', {\n workspaceId,\n teamId: issue.team.id,\n });\n return null;\n }\n throw error;\n }\n let matched: LinearWorkflowState | undefined;\n if (target.kind === 'byType') {\n matched = states\n .slice()\n .sort((a, b) => a.position - b.position)\n .find(s => s.type === target.stateType);\n } else {\n const wanted = target.name.toLowerCase();\n matched = states.find(s => s.name.toLowerCase() === wanted);\n }\n if (!matched) {\n logPlatformWarn('Platform Linear: no workflow state matched target; skipping updateIssue.', {\n workspaceId,\n teamId: issue.team.id,\n target,\n });\n return null;\n }\n\n try {\n await this.#client.request<LinearIssue>(\n 'PATCH',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(input.issueId)}`,\n { stateId: matched.id },\n );\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n\n const refreshed = await this.#findIssue(input.sourceId, input.issueId);\n return refreshed ? parseIssue(refreshed.issue) : null;\n },\n };\n\n readonly #rules: LinearEventRules;\n\n get rules(): LinearEventRules {\n return this.#rules;\n }\n\n constructor({ rules }: { rules?: LinearRuleOverrides } = {}) {\n this.#rules = resolveLinearRules(rules);\n const config = platformApiClientConfigFromEnv();\n this.#client = new PlatformApiClient(config);\n this.#endpointHost = new URL(config.baseUrl).host;\n }\n\n get storage(): LinearStorageHandle {\n const now = new Date();\n return {\n integrationId: this.id,\n connections: {\n get: async (orgId: string) => ({\n id: `platform-linear:${orgId}`,\n orgId,\n userId: null,\n data: {\n accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,\n refreshToken: null,\n expiresAtMs: null,\n scope: 'read,comments:create',\n workspaceName: null,\n workspaceUrlKey: null,\n } satisfies LinearConnectionData,\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }),\n },\n } as unknown as LinearStorageHandle;\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) throw new Error('PlatformLinearIntegration projects storage has not been initialized.');\n return this.#projects;\n }\n\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth?: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\n logPlatformInfo('Platform Linear integration initialized', { endpointHost: this.#endpointHost });\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n try {\n const project = await this.projects.getById({ id: resourceId });\n return project?.orgId ?? null;\n } catch {\n return null;\n }\n }\n\n async loadConnection(orgId: string): Promise<LinearConnectionRow | null> {\n const workspace = (await this.#listWorkspaces())[0];\n if (!workspace) return null;\n const now = new Date();\n return {\n id: `platform-linear:${orgId}`,\n orgId,\n userId: null,\n accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,\n scope: 'read,comments:create',\n refreshToken: null,\n expiresAt: null,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n async getFreshAccessToken(_connection: LinearConnectionRow): Promise<string> {\n return PLATFORM_MANAGED_CONNECTION_TOKEN;\n }\n\n /**\n * Background-dispatch context: platform-managed connection token when the\n * org has a connected workspace. Linear work items store the issue UUID\n * directly as `externalId`.\n */\n async #resolveIntakeDispatch({\n orgId,\n externalSource,\n }: {\n orgId: string;\n externalSource: { type: string; externalId: string };\n }): Promise<{ connection: IntegrationConnection; issueId: string } | null> {\n if (externalSource.type !== 'issue') return null;\n const connection = await this.loadConnection(orgId);\n if (!connection) return null;\n return {\n connection: { type: 'oauth', accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN },\n issueId: externalSource.externalId,\n };\n }\n\n canPostComments(connection: LinearConnectionRow): boolean {\n const scopes = (connection.scope ?? '').split(/[\\s,]+/).filter(Boolean);\n return scopes.some(scope => scope === 'comments:create' || scope === 'write' || scope === 'admin');\n }\n\n async checkConnection(orgId: string): Promise<LinearConnectionCheck> {\n const connection = await this.loadConnection(orgId);\n return {\n connected: connection !== null,\n canComment: connection !== null && this.canPostComments(connection),\n checkedAt: Date.now(),\n };\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n const pollingEnabled = process.env.MASTRACODE_PLATFORM_LINEAR_POLLING_ENABLED?.trim().toLowerCase() !== 'false';\n const reconcileEnabled = linearIssueReconciliationEnabled();\n if (!pollingEnabled && !reconcileEnabled) return [];\n\n const ingest = attachLinearRules(this, ctx);\n const reconcile = reconcileEnabled ? attachLinearIssueReconciler(this, ctx) : undefined;\n const workItems = ctx.runtime?.workItems;\n if (!workItems) return [];\n if (!ingest && !reconcile) return [];\n\n const pollIntervalMs = optionalPositiveIntegerEnv('MASTRACODE_PLATFORM_LINEAR_POLLING_INTERVAL_MS');\n const reconcileIntervalMs = linearIssueReconciliationInterval();\n\n return [\n new PlatformLinearEventWorker({\n client: this.#client,\n linear: { listWorkspaces: () => this.listWorkspaces() },\n storage: ctx.storage.generic as unknown as PlatformLinearEventStorage,\n projects: ctx.storage.projects,\n workItems,\n ...(ingest ? { ingestFactoryIssue: ingest } : {}),\n ...(reconcile ? { reconcileFactoryState: reconcile } : {}),\n pollEventsEnabled: pollingEnabled,\n ...(pollIntervalMs !== undefined ? { intervalMs: pollIntervalMs } : {}),\n ...(reconcileIntervalMs !== undefined ? { reconcileIntervalMs } : {}),\n }),\n ];\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n return [\n this.#connectRoute(ctx),\n ...buildLinearRoutes({\n auth: ctx.auth,\n linear: this as unknown as LinearIntegration,\n stateSigner: ctx.stateSigner,\n baseUrl: ctx.baseUrl,\n intake: ctx.storage.intake,\n projects: ctx.storage.projects,\n ingestFactoryIssues: attachLinearRules(this, ctx),\n workItems: ctx.runtime?.workItems,\n boards: ctx.runtime?.boards,\n }).filter(route => !route.path.startsWith('/auth/linear/')),\n ];\n }\n\n #connectRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/auth/linear/connect', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n const returnTo = c.req.query('return_to') || '/';\n const originator = routeBaseUrl(ctx, c.req.url);\n logPlatformInfo('Starting Platform Linear connect flow', {\n orgId: tenant.orgId,\n returnTo,\n originator,\n });\n const query = new URLSearchParams({ return_to: returnTo, originator });\n const location = await this.#client.requestRedirect('GET', `${API_PREFIX}/authorize?${query}`);\n return c.redirect(location);\n },\n });\n }\n\n async agentTools({ requestContext }: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildLinearAgentTools({ requestContext, linear: this as unknown as LinearIntegration });\n }\n\n diagnostics(): Record<string, unknown> {\n return { mode: 'platform', endpointHost: this.#endpointHost };\n }\n\n async listProjects(): Promise<Array<LinearProject & { workspaceId: string }>> {\n return (await this.#listProjectSources()).map(({ workspace, project }) => ({\n ...project,\n id: encodeSourceId(workspace.linearWorkspaceId, project.id),\n workspaceId: workspace.linearWorkspaceId,\n }));\n }\n\n async listTeams(_accessToken: string): Promise<Array<LinearTeam & { workspaceId: string; sourceId: string }>> {\n return (await this.#listTeamSources()).map(({ workspace, team }) => ({\n ...team,\n workspaceId: workspace.linearWorkspaceId,\n sourceId: encodeTeamSourceId(workspace.linearWorkspaceId, team.id),\n }));\n }\n\n async fetchIssueDetail(\n _accessToken: string,\n idOrIdentifier: string,\n sourceIds?: string[],\n routedSourceIds?: string[],\n ): Promise<LinearRouteIssueDetail | null> {\n const located = await this.#findIssue(undefined, idOrIdentifier, sourceIds, routedSourceIds);\n if (!located) return null;\n const comments = await this.#loadComments(located.workspaceId, located.issue.id, located.issue.comments);\n const issue = located.issue;\n return {\n id: issue.id,\n projectId: issue.project?.id ?? null,\n workspaceId: located.workspaceId,\n teamId: issue.team.id,\n identifier: issue.identifier,\n title: issue.title,\n description: issue.description?.trim() ? issue.description : null,\n url: issue.url,\n state: issue.state.name,\n stateType: issue.state.type,\n priorityLabel: issue.priorityLabel,\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n creator: issue.creator?.displayName ?? issue.creator?.name ?? null,\n team: issue.team.key,\n labels: issue.labels.map(label => label.name),\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n comments: comments.map(comment => ({\n author: comment.user?.displayName ?? comment.user?.name ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n })),\n };\n }\n\n sourceMatchesIssue(\n sourceId: string,\n issue: Pick<LinearRouteIssueDetail, 'workspaceId' | 'projectId' | 'teamId'>,\n ): boolean {\n const source = parseSourceId(sourceId);\n if (issue.workspaceId !== source.workspaceId) return false;\n return source.kind === 'team' ? issue.teamId === source.teamId : issue.projectId === source.projectId;\n }\n\n async #listProjectSources(workspaceIds?: ReadonlySet<string>): Promise<ProjectSource[]> {\n const workspaces = await this.#listWorkspaces();\n const scopedWorkspaces = workspaceIds\n ? workspaces.filter(workspace => workspaceIds.has(workspace.linearWorkspaceId))\n : workspaces;\n const projectGroups = await Promise.all(\n scopedWorkspaces.map(async workspace => {\n const projects: LinearProject[] = [];\n let after: string | undefined;\n for (let page = 0; page < MAX_REFERENCE_PAGES; page += 1) {\n const query = new URLSearchParams({ first: '200' });\n if (after) query.set('after', after);\n const result = await this.#client.request<{ projects: LinearProject[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/projects?${query}`,\n );\n projects.push(...result.projects);\n if (!result.pageInfo.hasNextPage || !result.pageInfo.endCursor) break;\n // A page that hands back the cursor it was asked for would replay forever.\n if (result.pageInfo.endCursor === after) throw invalidLinearCursor();\n after = result.pageInfo.endCursor;\n }\n return projects.map(project => ({ workspace, project }));\n }),\n );\n return projectGroups.flat();\n }\n\n async #listTeamSources(workspaceIds?: ReadonlySet<string>): Promise<TeamSource[]> {\n const workspaces = await this.#listWorkspaces();\n const scopedWorkspaces = workspaceIds\n ? workspaces.filter(workspace => workspaceIds.has(workspace.linearWorkspaceId))\n : workspaces;\n const teamGroups = await Promise.all(\n scopedWorkspaces.map(async workspace => {\n const teams: LinearTeam[] = [];\n let after: string | undefined;\n for (let page = 0; page < MAX_REFERENCE_PAGES; page += 1) {\n const query = new URLSearchParams({ first: '200' });\n if (after) query.set('after', after);\n const result = await this.#client.request<{ teams: LinearTeam[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/teams?${query}`,\n );\n teams.push(...result.teams);\n if (!result.pageInfo.hasNextPage || !result.pageInfo.endCursor) break;\n // A page that hands back the cursor it was asked for would replay forever.\n if (result.pageInfo.endCursor === after) throw invalidLinearCursor();\n after = result.pageInfo.endCursor;\n }\n return teams.map(team => ({ workspace, team }));\n }),\n );\n return teamGroups.flat();\n }\n\n async listWorkspaces(): Promise<LinearWorkspace[]> {\n return this.#listWorkspaces();\n }\n\n async #listWorkspaces(): Promise<LinearWorkspace[]> {\n const result = await this.#client.request<{ workspaces: LinearWorkspace[] }>('GET', `${API_PREFIX}/workspaces`);\n return result.workspaces.filter(workspace => workspace.connected);\n }\n\n async #listIssues(sourceIds: string[], cursor?: string, labels?: string[], attributionSourceIds = sourceIds) {\n type ListedIssue = { issue: LinearIssue; sourceId: string; workspace: LinearWorkspace };\n if (sourceIds.length === 0) return { issues: [] as ListedIssue[], nextCursor: null };\n\n // Resolve every selected source to a concrete listing descriptor. Project\n // and team sources are listed separately: a project source filters by\n // `projectIds`, a team source filters by `teamId` (and returns projectless\n // issues too). Filters are never combined on one request.\n const parsedSources = sourceIds.map(sourceId => parseSourceId(sourceId));\n const projectWorkspaceIds = new Set(\n parsedSources.flatMap(source => (source.kind === 'project' ? [source.workspaceId] : [])),\n );\n const teamWorkspaceIds = new Set(\n parsedSources.flatMap(source => (source.kind === 'team' ? [source.workspaceId] : [])),\n );\n const [projectSources, teamSources] = await Promise.all([\n projectWorkspaceIds.size > 0\n ? this.#listProjectSources(projectWorkspaceIds)\n : Promise.resolve([] as ProjectSource[]),\n teamWorkspaceIds.size > 0 ? this.#listTeamSources(teamWorkspaceIds) : Promise.resolve([] as TeamSource[]),\n ]);\n const projectMap = new Map(\n projectSources.map(source => [encodeSourceId(source.workspace.linearWorkspaceId, source.project.id), source]),\n );\n const teamMap = new Map(\n teamSources.map(source => [encodeTeamSourceId(source.workspace.linearWorkspaceId, source.team.id), source]),\n );\n\n type Descriptor = {\n kind: 'project' | 'team';\n sourceId: string;\n workspace: LinearWorkspace;\n query: URLSearchParams;\n };\n const descriptors: Descriptor[] = [];\n const selectedProjectIdsByWorkspace = new Map<string, Set<string>>();\n for (const attributionSourceId of attributionSourceIds) {\n const source = parseSourceId(attributionSourceId);\n if (source.kind !== 'project') continue;\n const selectedProjectIds = selectedProjectIdsByWorkspace.get(source.workspaceId) ?? new Set<string>();\n selectedProjectIds.add(source.projectId);\n selectedProjectIdsByWorkspace.set(source.workspaceId, selectedProjectIds);\n }\n for (const sourceId of sourceIds) {\n const projectSource = projectMap.get(sourceId);\n if (projectSource) {\n const query = new URLSearchParams({\n first: String(PAGE_SIZE),\n projectIds: projectSource.project.id,\n stateType: 'triage,backlog,unstarted,started',\n orderBy: 'updatedAt',\n });\n const workspaceId = projectSource.workspace.linearWorkspaceId;\n const selectedProjectIds = selectedProjectIdsByWorkspace.get(workspaceId) ?? new Set<string>();\n selectedProjectIds.add(projectSource.project.id);\n selectedProjectIdsByWorkspace.set(workspaceId, selectedProjectIds);\n descriptors.push({ kind: 'project', sourceId, workspace: projectSource.workspace, query });\n continue;\n }\n const teamSource = teamMap.get(sourceId);\n if (teamSource) {\n const query = new URLSearchParams({\n first: String(PAGE_SIZE),\n teamId: teamSource.team.id,\n stateType: 'triage,backlog,unstarted,started',\n orderBy: 'updatedAt',\n });\n descriptors.push({ kind: 'team', sourceId, workspace: teamSource.workspace, query });\n }\n }\n\n const cursors = await decodeCursor(cursor, sourceIds, attributionSourceIds);\n const normalizedLabels = normalizeLabels(labels);\n const nextState: Record<string, string | null> = {};\n let hasNextPage = false;\n const pages = await Promise.all(\n descriptors.map(async ({ kind, sourceId, workspace, query }) => {\n if (cursors[sourceId] === null) {\n nextState[sourceId] = null;\n return [] as ListedIssue[];\n }\n const after = cursors[sourceId];\n if (after) query.set('after', after);\n const result = await this.#client.request<{ issues: LinearIssue[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/issues?${query}`,\n );\n const next = result.pageInfo.hasNextPage ? result.pageInfo.endCursor : null;\n // A page that hands back the cursor it was asked for would replay forever.\n if (next !== null && next === after) throw invalidLinearCursor();\n nextState[sourceId] = next;\n hasNextPage ||= next !== null;\n return result.issues\n .filter(\n issue => normalizedLabels.length === 0 || issue.labels.some(label => normalizedLabels.includes(label.name)),\n )\n .filter(\n issue =>\n kind === 'project' ||\n issue.project === null ||\n !selectedProjectIdsByWorkspace.get(workspace.linearWorkspaceId)?.has(issue.project.id),\n )\n .map(issue => ({ issue, sourceId, workspace }));\n }),\n );\n return {\n issues: dedupeIssuesBySource(pages.flat(), sourceIds),\n nextCursor: hasNextPage ? await encodeCursor(nextState, sourceIds, attributionSourceIds) : null,\n };\n }\n\n async #findIssue(\n sourceId: string | undefined,\n issueId: string,\n sourceIds?: string[],\n routedSourceIds?: string[],\n ): Promise<{\n workspaceId: string;\n issue: LinearIssue & { comments?: { nodes: LinearComment[]; pageInfo: PageInfo } };\n } | null> {\n const scopedSourceIds = sourceIds ?? (sourceId ? [sourceId] : undefined);\n const scopedSources = scopedSourceIds?.map(sourceKey => ({\n sourceKey,\n source: parseSourceId(sourceKey),\n }));\n const routedSourceSet = routedSourceIds ? new Set(routedSourceIds) : undefined;\n const workspaceIds = routedSourceIds\n ? [...new Set(routedSourceIds.map(sourceKey => parseSourceId(sourceKey).workspaceId))]\n : scopedSources\n ? [...new Set(scopedSources.map(({ source }) => source.workspaceId))]\n : await this.#candidateWorkspaceIds(undefined);\n for (const workspaceId of workspaceIds) {\n try {\n const issue = await this.#client.request<\n LinearIssue & { comments?: { nodes: LinearComment[]; pageInfo: PageInfo } }\n >(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}?include=comments`,\n );\n if (scopedSources) {\n const matching = scopedSources.filter(\n ({ source }) =>\n source.workspaceId === workspaceId &&\n (source.kind === 'team' ? issue.team.id === source.teamId : issue.project?.id === source.projectId),\n );\n const winner = matching.find(({ source }) => source.kind === 'project') ?? matching[0];\n if (!winner || (routedSourceSet && !routedSourceSet.has(winner.sourceKey))) continue;\n }\n return { workspaceId, issue };\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n return null;\n }\n\n async #resolveWorkspaceForIssue(sourceId: string | undefined, issueId: string): Promise<string | null> {\n const workspaceIds = await this.#candidateWorkspaceIds(sourceId);\n if (workspaceIds.length === 1) return workspaceIds[0]!;\n for (const workspaceId of workspaceIds) {\n try {\n await this.#client.request<LinearIssue>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}`,\n );\n return workspaceId;\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n return null;\n }\n\n async #listWorkflowStates(workspaceId: string, teamId: string): Promise<LinearWorkflowState[]> {\n const cacheKey = `${workspaceId}:${teamId}`;\n const cached = this.#workflowStatesByTeam.get(cacheKey);\n if (cached) return cached;\n const result = await this.#client.request<{ workflowStates: LinearWorkflowState[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/workflow-states?teamId=${encodeURIComponent(teamId)}&first=100`,\n );\n this.#workflowStatesByTeam.set(cacheKey, result.workflowStates);\n return result.workflowStates;\n }\n\n async #candidateWorkspaceIds(sourceId: string | undefined): Promise<string[]> {\n if (sourceId) return [parseSourceId(sourceId).workspaceId];\n return (await this.#listWorkspaces()).map(workspace => workspace.linearWorkspaceId);\n }\n\n async #loadComments(\n workspaceId: string,\n issueId: string,\n embedded: { nodes: LinearComment[]; pageInfo: PageInfo } | undefined,\n ): Promise<LinearComment[]> {\n const comments = [...(embedded?.nodes ?? [])];\n let pageInfo = embedded?.pageInfo;\n let page = 0;\n while (pageInfo?.hasNextPage && pageInfo.endCursor && page < MAX_COMMENT_PAGES) {\n const result = await this.#client.request<{ comments: LinearComment[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments?first=200&after=${encodeURIComponent(pageInfo.endCursor)}`,\n );\n comments.push(...result.comments);\n if (result.pageInfo.hasNextPage && result.pageInfo.endCursor === pageInfo.endCursor) throw invalidLinearCursor();\n pageInfo = result.pageInfo;\n page += 1;\n }\n return comments;\n }\n}\n\nfunction parseIssue(issue: LinearIssue): IntakeIssue {\n return {\n id: issue.id,\n identifier: issue.identifier,\n title: issue.title,\n url: issue.url,\n author: issue.creator?.displayName ?? issue.creator?.name ?? null,\n state: issue.state.name,\n stateType: issue.state.type,\n priority: issue.priorityLabel,\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n source: issue.team.key,\n labels: issue.labels.map(label => label.name),\n commentCount: null,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n };\n}\n\nfunction parseIssueDetail(issue: LinearIssue, comments: LinearComment[]): IntakeIssueDetail {\n return {\n ...parseIssue(issue),\n commentCount: comments.length,\n description: issue.description?.trim() ? issue.description : null,\n comments: comments.map(comment => ({\n author: comment.user?.displayName ?? comment.user?.name ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n })),\n };\n}\n\n/**\n * Deduplicate issues that surfaced from more than one selected source (a team\n * source and one of its projects both select the same issue). Keeps one entry\n * per Linear issue UUID.\n *\n * Tie-breaker — MOST-SPECIFIC-WINS: a project source beats a team source, so an\n * issue that belongs to a selected project is attributed to that project's\n * source (and therefore routes to the project's board). The team source covers\n * only the remainder — projectless issues and issues in projects that were not\n * separately selected. `selectionOrder` breaks a same-kind tie deterministically\n * by the order the sources were selected.\n *\n * This function is the single precedence point; change the comparison here to\n * change the routing policy.\n */\nfunction dedupeIssuesBySource<T extends { issue: { id: string }; sourceId: string }>(\n entries: T[],\n selectionOrder: string[],\n): T[] {\n const rank = new Map(selectionOrder.map((id, index) => [id, index]));\n const specificity = (sourceId: string): number => (parseSourceId(sourceId).kind === 'project' ? 0 : 1);\n const winners = new Map<string, T>();\n for (const entry of entries) {\n const existing = winners.get(entry.issue.id);\n if (!existing) {\n winners.set(entry.issue.id, entry);\n continue;\n }\n const bySpecificity = specificity(entry.sourceId) - specificity(existing.sourceId);\n const better =\n bySpecificity < 0 ||\n (bySpecificity === 0 &&\n (rank.get(entry.sourceId) ?? Number.MAX_SAFE_INTEGER) <\n (rank.get(existing.sourceId) ?? Number.MAX_SAFE_INTEGER));\n if (better) winners.set(entry.issue.id, entry);\n }\n // Preserve first-seen order of the surviving issues.\n const seen = new Set<string>();\n const ordered: T[] = [];\n for (const entry of entries) {\n if (seen.has(entry.issue.id)) continue;\n seen.add(entry.issue.id);\n ordered.push(winners.get(entry.issue.id)!);\n }\n return ordered;\n}\n\nexport function encodeSourceId(workspaceId: string, projectId: string): string {\n return `linear-project:${Buffer.from(JSON.stringify({ workspaceId, projectId })).toString('base64url')}`;\n}\n\nfunction decodeSourceId(sourceId: string): { workspaceId: string; projectId: string } {\n if (!sourceId.startsWith('linear-project:')) throw new Error('Linear project source id is invalid.');\n try {\n const parsed = JSON.parse(Buffer.from(sourceId.slice('linear-project:'.length), 'base64url').toString('utf8')) as {\n workspaceId?: unknown;\n projectId?: unknown;\n };\n if (typeof parsed.workspaceId !== 'string' || !parsed.workspaceId) throw new Error();\n if (typeof parsed.projectId !== 'string' || !parsed.projectId) throw new Error();\n return { workspaceId: parsed.workspaceId, projectId: parsed.projectId };\n } catch {\n throw new Error('Linear project source id is invalid.');\n }\n}\n\nconst TEAM_SOURCE_PREFIX = 'linear-team:';\n\nexport function encodeTeamSourceId(workspaceId: string, teamId: string): string {\n return `${TEAM_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ workspaceId, teamId })).toString('base64url')}`;\n}\n\nexport function decodeTeamSourceId(sourceId: string): { workspaceId: string; teamId: string } {\n if (!sourceId.startsWith(TEAM_SOURCE_PREFIX)) throw new Error('Linear team source id is invalid.');\n try {\n const parsed = JSON.parse(Buffer.from(sourceId.slice(TEAM_SOURCE_PREFIX.length), 'base64url').toString('utf8')) as {\n workspaceId?: unknown;\n teamId?: unknown;\n };\n if (typeof parsed.workspaceId !== 'string' || !parsed.workspaceId) throw new Error();\n if (typeof parsed.teamId !== 'string' || !parsed.teamId) throw new Error();\n return { workspaceId: parsed.workspaceId, teamId: parsed.teamId };\n } catch {\n throw new Error('Linear team source id is invalid.');\n }\n}\n\n/**\n * A decoded intake source: either a Linear project or a whole Linear team.\n * Downstream code discriminates on `kind` to build the right issue-listing\n * filter and to apply most-specific-wins routing (project beats team).\n */\nexport type DecodedSource =\n { kind: 'project'; workspaceId: string; projectId: string } | { kind: 'team'; workspaceId: string; teamId: string };\n\n/** Discriminate any Linear source id by its prefix and decode it. */\nexport function parseSourceId(sourceId: string): DecodedSource {\n if (sourceId.startsWith(TEAM_SOURCE_PREFIX)) {\n return { kind: 'team', ...decodeTeamSourceId(sourceId) };\n }\n return { kind: 'project', ...decodeSourceId(sourceId) };\n}\n\nfunction normalizeLabels(labels: string[] | undefined): string[] {\n return [...new Set((labels ?? []).map(label => label.trim()).filter(Boolean))];\n}\n\ntype PlatformListCursor = { v: 1; sourceSet: string; cursors: Array<string | null> };\n\nfunction canonicalSourceIds(sourceIds: string[]): string[] {\n return [...new Set(sourceIds)].sort();\n}\n\nasync function linearSourceSetFingerprint(sourceIds: string[], attributionSourceIds: string[]): Promise<string> {\n const scope = {\n sourceIds: canonicalSourceIds(sourceIds),\n attributionSourceIds: canonicalSourceIds(attributionSourceIds),\n };\n const bytes = new TextEncoder().encode(JSON.stringify(scope));\n return Buffer.from(await globalThis.crypto.subtle.digest('SHA-256', bytes)).toString('base64url');\n}\n\nfunction invalidLinearCursor(): Error {\n return Object.assign(new Error('Linear cursor is invalid or stale.'), { code: 'invalid_cursor' as const });\n}\n\nasync function decodeCursor(\n cursor: string | undefined,\n sourceIds: string[],\n attributionSourceIds: string[],\n): Promise<Record<string, string | null | undefined>> {\n if (!cursor) return {};\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Partial<PlatformListCursor>;\n const canonical = canonicalSourceIds(sourceIds);\n if (\n parsed.v !== 1 ||\n parsed.sourceSet !== (await linearSourceSetFingerprint(sourceIds, attributionSourceIds)) ||\n !Array.isArray(parsed.cursors) ||\n parsed.cursors.length !== canonical.length ||\n !parsed.cursors.every(value => value === null || typeof value === 'string')\n ) {\n throw invalidLinearCursor();\n }\n return Object.fromEntries(canonical.map((sourceId, index) => [sourceId, parsed.cursors![index]]));\n } catch {\n throw invalidLinearCursor();\n }\n}\n\nasync function encodeCursor(\n state: Record<string, string | null>,\n sourceIds: string[],\n attributionSourceIds: string[],\n): Promise<string> {\n const canonical = canonicalSourceIds(sourceIds);\n const cursor: PlatformListCursor = {\n v: 1,\n sourceSet: await linearSourceSetFingerprint(sourceIds, attributionSourceIds),\n cursors: canonical.map(sourceId => state[sourceId] ?? null),\n };\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction requireLinearConnection(connection: IntegrationConnection): void {\n if (connection.type !== 'oauth') {\n throw new Error('Linear capabilities require an OAuth connection.');\n }\n}\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof PlatformApiError && error.status === 404;\n}\n\nfunction optionalPositiveIntegerEnv(name: string): number | undefined {\n const value = process.env[name]?.trim();\n if (!value) return undefined;\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed) || parsed <= 0) {\n throw new Error(`${name} must be a positive integer.`);\n }\n return parsed;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { actorFromChannelAuthor } from "../../storage/domains/comments/actor.js";
2
2
  import { readRequestContextOrgId, seedSessionOrg } from "../../session/org-seed.js";
3
+ import { prepareSessionRunContext } from "../subscription-session.js";
3
4
  import { applyPersonalMemorySettings } from "../../session/memory-settings-hydration.js";
4
5
  import { FactorySourceControlConflictError, createSourceControlSessionLookup, hydrateFactorySession, resolveFactoryDefaultModelId, resolveFactoryProjectForSession, resolveFactorySourceControl, resolveFactorySourceRepository } from "../../session/factory-session.js";
5
6
  import { resolveEmojiShortcodes } from "./emoji.js";
6
7
  import { slackCommentSource } from "./feed-publisher.js";
7
8
  import { ChannelSessionRejectedError } from "@mastra/core/channels";
8
- import { randomUUID } from "crypto";
9
9
  import { createSlackAdapter } from "@mastra/slack";
10
10
  import { Actions, Card, CardText, LinkButton } from "chat";
11
11
  //#region src/integrations/slack/slack.ts
@@ -238,7 +238,7 @@ function createChannelResourceIdResolver(deps) {
238
238
  });
239
239
  if (existing) return existing.sessionId;
240
240
  return (await sourceControl.sessions.create({
241
- sessionId: randomUUID(),
241
+ sessionId: globalThis.crypto.randomUUID(),
242
242
  projectRepositoryId: repo.projectRepositoryId,
243
243
  orgId,
244
244
  userId: link.userId,
@@ -332,15 +332,21 @@ function createChannelSessionStartHook(deps) {
332
332
  await seedSessionOrg(session, owner.orgId);
333
333
  const modeModelKey = `modeModelId_${session.mode.get()}`;
334
334
  const persistedModelId = await session.thread.getSetting({ key: modeModelKey });
335
- if (typeof persistedModelId === "string") await hydrateFactorySession(session, {
336
- orgId: owner.orgId,
337
- factoryProjectId: owner.factoryProjectId,
338
- observationalMemoryModelId: persistedModelId,
339
- memorySettings
340
- });
341
- else {
335
+ if (typeof persistedModelId === "string") {
336
+ await hydrateFactorySession(session, {
337
+ orgId: owner.orgId,
338
+ factoryProjectId: owner.factoryProjectId,
339
+ observationalMemoryModelId: persistedModelId,
340
+ memorySettings
341
+ });
342
+ for (const agentType of SUBAGENT_TYPES) {
343
+ const modelId = await session.thread.getSetting({ key: pinnedSubagentModelKey(agentType) });
344
+ if (typeof modelId === "string") await applySubagentModel(session, agentType, modelId);
345
+ }
346
+ } else {
342
347
  const factoryModelId = await resolveFactoryDefaultModelId(projects, owner.factoryProjectId);
343
- const selectedModelId = await resolveActivePackBuildModel(modelPacks, owner) ?? factoryModelId;
348
+ const userPackModels = await resolveActivePackModels(modelPacks, owner);
349
+ const selectedModelId = (userPackModels?.build || void 0) ?? factoryModelId;
344
350
  await hydrateFactorySession(session, {
345
351
  orgId: owner.orgId,
346
352
  factoryProjectId: owner.factoryProjectId,
@@ -381,6 +387,19 @@ function createChannelSessionStartHook(deps) {
381
387
  modelId: currentModelId
382
388
  });
383
389
  }
390
+ const packSubagentModels = {
391
+ explore: userPackModels?.fast,
392
+ plan: userPackModels?.plan,
393
+ execute: userPackModels?.build
394
+ };
395
+ for (const agentType of SUBAGENT_TYPES) {
396
+ const modelId = packSubagentModels[agentType] || factoryModelId;
397
+ if (!modelId) continue;
398
+ if (await applySubagentModel(session, agentType, modelId)) await session.thread.setSetting({
399
+ key: pinnedSubagentModelKey(agentType),
400
+ value: modelId
401
+ });
402
+ }
384
403
  }
385
404
  await applyPersonalMemorySettings(session, {
386
405
  memorySettings,
@@ -389,21 +408,44 @@ function createChannelSessionStartHook(deps) {
389
408
  });
390
409
  };
391
410
  }
411
+ const SUBAGENT_TYPES = [
412
+ "explore",
413
+ "plan",
414
+ "execute"
415
+ ];
416
+ const pinnedSubagentModelKey = (agentType) => `slackSubagentModelId_${agentType}`;
417
+ /** Best-effort: a subagent model that can't be applied must not fail the message. */
418
+ async function applySubagentModel(session, agentType, modelId) {
419
+ try {
420
+ await session.subagents.model.set({
421
+ modelId,
422
+ agentType
423
+ });
424
+ return true;
425
+ } catch (error) {
426
+ console.warn("[slack] Failed to apply the subagent model", {
427
+ agentType,
428
+ modelId,
429
+ error: error instanceof Error ? error.message : String(error)
430
+ });
431
+ return false;
432
+ }
433
+ }
392
434
  /**
393
- * The `build` model of the sender's active model pack — the model that user
394
- * chose for themselves — or `undefined` when they have no pack.
435
+ * The models of the sender's active model pack — the models that user chose
436
+ * for themselves — or `undefined` when they have no pack.
395
437
  *
396
438
  * Best-effort by design: an uninitialized model-packs domain, a read failure, or
397
439
  * a pack saved without a build model all mean "no personal preference", which
398
440
  * falls through to the factory default rather than failing the dispatch.
399
441
  */
400
- async function resolveActivePackBuildModel(modelPacks, owner) {
442
+ async function resolveActivePackModels(modelPacks, owner) {
401
443
  if (!modelPacks) return void 0;
402
444
  try {
403
445
  return (await modelPacks.getActive({
404
446
  orgId: owner.orgId,
405
447
  userId: owner.userId
406
- }))?.models.build || void 0;
448
+ }))?.models;
407
449
  } catch (error) {
408
450
  console.warn("[slack] model pack lookup failed for a new session", {
409
451
  orgId: owner.orgId,
@@ -430,6 +472,21 @@ async function findInternalThread(mastra, thread) {
430
472
  }) ?? { threads: [] };
431
473
  return threads[0];
432
474
  }
475
+ async function prepareExistingSessionOwnerContext(thread, deps, ctx, options) {
476
+ const sourceControls = configuredSourceControls(deps);
477
+ if (sourceControls.length === 0 && !options.requireInternalThread) return "ready";
478
+ const internalThread = await findInternalThread(ctx.mastra, thread);
479
+ if (!internalThread) {
480
+ if (options.requireInternalThread) throw new Error(`Could not resolve the internal Slack thread for ${thread.id}.`);
481
+ return "ready";
482
+ }
483
+ if (sourceControls.length === 0 || internalThread.resourceId.startsWith("channel:")) return "ready";
484
+ if (!options.expectedOrgId) throw new Error(`Could not authorize the owner of Slack Factory session ${internalThread.resourceId}.`);
485
+ const preparation = await prepareSessionRunContext(ctx.requestContext, internalThread.resourceId, { sessions: createSourceControlSessionLookup(sourceControls) }, { expectedOrgId: options.expectedOrgId });
486
+ if (preparation === "organization-mismatch") return preparation;
487
+ if (preparation === "unavailable") throw new Error(`Could not authorize the owner of Slack Factory session ${internalThread.resourceId}.`);
488
+ return "ready";
489
+ }
433
490
  /**
434
491
  * Build the "new session" handler for mention / direct-message events. A mention or
435
492
  * DM on a not-yet-subscribed thread starts a NEW session; once subscribed, later
@@ -441,7 +498,8 @@ async function findInternalThread(mastra, thread) {
441
498
  * where possible); otherwise the dispatch context — with `routed` present
442
499
  * only when a linked sender resolved to a factory.
443
500
  */
444
- async function gateDispatch(thread, message, { accountLinks, projects }, ctx) {
501
+ async function gateDispatch(thread, message, deps, ctx, options = { requireInternalThread: false }) {
502
+ const { accountLinks, projects } = deps;
445
503
  const sender = await resolveLinkedSender({
446
504
  thread,
447
505
  message,
@@ -453,6 +511,13 @@ async function gateDispatch(thread, message, { accountLinks, projects }, ctx) {
453
511
  id: sender.link.userId,
454
512
  organizationId: sender.link.orgId
455
513
  });
514
+ if (await prepareExistingSessionOwnerContext(thread, deps, ctx, {
515
+ expectedOrgId: sender.link.orgId,
516
+ requireInternalThread: options.requireInternalThread
517
+ }) === "organization-mismatch") {
518
+ await thread.post("This thread belongs to a Factory session in another organization.");
519
+ return null;
520
+ }
456
521
  const route = await resolveFactoryForLink({
457
522
  thread,
458
523
  ...sender,
@@ -523,12 +588,60 @@ async function upsertThreadWorkItem({ workItems, thread, message, link, factoryP
523
588
  console.warn("[slack] work-item creation failed for thread", thread.id, error);
524
589
  }
525
590
  }
591
+ function preDispatchErrorDetails(error) {
592
+ const readMessage = (value) => {
593
+ const message = typeof value === "string" ? value : value && typeof value === "object" && "message" in value ? value.message : void 0;
594
+ return typeof message === "string" && message.length > 0 ? message : void 0;
595
+ };
596
+ try {
597
+ const message = readMessage(error) ?? "Unknown error";
598
+ const cause = error && typeof error === "object" && "cause" in error ? readMessage(error.cause) : void 0;
599
+ return {
600
+ message,
601
+ ...cause ? { cause: { message: cause } } : {}
602
+ };
603
+ } catch {
604
+ return { message: "Error details unavailable" };
605
+ }
606
+ }
607
+ async function reportPreDispatchError(thread, message, ctx, error) {
608
+ try {
609
+ if (error instanceof ChannelSessionRejectedError) return;
610
+ } catch {}
611
+ const correlation = {
612
+ platform: thread.adapter.name,
613
+ threadId: thread.id,
614
+ messageId: message.id,
615
+ authorId: message.author.userId
616
+ };
617
+ const logger = typeof ctx.mastra?.getLogger === "function" ? ctx.mastra.getLogger() : void 0;
618
+ const logError = (line) => logger ? logger.error(line) : console.error(line);
619
+ logError(`[slack] Pre-dispatch failure ${JSON.stringify({
620
+ ...correlation,
621
+ error: preDispatchErrorDetails(error)
622
+ })}`);
623
+ try {
624
+ await thread.post(`Couldn’t start processing your message. Please try again.\n\`messageId: ${message.id}\``);
625
+ } catch (deliveryError) {
626
+ logError(`[slack] Failed to deliver pre-dispatch error reply ${JSON.stringify({
627
+ ...correlation,
628
+ error: preDispatchErrorDetails(deliveryError)
629
+ })}`);
630
+ }
631
+ }
526
632
  function createNewSessionChatHandler(deps) {
527
633
  const { workItems } = deps;
528
634
  return async (thread, message, defaultHandler, ctx) => {
529
- const gate = await gateDispatch(thread, message, deps, ctx);
530
- if (!gate) return;
531
- const isNewSession = !await thread.isSubscribed();
635
+ let gate;
636
+ let isNewSession;
637
+ try {
638
+ gate = await gateDispatch(thread, message, deps, ctx);
639
+ if (!gate) return;
640
+ isNewSession = !await thread.isSubscribed();
641
+ } catch (error) {
642
+ await reportPreDispatchError(thread, message, ctx, error);
643
+ return;
644
+ }
532
645
  await defaultHandler(thread, message);
533
646
  if (!isNewSession) return;
534
647
  const internalThread = await findInternalThread(ctx.mastra, thread);
@@ -621,7 +734,12 @@ const createHandlers = (deps) => {
621
734
  return {
622
735
  onSubscribedMessage: async (thread, message, defaultHandler, ctx) => {
623
736
  if (/^aside\b/i.test(message.text)) return ingestAside(thread, message, deps);
624
- if (!await gateDispatch(thread, message, deps, ctx)) return;
737
+ try {
738
+ if (!await gateDispatch(thread, message, deps, ctx, { requireInternalThread: true })) return;
739
+ } catch (error) {
740
+ await reportPreDispatchError(thread, message, ctx, error);
741
+ return;
742
+ }
625
743
  await defaultHandler(thread, message);
626
744
  },
627
745
  onMention: newSessionChatHandler,