@mastra/factory 0.21.0-alpha.3 → 0.21.0-alpha.5

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 (87) hide show
  1. package/dist/auth.d.ts +7 -0
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +12 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/boards/relocate.d.ts +2 -2
  6. package/dist/boards/relocate.d.ts.map +1 -1
  7. package/dist/boards/relocate.js +5 -4
  8. package/dist/boards/relocate.js.map +1 -1
  9. package/dist/factory.d.ts +9 -0
  10. package/dist/factory.d.ts.map +1 -1
  11. package/dist/factory.js +56 -23
  12. package/dist/factory.js.map +1 -1
  13. package/dist/integrations/github/routes.js +1 -0
  14. package/dist/integrations/github/routes.js.map +1 -1
  15. package/dist/integrations/linear/issue-reconciler.d.ts +3 -1
  16. package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -1
  17. package/dist/integrations/linear/issue-reconciler.js +1 -1
  18. package/dist/integrations/linear/issue-reconciler.js.map +1 -1
  19. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  20. package/dist/integrations/platform/linear/integration.js +10 -9
  21. package/dist/integrations/platform/linear/integration.js.map +1 -1
  22. package/dist/integrations/slack/integration.d.ts.map +1 -1
  23. package/dist/integrations/slack/integration.js +0 -1
  24. package/dist/integrations/slack/integration.js.map +1 -1
  25. package/dist/integrations/slack/slack.d.ts +0 -17
  26. package/dist/integrations/slack/slack.d.ts.map +1 -1
  27. package/dist/integrations/slack/slack.js +8 -40
  28. package/dist/integrations/slack/slack.js.map +1 -1
  29. package/dist/routes/config.d.ts +15 -36
  30. package/dist/routes/config.d.ts.map +1 -1
  31. package/dist/routes/config.js +72 -123
  32. package/dist/routes/config.js.map +1 -1
  33. package/dist/routes/knowledge.d.ts +115 -21
  34. package/dist/routes/knowledge.d.ts.map +1 -1
  35. package/dist/routes/knowledge.js +715 -30
  36. package/dist/routes/knowledge.js.map +1 -1
  37. package/dist/routes/oauth.d.ts +0 -3
  38. package/dist/routes/oauth.d.ts.map +1 -1
  39. package/dist/routes/oauth.js +2 -10
  40. package/dist/routes/oauth.js.map +1 -1
  41. package/dist/routes/provider-credentials.d.ts.map +1 -1
  42. package/dist/routes/provider-credentials.js +2 -1
  43. package/dist/routes/provider-credentials.js.map +1 -1
  44. package/dist/routes/surface.d.ts +1 -0
  45. package/dist/routes/surface.d.ts.map +1 -1
  46. package/dist/routes/surface.js +6 -3
  47. package/dist/routes/surface.js.map +1 -1
  48. package/dist/rules/binding-context.d.ts +19 -0
  49. package/dist/rules/binding-context.d.ts.map +1 -1
  50. package/dist/rules/binding-context.js +39 -1
  51. package/dist/rules/binding-context.js.map +1 -1
  52. package/dist/rules/dispatcher.d.ts +2 -13
  53. package/dist/rules/dispatcher.d.ts.map +1 -1
  54. package/dist/rules/dispatcher.js +0 -6
  55. package/dist/rules/dispatcher.js.map +1 -1
  56. package/dist/rules/processor.d.ts +35 -1
  57. package/dist/rules/processor.d.ts.map +1 -1
  58. package/dist/rules/processor.js +80 -4
  59. package/dist/rules/processor.js.map +1 -1
  60. package/dist/rules/start-coordinator.d.ts +1 -2
  61. package/dist/rules/start-coordinator.d.ts.map +1 -1
  62. package/dist/rules/start-coordinator.js +2 -6
  63. package/dist/rules/start-coordinator.js.map +1 -1
  64. package/dist/session/factory-session.d.ts +5 -43
  65. package/dist/session/factory-session.d.ts.map +1 -1
  66. package/dist/session/factory-session.js +6 -47
  67. package/dist/session/factory-session.js.map +1 -1
  68. package/dist/session/memory-settings-hydration.d.ts +17 -89
  69. package/dist/session/memory-settings-hydration.d.ts.map +1 -1
  70. package/dist/session/memory-settings-hydration.js +25 -111
  71. package/dist/session/memory-settings-hydration.js.map +1 -1
  72. package/dist/storage/domains/memory-settings/base.d.ts +5 -17
  73. package/dist/storage/domains/memory-settings/base.d.ts.map +1 -1
  74. package/dist/storage/domains/memory-settings/base.js +8 -15
  75. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  76. package/dist/supervisor/session.d.ts +2 -4
  77. package/dist/supervisor/session.d.ts.map +1 -1
  78. package/dist/supervisor/session.js +3 -5
  79. package/dist/supervisor/session.js.map +1 -1
  80. package/dist/workspace.d.ts.map +1 -1
  81. package/dist/workspace.js +2 -2
  82. package/dist/workspace.js.map +1 -1
  83. package/package.json +7 -7
  84. package/dist/routes/om-seed.d.ts +0 -22
  85. package/dist/routes/om-seed.d.ts.map +0 -1
  86. package/dist/routes/om-seed.js +0 -40
  87. package/dist/routes/om-seed.js.map +0 -1
