@mastra/factory 0.24.0-alpha.0 → 0.24.0-alpha.2

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 (196) hide show
  1. package/README.md +80 -4
  2. package/dist/auth.d.ts +8 -0
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +15 -1
  5. package/dist/auth.js.map +1 -1
  6. package/dist/boards/work-transition-policy.d.ts.map +1 -1
  7. package/dist/boards/work-transition-policy.js +10 -0
  8. package/dist/boards/work-transition-policy.js.map +1 -1
  9. package/dist/boards/work.d.ts +14 -0
  10. package/dist/boards/work.d.ts.map +1 -1
  11. package/dist/boards/work.js +70 -7
  12. package/dist/boards/work.js.map +1 -1
  13. package/dist/capabilities/version-control.d.ts +26 -0
  14. package/dist/capabilities/version-control.d.ts.map +1 -1
  15. package/dist/factory.d.ts.map +1 -1
  16. package/dist/factory.js +173 -11
  17. package/dist/factory.js.map +1 -1
  18. package/dist/integrations/github/integration.d.ts.map +1 -1
  19. package/dist/integrations/github/integration.js +37 -1
  20. package/dist/integrations/github/integration.js.map +1 -1
  21. package/dist/integrations/gitlab/api.d.ts +15 -0
  22. package/dist/integrations/gitlab/api.d.ts.map +1 -1
  23. package/dist/integrations/gitlab/api.js +9 -0
  24. package/dist/integrations/gitlab/api.js.map +1 -1
  25. package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
  26. package/dist/integrations/gitlab/version-control.js +37 -3
  27. package/dist/integrations/gitlab/version-control.js.map +1 -1
  28. package/dist/integrations/incidentio/api.d.ts +9 -0
  29. package/dist/integrations/incidentio/api.d.ts.map +1 -1
  30. package/dist/integrations/incidentio/api.js +3 -0
  31. package/dist/integrations/incidentio/api.js.map +1 -1
  32. package/dist/integrations/incidentio/integration.d.ts.map +1 -1
  33. package/dist/integrations/incidentio/integration.js +1 -0
  34. package/dist/integrations/incidentio/integration.js.map +1 -1
  35. package/dist/integrations/jira/api.d.ts +7 -0
  36. package/dist/integrations/jira/api.d.ts.map +1 -1
  37. package/dist/integrations/jira/api.js +4 -0
  38. package/dist/integrations/jira/api.js.map +1 -1
  39. package/dist/integrations/platform/api-client.d.ts +6 -0
  40. package/dist/integrations/platform/api-client.d.ts.map +1 -1
  41. package/dist/integrations/platform/api-client.js +6 -1
  42. package/dist/integrations/platform/api-client.js.map +1 -1
  43. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  44. package/dist/integrations/platform/github/integration.js +27 -1
  45. package/dist/integrations/platform/github/integration.js.map +1 -1
  46. package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
  47. package/dist/integrations/platform/incidentio/integration.js +1 -0
  48. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  49. package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
  50. package/dist/integrations/platform/jira/integration.js +1 -0
  51. package/dist/integrations/platform/jira/integration.js.map +1 -1
  52. package/dist/integrations/subscription-session.d.ts +15 -0
  53. package/dist/integrations/subscription-session.d.ts.map +1 -1
  54. package/dist/integrations/subscription-session.js +30 -13
  55. package/dist/integrations/subscription-session.js.map +1 -1
  56. package/dist/provider-display-name.d.ts +2 -0
  57. package/dist/provider-display-name.d.ts.map +1 -0
  58. package/dist/provider-display-name.js +16 -0
  59. package/dist/provider-display-name.js.map +1 -0
  60. package/dist/routes/config.js +2 -2
  61. package/dist/routes/config.js.map +1 -1
  62. package/dist/routes/contracts.d.ts +4 -0
  63. package/dist/routes/contracts.d.ts.map +1 -1
  64. package/dist/routes/contracts.js +3 -1
  65. package/dist/routes/contracts.js.map +1 -1
  66. package/dist/routes/oauth.js +1 -1
  67. package/dist/routes/oauth.js.map +1 -1
  68. package/dist/routes/provider-credentials.d.ts +6 -2
  69. package/dist/routes/provider-credentials.d.ts.map +1 -1
  70. package/dist/routes/provider-credentials.js +20 -7
  71. package/dist/routes/provider-credentials.js.map +1 -1
  72. package/dist/routes/source-control-sessions.d.ts +1 -0
  73. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  74. package/dist/routes/source-control-sessions.js +1 -1
  75. package/dist/routes/source-control-sessions.js.map +1 -1
  76. package/dist/routes/surface.d.ts +1 -0
  77. package/dist/routes/surface.d.ts.map +1 -1
  78. package/dist/routes/surface.js +1 -0
  79. package/dist/routes/surface.js.map +1 -1
  80. package/dist/routes/work-items.d.ts.map +1 -1
  81. package/dist/routes/work-items.js +57 -117
  82. package/dist/routes/work-items.js.map +1 -1
  83. package/dist/rules/dispatcher.d.ts +7 -0
  84. package/dist/rules/dispatcher.d.ts.map +1 -1
  85. package/dist/rules/dispatcher.js +60 -3
  86. package/dist/rules/dispatcher.js.map +1 -1
  87. package/dist/rules/processor.d.ts.map +1 -1
  88. package/dist/rules/processor.js +2 -1
  89. package/dist/rules/processor.js.map +1 -1
  90. package/dist/rules/tools.d.ts.map +1 -1
  91. package/dist/rules/tools.js +82 -3
  92. package/dist/rules/tools.js.map +1 -1
  93. package/dist/rules/transition-service.d.ts +17 -0
  94. package/dist/rules/transition-service.d.ts.map +1 -1
  95. package/dist/rules/transition-service.js +25 -0
  96. package/dist/rules/transition-service.js.map +1 -1
  97. package/dist/rules/types.d.ts +2 -0
  98. package/dist/rules/types.d.ts.map +1 -1
  99. package/dist/rules/types.js.map +1 -1
  100. package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
  101. package/dist/sandbox/local-factory-sandbox.js +1 -0
  102. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  103. package/dist/session/factory-session.d.ts +15 -1
  104. package/dist/session/factory-session.d.ts.map +1 -1
  105. package/dist/session/factory-session.js +4 -2
  106. package/dist/session/factory-session.js.map +1 -1
  107. package/dist/session/reply-credential-processor.d.ts +15 -0
  108. package/dist/session/reply-credential-processor.d.ts.map +1 -0
  109. package/dist/session/reply-credential-processor.js +40 -0
  110. package/dist/session/reply-credential-processor.js.map +1 -0
  111. package/dist/session/source-control-tools.d.ts +19 -1
  112. package/dist/session/source-control-tools.d.ts.map +1 -1
  113. package/dist/session/source-control-tools.js +70 -3
  114. package/dist/session/source-control-tools.js.map +1 -1
  115. package/dist/storage/domains/projects/base.d.ts +6 -0
  116. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  117. package/dist/storage/domains/projects/base.js +12 -0
  118. package/dist/storage/domains/projects/base.js.map +1 -1
  119. package/dist/storage/domains/source-control/base.d.ts +17 -0
  120. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  121. package/dist/storage/domains/source-control/base.js +23 -0
  122. package/dist/storage/domains/source-control/base.js.map +1 -1
  123. package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
  124. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  125. package/dist/storage/domains/source-control/inmemory.js +5 -1
  126. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  127. package/dist/storage/domains/work-items/base.d.ts +56 -0
  128. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  129. package/dist/storage/domains/work-items/base.js +139 -25
  130. package/dist/storage/domains/work-items/base.js.map +1 -1
  131. package/dist/tools/approval-tools.d.ts +15 -0
  132. package/dist/tools/approval-tools.d.ts.map +1 -0
  133. package/dist/tools/approval-tools.js +77 -0
  134. package/dist/tools/approval-tools.js.map +1 -0
  135. package/dist/tools/deliver.d.ts +54 -0
  136. package/dist/tools/deliver.d.ts.map +1 -0
  137. package/dist/tools/deliver.js +105 -0
  138. package/dist/tools/deliver.js.map +1 -0
  139. package/dist/tools/index.d.ts +29 -0
  140. package/dist/tools/index.d.ts.map +1 -0
  141. package/dist/tools/index.js +39 -0
  142. package/dist/tools/index.js.map +1 -0
  143. package/dist/tools/messaging-tools.d.ts +28 -0
  144. package/dist/tools/messaging-tools.d.ts.map +1 -0
  145. package/dist/tools/messaging-tools.js +312 -0
  146. package/dist/tools/messaging-tools.js.map +1 -0
  147. package/dist/tools/permissions.d.ts +31 -0
  148. package/dist/tools/permissions.d.ts.map +1 -0
  149. package/dist/tools/permissions.js +44 -0
  150. package/dist/tools/permissions.js.map +1 -0
  151. package/dist/tools/read-tools.d.ts +27 -0
  152. package/dist/tools/read-tools.d.ts.map +1 -0
  153. package/dist/tools/read-tools.js +285 -0
  154. package/dist/tools/read-tools.js.map +1 -0
  155. package/dist/tools/reports.d.ts +163 -0
  156. package/dist/tools/reports.d.ts.map +1 -0
  157. package/dist/tools/reports.js +352 -0
  158. package/dist/tools/reports.js.map +1 -0
  159. package/dist/tools/run-watch.d.ts +46 -0
  160. package/dist/tools/run-watch.d.ts.map +1 -0
  161. package/dist/tools/run-watch.js +102 -0
  162. package/dist/tools/run-watch.js.map +1 -0
  163. package/dist/tools/scope.d.ts +45 -0
  164. package/dist/tools/scope.d.ts.map +1 -0
  165. package/dist/tools/scope.js +73 -0
  166. package/dist/tools/scope.js.map +1 -0
  167. package/dist/tools/session-tools.d.ts +31 -0
  168. package/dist/tools/session-tools.d.ts.map +1 -0
  169. package/dist/tools/session-tools.js +312 -0
  170. package/dist/tools/session-tools.js.map +1 -0
  171. package/dist/tools/sessions-state-processor.d.ts +37 -0
  172. package/dist/tools/sessions-state-processor.d.ts.map +1 -0
  173. package/dist/tools/sessions-state-processor.js +206 -0
  174. package/dist/tools/sessions-state-processor.js.map +1 -0
  175. package/dist/tools/work-item-tools.d.ts +28 -0
  176. package/dist/tools/work-item-tools.d.ts.map +1 -0
  177. package/dist/tools/work-item-tools.js +322 -0
  178. package/dist/tools/work-item-tools.js.map +1 -0
  179. package/dist/work-items/create-work-item.d.ts +55 -0
  180. package/dist/work-items/create-work-item.d.ts.map +1 -0
  181. package/dist/work-items/create-work-item.js +101 -0
  182. package/dist/work-items/create-work-item.js.map +1 -0
  183. package/dist/work-items/decision-actions.d.ts +42 -0
  184. package/dist/work-items/decision-actions.d.ts.map +1 -0
  185. package/dist/work-items/decision-actions.js +70 -0
  186. package/dist/work-items/decision-actions.js.map +1 -0
  187. package/dist/workspace.d.ts.map +1 -1
  188. package/dist/workspace.js +3 -1
  189. package/dist/workspace.js.map +1 -1
  190. package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
  191. package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
  192. package/factory-skills/factory-plan/SKILL.md +4 -4
  193. package/factory-skills/factory-rereview/SKILL.md +3 -1
  194. package/factory-skills/factory-review/SKILL.md +3 -1
  195. package/factory-skills/factory-self-review/SKILL.md +46 -0
  196. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#clientConfig","#platformClient","#endpointHost","#intakeByConnectionId","#rules","#orgIdByResourceId","#projects","#auth","#intakeStorage","#activeConnections","#connectionContexts","#connectionIntake","#intakeForConnection","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#scopeResolvedDispatch","#withBaseSource"],"sources":["../../../../src/integrations/platform/incidentio/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../../capabilities/intake.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { IntakeStorage } from '../../../storage/domains/intake/base.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport { buildIncidentioAgentTools } from '../../incidentio/agent-tools.js';\nimport { IncidentioApiClient, IncidentioApiError } from '../../incidentio/api.js';\nimport { resolveIncidentioRules } from '../../incidentio/default-rules.js';\nimport type { IncidentioEventRules, IncidentioRuleOverrides } from '../../incidentio/default-rules.js';\nimport {\n createIncidentioIntake,\n INCIDENTIO_FOLLOW_UPS_SOURCE_ID,\n INCIDENTIO_INCIDENTS_SOURCE_ID,\n} from '../../incidentio/intake.js';\nimport { attachIncidentioIssueReconciler } from '../../incidentio/issue-reconciler.js';\nimport {\n incidentioReconciliationEnabled,\n incidentioReconciliationInterval,\n} from '../../incidentio/reconciliation-config.js';\nimport { buildIncidentioRoutes } from '../../incidentio/routes.js';\nimport { attachIncidentioRules } from '../../incidentio/rules.js';\nimport { IssueReconcileWorker } from '../../issue-reconcile-worker.js';\nimport { PlatformApiClient, platformApiClientConfigFromEnv, type PlatformApiClientConfig } from '../api-client.js';\n\nexport interface PlatformIncidentioIntegrationConfig {\n clientConfig?: PlatformApiClientConfig;\n /** Per-event replacements for the default Factory incident.io rules. */\n rules?: IncidentioRuleOverrides;\n}\n\n/**\n * Provider config key registered for incident.io in Mastra Platform's\n * integrations catalog. Connections are discovered at runtime — no\n * deploy-time connection ID wiring.\n */\nconst PLATFORM_INCIDENTIO_PROVIDER_CONFIG_KEY = 'incident-io';\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst CONNECTION_TOKEN_PREFIX = 'incidentio-connection:';\nconst SCOPED_SOURCE_PREFIX = 'incidentio-source:';\nconst BASE_SOURCE_IDS = [INCIDENTIO_INCIDENTS_SOURCE_ID, INCIDENTIO_FOLLOW_UPS_SOURCE_ID] as const;\n\ninterface PlatformIntegrationConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\ninterface ScopedSourceId {\n connectionId: string;\n sourceId: string;\n}\n\n/** An intake plus the exact inner connection it was resolved for, so operations discover active connections once. */\ninterface ResolvedIncidentioIntake {\n intake: Intake;\n connection: IntegrationConnection;\n}\n\ninterface AggregatePageCursor {\n /** Connection ID the cursor points at; keyed by ID so the active list can change between pages. */\n connectionId: string;\n inner?: string;\n}\n\nexport function encodeScopedSourceId(connectionId: string, sourceId: string): string {\n return `${SCOPED_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ connectionId, sourceId })).toString('base64url')}`;\n}\n\nexport function decodeScopedSourceId(value: string): ScopedSourceId | null {\n if (!value.startsWith(SCOPED_SOURCE_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(value.slice(SCOPED_SOURCE_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n return typeof parsed.connectionId === 'string' && typeof parsed.sourceId === 'string'\n ? { connectionId: parsed.connectionId, sourceId: parsed.sourceId }\n : null;\n } catch {\n return null;\n }\n}\n\nfunction incidentioConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\n/**\n * Base (per-connection) source ids selected for a connection. Scoped ids\n * select their own connection; legacy unscoped ids (`incidentio:incidents`)\n * from pre-discovery deploys apply to every connection.\n */\nfunction baseSourceIdsFor(connectionId: string, sourceIds: string[]): string[] {\n const selected = new Set<string>();\n for (const sourceId of sourceIds) {\n const scoped = decodeScopedSourceId(sourceId);\n if (scoped) {\n if (scoped.connectionId === connectionId) selected.add(scoped.sourceId);\n continue;\n }\n if ((BASE_SOURCE_IDS as readonly string[]).includes(sourceId)) selected.add(sourceId);\n }\n return [...selected];\n}\n\nfunction encodePageCursor(cursor: AggregatePageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): AggregatePageCursor | null {\n if (!cursor) return null;\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Partial<AggregatePageCursor>;\n if (typeof parsed.connectionId !== 'string' || !parsed.connectionId) return null;\n return {\n connectionId: parsed.connectionId,\n ...(typeof parsed.inner === 'string' && parsed.inner ? { inner: parsed.inner } : {}),\n };\n } catch {\n return null;\n }\n}\n\ninterface IncidentioConnectionContext {\n connection: PlatformIntegrationConnection;\n intake: Intake;\n}\n\nexport class PlatformIncidentioIntegration implements FactoryIntegration {\n readonly id = 'incidentio';\n readonly #clientConfig: PlatformApiClientConfig;\n readonly #platformClient: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #intakeByConnectionId = new Map<string, Intake>();\n readonly #rules: IncidentioEventRules;\n\n /** Bound once by the factory via `initialize()` before any surface is used. */\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n #intakeStorage: IntakeStorage | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n constructor(config: PlatformIncidentioIntegrationConfig = {}) {\n this.#clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();\n this.#platformClient = new PlatformApiClient(this.#clientConfig);\n this.#endpointHost = new URL(this.#clientConfig.baseUrl).host;\n this.#rules = resolveIncidentioRules(config.rules);\n }\n\n /** Event rules driving automatic follow-up materialization and close handling. */\n get rules(): IncidentioEventRules {\n return this.#rules;\n }\n\n initialize({\n projects,\n auth,\n intake,\n }: {\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n intake: IntakeStorage;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#intakeStorage = intake;\n }\n\n /** Cross-integration intake selection/binding domain — agent-tool authorization. */\n get intakeStorage(): IntakeStorage {\n if (!this.#intakeStorage) {\n throw new Error('PlatformIncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#intakeStorage;\n }\n\n /** Factory projects domain — maps a session's resourceId to its owning org. */\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error('PlatformIncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#projects;\n }\n\n /** Whether the host runs with web auth enabled; every surface is inert without it. */\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n /**\n * Map a session's resourceId (the factory project id) to its owning org.\n * Same caching semantics as the Jira integration: definitive misses are\n * cached, transient database failures are not.\n */\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_RE.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n let orgId: string | null;\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n orgId = project?.orgId ?? null;\n } catch {\n // Transient database failure: skip the tools for this request but don't\n // cache the miss, so the next request retries the lookup.\n return null;\n }\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n }\n\n /**\n * Org-scoped agent tools: the read-only follow-up detail tool for sessions\n * whose resource is a factory project with an active Platform-managed\n * incident.io connection.\n */\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n if (!(await this.hasActiveConnections())) return {};\n return buildIncidentioAgentTools({ requestContext: args.requestContext, incidentio: this });\n }\n\n async listConnections(): Promise<PlatformIntegrationConnection[]> {\n const providerKey = encodeURIComponent(PLATFORM_INCIDENTIO_PROVIDER_CONFIG_KEY);\n const result = await this.#platformClient.request<{ connections: PlatformIntegrationConnection[] }>(\n 'GET',\n `/v2/connections?providerKey=${providerKey}`,\n );\n return result.connections;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return (await this.#activeConnections()).length > 0;\n }\n\n clearCaches(): void {\n this.#intakeByConnectionId.clear();\n this.#orgIdByResourceId.clear();\n }\n\n async #activeConnections(): Promise<PlatformIntegrationConnection[]> {\n const connections = (await this.listConnections()).filter(connection => connection.status === 'active');\n // Stable ordering keeps aggregate page cursors valid across requests.\n return connections.slice().sort((left, right) => left.id.localeCompare(right.id));\n }\n\n #connectionIntake(connectionId: string): Intake {\n const cached = this.#intakeByConnectionId.get(connectionId);\n if (cached) return cached;\n const proxyBaseUrl = `${this.#clientConfig.baseUrl.replace(/\\/+$/, '')}/v2/connections/${encodeURIComponent(\n connectionId,\n )}/proxy`;\n const intake = createIncidentioIntake({\n api: new IncidentioApiClient({\n baseUrl: proxyBaseUrl,\n accessToken: this.#clientConfig.accessToken,\n ...(this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}),\n }),\n connection: incidentioConnection(connectionId),\n });\n this.#intakeByConnectionId.set(connectionId, intake);\n return intake;\n }\n\n async #connectionContexts(): Promise<IncidentioConnectionContext[]> {\n return (await this.#activeConnections()).map(connection => ({\n connection,\n intake: this.#connectionIntake(connection.id),\n }));\n }\n\n /**\n * Resolve the intake for an explicit connection token, or fall back to the\n * sole active connection so legacy single-connection callers keep working.\n *\n * A caller-supplied connection token is only a claim: the id it carries\n * must match a connection the deployment actually discovered as active\n * before any proxy request is minted with the Platform credential.\n */\n async #intakeForConnection(connection: IntegrationConnection): Promise<ResolvedIncidentioIntake> {\n const connectionId = connectionIdFromConnection(connection);\n const active = await this.#activeConnections();\n if (connectionId) {\n if (!active.some(candidate => candidate.id === connectionId)) {\n throw new IncidentioApiError('incident.io connection is unavailable or requires reauthentication.', 401);\n }\n return { intake: this.#connectionIntake(connectionId), connection: incidentioConnection(connectionId) };\n }\n if (active.length === 1) {\n const soleId = active[0]!.id;\n return { intake: this.#connectionIntake(soleId), connection: incidentioConnection(soleId) };\n }\n throw new IncidentioApiError(\n active.length === 0\n ? 'incident.io connection is unavailable or requires reauthentication.'\n : 'incident.io request must identify a connection when multiple accounts are connected.',\n active.length === 0 ? 401 : 400,\n );\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n\n async #resolveIntakeDispatch(input: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (input.externalSource.type !== 'issue') return null;\n const externalId = input.externalSource.externalId;\n const contexts = await this.#connectionContexts();\n if (contexts.length === 0) return null;\n if (contexts.length === 1) {\n const resolved = await contexts[0]!.intake.resolveIntakeDispatch?.(input);\n return resolved ? this.#scopeResolvedDispatch(contexts[0]!.connection.id, resolved) : null;\n }\n // Multiple accounts: item references carry no connection id, so probe\n // each connection for the item — first account that knows it wins\n // (incident.io ids are globally unique ULIDs, collisions are not a\n // practical concern and this capability is read-mostly).\n let firstError: unknown;\n for (const context of contexts) {\n try {\n const issue = await context.intake.getIssue({\n connection: incidentioConnection(context.connection.id),\n issueId: externalId,\n });\n if (!issue) continue;\n const resolved = await context.intake.resolveIntakeDispatch?.(input);\n return resolved ? this.#scopeResolvedDispatch(context.connection.id, resolved) : null;\n } catch (error) {\n if (error instanceof IncidentioApiError && error.status === 404) continue;\n firstError ??= error;\n }\n }\n if (firstError !== undefined) throw firstError;\n return null;\n }\n\n #scopeResolvedDispatch(connectionId: string, resolved: ResolvedIntakeDispatch): ResolvedIntakeDispatch {\n return {\n ...resolved,\n connection: incidentioConnection(connectionId),\n ...(resolved.sourceId ? { sourceId: encodeScopedSourceId(connectionId, resolved.sourceId) } : {}),\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const contexts = await this.#connectionContexts();\n const sources: IntakeSource[] = [];\n for (const context of contexts) {\n const account = context.connection.accountLabel;\n const inner = await context.intake.listSources({ orgId: '', userId: '' });\n for (const source of inner) {\n sources.push({\n ...source,\n id: encodeScopedSourceId(context.connection.id, source.id),\n name: contexts.length > 1 && account ? `${source.name} (${account})` : source.name,\n metadata: {\n ...(source.metadata ?? {}),\n connectionId: context.connection.id,\n ...(account ? { account } : {}),\n },\n });\n }\n }\n return sources;\n }\n\n async #listItems(input: ListIntakeItemsInput): Promise<IntakeItemPage> {\n if (input.sourceIds.length === 0) return { items: [], nextCursor: null };\n const contexts = await this.#connectionContexts();\n const cursor = decodePageCursor(input.cursor);\n\n // Resume by connection ID: the active list can change between pages, so an\n // index would replay or skip connections. When the cursor's connection is\n // gone, resume at its sorted successor and drop the inner cursor.\n let startIndex = 0;\n let exactMatch = false;\n if (cursor) {\n const index = contexts.findIndex(context => context.connection.id === cursor.connectionId);\n exactMatch = index !== -1;\n startIndex = exactMatch\n ? index\n : contexts.findIndex(context => context.connection.id.localeCompare(cursor.connectionId) > 0);\n if (startIndex === -1) return { items: [], nextCursor: null };\n }\n\n for (let index = startIndex; index < contexts.length; index++) {\n const context = contexts[index]!;\n const baseSourceIds = baseSourceIdsFor(context.connection.id, input.sourceIds);\n if (baseSourceIds.length === 0) continue;\n const page = await context.intake.listItems({\n orgId: input.orgId,\n userId: input.userId,\n sourceIds: baseSourceIds,\n ...(index === startIndex && exactMatch && cursor?.inner ? { cursor: cursor.inner } : {}),\n });\n const nextCursor = page.nextCursor\n ? encodePageCursor({ connectionId: context.connection.id, inner: page.nextCursor })\n : index + 1 < contexts.length\n ? encodePageCursor({ connectionId: contexts[index + 1]!.connection.id })\n : null;\n return {\n items: page.items.map(item => ({\n ...item,\n sourceId: encodeScopedSourceId(context.connection.id, item.sourceId),\n metadata: { ...(item.metadata ?? {}), connectionId: context.connection.id },\n })),\n nextCursor,\n };\n }\n return { items: [], nextCursor: null };\n }\n\n async #listIssues(input: ListIntakeIssuesInput): Promise<{ issues: IntakeIssue[]; nextCursor: string | null }> {\n const resolved = await this.#intakeForConnection(input.connection);\n // Scope to the connection that was actually resolved (which may be the\n // sole-connection fallback), so a source id scoped to a different\n // connection is dropped instead of being read against this one.\n const connectionId = connectionIdFromConnection(resolved.connection);\n const baseSourceIds = connectionId\n ? baseSourceIdsFor(connectionId, input.sourceIds)\n : input.sourceIds.map(sourceId => decodeScopedSourceId(sourceId)?.sourceId ?? sourceId);\n return resolved.intake.listIssues({\n ...input,\n connection: resolved.connection,\n sourceIds: baseSourceIds,\n });\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const resolved = await this.#intakeForConnection(input.connection);\n return resolved.intake.getIssue({\n ...this.#withBaseSource(input),\n connection: resolved.connection,\n });\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#intakeForConnection(input.connection);\n return resolved.intake.createComment({\n ...this.#withBaseSource(input),\n connection: resolved.connection,\n });\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#intakeForConnection(input.connection);\n return resolved.intake.updateIssue({\n ...this.#withBaseSource(input),\n connection: resolved.connection,\n });\n }\n\n #withBaseSource<T extends { sourceId?: string }>(input: T): T {\n if (!input.sourceId) return input;\n const scoped = decodeScopedSourceId(input.sourceId);\n return scoped ? { ...input, sourceId: scoped.sourceId } : input;\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!incidentioReconciliationEnabled()) return [];\n const reconcile = attachIncidentioIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = incidentioReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n /**\n * The integration's HTTP surface: `/web/incidentio/*` Mastra `apiRoutes`\n * (status + follow-up listing/detail for Intake), aggregated across every\n * discovered Platform connection. A board-scoped listing also feeds the\n * incident.io event rules.\n */\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildIncidentioRoutes({\n incidentio: this,\n mode: 'platform',\n auth: ctx.auth,\n intake: ctx.storage.intake,\n ingestFactoryIssues: attachIncidentioRules(this, ctx),\n });\n }\n\n diagnostics(): Record<string, unknown> {\n return { configured: true, mode: 'platform', endpointHost: this.#endpointHost };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","names":["#clientConfig","#platformClient","#endpointHost","#intakeByConnectionId","#rules","#orgIdByResourceId","#projects","#auth","#intakeStorage","#activeConnections","#connectionContexts","#connectionIntake","#intakeForConnection","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#scopeResolvedDispatch","#withBaseSource"],"sources":["../../../../src/integrations/platform/incidentio/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../../capabilities/intake.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { IntakeStorage } from '../../../storage/domains/intake/base.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport { buildIncidentioAgentTools } from '../../incidentio/agent-tools.js';\nimport { IncidentioApiClient, IncidentioApiError } from '../../incidentio/api.js';\nimport { resolveIncidentioRules } from '../../incidentio/default-rules.js';\nimport type { IncidentioEventRules, IncidentioRuleOverrides } from '../../incidentio/default-rules.js';\nimport {\n createIncidentioIntake,\n INCIDENTIO_FOLLOW_UPS_SOURCE_ID,\n INCIDENTIO_INCIDENTS_SOURCE_ID,\n} from '../../incidentio/intake.js';\nimport { attachIncidentioIssueReconciler } from '../../incidentio/issue-reconciler.js';\nimport {\n incidentioReconciliationEnabled,\n incidentioReconciliationInterval,\n} from '../../incidentio/reconciliation-config.js';\nimport { buildIncidentioRoutes } from '../../incidentio/routes.js';\nimport { attachIncidentioRules } from '../../incidentio/rules.js';\nimport { IssueReconcileWorker } from '../../issue-reconcile-worker.js';\nimport { PlatformApiClient, platformApiClientConfigFromEnv, type PlatformApiClientConfig } from '../api-client.js';\n\nexport interface PlatformIncidentioIntegrationConfig {\n clientConfig?: PlatformApiClientConfig;\n /** Per-event replacements for the default Factory incident.io rules. */\n rules?: IncidentioRuleOverrides;\n}\n\n/**\n * Provider config key registered for incident.io in Mastra Platform's\n * integrations catalog. Connections are discovered at runtime — no\n * deploy-time connection ID wiring.\n */\nconst PLATFORM_INCIDENTIO_PROVIDER_CONFIG_KEY = 'incident-io';\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst CONNECTION_TOKEN_PREFIX = 'incidentio-connection:';\nconst SCOPED_SOURCE_PREFIX = 'incidentio-source:';\nconst BASE_SOURCE_IDS = [INCIDENTIO_INCIDENTS_SOURCE_ID, INCIDENTIO_FOLLOW_UPS_SOURCE_ID] as const;\n\ninterface PlatformIntegrationConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\ninterface ScopedSourceId {\n connectionId: string;\n sourceId: string;\n}\n\n/** An intake plus the exact inner connection it was resolved for, so operations discover active connections once. */\ninterface ResolvedIncidentioIntake {\n intake: Intake;\n connection: IntegrationConnection;\n}\n\ninterface AggregatePageCursor {\n /** Connection ID the cursor points at; keyed by ID so the active list can change between pages. */\n connectionId: string;\n inner?: string;\n}\n\nexport function encodeScopedSourceId(connectionId: string, sourceId: string): string {\n return `${SCOPED_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ connectionId, sourceId })).toString('base64url')}`;\n}\n\nexport function decodeScopedSourceId(value: string): ScopedSourceId | null {\n if (!value.startsWith(SCOPED_SOURCE_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(value.slice(SCOPED_SOURCE_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n return typeof parsed.connectionId === 'string' && typeof parsed.sourceId === 'string'\n ? { connectionId: parsed.connectionId, sourceId: parsed.sourceId }\n : null;\n } catch {\n return null;\n }\n}\n\nfunction incidentioConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\n/**\n * Base (per-connection) source ids selected for a connection. Scoped ids\n * select their own connection; legacy unscoped ids (`incidentio:incidents`)\n * from pre-discovery deploys apply to every connection.\n */\nfunction baseSourceIdsFor(connectionId: string, sourceIds: string[]): string[] {\n const selected = new Set<string>();\n for (const sourceId of sourceIds) {\n const scoped = decodeScopedSourceId(sourceId);\n if (scoped) {\n if (scoped.connectionId === connectionId) selected.add(scoped.sourceId);\n continue;\n }\n if ((BASE_SOURCE_IDS as readonly string[]).includes(sourceId)) selected.add(sourceId);\n }\n return [...selected];\n}\n\nfunction encodePageCursor(cursor: AggregatePageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): AggregatePageCursor | null {\n if (!cursor) return null;\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Partial<AggregatePageCursor>;\n if (typeof parsed.connectionId !== 'string' || !parsed.connectionId) return null;\n return {\n connectionId: parsed.connectionId,\n ...(typeof parsed.inner === 'string' && parsed.inner ? { inner: parsed.inner } : {}),\n };\n } catch {\n return null;\n }\n}\n\ninterface IncidentioConnectionContext {\n connection: PlatformIntegrationConnection;\n intake: Intake;\n}\n\nexport class PlatformIncidentioIntegration implements FactoryIntegration {\n readonly id = 'incidentio';\n readonly #clientConfig: PlatformApiClientConfig;\n readonly #platformClient: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #intakeByConnectionId = new Map<string, Intake>();\n readonly #rules: IncidentioEventRules;\n\n /** Bound once by the factory via `initialize()` before any surface is used. */\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n #intakeStorage: IntakeStorage | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n constructor(config: PlatformIncidentioIntegrationConfig = {}) {\n this.#clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();\n this.#platformClient = new PlatformApiClient(this.#clientConfig);\n this.#endpointHost = new URL(this.#clientConfig.baseUrl).host;\n this.#rules = resolveIncidentioRules(config.rules);\n }\n\n /** Event rules driving automatic follow-up materialization and close handling. */\n get rules(): IncidentioEventRules {\n return this.#rules;\n }\n\n initialize({\n projects,\n auth,\n intake,\n }: {\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n intake: IntakeStorage;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#intakeStorage = intake;\n }\n\n /** Cross-integration intake selection/binding domain — agent-tool authorization. */\n get intakeStorage(): IntakeStorage {\n if (!this.#intakeStorage) {\n throw new Error('PlatformIncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#intakeStorage;\n }\n\n /** Factory projects domain — maps a session's resourceId to its owning org. */\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error('PlatformIncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#projects;\n }\n\n /** Whether the host runs with web auth enabled; every surface is inert without it. */\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n /**\n * Map a session's resourceId (the factory project id) to its owning org.\n * Same caching semantics as the Jira integration: definitive misses are\n * cached, transient database failures are not.\n */\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_RE.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n let orgId: string | null;\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n orgId = project?.orgId ?? null;\n } catch {\n // Transient database failure: skip the tools for this request but don't\n // cache the miss, so the next request retries the lookup.\n return null;\n }\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n }\n\n /**\n * Org-scoped agent tools: the read-only follow-up detail tool for sessions\n * whose resource is a factory project with an active Platform-managed\n * incident.io connection.\n */\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n if (!(await this.hasActiveConnections())) return {};\n return buildIncidentioAgentTools({ requestContext: args.requestContext, incidentio: this });\n }\n\n async listConnections(): Promise<PlatformIntegrationConnection[]> {\n const providerKey = encodeURIComponent(PLATFORM_INCIDENTIO_PROVIDER_CONFIG_KEY);\n const result = await this.#platformClient.request<{ connections: PlatformIntegrationConnection[] }>(\n 'GET',\n `/v2/connections?providerKey=${providerKey}`,\n );\n return result.connections;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return (await this.#activeConnections()).length > 0;\n }\n\n clearCaches(): void {\n this.#intakeByConnectionId.clear();\n this.#orgIdByResourceId.clear();\n }\n\n async #activeConnections(): Promise<PlatformIntegrationConnection[]> {\n const connections = (await this.listConnections()).filter(connection => connection.status === 'active');\n // Stable ordering keeps aggregate page cursors valid across requests.\n return connections.slice().sort((left, right) => left.id.localeCompare(right.id));\n }\n\n #connectionIntake(connectionId: string): Intake {\n const cached = this.#intakeByConnectionId.get(connectionId);\n if (cached) return cached;\n const proxyBaseUrl = `${this.#clientConfig.baseUrl.replace(/\\/+$/, '')}/v2/connections/${encodeURIComponent(\n connectionId,\n )}/proxy`;\n const intake = createIncidentioIntake({\n api: new IncidentioApiClient({\n baseUrl: proxyBaseUrl,\n accessToken: this.#clientConfig.accessToken,\n ...(this.#clientConfig.projectId ? { projectId: this.#clientConfig.projectId } : {}),\n ...(this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}),\n }),\n connection: incidentioConnection(connectionId),\n });\n this.#intakeByConnectionId.set(connectionId, intake);\n return intake;\n }\n\n async #connectionContexts(): Promise<IncidentioConnectionContext[]> {\n return (await this.#activeConnections()).map(connection => ({\n connection,\n intake: this.#connectionIntake(connection.id),\n }));\n }\n\n /**\n * Resolve the intake for an explicit connection token, or fall back to the\n * sole active connection so legacy single-connection callers keep working.\n *\n * A caller-supplied connection token is only a claim: the id it carries\n * must match a connection the deployment actually discovered as active\n * before any proxy request is minted with the Platform credential.\n */\n async #intakeForConnection(connection: IntegrationConnection): Promise<ResolvedIncidentioIntake> {\n const connectionId = connectionIdFromConnection(connection);\n const active = await this.#activeConnections();\n if (connectionId) {\n if (!active.some(candidate => candidate.id === connectionId)) {\n throw new IncidentioApiError('incident.io connection is unavailable or requires reauthentication.', 401);\n }\n return { intake: this.#connectionIntake(connectionId), connection: incidentioConnection(connectionId) };\n }\n if (active.length === 1) {\n const soleId = active[0]!.id;\n return { intake: this.#connectionIntake(soleId), connection: incidentioConnection(soleId) };\n }\n throw new IncidentioApiError(\n active.length === 0\n ? 'incident.io connection is unavailable or requires reauthentication.'\n : 'incident.io request must identify a connection when multiple accounts are connected.',\n active.length === 0 ? 401 : 400,\n );\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n\n async #resolveIntakeDispatch(input: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (input.externalSource.type !== 'issue') return null;\n const externalId = input.externalSource.externalId;\n const contexts = await this.#connectionContexts();\n if (contexts.length === 0) return null;\n if (contexts.length === 1) {\n const resolved = await contexts[0]!.intake.resolveIntakeDispatch?.(input);\n return resolved ? this.#scopeResolvedDispatch(contexts[0]!.connection.id, resolved) : null;\n }\n // Multiple accounts: item references carry no connection id, so probe\n // each connection for the item — first account that knows it wins\n // (incident.io ids are globally unique ULIDs, collisions are not a\n // practical concern and this capability is read-mostly).\n let firstError: unknown;\n for (const context of contexts) {\n try {\n const issue = await context.intake.getIssue({\n connection: incidentioConnection(context.connection.id),\n issueId: externalId,\n });\n if (!issue) continue;\n const resolved = await context.intake.resolveIntakeDispatch?.(input);\n return resolved ? this.#scopeResolvedDispatch(context.connection.id, resolved) : null;\n } catch (error) {\n if (error instanceof IncidentioApiError && error.status === 404) continue;\n firstError ??= error;\n }\n }\n if (firstError !== undefined) throw firstError;\n return null;\n }\n\n #scopeResolvedDispatch(connectionId: string, resolved: ResolvedIntakeDispatch): ResolvedIntakeDispatch {\n return {\n ...resolved,\n connection: incidentioConnection(connectionId),\n ...(resolved.sourceId ? { sourceId: encodeScopedSourceId(connectionId, resolved.sourceId) } : {}),\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const contexts = await this.#connectionContexts();\n const sources: IntakeSource[] = [];\n for (const context of contexts) {\n const account = context.connection.accountLabel;\n const inner = await context.intake.listSources({ orgId: '', userId: '' });\n for (const source of inner) {\n sources.push({\n ...source,\n id: encodeScopedSourceId(context.connection.id, source.id),\n name: contexts.length > 1 && account ? `${source.name} (${account})` : source.name,\n metadata: {\n ...(source.metadata ?? {}),\n connectionId: context.connection.id,\n ...(account ? { account } : {}),\n },\n });\n }\n }\n return sources;\n }\n\n async #listItems(input: ListIntakeItemsInput): Promise<IntakeItemPage> {\n if (input.sourceIds.length === 0) return { items: [], nextCursor: null };\n const contexts = await this.#connectionContexts();\n const cursor = decodePageCursor(input.cursor);\n\n // Resume by connection ID: the active list can change between pages, so an\n // index would replay or skip connections. When the cursor's connection is\n // gone, resume at its sorted successor and drop the inner cursor.\n let startIndex = 0;\n let exactMatch = false;\n if (cursor) {\n const index = contexts.findIndex(context => context.connection.id === cursor.connectionId);\n exactMatch = index !== -1;\n startIndex = exactMatch\n ? index\n : contexts.findIndex(context => context.connection.id.localeCompare(cursor.connectionId) > 0);\n if (startIndex === -1) return { items: [], nextCursor: null };\n }\n\n for (let index = startIndex; index < contexts.length; index++) {\n const context = contexts[index]!;\n const baseSourceIds = baseSourceIdsFor(context.connection.id, input.sourceIds);\n if (baseSourceIds.length === 0) continue;\n const page = await context.intake.listItems({\n orgId: input.orgId,\n userId: input.userId,\n sourceIds: baseSourceIds,\n ...(index === startIndex && exactMatch && cursor?.inner ? { cursor: cursor.inner } : {}),\n });\n const nextCursor = page.nextCursor\n ? encodePageCursor({ connectionId: context.connection.id, inner: page.nextCursor })\n : index + 1 < contexts.length\n ? encodePageCursor({ connectionId: contexts[index + 1]!.connection.id })\n : null;\n return {\n items: page.items.map(item => ({\n ...item,\n sourceId: encodeScopedSourceId(context.connection.id, item.sourceId),\n metadata: { ...(item.metadata ?? {}), connectionId: context.connection.id },\n })),\n nextCursor,\n };\n }\n return { items: [], nextCursor: null };\n }\n\n async #listIssues(input: ListIntakeIssuesInput): Promise<{ issues: IntakeIssue[]; nextCursor: string | null }> {\n const resolved = await this.#intakeForConnection(input.connection);\n // Scope to the connection that was actually resolved (which may be the\n // sole-connection fallback), so a source id scoped to a different\n // connection is dropped instead of being read against this one.\n const connectionId = connectionIdFromConnection(resolved.connection);\n const baseSourceIds = connectionId\n ? baseSourceIdsFor(connectionId, input.sourceIds)\n : input.sourceIds.map(sourceId => decodeScopedSourceId(sourceId)?.sourceId ?? sourceId);\n return resolved.intake.listIssues({\n ...input,\n connection: resolved.connection,\n sourceIds: baseSourceIds,\n });\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const resolved = await this.#intakeForConnection(input.connection);\n return resolved.intake.getIssue({\n ...this.#withBaseSource(input),\n connection: resolved.connection,\n });\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#intakeForConnection(input.connection);\n return resolved.intake.createComment({\n ...this.#withBaseSource(input),\n connection: resolved.connection,\n });\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#intakeForConnection(input.connection);\n return resolved.intake.updateIssue({\n ...this.#withBaseSource(input),\n connection: resolved.connection,\n });\n }\n\n #withBaseSource<T extends { sourceId?: string }>(input: T): T {\n if (!input.sourceId) return input;\n const scoped = decodeScopedSourceId(input.sourceId);\n return scoped ? { ...input, sourceId: scoped.sourceId } : input;\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!incidentioReconciliationEnabled()) return [];\n const reconcile = attachIncidentioIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = incidentioReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n /**\n * The integration's HTTP surface: `/web/incidentio/*` Mastra `apiRoutes`\n * (status + follow-up listing/detail for Intake), aggregated across every\n * discovered Platform connection. A board-scoped listing also feeds the\n * incident.io event rules.\n */\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildIncidentioRoutes({\n incidentio: this,\n mode: 'platform',\n auth: ctx.auth,\n intake: ctx.storage.intake,\n ingestFactoryIssues: attachIncidentioRules(this, ctx),\n });\n }\n\n diagnostics(): Record<string, unknown> {\n return { configured: true, mode: 'platform', endpointHost: this.#endpointHost };\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/jira/integration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/jira/integration.ts"],"names":[],"mappings":"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"}