@@ -92,8 +92,8 @@ var PlatformLinearIntegration = class {
92
92
  requireLinearConnection(connection);
93
93
  const result = await this.#listIssues(sourceIds, cursor, labels, attributionSourceIds);
94
94
  return {
95
- issues: result.issues.map(({ issue, sourceId }) => ({
96
- ...parseIssue(issue),
95
+ issues: result.issues.map(({ issue, sourceId, workspace }) => ({
96
+ ...parseIssue(issue, workspace.linearWorkspaceId),
97
97
  sourceId
98
98
  })),
99
99
  nextCursor: result.nextCursor
@@ -104,7 +104,7 @@ var PlatformLinearIntegration = class {
104
104
  const located = await this.#findIssue(sourceId, issueId);
105
105
  if (!located) return null;
106
106
  const comments = await this.#loadComments(located.workspaceId, issueId, located.issue.comments);
107
- return parseIssueDetail(located.issue, comments);
107
+ return parseIssueDetail(located.issue, comments, located.workspaceId);
108
108
  },
109
109
  createComment: async ({ connection, sourceId, issueId, body }) => {
110
110
  requireLinearConnection(connection);
@@ -127,8 +127,8 @@ var PlatformLinearIntegration = class {
127
127
  if (!located) return null;
128
128
  const { workspaceId, issue } = located;
129
129
  const target = input.state;
130
- if (target.kind === "byType" && issue.state.type === target.stateType) return parseIssue(issue);
131
- if (target.kind === "byName" && issue.state.name.toLowerCase() === target.name.toLowerCase()) return parseIssue(issue);
130
+ if (target.kind === "byType" && issue.state.type === target.stateType) return parseIssue(issue, workspaceId);
131
+ if (target.kind === "byName" && issue.state.name.toLowerCase() === target.name.toLowerCase()) return parseIssue(issue, workspaceId);
132
132
  let states;
133
133
  try {
134
134
  states = await this.#listWorkflowStates(workspaceId, issue.team.id);
@@ -163,7 +163,7 @@ var PlatformLinearIntegration = class {
163
163
  throw error;
164
164
  }
165
165
  const refreshed = await this.#findIssue(input.sourceId, input.issueId);
166
- return refreshed ? parseIssue(refreshed.issue) : null;
166
+ return refreshed ? parseIssue(refreshed.issue, refreshed.workspaceId) : null;
167
167
  }
168
168
  };
169
169
  #rules;
@@ -578,7 +578,7 @@ var PlatformLinearIntegration = class {
578
578
  return comments;
579
579
  }
580
580
  };
581
- function parseIssue(issue) {
581
+ function parseIssue(issue, workspaceId) {
582
582
  return {
583
583
  id: issue.id,
584
584
  identifier: issue.identifier,
@@ -590,15 +590,16 @@ function parseIssue(issue) {
590
590
  priority: issue.priorityLabel,
591
591
  assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,
592
592
  source: issue.team.key,
593
+ projectId: issue.project ? encodeSourceId(workspaceId, issue.project.id) : null,
593
594
  labels: issue.labels.map((label) => label.name),
594
595
  commentCount: null,
595
596
  createdAt: issue.createdAt,
596
597
  updatedAt: issue.updatedAt
597
598
  };
598
599
  }
599
- function parseIssueDetail(issue, comments) {
600
+ function parseIssueDetail(issue, comments, workspaceId) {
600
601
  return {
601
- ...parseIssue(issue),
602
+ ...parseIssue(issue, workspaceId),
602
603
  commentCount: comments.length,
603
604
  description: issue.description?.trim() ? issue.description : null,
604
605
  comments: comments.map((comment) => ({
@@ -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 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
+ {"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, workspace }) => ({\n ...parseIssue(issue, workspace.linearWorkspaceId),\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, located.workspaceId);\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, workspaceId);\n }\n if (target.kind === 'byName' && issue.state.name.toLowerCase() === target.name.toLowerCase()) {\n return parseIssue(issue, workspaceId);\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, refreshed.workspaceId) : 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, workspaceId: string): 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 // Use the same workspace-scoped identity as listProjects and intake bindings.\n // The source may be a team, so it cannot stand in for the issue's project.\n projectId: issue.project ? encodeSourceId(workspaceId, issue.project.id) : null,\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[], workspaceId: string): IntakeIssueDetail {\n return {\n ...parseIssue(issue, workspaceId),\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":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AAyBD,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IA4BxD,aAAa,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAI7D,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AAyBD,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IA2BxD,aAAa,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAI7D,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
@@ -49,7 +49,6 @@ var SlackIntegration = class {
49
49
  accountLinks: ctx.storage.channelIdentity,
50
50
  projects: ctx.storage.projects,
51
51
  sourceControls,
52
- memorySettings: ctx.storage.memorySettings,
53
52
  modelDefaults: ctx.storage.modelDefaults,
54
53
  workItems: ctx.runtime?.workItems,
55
54
  feed: ctx.feed,
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { TypingStatusFn } from '@mastra/core/channels';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n /**\n * Overrides for the Slack channel adapter entry.\n *\n * @example\n * ```ts\n * new SlackIntegration({\n * signingSecret,\n * adapterOptions: {\n * streaming: true,\n * toolDisplay: 'grouped',\n * },\n * });\n * ```\n */\n adapterOptions?: SlackAdapterChannelConfig;\n}\n\nconst factoryTypingStatus: TypingStatusFn = chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return 'is typing...';\n case 'tool-call':\n return 'is working...';\n default:\n return undefined;\n }\n};\n\n/**\n * Drops keys the caller set to `undefined` so spreading the overrides cannot\n * erase the Factory defaults they sit on top of.\n */\nfunction adapterOverrides(options: SlackAdapterChannelConfig | undefined): SlackAdapterChannelConfig {\n return Object.fromEntries(\n Object.entries(options ?? {}).filter(([, value]) => value !== undefined),\n ) as SlackAdapterChannelConfig;\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions select the source-control provider from the linked\n // repository on each Factory project.\n const sourceControls =\n ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);\n this.#repoBackedSessions = sourceControls.length > 0;\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControls,\n memorySettings: ctx.storage.memorySettings,\n modelDefaults: ctx.storage.modelDefaults,\n workItems: ctx.runtime?.workItems,\n feed: ctx.feed,\n adapterOptions: {\n toolDisplay: 'hidden',\n typingStatus: factoryTypingStatus,\n ...adapterOverrides(this.#config.adapterOptions),\n },\n });\n }\n\n feedPublisher(ctx: IntegrationContext): WorkItemFeedPublisher {\n return new SlackFeedPublisher({ controller: ctx.controller });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;;AAyEA,MAAM,uBAAsC,UAAS;CACnD,QAAQ,MAAM,MAAd;EACE,KAAK,mBACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,SACE;CACJ;AACF;;;;;AAMA,SAAS,iBAAiB,SAA2E;CACnG,OAAO,OAAO,YACZ,OAAO,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACzE;AACF;AAEA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,iBACJ,IAAI,QAAQ,mBAAmB,IAAI,QAAQ,qBAAqB,CAAC,IAAI,QAAQ,kBAAkB,IAAI,CAAC;EACtG,KAAKC,sBAAsB,eAAe,SAAS;EACnD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB;GACA,gBAAgB,IAAI,QAAQ;GAC5B,eAAe,IAAI,QAAQ;GAC3B,WAAW,IAAI,SAAS;GACxB,MAAM,IAAI;GACV,gBAAgB;IACd,aAAa;IACb,cAAc;IACd,GAAG,iBAAiB,KAAKA,QAAQ,cAAc;GACjD;EACF,CAAC;CACH;CAEA,cAAc,KAAgD;EAC5D,OAAO,IAAI,mBAAmB,EAAE,YAAY,IAAI,WAAW,CAAC;CAC9D;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
1
+ {"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { TypingStatusFn } from '@mastra/core/channels';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n /**\n * Overrides for the Slack channel adapter entry.\n *\n * @example\n * ```ts\n * new SlackIntegration({\n * signingSecret,\n * adapterOptions: {\n * streaming: true,\n * toolDisplay: 'grouped',\n * },\n * });\n * ```\n */\n adapterOptions?: SlackAdapterChannelConfig;\n}\n\nconst factoryTypingStatus: TypingStatusFn = chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return 'is typing...';\n case 'tool-call':\n return 'is working...';\n default:\n return undefined;\n }\n};\n\n/**\n * Drops keys the caller set to `undefined` so spreading the overrides cannot\n * erase the Factory defaults they sit on top of.\n */\nfunction adapterOverrides(options: SlackAdapterChannelConfig | undefined): SlackAdapterChannelConfig {\n return Object.fromEntries(\n Object.entries(options ?? {}).filter(([, value]) => value !== undefined),\n ) as SlackAdapterChannelConfig;\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions select the source-control provider from the linked\n // repository on each Factory project.\n const sourceControls =\n ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);\n this.#repoBackedSessions = sourceControls.length > 0;\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControls,\n modelDefaults: ctx.storage.modelDefaults,\n workItems: ctx.runtime?.workItems,\n feed: ctx.feed,\n adapterOptions: {\n toolDisplay: 'hidden',\n typingStatus: factoryTypingStatus,\n ...adapterOverrides(this.#config.adapterOptions),\n },\n });\n }\n\n feedPublisher(ctx: IntegrationContext): WorkItemFeedPublisher {\n return new SlackFeedPublisher({ controller: ctx.controller });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;;AAyEA,MAAM,uBAAsC,UAAS;CACnD,QAAQ,MAAM,MAAd;EACE,KAAK,mBACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,SACE;CACJ;AACF;;;;;AAMA,SAAS,iBAAiB,SAA2E;CACnG,OAAO,OAAO,YACZ,OAAO,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACzE;AACF;AAEA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,iBACJ,IAAI,QAAQ,mBAAmB,IAAI,QAAQ,qBAAqB,CAAC,IAAI,QAAQ,kBAAkB,IAAI,CAAC;EACtG,KAAKC,sBAAsB,eAAe,SAAS;EACnD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB;GACA,eAAe,IAAI,QAAQ;GAC3B,WAAW,IAAI,SAAS;GACxB,MAAM,IAAI;GACV,gBAAgB;IACd,aAAa;IACb,cAAc;IACd,GAAG,iBAAiB,KAAKA,QAAQ,cAAc;GACjD;EACF,CAAC;CACH;CAEA,cAAc,KAAgD;EAC5D,OAAO,IAAI,mBAAmB,EAAE,YAAY,IAAI,WAAW,CAAC;CAC9D;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
@@ -2,7 +2,6 @@ import type { ChannelHandler, ChannelHandlers, ChannelSessionResolve, ChannelSes
2
2
  import type { SlackAdapterChannelConfig } from '@mastra/slack';
3
3
  import type { ChannelAccountLink, ChannelAccountLinkKey, ChannelIdentityStorage } from '../../storage/domains/channel-identity/base.js';
4
4
  import type { CommentsDomain } from '../../storage/domains/comments/domain.js';
5
- import type { MemorySettingsStorage } from '../../storage/domains/memory-settings/base.js';
6
5
  import type { ModelDefaultsStorage } from '../../storage/domains/model-defaults/base.js';
7
6
  import type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';
8
7
  import type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';
@@ -43,13 +42,6 @@ interface SlackChannelDeps {
43
42
  sourceControl?: SourceControlStorageHandle;
44
43
  /** Registered source-control partitions used to select the provider linked to each Factory project. */
45
44
  sourceControls?: readonly SourceControlStorageHandle[];
46
- /**
47
- * Observational-memory settings domain. When provided, a repo-backed session
48
- * adopts its factory project's shared memory settings on start, matching the
49
- * web kickoff — and the linked sender's own settings win over them for every
50
- * knob the sender has saved.
51
- */
52
- memorySettings?: MemorySettingsStorage;
53
45
  /**
54
46
  * Model-defaults domain. When provided, a new repo-backed session starts on
55
47
  * the linked sender's default model before the factory's shared default. Read
@@ -189,19 +181,10 @@ export declare function createChannelSessionResolver(deps: SlackChannelDeps): Ch
189
181
  * default, else the SDK's built-in model. The choice is persisted on the thread
190
182
  * as `currentModelId`, so it outlives the process that made it.
191
183
  *
192
- * Observational memory is configured here too, in the same order of who chose
193
- * it: the project's shared settings first, then the linked sender's own row,
194
- * which wins for every knob they have saved. The pair is re-applied on every
195
- * start rather than once — memory settings are stored preference, not a choice
196
- * made on this thread, and a restarted process re-resolves the project's row
197
- * before this hook runs.
198
- *
199
184
  * The model resolution is skipped on a session that already has a model
200
185
  * persisted on the thread. That is the durable record of a deliberate choice —
201
186
  * either an earlier start or a user's own switch — and re-applying a preference
202
187
  * over it would undo the user's selection every time the process restarts.
203
- * Memory settings have no such per-thread record, so they are re-applied on
204
- * every start.
205
188
  */
206
189
  export declare function createChannelSessionStartHook(deps: SlackChannelDeps): ChannelSessionStart;
207
190
  /**
@@ -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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"}
@@ -2,7 +2,6 @@ import { actorFromChannelAuthor } from "../../storage/domains/comments/actor.js"
2
2
  import { primeTenantCredentialsForRequestContext } from "../../routes/tenant-credentials.js";
3
3
  import { readRequestContextOrgId, seedSessionOrg } from "../../session/org-seed.js";
4
4
  import { prepareSessionRunContext } from "../subscription-session.js";
5
- import { applyPersonalMemorySettings } from "../../session/memory-settings-hydration.js";
6
5
  import { FactorySourceControlConflictError, createSourceControlSessionLookup, hydrateFactorySession, resolveFactoryDefaultModelId, resolveFactoryProjectForSession, resolveFactorySourceControl, resolveFactorySourceRepository } from "../../session/factory-session.js";
7
6
  import { resolveEmojiShortcodes } from "./emoji.js";
8
7
  import { slackCommentSource } from "./feed-publisher.js";
@@ -302,22 +301,13 @@ function createChannelSessionResolver(deps) {
302
301
  * default, else the SDK's built-in model. The choice is persisted on the thread
303
302
  * as `currentModelId`, so it outlives the process that made it.
304
303
  *
305
- * Observational memory is configured here too, in the same order of who chose
306
- * it: the project's shared settings first, then the linked sender's own row,
307
- * which wins for every knob they have saved. The pair is re-applied on every
308
- * start rather than once — memory settings are stored preference, not a choice
309
- * made on this thread, and a restarted process re-resolves the project's row
310
- * before this hook runs.
311
- *
312
304
  * The model resolution is skipped on a session that already has a model
313
305
  * persisted on the thread. That is the durable record of a deliberate choice —
314
306
  * either an earlier start or a user's own switch — and re-applying a preference
315
307
  * over it would undo the user's selection every time the process restarts.
316
- * Memory settings have no such per-thread record, so they are re-applied on
317
- * every start.
318
308
  */
319
309
  function createChannelSessionStartHook(deps) {
320
- const { projects, memorySettings, modelDefaults } = deps;
310
+ const { projects, modelDefaults } = deps;
321
311
  const sourceControlSessions = createSourceControlSessionLookup(configuredSourceControls(deps));
322
312
  return async ({ session, thread, requestContext }) => {
323
313
  await seedSessionOrg(session, readRequestContextOrgId(requestContext));
@@ -334,7 +324,7 @@ function createChannelSessionStartHook(deps) {
334
324
  await seedSessionOrg(session, owner.orgId);
335
325
  const modeId = session.mode.get();
336
326
  const activeThreadId = session.thread.getId();
337
- const persistedModelId = await migratePersistedModelSelection({
327
+ if (await migratePersistedModelSelection({
338
328
  getMetadata: async () => activeThreadId ? (await session.thread.getById({ threadId: activeThreadId }))?.metadata ?? {} : {},
339
329
  modeId,
340
330
  set: (key, value) => activeThreadId ? session.thread.setSettingOn({
@@ -343,28 +333,17 @@ function createChannelSessionStartHook(deps) {
343
333
  value
344
334
  }) : Promise.resolve(),
345
335
  threadId: activeThreadId ?? void 0
346
- });
347
- if (persistedModelId) {
348
- await hydrateFactorySession(session, {
349
- orgId: owner.orgId,
350
- factoryProjectId: owner.factoryProjectId,
351
- observationalMemoryModelId: persistedModelId,
352
- memorySettings
353
- });
354
- for (const agentType of SUBAGENT_TYPES) {
355
- const modelId = await session.thread.getSetting({ key: pinnedSubagentModelKey(agentType) });
356
- if (typeof modelId === "string") await applySubagentModel(session, agentType, modelId);
357
- }
358
- } else {
336
+ })) for (const agentType of SUBAGENT_TYPES) {
337
+ const modelId = await session.thread.getSetting({ key: pinnedSubagentModelKey(agentType) });
338
+ if (typeof modelId === "string") await applySubagentModel(session, agentType, modelId);
339
+ }
340
+ else {
359
341
  const factoryModelId = await resolveFactoryDefaultModelId(projects, owner.factoryProjectId);
360
342
  const userModelId = await resolveUserDefaultModel(modelDefaults, owner);
361
343
  const selectedModelId = userModelId ?? factoryModelId;
362
344
  await hydrateFactorySession(session, {
363
345
  orgId: owner.orgId,
364
- factoryProjectId: owner.factoryProjectId,
365
- defaultModelId: factoryModelId,
366
- observationalMemoryModelId: selectedModelId,
367
- memorySettings
346
+ defaultModelId: factoryModelId
368
347
  });
369
348
  if (selectedModelId && selectedModelId !== factoryModelId) try {
370
349
  await session.model.switch(selectedModelId);
@@ -374,12 +353,6 @@ function createChannelSessionStartHook(deps) {
374
353
  error: error instanceof Error ? error.message : String(error)
375
354
  });
376
355
  const currentModelId = session.model.get();
377
- await hydrateFactorySession(session, {
378
- orgId: owner.orgId,
379
- factoryProjectId: owner.factoryProjectId,
380
- observationalMemoryModelId: currentModelId,
381
- memorySettings
382
- });
383
356
  if (currentModelId && !factoryModelId) try {
384
357
  await session.model.switch(currentModelId);
385
358
  } catch (saveError) {
@@ -402,11 +375,6 @@ function createChannelSessionStartHook(deps) {
402
375
  });
403
376
  }
404
377
  }
405
- await applyPersonalMemorySettings(session, {
406
- memorySettings,
407
- orgId: owner.orgId,
408
- userId: owner.userId
409
- });
410
378
  };
411
379
  }
412
380
  const SUBAGENT_TYPES = [