@@ -381,6 +381,7 @@ var PlatformJiraIntegration = class {
381
381
  const api = new JiraApiClient({
382
382
  baseUrl: `${proxyBaseUrl}/ex/jira/${encodeURIComponent(cloudId)}`,
383
383
  accessToken: this.#clientConfig.accessToken,
384
+ ...this.#clientConfig.projectId ? { projectId: this.#clientConfig.projectId } : {},
384
385
  ...this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}
385
386
  });
386
387
  let siteUrl = this.#siteUrlByConnectionId.get(connection.id);
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#clientConfig","#platformClient","#endpointHost","#cloudIdByConnectionId","#siteUrlByConnectionId","#orgIdByResourceId","#rules","#projects","#auth","#activeConnections","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#connectionContext","#connectionContextById","#toIntakeIssue","#findIssue","#listAllComments","#resolveRequest","#cloudId"],"sources":["../../../../src/integrations/platform/jira/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} 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 { IssueReconcileWorker } from '../../issue-reconcile-worker.js';\nimport { adfToText } from '../../jira/adf.js';\nimport type { JiraComment, JiraIssue, JiraTransition } from '../../jira/api.js';\nimport { JiraApiClient, JiraApiError } from '../../jira/api.js';\nimport type { JiraEventRules, JiraRuleOverrides } from '../../jira/default-rules.js';\nimport { resolveJiraRules } from '../../jira/default-rules.js';\nimport { attachJiraIssueReconciler } from '../../jira/issue-reconciler.js';\nimport { jiraReconciliationEnabled, jiraReconciliationInterval } from '../../jira/reconciliation-config.js';\nimport { attachJiraRules } from '../../jira/rules.js';\nimport {\n logPlatformInfo,\n PlatformApiClient,\n platformApiClientConfigFromEnv,\n type PlatformApiClientConfig,\n} from '../api-client.js';\nimport { buildPlatformJiraAgentTools } from './agent-tools.js';\nimport { buildPlatformJiraRoutes } from './routes.js';\n\ninterface PlatformIntegrationConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\ninterface JiraConnectionContext {\n connection: PlatformIntegrationConnection;\n api: JiraApiClient;\n siteUrl: string;\n}\n\ninterface PlatformConnectionContextResponse {\n connection_config: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n}\n\ninterface JiraIssueReference {\n connectionId: string;\n issueId: string;\n projectId?: string;\n}\n\ninterface JiraPageCursor {\n group: number;\n jira?: string;\n}\n\nconst ISSUE_COMMENTS_MAX_PAGES = 20;\n\n/** Hard stop for project pagination so a misbehaving `isLast` can't loop forever. */\nconst PROJECT_SEARCH_MAX_PAGES = 50;\nconst ISSUE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*-\\d+$/;\nconst PLATFORM_JIRA_PROVIDER_CONFIG_KEY = 'jira';\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst JIRA_CONNECTION_TOKEN_PREFIX = 'jira-connection:';\nconst JIRA_ISSUE_REF_PREFIX = 'jira-issue:';\nconst JIRA_SOURCE_PREFIX = 'jira-project:';\n\nconst STATE_TYPE_TO_CATEGORY: Record<'unstarted' | 'started' | 'completed', string> = {\n unstarted: 'new',\n started: 'indeterminate',\n completed: 'done',\n};\n\nfunction stateTypeFromCategory(key: string | undefined): string | null {\n switch (key) {\n case 'new':\n return 'unstarted';\n case 'indeterminate':\n return 'started';\n case 'done':\n return 'completed';\n default:\n return null;\n }\n}\n\nexport interface PlatformJiraIntegrationConfig {\n clientConfig?: PlatformApiClientConfig;\n /** Per-event replacements; omitted events retain defaults, null disables. */\n rules?: JiraRuleOverrides;\n}\n\nexport class PlatformJiraIntegration implements FactoryIntegration {\n readonly id = 'jira';\n readonly #clientConfig: PlatformApiClientConfig;\n readonly #platformClient: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #cloudIdByConnectionId = new Map<string, string>();\n readonly #siteUrlByConnectionId = new Map<string, string>();\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n readonly #rules: JiraEventRules;\n\n get rules(): JiraEventRules {\n return this.#rules;\n }\n\n constructor(config: PlatformJiraIntegrationConfig = {}) {\n this.#rules = resolveJiraRules(config.rules);\n this.#clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();\n this.#platformClient = new PlatformApiClient(this.#clientConfig);\n this.#endpointHost = new URL(this.#clientConfig.baseUrl).host;\n }\n\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\n logPlatformInfo('Platform Jira integration initialized', { endpointHost: this.#endpointHost });\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects)\n throw new Error('PlatformJiraIntegration is not initialized — the factory binds storage during prepare().');\n return this.#projects;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n this.#cloudIdByConnectionId.clear();\n this.#siteUrlByConnectionId.clear();\n }\n\n async listConnections(): Promise<PlatformIntegrationConnection[]> {\n const providerKey = encodeURIComponent(PLATFORM_JIRA_PROVIDER_CONFIG_KEY);\n const result = await this.#platformClient.request<{ connections: PlatformIntegrationConnection[] }>(\n 'GET',\n `/v2/connections?providerKey=${providerKey}`,\n );\n return result.connections;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return (await this.#activeConnections()).length > 0;\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n return {\n connection: jiraConnection(reference.connectionId),\n ...(reference.projectId ? { sourceId: encodeSourceId(reference.connectionId, reference.projectId) } : {}),\n issueId: reference.issueId,\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources: IntakeSource[] = [];\n for (const connection of await this.#activeConnections()) {\n const context = await this.#connectionContext(connection);\n let startAt = 0;\n for (let pageCount = 0; pageCount < PROJECT_SEARCH_MAX_PAGES; pageCount++) {\n const page = await context.api.listProjects({ startAt });\n for (const project of page.values) {\n sources.push({\n id: encodeSourceId(connection.id, project.id),\n name: project.name,\n type: 'project',\n metadata: {\n key: project.key,\n connectionId: connection.id,\n site: new URL(context.siteUrl).host,\n },\n });\n }\n if (page.isLast || page.values.length === 0) break;\n startAt += page.values.length;\n }\n }\n return sources;\n }\n\n async #listItems({ sourceIds, cursor }: ListIntakeItemsInput): Promise<IntakeItemPage> {\n if (sourceIds.length === 0) return { items: [], nextCursor: null };\n const page = await this.listActiveIssues(cursor, sourceIds);\n return {\n items: page.issues.map(issue => ({\n source: { type: 'issue', externalId: issue.externalId, url: issue.url },\n sourceId: issue.sourceId,\n title: `${issue.identifier}: ${issue.title}`,\n status: issue.state ?? undefined,\n labels: issue.labels,\n assignee: issue.assignee,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata: {\n identifier: issue.identifier,\n issueRef: issue.externalId,\n state: issue.state,\n stateType: issue.stateType,\n priority: issue.priority,\n project: issue.source,\n site: issue.site,\n assignee: issue.assignee,\n assignees: issue.assignees ?? [],\n creator: issue.author,\n author: issue.author,\n labels: issue.labels,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n },\n })),\n nextCursor: page.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput): Promise<{ issues: IntakeIssue[]; nextCursor: string | null }> {\n const page = await this.listActiveIssues(input.cursor, input.sourceIds, input.labels);\n return { issues: page.issues, nextCursor: page.nextCursor };\n }\n\n async listActiveIssues(\n cursor?: string,\n sourceIds: string[] = [],\n labels?: string[],\n ): Promise<{\n issues: Array<IntakeIssue & { sourceId: string; externalId: string; site: string }>;\n nextCursor: string | null;\n }> {\n const grouped = groupSources(sourceIds);\n if (grouped.length === 0) return { issues: [], nextCursor: null };\n const decodedCursor = decodePageCursor(cursor);\n if (decodedCursor.group >= grouped.length) return { issues: [], nextCursor: null };\n\n for (let groupIndex = decodedCursor.group; groupIndex < grouped.length; groupIndex++) {\n const group = grouped[groupIndex]!;\n const context = await this.#connectionContextById(group.connectionId);\n const jql = buildIntakeJql(group.projectIds, labels);\n if (jql === null) continue;\n const page = await context.api.searchIssues({\n jql,\n ...(groupIndex === decodedCursor.group && decodedCursor.jira ? { nextPageToken: decodedCursor.jira } : {}),\n });\n const nextCursor = page.nextPageToken\n ? encodePageCursor({ group: groupIndex, jira: page.nextPageToken })\n : groupIndex + 1 < grouped.length\n ? encodePageCursor({ group: groupIndex + 1 })\n : null;\n return {\n issues: page.issues.map(issue => {\n const projectId = issue.fields.project?.id ?? group.projectIds[0] ?? '';\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n sourceId: encodeSourceId(group.connectionId, projectId),\n externalId: encodeIssueReference({ connectionId: group.connectionId, issueId: issue.key, projectId }),\n site: new URL(context.siteUrl).host,\n };\n }),\n nextCursor,\n };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const { context, issue } = (await this.#findIssue(input)) ?? {};\n if (!context || !issue) return null;\n const { comments, total } = await this.#listAllComments(context.api, issue.key);\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n commentCount: total,\n description: issue.fields.description ? adfToText(issue.fields.description) || null : null,\n comments: comments.map(comment => ({\n author: comment.author?.displayName ?? null,\n body: adfToText(comment.body),\n createdAt: comment.created,\n })),\n };\n }\n\n async #listAllComments(api: JiraApiClient, keyOrId: string): Promise<{ comments: JiraComment[]; total: number }> {\n const comments: JiraComment[] = [];\n let total = 0;\n for (let page = 0; page < ISSUE_COMMENTS_MAX_PAGES; page++) {\n const result = await api.listComments(keyOrId, { startAt: comments.length });\n comments.push(...result.comments);\n total = result.total;\n if (result.comments.length === 0 || comments.length >= total) break;\n }\n return { comments, total };\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n let key = resolved.issueId;\n if (!ISSUE_KEY_PATTERN.test(key)) {\n try {\n key = (await resolved.context.api.getIssue(key)).key;\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n }\n let comment;\n try {\n comment = await resolved.context.api.createComment(key, input.body);\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n return {\n id: comment.id,\n url: `${resolved.context.siteUrl}/browse/${key}?focusedCommentId=${comment.id}`,\n };\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: JiraIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.issueId);\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n if (currentStatusMatches(issue, input.state)) return this.#toIntakeIssue(issue, resolved.context.siteUrl);\n const transitions = await resolved.context.api.listTransitions(issue.key);\n const transition = resolveTransition(transitions, input.state);\n if (!transition) return null;\n await resolved.context.api.applyTransition(issue.key, transition.id);\n return this.#toIntakeIssue(await resolved.context.api.getIssue(issue.key), resolved.context.siteUrl);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issueId: string }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n if (connectionId) return { context: await this.#connectionContextById(connectionId), issueId };\n const active = await this.#activeConnections();\n if (active.length !== 1) {\n throw new JiraApiError('Jira issue reference must identify a connection when multiple sites are connected.', 400);\n }\n return { context: await this.#connectionContext(active[0]!), issueId };\n }\n\n /**\n * Locate an issue for a read. A connection-scoped reference resolves\n * directly; a plain issue key (the agent tool's normal input) is looked up\n * across every active connection so multi-site organizations can still\n * fetch by key — the first site that knows the key wins, which is safe\n * because Jira project-key prefixes make cross-site collisions unlikely and\n * the tool is read-only.\n */\n async #findIssue(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issue: JiraIssue } | null> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n\n const candidates: JiraConnectionContext[] = [];\n if (connectionId) {\n candidates.push(await this.#connectionContextById(connectionId));\n } else {\n const active = await this.#activeConnections();\n if (active.length === 0) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n for (const connection of active) candidates.push(await this.#connectionContext(connection));\n }\n\n let firstError: unknown;\n for (const context of candidates) {\n try {\n return { context, issue: await context.api.getIssue(issueId) };\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) continue;\n firstError ??= error;\n }\n }\n // Not found anywhere. If a site failed for a non-404 reason, surface that\n // failure instead of a silent \"not found\".\n if (firstError !== undefined) throw firstError;\n return null;\n }\n\n #toIntakeIssue(issue: JiraIssue, siteUrl: string): IntakeIssue {\n const { fields } = issue;\n return {\n id: issue.id,\n identifier: issue.key,\n title: fields.summary ?? issue.key,\n url: `${siteUrl}/browse/${issue.key}`,\n author: fields.reporter?.displayName ?? null,\n state: fields.status?.name ?? null,\n stateType: stateTypeFromCategory(fields.status?.statusCategory?.key),\n priority: fields.priority?.name ?? null,\n assignee: fields.assignee?.displayName ?? null,\n source: fields.project?.key ?? null,\n labels: fields.labels ?? [],\n commentCount: null,\n createdAt: fields.created ?? '',\n updatedAt: fields.updated ?? '',\n };\n }\n\n async #activeConnections(): Promise<PlatformIntegrationConnection[]> {\n return (await this.listConnections()).filter(connection => connection.status === 'active');\n }\n\n async #connectionContextById(connectionId: string): Promise<JiraConnectionContext> {\n const connection = (await this.#activeConnections()).find(candidate => candidate.id === connectionId);\n if (!connection) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n return this.#connectionContext(connection);\n }\n\n async #cloudId(connectionId: string): Promise<string> {\n const cached = this.#cloudIdByConnectionId.get(connectionId);\n if (cached) return cached;\n\n const context = await this.#platformClient.request<PlatformConnectionContextResponse>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connectionId)}/context`,\n );\n const cloudId = context.connection_config?.cloudId;\n if (typeof cloudId !== 'string' || !UUID_PATTERN.test(cloudId)) {\n throw new JiraApiError('Platform Jira connection context is missing a valid cloudId.', 502);\n }\n this.#cloudIdByConnectionId.set(connectionId, cloudId);\n return cloudId;\n }\n\n async #connectionContext(connection: PlatformIntegrationConnection): Promise<JiraConnectionContext> {\n const proxyBaseUrl = `${this.#clientConfig.baseUrl.replace(/\\/+$/, '')}/v2/connections/${encodeURIComponent(\n connection.id,\n )}/proxy`;\n const cloudId = await this.#cloudId(connection.id);\n const api = new JiraApiClient({\n baseUrl: `${proxyBaseUrl}/ex/jira/${encodeURIComponent(cloudId)}`,\n accessToken: this.#clientConfig.accessToken,\n ...(this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}),\n });\n let siteUrl = this.#siteUrlByConnectionId.get(connection.id);\n if (!siteUrl) {\n siteUrl = connection.accountLabel\n ? normalizeSiteUrl(connection.accountLabel)\n : normalizeSiteUrl((await api.getServerInfo()).baseUrl);\n this.#siteUrlByConnectionId.set(connection.id, siteUrl);\n }\n return { connection, api, siteUrl };\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!jiraReconciliationEnabled()) return [];\n const reconcile = attachJiraIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = jiraReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildPlatformJiraRoutes({\n jira: this,\n auth: ctx.auth,\n intake: ctx.storage.intake,\n appDbConfigured: Boolean(ctx.factoryStorage),\n ingestFactoryIssues: attachJiraRules(this, ctx),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildPlatformJiraAgentTools({ requestContext: args.requestContext, jira: this });\n }\n\n diagnostics(): Record<string, unknown> {\n return { configured: true, mode: 'platform', endpointHost: this.#endpointHost };\n }\n}\n\nfunction jiraConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${JIRA_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(JIRA_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(JIRA_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nexport function encodeSourceId(connectionId: string, projectId: string): string {\n return `${JIRA_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ connectionId, projectId })).toString('base64url')}`;\n}\n\nexport function decodeSourceId(sourceId: string): { connectionId: string; projectId: string } | null {\n if (!sourceId.startsWith(JIRA_SOURCE_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(sourceId.slice(JIRA_SOURCE_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n return typeof parsed.connectionId === 'string' && typeof parsed.projectId === 'string'\n ? { connectionId: parsed.connectionId, projectId: parsed.projectId }\n : null;\n } catch {\n return null;\n }\n}\n\nexport function encodeIssueReference(reference: JiraIssueReference): string {\n return `${JIRA_ISSUE_REF_PREFIX}${Buffer.from(JSON.stringify(reference)).toString('base64url')}`;\n}\n\nexport function decodeIssueReference(value: string): JiraIssueReference | null {\n if (!value.startsWith(JIRA_ISSUE_REF_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(value.slice(JIRA_ISSUE_REF_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n if (typeof parsed.connectionId !== 'string' || typeof parsed.issueId !== 'string') return null;\n return {\n connectionId: parsed.connectionId,\n issueId: parsed.issueId,\n ...(typeof parsed.projectId === 'string' ? { projectId: parsed.projectId } : {}),\n };\n } catch {\n return null;\n }\n}\n\nfunction groupSources(sourceIds: string[]): Array<{ connectionId: string; projectIds: string[] }> {\n const groups = new Map<string, Set<string>>();\n for (const sourceId of sourceIds) {\n const source = decodeSourceId(sourceId);\n if (!source) continue;\n const projectIds = groups.get(source.connectionId) ?? new Set<string>();\n projectIds.add(source.projectId);\n groups.set(source.connectionId, projectIds);\n }\n return [...groups.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([connectionId, projectIds]) => ({ connectionId, projectIds: [...projectIds].sort() }));\n}\n\nfunction encodePageCursor(cursor: JiraPageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): JiraPageCursor {\n if (!cursor) return { group: 0 };\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isInteger(parsed.group) || (parsed.group as number) < 0) throw new Error();\n return {\n group: parsed.group as number,\n ...(typeof parsed.jira === 'string' ? { jira: parsed.jira } : {}),\n };\n } catch {\n throw new JiraApiError('Jira pagination cursor is invalid.', 400);\n }\n}\n\nfunction normalizeSiteUrl(value: string): string {\n const candidate = /^https?:\\/\\//i.test(value) ? value : `https://${value}`;\n // `accountLabel` is free-form Platform data — an unparsable value must\n // surface as the classified 502 the routes already handle, not a raw\n // TypeError escaping the connection context.\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new JiraApiError('Jira site URL is invalid.', 502);\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') throw new JiraApiError('Jira site URL is invalid.', 502);\n return url.origin;\n}\n\nfunction buildIntakeJql(projectIds?: string[], labels?: string[]): string | null {\n const clauses: string[] = [];\n const safeProjects = (projectIds ?? [])\n .map(id => id.trim())\n .filter(id => /^[A-Za-z0-9_]+$/.test(id))\n .map(id => (/^\\d+$/.test(id) ? id : `\"${id}\"`));\n if (projectIds && safeProjects.length === 0) return null;\n if (safeProjects.length > 0) clauses.push(`project IN (${safeProjects.join(', ')})`);\n clauses.push('statusCategory != Done');\n const safeLabels = [...new Set((labels ?? []).map(label => label.trim().replace(/[\"\\\\]/g, '')).filter(Boolean))];\n if (safeLabels.length > 0) clauses.push(`labels IN (${safeLabels.map(label => `\"${label}\"`).join(', ')})`);\n return `${clauses.join(' AND ')} ORDER BY updated DESC`;\n}\n\nfunction currentStatusMatches(issue: JiraIssue, target: UpdateIntakeIssueInput['state']): boolean {\n const status = issue.fields.status;\n if (!status) return false;\n if (target.kind === 'byName') return status.name.toLowerCase() === target.name.toLowerCase();\n if (target.stateType === 'canceled') return status.statusCategory?.key === 'done' && /cancel/i.test(status.name);\n return status.statusCategory?.key === STATE_TYPE_TO_CATEGORY[target.stateType];\n}\n\nfunction resolveTransition(\n transitions: JiraTransition[],\n target: UpdateIntakeIssueInput['state'],\n): JiraTransition | null {\n if (target.kind === 'byName') {\n const wanted = target.name.toLowerCase();\n return transitions.find(transition => transition.to?.name.toLowerCase() === wanted) ?? null;\n }\n if (target.stateType === 'canceled') {\n return (\n transitions.find(\n transition => transition.to?.statusCategory?.key === 'done' && /cancel/i.test(transition.to.name),\n ) ?? null\n );\n }\n const category = STATE_TYPE_TO_CATEGORY[target.stateType];\n return transitions.find(transition => transition.to?.statusCategory?.key === category) ?? null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","names":["#clientConfig","#platformClient","#endpointHost","#cloudIdByConnectionId","#siteUrlByConnectionId","#orgIdByResourceId","#rules","#projects","#auth","#activeConnections","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#connectionContext","#connectionContextById","#toIntakeIssue","#findIssue","#listAllComments","#resolveRequest","#cloudId"],"sources":["../../../../src/integrations/platform/jira/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} 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 { IssueReconcileWorker } from '../../issue-reconcile-worker.js';\nimport { adfToText } from '../../jira/adf.js';\nimport type { JiraComment, JiraIssue, JiraTransition } from '../../jira/api.js';\nimport { JiraApiClient, JiraApiError } from '../../jira/api.js';\nimport type { JiraEventRules, JiraRuleOverrides } from '../../jira/default-rules.js';\nimport { resolveJiraRules } from '../../jira/default-rules.js';\nimport { attachJiraIssueReconciler } from '../../jira/issue-reconciler.js';\nimport { jiraReconciliationEnabled, jiraReconciliationInterval } from '../../jira/reconciliation-config.js';\nimport { attachJiraRules } from '../../jira/rules.js';\nimport {\n logPlatformInfo,\n PlatformApiClient,\n platformApiClientConfigFromEnv,\n type PlatformApiClientConfig,\n} from '../api-client.js';\nimport { buildPlatformJiraAgentTools } from './agent-tools.js';\nimport { buildPlatformJiraRoutes } from './routes.js';\n\ninterface PlatformIntegrationConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\ninterface JiraConnectionContext {\n connection: PlatformIntegrationConnection;\n api: JiraApiClient;\n siteUrl: string;\n}\n\ninterface PlatformConnectionContextResponse {\n connection_config: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n}\n\ninterface JiraIssueReference {\n connectionId: string;\n issueId: string;\n projectId?: string;\n}\n\ninterface JiraPageCursor {\n group: number;\n jira?: string;\n}\n\nconst ISSUE_COMMENTS_MAX_PAGES = 20;\n\n/** Hard stop for project pagination so a misbehaving `isLast` can't loop forever. */\nconst PROJECT_SEARCH_MAX_PAGES = 50;\nconst ISSUE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*-\\d+$/;\nconst PLATFORM_JIRA_PROVIDER_CONFIG_KEY = 'jira';\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst JIRA_CONNECTION_TOKEN_PREFIX = 'jira-connection:';\nconst JIRA_ISSUE_REF_PREFIX = 'jira-issue:';\nconst JIRA_SOURCE_PREFIX = 'jira-project:';\n\nconst STATE_TYPE_TO_CATEGORY: Record<'unstarted' | 'started' | 'completed', string> = {\n unstarted: 'new',\n started: 'indeterminate',\n completed: 'done',\n};\n\nfunction stateTypeFromCategory(key: string | undefined): string | null {\n switch (key) {\n case 'new':\n return 'unstarted';\n case 'indeterminate':\n return 'started';\n case 'done':\n return 'completed';\n default:\n return null;\n }\n}\n\nexport interface PlatformJiraIntegrationConfig {\n clientConfig?: PlatformApiClientConfig;\n /** Per-event replacements; omitted events retain defaults, null disables. */\n rules?: JiraRuleOverrides;\n}\n\nexport class PlatformJiraIntegration implements FactoryIntegration {\n readonly id = 'jira';\n readonly #clientConfig: PlatformApiClientConfig;\n readonly #platformClient: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #cloudIdByConnectionId = new Map<string, string>();\n readonly #siteUrlByConnectionId = new Map<string, string>();\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n readonly #rules: JiraEventRules;\n\n get rules(): JiraEventRules {\n return this.#rules;\n }\n\n constructor(config: PlatformJiraIntegrationConfig = {}) {\n this.#rules = resolveJiraRules(config.rules);\n this.#clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();\n this.#platformClient = new PlatformApiClient(this.#clientConfig);\n this.#endpointHost = new URL(this.#clientConfig.baseUrl).host;\n }\n\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\n logPlatformInfo('Platform Jira integration initialized', { endpointHost: this.#endpointHost });\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects)\n throw new Error('PlatformJiraIntegration is not initialized — the factory binds storage during prepare().');\n return this.#projects;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n this.#cloudIdByConnectionId.clear();\n this.#siteUrlByConnectionId.clear();\n }\n\n async listConnections(): Promise<PlatformIntegrationConnection[]> {\n const providerKey = encodeURIComponent(PLATFORM_JIRA_PROVIDER_CONFIG_KEY);\n const result = await this.#platformClient.request<{ connections: PlatformIntegrationConnection[] }>(\n 'GET',\n `/v2/connections?providerKey=${providerKey}`,\n );\n return result.connections;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return (await this.#activeConnections()).length > 0;\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n return {\n connection: jiraConnection(reference.connectionId),\n ...(reference.projectId ? { sourceId: encodeSourceId(reference.connectionId, reference.projectId) } : {}),\n issueId: reference.issueId,\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources: IntakeSource[] = [];\n for (const connection of await this.#activeConnections()) {\n const context = await this.#connectionContext(connection);\n let startAt = 0;\n for (let pageCount = 0; pageCount < PROJECT_SEARCH_MAX_PAGES; pageCount++) {\n const page = await context.api.listProjects({ startAt });\n for (const project of page.values) {\n sources.push({\n id: encodeSourceId(connection.id, project.id),\n name: project.name,\n type: 'project',\n metadata: {\n key: project.key,\n connectionId: connection.id,\n site: new URL(context.siteUrl).host,\n },\n });\n }\n if (page.isLast || page.values.length === 0) break;\n startAt += page.values.length;\n }\n }\n return sources;\n }\n\n async #listItems({ sourceIds, cursor }: ListIntakeItemsInput): Promise<IntakeItemPage> {\n if (sourceIds.length === 0) return { items: [], nextCursor: null };\n const page = await this.listActiveIssues(cursor, sourceIds);\n return {\n items: page.issues.map(issue => ({\n source: { type: 'issue', externalId: issue.externalId, url: issue.url },\n sourceId: issue.sourceId,\n title: `${issue.identifier}: ${issue.title}`,\n status: issue.state ?? undefined,\n labels: issue.labels,\n assignee: issue.assignee,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata: {\n identifier: issue.identifier,\n issueRef: issue.externalId,\n state: issue.state,\n stateType: issue.stateType,\n priority: issue.priority,\n project: issue.source,\n site: issue.site,\n assignee: issue.assignee,\n assignees: issue.assignees ?? [],\n creator: issue.author,\n author: issue.author,\n labels: issue.labels,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n },\n })),\n nextCursor: page.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput): Promise<{ issues: IntakeIssue[]; nextCursor: string | null }> {\n const page = await this.listActiveIssues(input.cursor, input.sourceIds, input.labels);\n return { issues: page.issues, nextCursor: page.nextCursor };\n }\n\n async listActiveIssues(\n cursor?: string,\n sourceIds: string[] = [],\n labels?: string[],\n ): Promise<{\n issues: Array<IntakeIssue & { sourceId: string; externalId: string; site: string }>;\n nextCursor: string | null;\n }> {\n const grouped = groupSources(sourceIds);\n if (grouped.length === 0) return { issues: [], nextCursor: null };\n const decodedCursor = decodePageCursor(cursor);\n if (decodedCursor.group >= grouped.length) return { issues: [], nextCursor: null };\n\n for (let groupIndex = decodedCursor.group; groupIndex < grouped.length; groupIndex++) {\n const group = grouped[groupIndex]!;\n const context = await this.#connectionContextById(group.connectionId);\n const jql = buildIntakeJql(group.projectIds, labels);\n if (jql === null) continue;\n const page = await context.api.searchIssues({\n jql,\n ...(groupIndex === decodedCursor.group && decodedCursor.jira ? { nextPageToken: decodedCursor.jira } : {}),\n });\n const nextCursor = page.nextPageToken\n ? encodePageCursor({ group: groupIndex, jira: page.nextPageToken })\n : groupIndex + 1 < grouped.length\n ? encodePageCursor({ group: groupIndex + 1 })\n : null;\n return {\n issues: page.issues.map(issue => {\n const projectId = issue.fields.project?.id ?? group.projectIds[0] ?? '';\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n sourceId: encodeSourceId(group.connectionId, projectId),\n externalId: encodeIssueReference({ connectionId: group.connectionId, issueId: issue.key, projectId }),\n site: new URL(context.siteUrl).host,\n };\n }),\n nextCursor,\n };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const { context, issue } = (await this.#findIssue(input)) ?? {};\n if (!context || !issue) return null;\n const { comments, total } = await this.#listAllComments(context.api, issue.key);\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n commentCount: total,\n description: issue.fields.description ? adfToText(issue.fields.description) || null : null,\n comments: comments.map(comment => ({\n author: comment.author?.displayName ?? null,\n body: adfToText(comment.body),\n createdAt: comment.created,\n })),\n };\n }\n\n async #listAllComments(api: JiraApiClient, keyOrId: string): Promise<{ comments: JiraComment[]; total: number }> {\n const comments: JiraComment[] = [];\n let total = 0;\n for (let page = 0; page < ISSUE_COMMENTS_MAX_PAGES; page++) {\n const result = await api.listComments(keyOrId, { startAt: comments.length });\n comments.push(...result.comments);\n total = result.total;\n if (result.comments.length === 0 || comments.length >= total) break;\n }\n return { comments, total };\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n let key = resolved.issueId;\n if (!ISSUE_KEY_PATTERN.test(key)) {\n try {\n key = (await resolved.context.api.getIssue(key)).key;\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n }\n let comment;\n try {\n comment = await resolved.context.api.createComment(key, input.body);\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n return {\n id: comment.id,\n url: `${resolved.context.siteUrl}/browse/${key}?focusedCommentId=${comment.id}`,\n };\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: JiraIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.issueId);\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n if (currentStatusMatches(issue, input.state)) return this.#toIntakeIssue(issue, resolved.context.siteUrl);\n const transitions = await resolved.context.api.listTransitions(issue.key);\n const transition = resolveTransition(transitions, input.state);\n if (!transition) return null;\n await resolved.context.api.applyTransition(issue.key, transition.id);\n return this.#toIntakeIssue(await resolved.context.api.getIssue(issue.key), resolved.context.siteUrl);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issueId: string }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n if (connectionId) return { context: await this.#connectionContextById(connectionId), issueId };\n const active = await this.#activeConnections();\n if (active.length !== 1) {\n throw new JiraApiError('Jira issue reference must identify a connection when multiple sites are connected.', 400);\n }\n return { context: await this.#connectionContext(active[0]!), issueId };\n }\n\n /**\n * Locate an issue for a read. A connection-scoped reference resolves\n * directly; a plain issue key (the agent tool's normal input) is looked up\n * across every active connection so multi-site organizations can still\n * fetch by key — the first site that knows the key wins, which is safe\n * because Jira project-key prefixes make cross-site collisions unlikely and\n * the tool is read-only.\n */\n async #findIssue(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issue: JiraIssue } | null> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n\n const candidates: JiraConnectionContext[] = [];\n if (connectionId) {\n candidates.push(await this.#connectionContextById(connectionId));\n } else {\n const active = await this.#activeConnections();\n if (active.length === 0) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n for (const connection of active) candidates.push(await this.#connectionContext(connection));\n }\n\n let firstError: unknown;\n for (const context of candidates) {\n try {\n return { context, issue: await context.api.getIssue(issueId) };\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) continue;\n firstError ??= error;\n }\n }\n // Not found anywhere. If a site failed for a non-404 reason, surface that\n // failure instead of a silent \"not found\".\n if (firstError !== undefined) throw firstError;\n return null;\n }\n\n #toIntakeIssue(issue: JiraIssue, siteUrl: string): IntakeIssue {\n const { fields } = issue;\n return {\n id: issue.id,\n identifier: issue.key,\n title: fields.summary ?? issue.key,\n url: `${siteUrl}/browse/${issue.key}`,\n author: fields.reporter?.displayName ?? null,\n state: fields.status?.name ?? null,\n stateType: stateTypeFromCategory(fields.status?.statusCategory?.key),\n priority: fields.priority?.name ?? null,\n assignee: fields.assignee?.displayName ?? null,\n source: fields.project?.key ?? null,\n labels: fields.labels ?? [],\n commentCount: null,\n createdAt: fields.created ?? '',\n updatedAt: fields.updated ?? '',\n };\n }\n\n async #activeConnections(): Promise<PlatformIntegrationConnection[]> {\n return (await this.listConnections()).filter(connection => connection.status === 'active');\n }\n\n async #connectionContextById(connectionId: string): Promise<JiraConnectionContext> {\n const connection = (await this.#activeConnections()).find(candidate => candidate.id === connectionId);\n if (!connection) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n return this.#connectionContext(connection);\n }\n\n async #cloudId(connectionId: string): Promise<string> {\n const cached = this.#cloudIdByConnectionId.get(connectionId);\n if (cached) return cached;\n\n const context = await this.#platformClient.request<PlatformConnectionContextResponse>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connectionId)}/context`,\n );\n const cloudId = context.connection_config?.cloudId;\n if (typeof cloudId !== 'string' || !UUID_PATTERN.test(cloudId)) {\n throw new JiraApiError('Platform Jira connection context is missing a valid cloudId.', 502);\n }\n this.#cloudIdByConnectionId.set(connectionId, cloudId);\n return cloudId;\n }\n\n async #connectionContext(connection: PlatformIntegrationConnection): Promise<JiraConnectionContext> {\n const proxyBaseUrl = `${this.#clientConfig.baseUrl.replace(/\\/+$/, '')}/v2/connections/${encodeURIComponent(\n connection.id,\n )}/proxy`;\n const cloudId = await this.#cloudId(connection.id);\n const api = new JiraApiClient({\n baseUrl: `${proxyBaseUrl}/ex/jira/${encodeURIComponent(cloudId)}`,\n accessToken: this.#clientConfig.accessToken,\n ...(this.#clientConfig.projectId ? { projectId: this.#clientConfig.projectId } : {}),\n ...(this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}),\n });\n let siteUrl = this.#siteUrlByConnectionId.get(connection.id);\n if (!siteUrl) {\n siteUrl = connection.accountLabel\n ? normalizeSiteUrl(connection.accountLabel)\n : normalizeSiteUrl((await api.getServerInfo()).baseUrl);\n this.#siteUrlByConnectionId.set(connection.id, siteUrl);\n }\n return { connection, api, siteUrl };\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!jiraReconciliationEnabled()) return [];\n const reconcile = attachJiraIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = jiraReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildPlatformJiraRoutes({\n jira: this,\n auth: ctx.auth,\n intake: ctx.storage.intake,\n appDbConfigured: Boolean(ctx.factoryStorage),\n ingestFactoryIssues: attachJiraRules(this, ctx),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildPlatformJiraAgentTools({ requestContext: args.requestContext, jira: this });\n }\n\n diagnostics(): Record<string, unknown> {\n return { configured: true, mode: 'platform', endpointHost: this.#endpointHost };\n }\n}\n\nfunction jiraConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${JIRA_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(JIRA_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(JIRA_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nexport function encodeSourceId(connectionId: string, projectId: string): string {\n return `${JIRA_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ connectionId, projectId })).toString('base64url')}`;\n}\n\nexport function decodeSourceId(sourceId: string): { connectionId: string; projectId: string } | null {\n if (!sourceId.startsWith(JIRA_SOURCE_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(sourceId.slice(JIRA_SOURCE_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n return typeof parsed.connectionId === 'string' && typeof parsed.projectId === 'string'\n ? { connectionId: parsed.connectionId, projectId: parsed.projectId }\n : null;\n } catch {\n return null;\n }\n}\n\nexport function encodeIssueReference(reference: JiraIssueReference): string {\n return `${JIRA_ISSUE_REF_PREFIX}${Buffer.from(JSON.stringify(reference)).toString('base64url')}`;\n}\n\nexport function decodeIssueReference(value: string): JiraIssueReference | null {\n if (!value.startsWith(JIRA_ISSUE_REF_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(value.slice(JIRA_ISSUE_REF_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n if (typeof parsed.connectionId !== 'string' || typeof parsed.issueId !== 'string') return null;\n return {\n connectionId: parsed.connectionId,\n issueId: parsed.issueId,\n ...(typeof parsed.projectId === 'string' ? { projectId: parsed.projectId } : {}),\n };\n } catch {\n return null;\n }\n}\n\nfunction groupSources(sourceIds: string[]): Array<{ connectionId: string; projectIds: string[] }> {\n const groups = new Map<string, Set<string>>();\n for (const sourceId of sourceIds) {\n const source = decodeSourceId(sourceId);\n if (!source) continue;\n const projectIds = groups.get(source.connectionId) ?? new Set<string>();\n projectIds.add(source.projectId);\n groups.set(source.connectionId, projectIds);\n }\n return [...groups.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([connectionId, projectIds]) => ({ connectionId, projectIds: [...projectIds].sort() }));\n}\n\nfunction encodePageCursor(cursor: JiraPageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): JiraPageCursor {\n if (!cursor) return { group: 0 };\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isInteger(parsed.group) || (parsed.group as number) < 0) throw new Error();\n return {\n group: parsed.group as number,\n ...(typeof parsed.jira === 'string' ? { jira: parsed.jira } : {}),\n };\n } catch {\n throw new JiraApiError('Jira pagination cursor is invalid.', 400);\n }\n}\n\nfunction normalizeSiteUrl(value: string): string {\n const candidate = /^https?:\\/\\//i.test(value) ? value : `https://${value}`;\n // `accountLabel` is free-form Platform data — an unparsable value must\n // surface as the classified 502 the routes already handle, not a raw\n // TypeError escaping the connection context.\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new JiraApiError('Jira site URL is invalid.', 502);\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') throw new JiraApiError('Jira site URL is invalid.', 502);\n return url.origin;\n}\n\nfunction buildIntakeJql(projectIds?: string[], labels?: string[]): string | null {\n const clauses: string[] = [];\n const safeProjects = (projectIds ?? [])\n .map(id => id.trim())\n .filter(id => /^[A-Za-z0-9_]+$/.test(id))\n .map(id => (/^\\d+$/.test(id) ? id : `\"${id}\"`));\n if (projectIds && safeProjects.length === 0) return null;\n if (safeProjects.length > 0) clauses.push(`project IN (${safeProjects.join(', ')})`);\n clauses.push('statusCategory != Done');\n const safeLabels = [...new Set((labels ?? []).map(label => label.trim().replace(/[\"\\\\]/g, '')).filter(Boolean))];\n if (safeLabels.length > 0) clauses.push(`labels IN (${safeLabels.map(label => `\"${label}\"`).join(', ')})`);\n return `${clauses.join(' AND ')} ORDER BY updated DESC`;\n}\n\nfunction currentStatusMatches(issue: JiraIssue, target: UpdateIntakeIssueInput['state']): boolean {\n const status = issue.fields.status;\n if (!status) return false;\n if (target.kind === 'byName') return status.name.toLowerCase() === target.name.toLowerCase();\n if (target.stateType === 'canceled') return status.statusCategory?.key === 'done' && /cancel/i.test(status.name);\n return status.statusCategory?.key === STATE_TYPE_TO_CATEGORY[target.stateType];\n}\n\nfunction resolveTransition(\n transitions: JiraTransition[],\n target: UpdateIntakeIssueInput['state'],\n): JiraTransition | null {\n if (target.kind === 'byName') {\n const wanted = target.name.toLowerCase();\n return transitions.find(transition => transition.to?.name.toLowerCase() === wanted) ?? null;\n }\n if (target.stateType === 'canceled') {\n return (\n transitions.find(\n transition => transition.to?.statusCategory?.key === 'done' && /cancel/i.test(transition.to.name),\n ) ?? null\n );\n }\n const category = STATE_TYPE_TO_CATEGORY[target.stateType];\n return transitions.find(transition => transition.to?.statusCategory?.key === category) ?? null;\n}\n"],"mappings":"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"}
@@ -42,6 +42,21 @@ export declare function subscriptionRunContext(subscription: SubscriptionSession
42
42
  export declare function prepareSessionRunContext(requestContext: RequestContext, sessionId: string, sourceControl: SubscriptionSessionLookup, options?: {
43
43
  expectedOrgId?: string;
44
44
  }): Promise<'prepared' | 'session-not-found' | 'organization-unresolved' | 'organization-mismatch'>;
45
+ /**
46
+ * Create the controller session for a Factory session that isn't live, as its
47
+ * row's owner in the row's organization. Used when an event or message has to
48
+ * reach a session nobody has open: there is no signed-in user, so the session
49
+ * runs as the person it belongs to.
50
+ */
51
+ export declare function createSessionAsRowOwner(controller: Pick<MountedMastraCode['controller'], 'createSession'>, args: {
52
+ sessionId: string;
53
+ ownerResourceId: string;
54
+ row: FactorySessionOwner;
55
+ tags: Record<string, string>;
56
+ scope?: string;
57
+ /** Bind this exact thread at creation instead of resuming or creating another. */
58
+ threadId?: string;
59
+ }): Promise<import("@mastra/core/harness").Session<import("@mastra/code-sdk/schema").MastraCodeState>>;
45
60
  export declare function resolveSubscriptionSession(controller: MountedMastraCode['controller'], subscription: SubscriptionSessionRow, options: {
46
61
  label: string;
47
62
  sourceControl?: SubscriptionSessionLookup;
@@ -1 +1 @@
1
- {"version":3,"file":"subscription-session.d.ts","sourceRoot":"","sources":["../../src/integrations/subscription-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAEvF,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE;QAAE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;KAAE,CAAC;CACtF;AAED;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,uBAAuB,CAAC;IAC3D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,sBAAsB,EACpC,aAAa,EAAE,yBAAyB,GAAG,SAAS,GACnD,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAyBrC;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,cAAc,EAAE,cAAc,EAC9B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,yBAAyB,EACxC,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,UAAU,GAAG,mBAAmB,GAAG,yBAAyB,GAAG,uBAAuB,CAAC,CAUjG;AAED,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,iBAAiB,CAAC,YAAY,CAAC,EAC3C,YAAY,EAAE,sBAAsB,EACpC,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,yBAAyB,CAAA;CAAE,kHA+EtE"}
1
+ {"version":3,"file":"subscription-session.d.ts","sourceRoot":"","sources":["../../src/integrations/subscription-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAEvF,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE;QAAE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;KAAE,CAAC;CACtF;AAED;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,uBAAuB,CAAC;IAC3D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,sBAAsB,EACpC,aAAa,EAAE,yBAAyB,GAAG,SAAS,GACnD,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAyBrC;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,cAAc,EAAE,cAAc,EAC9B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,yBAAyB,EACxC,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,UAAU,GAAG,mBAAmB,GAAG,yBAAyB,GAAG,uBAAuB,CAAC,CAUjG;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC,EAClE,IAAI,EAAE;IACJ,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,mBAAmB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,sGAeF;AAED,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,iBAAiB,CAAC,YAAY,CAAC,EAC3C,YAAY,EAAE,sBAAsB,EACpC,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,yBAAyB,CAAA;CAAE,kHA2EtE"}
@@ -53,6 +53,30 @@ async function prepareSessionRunContext(requestContext, sessionId, sourceControl
53
53
  await primeTenantCredentialsForRequestContext(requestContext);
54
54
  return "prepared";
55
55
  }
56
+ /**
57
+ * Create the controller session for a Factory session that isn't live, as its
58
+ * row's owner in the row's organization. Used when an event or message has to
59
+ * reach a session nobody has open: there is no signed-in user, so the session
60
+ * runs as the person it belongs to.
61
+ */
62
+ async function createSessionAsRowOwner(controller, args) {
63
+ const requestContext = new RequestContext();
64
+ requestContext.set("user", {
65
+ workosId: args.row.userId,
66
+ organizationId: args.row.orgId
67
+ });
68
+ const session = await controller.createSession({
69
+ id: args.sessionId,
70
+ ownerId: args.row.userId,
71
+ resourceId: args.ownerResourceId,
72
+ scope: args.scope,
73
+ tags: args.tags,
74
+ ...args.threadId ? { threadId: args.threadId } : {},
75
+ requestContext
76
+ });
77
+ await seedSessionOrg(session, args.row.orgId);
78
+ return session;
79
+ }
56
80
  async function resolveSubscriptionSession(controller, subscription, options) {
57
81
  const { label } = options;
58
82
  const { sessionId, resourceId, threadId } = subscription;
@@ -69,20 +93,13 @@ async function resolveSubscriptionSession(controller, subscription, options) {
69
93
  };
70
94
  const sessionRow = await options.sourceControl?.sessions.getBySessionId(sessionId);
71
95
  if (!sessionRow) throw new Error(`${label} subscription ${subscription.id} has no Factory session ${sessionId} to run as.`);
72
- const requestContext = new RequestContext();
73
- requestContext.set("user", {
74
- workosId: sessionRow.userId,
75
- organizationId: sessionRow.orgId
76
- });
77
- session = await controller.createSession({
78
- id: sessionId,
79
- ownerId: sessionRow.userId,
80
- resourceId: ownerResourceId,
96
+ session = await createSessionAsRowOwner(controller, {
97
+ sessionId,
98
+ ownerResourceId,
99
+ row: sessionRow,
81
100
  scope,
82
- tags,
83
- requestContext
101
+ tags
84
102
  });
85
- await seedSessionOrg(session, sessionRow.orgId);
86
103
  } else if (!hasResolvedOrg(session.state?.get()?.factoryOrgId)) try {
87
104
  const sessionRow = await options.sourceControl?.sessions.getBySessionId(sessionId);
88
105
  await seedSessionOrg(session, sessionRow?.orgId);
@@ -99,6 +116,6 @@ async function resolveSubscriptionSession(controller, subscription, options) {
99
116
  return session;
100
117
  }
101
118
  //#endregion
102
- export { prepareSessionRunContext, resolveSubscriptionSession, subscriptionRunContext };
119
+ export { createSessionAsRowOwner, prepareSessionRunContext, resolveSubscriptionSession, subscriptionRunContext };
103
120
 
104
121
  //# sourceMappingURL=subscription-session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"subscription-session.js","names":[],"sources":["../../src/integrations/subscription-session.ts"],"sourcesContent":["import type { MountedMastraCode } from '@mastra/code-sdk';\nimport { RequestContext } from '@mastra/core/request-context';\n\nimport { primeTenantCredentialsForRequestContext } from '../routes/tenant-credentials.js';\nimport { hasResolvedOrg, seedSessionOrg } from '../session/org-seed.js';\nimport type { IntegrationSubscription } from '../storage/domains/integrations/base.js';\n\n/** The Factory session row fields a woken session has to run as. */\nexport type FactorySessionOwner = { userId: string; orgId: string };\n\nexport interface SubscriptionSessionLookup {\n sessions: { getBySessionId(sessionId: string): Promise<FactorySessionOwner | null> };\n}\n\n/**\n * Bring the session that owns a subscribed thread online so a provider event\n * can be delivered to it. Provider-neutral: GitHub and GitLab subscriptions\n * carry the same session binding and differ only in their target data.\n */\nexport type SubscriptionSessionRow = IntegrationSubscription<{\n projectRepositoryId: string;\n subscribedByUserId?: string | null;\n}>;\n\n/**\n * The request context a provider-triggered run executes under: the user who\n * subscribed the thread, in the subscription's organization. A webhook carries\n * no signed-in user and tenant credential resolution fails closed without one,\n * so a woken run would otherwise stop before it could persist or answer the\n * notification. The subscription row already names that user, so delivery\n * costs no extra read; an older row without one falls back to the Factory\n * session's owner, the identity used when a session is recreated for delivery.\n */\nexport async function subscriptionRunContext(\n subscription: SubscriptionSessionRow,\n sourceControl: SubscriptionSessionLookup | undefined,\n): Promise<RequestContext | undefined> {\n let userId = subscription.data.subscribedByUserId ?? undefined;\n let orgId: string | undefined = subscription.orgId;\n if (!userId && subscription.sessionId && sourceControl) {\n const sessionRow = await sourceControl.sessions.getBySessionId(subscription.sessionId);\n userId = sessionRow?.userId;\n orgId = sessionRow?.orgId ?? orgId;\n }\n if (!userId || !hasResolvedOrg(orgId)) return undefined;\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: userId, organizationId: orgId });\n // The web auth middleware primes credential snapshots per request; a\n // webhook-triggered run has no request, so prime here or the model resolves\n // against an empty snapshot and the run fails closed before persisting. A\n // priming failure propagates: handing back an unprimed context would let the\n // caller accept the delivery, and retire a terminal subscription, for a run\n // that then fails without its credentials and cannot be redelivered.\n try {\n await primeTenantCredentialsForRequestContext(requestContext);\n } catch (error) {\n throw new Error(`Unable to prime tenant credentials for subscription ${subscription.id}; not delivered.`, {\n cause: error,\n });\n }\n return requestContext;\n}\n\n/**\n * Attach the Factory session's owner, in its organization, to the request\n * context of a run that has no inbound request (a notification or peer-signal\n * wake), and prime that tenant's credentials. The live session's `ownerId`\n * isn't used: sessions opened from the browser or Slack are owned by the\n * controller id, not a user. Leaves the context alone when `sessionId` names no\n * Factory session or its org is unresolved, so credential resolution fails\n * closed.\n */\nexport async function prepareSessionRunContext(\n requestContext: RequestContext,\n sessionId: string,\n sourceControl: SubscriptionSessionLookup,\n options?: { expectedOrgId?: string },\n): Promise<'prepared' | 'session-not-found' | 'organization-unresolved' | 'organization-mismatch'> {\n const sessionRow = await sourceControl.sessions.getBySessionId(sessionId);\n if (!sessionRow) return 'session-not-found';\n if (!hasResolvedOrg(sessionRow.orgId)) return 'organization-unresolved';\n if (options?.expectedOrgId !== undefined && sessionRow.orgId !== options.expectedOrgId) {\n return 'organization-mismatch';\n }\n requestContext.set('user', { workosId: sessionRow.userId, organizationId: sessionRow.orgId });\n await primeTenantCredentialsForRequestContext(requestContext);\n return 'prepared';\n}\n\nexport async function resolveSubscriptionSession(\n controller: MountedMastraCode['controller'],\n subscription: SubscriptionSessionRow,\n options: { label: string; sourceControl?: SubscriptionSessionLookup },\n) {\n const { label } = options;\n const { sessionId, resourceId, threadId } = subscription;\n if (!sessionId || !resourceId || !threadId) {\n throw new Error(`${label} subscription ${subscription.id} is missing its session binding.`);\n }\n // Read the thread straight from storage before touching sessions. This answers\n // two questions at once, and `queryThreadById` does it without constructing a\n // session (so no workspace or sandbox is provisioned just to make the check).\n //\n // First: do we even have this thread? A change request's events can reach a\n // deployment that never owned the subscribed thread, and delivery must not\n // fabricate a session for a thread that lives somewhere else.\n //\n // Second: which resource owns it? The subscription records the Factory project\n // as its `resourceId`, but an unscoped session is registered under its own id,\n // so the stored value routinely names a resource that does not own the thread.\n // The thread row is the authoritative answer; the stored id is only a fallback.\n const thread = await controller.queryThreadById({ threadId });\n if (!thread) return undefined;\n const ownerResourceId = thread.resourceId || resourceId;\n const scope = subscription.sessionScope || undefined;\n let session = await controller.getSessionByResource(ownerResourceId, scope);\n if (!session) {\n const tags = {\n factoryProjectId: resourceId,\n projectRepositoryId: subscription.data.projectRepositoryId,\n };\n // Creating the session resolves its workspace, which authorizes the caller\n // against the Factory session row — no signed-in user, so run as its owner.\n // The session is created under the resource that owns the thread, so the\n // thread switch below resolves; the persisted Factory session is keyed by\n // the subscription's session ID.\n const sessionRow = await options.sourceControl?.sessions.getBySessionId(sessionId);\n if (!sessionRow) {\n throw new Error(`${label} subscription ${subscription.id} has no Factory session ${sessionId} to run as.`);\n }\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: sessionRow.userId, organizationId: sessionRow.orgId });\n session = await controller.createSession({\n id: sessionId,\n ownerId: sessionRow.userId,\n resourceId: ownerResourceId,\n scope,\n tags,\n requestContext,\n });\n await seedSessionOrg(session, sessionRow.orgId);\n } else if (!hasResolvedOrg(session.state?.get()?.factoryOrgId)) {\n // A session created before the org seed existed carries the project tag and\n // no org, so capture would refuse for the rest of its life even though the\n // org is recoverable. Heal it — but only here, and only when it is missing:\n // hoisting the row fetch above would make an existing-session delivery throw\n // on a missing row where it previously succeeded, and fetching it every time\n // would add a storage read to every delivery. A row that is missing or a\n // lookup that throws leaves the session marked unresolved, and delivery\n // continues either way.\n try {\n const sessionRow = await options.sourceControl?.sessions.getBySessionId(sessionId);\n await seedSessionOrg(session, sessionRow?.orgId);\n } catch (error) {\n console.warn(`[${label} webhook] Unable to resolve the session organization.`, error);\n await seedSessionOrg(session, undefined);\n }\n } else if (session.state?.get()?.factoryOrgUnresolved) {\n // The org is present, so an earlier failed resolution left a stale marker\n // behind. Clear it without a storage read — nothing else re-seeds a session\n // once the start hook has run, so the marker would otherwise outlive its\n // cause.\n await seedSessionOrg(session, session.state.get()?.factoryOrgId);\n }\n if (session.thread.getId() !== threadId) {\n await session.thread.switch({ threadId, emitEvent: false });\n }\n if (session.thread.getId() !== threadId) {\n throw new Error(`Session ${sessionId} did not bind thread ${threadId}.`);\n }\n return session;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"subscription-session.js","names":[],"sources":["../../src/integrations/subscription-session.ts"],"sourcesContent":["import type { MountedMastraCode } from '@mastra/code-sdk';\nimport { RequestContext } from '@mastra/core/request-context';\n\nimport { primeTenantCredentialsForRequestContext } from '../routes/tenant-credentials.js';\nimport { hasResolvedOrg, seedSessionOrg } from '../session/org-seed.js';\nimport type { IntegrationSubscription } from '../storage/domains/integrations/base.js';\n\n/** The Factory session row fields a woken session has to run as. */\nexport type FactorySessionOwner = { userId: string; orgId: string };\n\nexport interface SubscriptionSessionLookup {\n sessions: { getBySessionId(sessionId: string): Promise<FactorySessionOwner | null> };\n}\n\n/**\n * Bring the session that owns a subscribed thread online so a provider event\n * can be delivered to it. Provider-neutral: GitHub and GitLab subscriptions\n * carry the same session binding and differ only in their target data.\n */\nexport type SubscriptionSessionRow = IntegrationSubscription<{\n projectRepositoryId: string;\n subscribedByUserId?: string | null;\n}>;\n\n/**\n * The request context a provider-triggered run executes under: the user who\n * subscribed the thread, in the subscription's organization. A webhook carries\n * no signed-in user and tenant credential resolution fails closed without one,\n * so a woken run would otherwise stop before it could persist or answer the\n * notification. The subscription row already names that user, so delivery\n * costs no extra read; an older row without one falls back to the Factory\n * session's owner, the identity used when a session is recreated for delivery.\n */\nexport async function subscriptionRunContext(\n subscription: SubscriptionSessionRow,\n sourceControl: SubscriptionSessionLookup | undefined,\n): Promise<RequestContext | undefined> {\n let userId = subscription.data.subscribedByUserId ?? undefined;\n let orgId: string | undefined = subscription.orgId;\n if (!userId && subscription.sessionId && sourceControl) {\n const sessionRow = await sourceControl.sessions.getBySessionId(subscription.sessionId);\n userId = sessionRow?.userId;\n orgId = sessionRow?.orgId ?? orgId;\n }\n if (!userId || !hasResolvedOrg(orgId)) return undefined;\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: userId, organizationId: orgId });\n // The web auth middleware primes credential snapshots per request; a\n // webhook-triggered run has no request, so prime here or the model resolves\n // against an empty snapshot and the run fails closed before persisting. A\n // priming failure propagates: handing back an unprimed context would let the\n // caller accept the delivery, and retire a terminal subscription, for a run\n // that then fails without its credentials and cannot be redelivered.\n try {\n await primeTenantCredentialsForRequestContext(requestContext);\n } catch (error) {\n throw new Error(`Unable to prime tenant credentials for subscription ${subscription.id}; not delivered.`, {\n cause: error,\n });\n }\n return requestContext;\n}\n\n/**\n * Attach the Factory session's owner, in its organization, to the request\n * context of a run that has no inbound request (a notification or peer-signal\n * wake), and prime that tenant's credentials. The live session's `ownerId`\n * isn't used: sessions opened from the browser or Slack are owned by the\n * controller id, not a user. Leaves the context alone when `sessionId` names no\n * Factory session or its org is unresolved, so credential resolution fails\n * closed.\n */\nexport async function prepareSessionRunContext(\n requestContext: RequestContext,\n sessionId: string,\n sourceControl: SubscriptionSessionLookup,\n options?: { expectedOrgId?: string },\n): Promise<'prepared' | 'session-not-found' | 'organization-unresolved' | 'organization-mismatch'> {\n const sessionRow = await sourceControl.sessions.getBySessionId(sessionId);\n if (!sessionRow) return 'session-not-found';\n if (!hasResolvedOrg(sessionRow.orgId)) return 'organization-unresolved';\n if (options?.expectedOrgId !== undefined && sessionRow.orgId !== options.expectedOrgId) {\n return 'organization-mismatch';\n }\n requestContext.set('user', { workosId: sessionRow.userId, organizationId: sessionRow.orgId });\n await primeTenantCredentialsForRequestContext(requestContext);\n return 'prepared';\n}\n\n/**\n * Create the controller session for a Factory session that isn't live, as its\n * row's owner in the row's organization. Used when an event or message has to\n * reach a session nobody has open: there is no signed-in user, so the session\n * runs as the person it belongs to.\n */\nexport async function createSessionAsRowOwner(\n controller: Pick<MountedMastraCode['controller'], 'createSession'>,\n args: {\n sessionId: string;\n ownerResourceId: string;\n row: FactorySessionOwner;\n tags: Record<string, string>;\n scope?: string;\n /** Bind this exact thread at creation instead of resuming or creating another. */\n threadId?: string;\n },\n) {\n const requestContext = new RequestContext();\n requestContext.set('user', { workosId: args.row.userId, organizationId: args.row.orgId });\n const session = await controller.createSession({\n id: args.sessionId,\n ownerId: args.row.userId,\n resourceId: args.ownerResourceId,\n scope: args.scope,\n tags: args.tags,\n ...(args.threadId ? { threadId: args.threadId } : {}),\n requestContext,\n });\n await seedSessionOrg(session, args.row.orgId);\n return session;\n}\n\nexport async function resolveSubscriptionSession(\n controller: MountedMastraCode['controller'],\n subscription: SubscriptionSessionRow,\n options: { label: string; sourceControl?: SubscriptionSessionLookup },\n) {\n const { label } = options;\n const { sessionId, resourceId, threadId } = subscription;\n if (!sessionId || !resourceId || !threadId) {\n throw new Error(`${label} subscription ${subscription.id} is missing its session binding.`);\n }\n // Read the thread straight from storage before touching sessions. This answers\n // two questions at once, and `queryThreadById` does it without constructing a\n // session (so no workspace or sandbox is provisioned just to make the check).\n //\n // First: do we even have this thread? A change request's events can reach a\n // deployment that never owned the subscribed thread, and delivery must not\n // fabricate a session for a thread that lives somewhere else.\n //\n // Second: which resource owns it? The subscription records the Factory project\n // as its `resourceId`, but an unscoped session is registered under its own id,\n // so the stored value routinely names a resource that does not own the thread.\n // The thread row is the authoritative answer; the stored id is only a fallback.\n const thread = await controller.queryThreadById({ threadId });\n if (!thread) return undefined;\n const ownerResourceId = thread.resourceId || resourceId;\n const scope = subscription.sessionScope || undefined;\n let session = await controller.getSessionByResource(ownerResourceId, scope);\n if (!session) {\n const tags = {\n factoryProjectId: resourceId,\n projectRepositoryId: subscription.data.projectRepositoryId,\n };\n // Creating the session resolves its workspace, which authorizes the caller\n // against the Factory session row — no signed-in user, so run as its owner.\n // The session is created under the resource that owns the thread, so the\n // thread switch below resolves; the persisted Factory session is keyed by\n // the subscription's session ID.\n const sessionRow = await options.sourceControl?.sessions.getBySessionId(sessionId);\n if (!sessionRow) {\n throw new Error(`${label} subscription ${subscription.id} has no Factory session ${sessionId} to run as.`);\n }\n session = await createSessionAsRowOwner(controller, {\n sessionId,\n ownerResourceId,\n row: sessionRow,\n scope,\n tags,\n });\n } else if (!hasResolvedOrg(session.state?.get()?.factoryOrgId)) {\n // A session created before the org seed existed carries the project tag and\n // no org, so capture would refuse for the rest of its life even though the\n // org is recoverable. Heal it — but only here, and only when it is missing:\n // hoisting the row fetch above would make an existing-session delivery throw\n // on a missing row where it previously succeeded, and fetching it every time\n // would add a storage read to every delivery. A row that is missing or a\n // lookup that throws leaves the session marked unresolved, and delivery\n // continues either way.\n try {\n const sessionRow = await options.sourceControl?.sessions.getBySessionId(sessionId);\n await seedSessionOrg(session, sessionRow?.orgId);\n } catch (error) {\n console.warn(`[${label} webhook] Unable to resolve the session organization.`, error);\n await seedSessionOrg(session, undefined);\n }\n } else if (session.state?.get()?.factoryOrgUnresolved) {\n // The org is present, so an earlier failed resolution left a stale marker\n // behind. Clear it without a storage read — nothing else re-seeds a session\n // once the start hook has run, so the marker would otherwise outlive its\n // cause.\n await seedSessionOrg(session, session.state.get()?.factoryOrgId);\n }\n if (session.thread.getId() !== threadId) {\n await session.thread.switch({ threadId, emitEvent: false });\n }\n if (session.thread.getId() !== threadId) {\n throw new Error(`Session ${sessionId} did not bind thread ${threadId}.`);\n }\n return session;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function providerDisplayName(provider: string): string;
2
+ //# sourceMappingURL=provider-display-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-display-name.d.ts","sourceRoot":"","sources":["../src/provider-display-name.ts"],"names":[],"mappings":"AASA,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ5D"}
@@ -0,0 +1,16 @@
1
+ //#region src/provider-display-name.ts
2
+ const PROVIDER_DISPLAY_NAMES = {
3
+ anthropic: "Anthropic",
4
+ "github-copilot": "GitHub Copilot",
5
+ "kimi-code-plan-cn": "Kimi",
6
+ openai: "OpenAI",
7
+ "openai-codex": "OpenAI",
8
+ xai: "xAI"
9
+ };
10
+ function providerDisplayName(provider) {
11
+ return PROVIDER_DISPLAY_NAMES[provider] ?? provider.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
12
+ }
13
+ //#endregion
14
+ export { providerDisplayName };
15
+
16
+ //# sourceMappingURL=provider-display-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-display-name.js","names":[],"sources":["../src/provider-display-name.ts"],"sourcesContent":["const PROVIDER_DISPLAY_NAMES: Record<string, string> = {\n anthropic: 'Anthropic',\n 'github-copilot': 'GitHub Copilot',\n 'kimi-code-plan-cn': 'Kimi',\n openai: 'OpenAI',\n 'openai-codex': 'OpenAI',\n xai: 'xAI',\n};\n\nexport function providerDisplayName(provider: string): string {\n return (\n PROVIDER_DISPLAY_NAMES[provider] ??\n provider\n .split('-')\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join(' ')\n );\n}\n"],"mappings":";AAAA,MAAM,yBAAiD;CACrD,WAAW;CACX,kBAAkB;CAClB,qBAAqB;CACrB,QAAQ;CACR,gBAAgB;CAChB,KAAK;AACP;AAEA,SAAgB,oBAAoB,UAA0B;CAC5D,OACE,uBAAuB,aACvB,SACG,MAAM,GAAG,CAAC,CACV,KAAI,SAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CACzD,KAAK,GAAG;AAEf"}
@@ -1,4 +1,4 @@
1
- import { WEB_OAUTH_FLOW_KINDS, getAuthProviderId, listTenantCredentialsForRequest, resolveCredentialContext, tenantOrgId } from "./provider-credentials.js";
1
+ import { WEB_OAUTH_PROVIDERS, getAuthProviderId, listTenantCredentialsForRequest, resolveCredentialContext, tenantOrgId } from "./provider-credentials.js";
2
2
  import { resolveFactoryModelPolicy } from "../session/credential-policy.js";
3
3
  import "../session/memory-settings-hydration.js";
4
4
  import { factoryMemorySettingsUserId } from "../storage/domains/memory-settings/base.js";
@@ -57,7 +57,7 @@ async function listProviders({ controller, authStorage, tenantCredentials, deplo
57
57
  else if (authStorage?.hasStoredApiKey(model.provider)) source = "stored";
58
58
  else if (model.apiKeyEnvVar && process.env[model.apiKeyEnvVar]) source = "env";
59
59
  else if (model.hasApiKey) source = "env";
60
- const flowKind = WEB_OAUTH_FLOW_KINDS[model.provider];
60
+ const flowKind = WEB_OAUTH_PROVIDERS[model.provider]?.flow;
61
61
  seen.set(model.provider, {
62
62
  provider: model.provider,
63
63
  envVar: model.apiKeyEnvVar,