@mastra/factory 0.18.1-alpha.0 → 0.19.0-alpha.1

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 (65) hide show
  1. package/README.md +13 -1
  2. package/dist/boards/registry.d.ts.map +1 -1
  3. package/dist/boards/registry.js +0 -2
  4. package/dist/boards/registry.js.map +1 -1
  5. package/dist/factory.d.ts.map +1 -1
  6. package/dist/factory.js +12 -2
  7. package/dist/factory.js.map +1 -1
  8. package/dist/integrations/github/default-rules.d.ts +10 -2
  9. package/dist/integrations/github/default-rules.d.ts.map +1 -1
  10. package/dist/integrations/github/default-rules.js +25 -1
  11. package/dist/integrations/github/default-rules.js.map +1 -1
  12. package/dist/integrations/github/integration.js +1 -1
  13. package/dist/integrations/github/routes.d.ts.map +1 -1
  14. package/dist/integrations/github/routes.js +1 -1
  15. package/dist/integrations/github/routes.js.map +1 -1
  16. package/dist/integrations/github/rules.d.ts +5 -0
  17. package/dist/integrations/github/rules.d.ts.map +1 -1
  18. package/dist/integrations/github/rules.js +17 -8
  19. package/dist/integrations/github/rules.js.map +1 -1
  20. package/dist/integrations/github/stale-reviews.d.ts +22 -0
  21. package/dist/integrations/github/stale-reviews.d.ts.map +1 -0
  22. package/dist/integrations/github/stale-reviews.js +70 -0
  23. package/dist/integrations/github/stale-reviews.js.map +1 -0
  24. package/dist/integrations/jira/integration.d.ts.map +1 -1
  25. package/dist/integrations/jira/integration.js +7 -1
  26. package/dist/integrations/jira/integration.js.map +1 -1
  27. package/dist/integrations/platform/github/integration.js +1 -1
  28. package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
  29. package/dist/integrations/platform/jira/integration.js +7 -1
  30. package/dist/integrations/platform/jira/integration.js.map +1 -1
  31. package/dist/routes/attention-providers.d.ts +6 -7
  32. package/dist/routes/attention-providers.d.ts.map +1 -1
  33. package/dist/routes/attention-providers.js +12 -5
  34. package/dist/routes/attention-providers.js.map +1 -1
  35. package/dist/routes/attention.d.ts +2 -0
  36. package/dist/routes/attention.d.ts.map +1 -1
  37. package/dist/routes/attention.js +9 -3
  38. package/dist/routes/attention.js.map +1 -1
  39. package/dist/routes/work-items.d.ts.map +1 -1
  40. package/dist/routes/work-items.js +10 -5
  41. package/dist/routes/work-items.js.map +1 -1
  42. package/dist/rules/decision-applicability.d.ts +26 -0
  43. package/dist/rules/decision-applicability.d.ts.map +1 -0
  44. package/dist/rules/decision-applicability.js +47 -0
  45. package/dist/rules/decision-applicability.js.map +1 -0
  46. package/dist/rules/dispatcher.d.ts +4 -0
  47. package/dist/rules/dispatcher.d.ts.map +1 -1
  48. package/dist/rules/dispatcher.js +32 -22
  49. package/dist/rules/dispatcher.js.map +1 -1
  50. package/dist/rules/types.d.ts +18 -1
  51. package/dist/rules/types.d.ts.map +1 -1
  52. package/dist/rules/types.js.map +1 -1
  53. package/dist/rules/validation.d.ts.map +1 -1
  54. package/dist/rules/validation.js +20 -0
  55. package/dist/rules/validation.js.map +1 -1
  56. package/dist/storage/domains/work-items/base.d.ts +3 -0
  57. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  58. package/dist/storage/domains/work-items/base.js +15 -2
  59. package/dist/storage/domains/work-items/base.js.map +1 -1
  60. package/dist/supervisor/write-tools.d.ts +2 -0
  61. package/dist/supervisor/write-tools.d.ts.map +1 -1
  62. package/dist/supervisor/write-tools.js +3 -0
  63. package/dist/supervisor/write-tools.js.map +1 -1
  64. package/factory-skills/configure-factory-rules/SKILL.md +1 -1
  65. package/package.json +9 -9
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#config","#orgIdByResourceId","#rules","#projects","#auth","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#toIntakeIssue","#listAllComments"],"sources":["../../../src/integrations/jira/integration.ts"],"sourcesContent":["/**\n * `JiraIntegration` — the self-contained Jira Cloud integration.\n *\n * Implements the system-wide `FactoryIntegration` contract\n * (`../base.ts`): the deploy entry reads the `JIRA_*` env vars ONCE,\n * constructs an instance with explicit credentials, and passes it to\n * `MastraFactory`. Everything Jira-flavored the system does — project/issue\n * reads for Intake and the agent's issue tools — flows through this\n * instance. No other module reads `JIRA_*` env vars.\n *\n * Unlike Linear, credentials are **deployment-global** (Basic auth with\n * `email:apiToken` from the environment): there is no OAuth flow, no state\n * signer, and no per-org connection storage. The `IntegrationConnection`\n * arguments required by the `Intake` contract are accepted but not consulted\n * — the instance's own credentials authenticate every call.\n */\n\nimport 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 './adf.js';\nimport { buildJiraAgentTools } from './agent-tools.js';\nimport type { JiraComment, JiraIssue, JiraTransition } from './api.js';\nimport { JiraApiClient, JiraApiError } from './api.js';\nimport type { JiraEventRules, JiraRuleOverrides } from './default-rules.js';\nimport { resolveJiraRules } from './default-rules.js';\nimport { attachJiraIssueReconciler } from './issue-reconciler.js';\nimport { jiraReconciliationEnabled, jiraReconciliationInterval } from './reconciliation-config.js';\nimport { buildJiraRoutes } from './routes.js';\nimport { attachJiraRules } from './rules.js';\n\n/** Deployment-global Jira Cloud credentials. All credential fields are required. */\nexport interface JiraIntegrationConfig {\n /** Site base URL, e.g. `https://acme.atlassian.net`. */\n baseUrl: string;\n /** Atlassian account email the API token belongs to. */\n email: string;\n /** API token from id.atlassian.com → Security → API tokens. */\n apiToken: string;\n /** Per-event replacements; omitted events retain defaults, null disables. */\n rules?: JiraRuleOverrides;\n}\n\n/** Hard stop for comment pagination so a misbehaving `total` can't loop forever. */\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;\n\n/**\n * The `Intake` contract requires a connection argument, but Jira credentials\n * live on the integration instance — this inert placeholder is accepted and\n * ignored everywhere, and keeps the real API token out of resolved dispatches\n * and agent-tool calls.\n */\nexport const DEPLOYMENT_CONNECTION: IntegrationConnection = { type: 'oauth', accessToken: 'deployment-global' };\n\n/** Jira issue keys look like `ENG-42`. */\nconst ISSUE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*-\\d+$/;\n\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Capability state types (`capabilities/intake.ts`) → Jira status-category\n * keys. `canceled` has no Jira category — it is resolved separately against\n * `done`-category transitions whose status name contains \"cancel\".\n */\nconst STATE_TYPE_TO_CATEGORY: Record<'unstarted' | 'started' | 'completed', string> = {\n unstarted: 'new',\n started: 'indeterminate',\n completed: 'done',\n};\n\n/** Jira status-category keys → capability state types. */\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 class JiraIntegration implements FactoryIntegration {\n /** Stable integration identifier (see `../base.ts`). */\n readonly id = 'jira';\n\n readonly #config: JiraIntegrationConfig;\n /** Typed REST client bound to the deployment credentials. */\n readonly api: JiraApiClient;\n\n /** Bound once by the factory via `initialize()` before any surface is used. */\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: JiraIntegrationConfig) {\n this.#rules = resolveJiraRules(config.rules);\n // JiraApiClient validates the credential group and normalizes the URL.\n this.api = new JiraApiClient(config);\n this.#config = config;\n }\n\n /**\n * Bind the projects domain and the host auth seam. Jira has no per-org\n * connection rows, so the generic storage handle is unused — credentials\n * are deployment-global constructor config.\n */\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\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('JiraIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#projects;\n }\n\n /**\n * Whether the host runs with web auth enabled. Intake selections are\n * org-owned, so every Jira surface is inert without a tenant auth seam.\n */\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 Linear 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 // Non-UUID resource ids (local/dev resources) would make the uuid column\n // comparison throw — they're definitively \"not a project\", so cache that.\n if (!UUID_PATTERN.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 /** Test hook: clear the org cache between specs. */\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n /** Normalized site base URL (no trailing slash) — `/browse/<key>` links hang off this. */\n get baseUrl(): string {\n return this.api.baseUrl;\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 /**\n * Background-dispatch context. Credentials are deployment-global, so the\n * returned connection is a formality the Jira intake methods ignore; the\n * issue key is stored directly as the work item's `externalId` (mirroring\n * Linear, which stores the issue UUID).\n */\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n return {\n // Inert placeholder, never the real token: the Jira intake methods read\n // credentials from the client, and the resolved dispatch travels through\n // generic reconciler/dispatcher code that must stay secret-free.\n connection: DEPLOYMENT_CONNECTION,\n issueId: externalSource.externalId,\n };\n }\n\n /** All the site's projects (Settings intake-source picker), following `isLast` paging. */\n async #listSources(): Promise<IntakeSource[]> {\n const sources: IntakeSource[] = [];\n let startAt = 0;\n for (let pageCount = 0; pageCount < PROJECT_SEARCH_MAX_PAGES; pageCount++) {\n const page = await this.api.listProjects({ startAt });\n for (const project of page.values) {\n sources.push({\n id: project.id,\n name: project.name,\n type: 'project',\n metadata: { key: project.key },\n });\n }\n if (page.isLast || page.values.length === 0) break;\n startAt += page.values.length;\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.identifier, 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.identifier,\n state: issue.state,\n stateType: issue.stateType,\n priority: issue.priority,\n project: issue.source,\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 /**\n * One page of active issues (status category not Done), most recently\n * updated first, optionally filtered to projects and labels. The\n * capability cursor is Jira's opaque `nextPageToken` passed through.\n */\n async listActiveIssues(\n cursor?: string,\n projectIds?: string[],\n labels?: string[],\n ): Promise<{ issues: Array<IntakeIssue & { sourceId: string }>; nextCursor: string | null }> {\n const page = await this.api.searchIssues({\n jql: buildIntakeJql(projectIds, labels),\n ...(cursor ? { nextPageToken: cursor } : {}),\n });\n return {\n issues: page.issues.map(issue => ({\n ...this.#toIntakeIssue(issue),\n sourceId: issue.fields.project?.id ?? '',\n })),\n nextCursor: page.nextPageToken ?? null,\n };\n }\n\n async #getIssue({ issueId }: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n let issue: JiraIssue;\n try {\n issue = await this.api.getIssue(issueId);\n } catch (err) {\n if (err instanceof JiraApiError && err.status === 404) return null;\n throw err;\n }\n const { comments, total } = await this.#listAllComments(issue.key);\n return {\n ...this.#toIntakeIssue(issue),\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 /** Every comment on the issue, oldest first, bounded by {@link ISSUE_COMMENTS_MAX_PAGES}. */\n async #listAllComments(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 this.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({ issueId, body }: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n // The browse URL needs the issue key; resolve it when the caller passed a\n // numeric id instead.\n let key = issueId;\n if (!ISSUE_KEY_PATTERN.test(issueId)) {\n try {\n key = (await this.api.getIssue(issueId)).key;\n } catch (err) {\n if (err instanceof JiraApiError && err.status === 404) return null;\n throw err;\n }\n }\n const comment = await this.api.createComment(key, body);\n return {\n id: comment.id,\n url: `${this.baseUrl}/browse/${key}?focusedCommentId=${comment.id}`,\n };\n }\n\n /**\n * Move an issue to a target state by applying a workflow transition. Jira\n * only exposes transitions that are currently legal, so a policy miss (no\n * transition reaches the target) returns `null` per the `Intake` contract —\n * infrastructure errors (network, auth) still throw.\n */\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n let issue: JiraIssue;\n try {\n issue = await this.api.getIssue(input.issueId);\n } catch (err) {\n if (err instanceof JiraApiError && err.status === 404) return null;\n throw err;\n }\n if (currentStatusMatches(issue, input.state)) {\n return this.#toIntakeIssue(issue);\n }\n const transitions = await this.api.listTransitions(issue.key);\n const transition = resolveTransition(transitions, input.state);\n if (!transition) return null;\n await this.api.applyTransition(issue.key, transition.id);\n const fresh = await this.api.getIssue(issue.key);\n return this.#toIntakeIssue(fresh);\n }\n\n #toIntakeIssue(issue: JiraIssue): IntakeIssue {\n const { fields } = issue;\n return {\n id: issue.id,\n identifier: issue.key,\n title: fields.summary ?? issue.key,\n url: `${this.baseUrl}/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 sourceId: fields.project?.id ?? null,\n labels: fields.labels ?? [],\n commentCount: null,\n createdAt: fields.created ?? '',\n updatedAt: fields.updated ?? '',\n };\n }\n\n // ── FactoryIntegration surface ───────────────────────────────────────────\n\n /**\n * The integration's HTTP surface: `/web/jira/*` Mastra `apiRoutes` (status,\n * projects + issues for Intake). No OAuth routes — credentials are\n * deployment-global. Handlers operate on this instance.\n */\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildJiraRoutes({\n jira: this,\n auth: ctx.auth,\n intake: ctx.storage.intake,\n ingestFactoryIssues: attachJiraRules(this, ctx),\n });\n }\n\n /**\n * Background reconciliation, matching the Platform integration: keeps filed\n * Jira cards' metadata fresh and replays close events through the rules\n * ingress. Registered only when reconciliation is enabled.\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 /**\n * Org-scoped agent tools: the read-only issue-detail tool for sessions whose\n * resource is a factory project (Jira credentials are deployment-global,\n * so no per-org connection gates apply). v1 exposes no mutating Jira tools.\n */\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildJiraAgentTools({ requestContext: args.requestContext, jira: this });\n }\n\n /** Non-secret config snapshot for system diagnostics/startup logs. */\n diagnostics(): Record<string, unknown> {\n return {\n configured: true,\n site: new URL(this.baseUrl).host,\n email: maskEmail(this.#config.email),\n };\n }\n}\n\n/** `ops@acme.test` → `o***@acme.test` — enough to identify, never the value. */\nfunction maskEmail(email: string): string {\n const at = email.indexOf('@');\n if (at <= 0) return '***';\n return `${email[0]}***${email.slice(at)}`;\n}\n\n/**\n * Build the intake JQL. Source ids and labels come from stored org config /\n * request input, so both are sanitized before interpolation: numeric project\n * ids pass through bare, project keys are restricted to a safe charset, and\n * label quotes/backslashes are stripped.\n */\nfunction buildIntakeJql(projectIds?: string[], labels?: string[]): string {\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 (safeProjects.length > 0) {\n clauses.push(`project IN (${safeProjects.join(', ')})`);\n }\n clauses.push('statusCategory != Done');\n const safeLabels = [...new Set((labels ?? []).map(label => label.trim().replace(/[\"\\\\]/g, '')).filter(Boolean))];\n if (safeLabels.length > 0) {\n clauses.push(`labels IN (${safeLabels.map(label => `\"${label}\"`).join(', ')})`);\n }\n return `${clauses.join(' AND ')} ORDER BY updated DESC`;\n}\n\n/** Whether the issue is already in the requested target state. */\nfunction currentStatusMatches(issue: JiraIssue, target: UpdateIntakeIssueInput['state']): boolean {\n const status = issue.fields.status;\n if (!status) return false;\n if (target.kind === 'byName') {\n return status.name.toLowerCase() === target.name.toLowerCase();\n }\n if (target.stateType === 'canceled') {\n return status.statusCategory?.key === 'done' && /cancel/i.test(status.name);\n }\n return status.statusCategory?.key === STATE_TYPE_TO_CATEGORY[target.stateType];\n}\n\n/** Resolve the target state against the issue's currently-legal transitions. */\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":["#config","#orgIdByResourceId","#rules","#projects","#auth","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#toIntakeIssue","#listAllComments"],"sources":["../../../src/integrations/jira/integration.ts"],"sourcesContent":["/**\n * `JiraIntegration` — the self-contained Jira Cloud integration.\n *\n * Implements the system-wide `FactoryIntegration` contract\n * (`../base.ts`): the deploy entry reads the `JIRA_*` env vars ONCE,\n * constructs an instance with explicit credentials, and passes it to\n * `MastraFactory`. Everything Jira-flavored the system does — project/issue\n * reads for Intake and the agent's issue tools — flows through this\n * instance. No other module reads `JIRA_*` env vars.\n *\n * Unlike Linear, credentials are **deployment-global** (Basic auth with\n * `email:apiToken` from the environment): there is no OAuth flow, no state\n * signer, and no per-org connection storage. The `IntegrationConnection`\n * arguments required by the `Intake` contract are accepted but not consulted\n * — the instance's own credentials authenticate every call.\n */\n\nimport 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 './adf.js';\nimport { buildJiraAgentTools } from './agent-tools.js';\nimport type { JiraComment, JiraIssue, JiraTransition } from './api.js';\nimport { JiraApiClient, JiraApiError } from './api.js';\nimport type { JiraEventRules, JiraRuleOverrides } from './default-rules.js';\nimport { resolveJiraRules } from './default-rules.js';\nimport { attachJiraIssueReconciler } from './issue-reconciler.js';\nimport { jiraReconciliationEnabled, jiraReconciliationInterval } from './reconciliation-config.js';\nimport { buildJiraRoutes } from './routes.js';\nimport { attachJiraRules } from './rules.js';\n\n/** Deployment-global Jira Cloud credentials. All credential fields are required. */\nexport interface JiraIntegrationConfig {\n /** Site base URL, e.g. `https://acme.atlassian.net`. */\n baseUrl: string;\n /** Atlassian account email the API token belongs to. */\n email: string;\n /** API token from id.atlassian.com → Security → API tokens. */\n apiToken: string;\n /** Per-event replacements; omitted events retain defaults, null disables. */\n rules?: JiraRuleOverrides;\n}\n\n/** Hard stop for comment pagination so a misbehaving `total` can't loop forever. */\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;\n\n/**\n * The `Intake` contract requires a connection argument, but Jira credentials\n * live on the integration instance — this inert placeholder is accepted and\n * ignored everywhere, and keeps the real API token out of resolved dispatches\n * and agent-tool calls.\n */\nexport const DEPLOYMENT_CONNECTION: IntegrationConnection = { type: 'oauth', accessToken: 'deployment-global' };\n\n/** Jira issue keys look like `ENG-42`. */\nconst ISSUE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*-\\d+$/;\n\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Capability state types (`capabilities/intake.ts`) → Jira status-category\n * keys. `canceled` has no Jira category — it is resolved separately against\n * `done`-category transitions whose status name contains \"cancel\".\n */\nconst STATE_TYPE_TO_CATEGORY: Record<'unstarted' | 'started' | 'completed', string> = {\n unstarted: 'new',\n started: 'indeterminate',\n completed: 'done',\n};\n\n/** Jira status-category keys → capability state types. */\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 class JiraIntegration implements FactoryIntegration {\n /** Stable integration identifier (see `../base.ts`). */\n readonly id = 'jira';\n\n readonly #config: JiraIntegrationConfig;\n /** Typed REST client bound to the deployment credentials. */\n readonly api: JiraApiClient;\n\n /** Bound once by the factory via `initialize()` before any surface is used. */\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: JiraIntegrationConfig) {\n this.#rules = resolveJiraRules(config.rules);\n // JiraApiClient validates the credential group and normalizes the URL.\n this.api = new JiraApiClient(config);\n this.#config = config;\n }\n\n /**\n * Bind the projects domain and the host auth seam. Jira has no per-org\n * connection rows, so the generic storage handle is unused — credentials\n * are deployment-global constructor config.\n */\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\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('JiraIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#projects;\n }\n\n /**\n * Whether the host runs with web auth enabled. Intake selections are\n * org-owned, so every Jira surface is inert without a tenant auth seam.\n */\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 Linear 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 // Non-UUID resource ids (local/dev resources) would make the uuid column\n // comparison throw — they're definitively \"not a project\", so cache that.\n if (!UUID_PATTERN.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 /** Test hook: clear the org cache between specs. */\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n /** Normalized site base URL (no trailing slash) — `/browse/<key>` links hang off this. */\n get baseUrl(): string {\n return this.api.baseUrl;\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 /**\n * Background-dispatch context. Credentials are deployment-global, so the\n * returned connection is a formality the Jira intake methods ignore; the\n * issue key is stored directly as the work item's `externalId` (mirroring\n * Linear, which stores the issue UUID).\n */\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n return {\n // Inert placeholder, never the real token: the Jira intake methods read\n // credentials from the client, and the resolved dispatch travels through\n // generic reconciler/dispatcher code that must stay secret-free.\n connection: DEPLOYMENT_CONNECTION,\n issueId: externalSource.externalId,\n };\n }\n\n /** All the site's projects (Settings intake-source picker), following `isLast` paging. */\n async #listSources(): Promise<IntakeSource[]> {\n const sources: IntakeSource[] = [];\n let startAt = 0;\n for (let pageCount = 0; pageCount < PROJECT_SEARCH_MAX_PAGES; pageCount++) {\n const page = await this.api.listProjects({ startAt });\n for (const project of page.values) {\n sources.push({\n id: project.id,\n name: project.name,\n type: 'project',\n metadata: { key: project.key },\n });\n }\n if (page.isLast || page.values.length === 0) break;\n startAt += page.values.length;\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.identifier, 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.identifier,\n state: issue.state,\n stateType: issue.stateType,\n priority: issue.priority,\n project: issue.source,\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 /**\n * One page of active issues (status category not Done), most recently\n * updated first, optionally filtered to projects and labels. The\n * capability cursor is Jira's opaque `nextPageToken` passed through.\n */\n async listActiveIssues(\n cursor?: string,\n projectIds?: string[],\n labels?: string[],\n ): Promise<{ issues: Array<IntakeIssue & { sourceId: string }>; nextCursor: string | null }> {\n const page = await this.api.searchIssues({\n jql: buildIntakeJql(projectIds, labels),\n ...(cursor ? { nextPageToken: cursor } : {}),\n });\n return {\n issues: page.issues.map(issue => ({\n ...this.#toIntakeIssue(issue),\n sourceId: issue.fields.project?.id ?? '',\n })),\n nextCursor: page.nextPageToken ?? null,\n };\n }\n\n async #getIssue({ issueId }: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n let issue: JiraIssue;\n try {\n issue = await this.api.getIssue(issueId);\n } catch (err) {\n if (err instanceof JiraApiError && err.status === 404) return null;\n throw err;\n }\n const { comments, total } = await this.#listAllComments(issue.key);\n return {\n ...this.#toIntakeIssue(issue),\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 /** Every comment on the issue, oldest first, bounded by {@link ISSUE_COMMENTS_MAX_PAGES}. */\n async #listAllComments(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 this.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({ issueId, body }: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n // The browse URL needs the issue key; resolve it when the caller passed a\n // numeric id instead.\n let key = issueId;\n if (!ISSUE_KEY_PATTERN.test(issueId)) {\n try {\n key = (await this.api.getIssue(issueId)).key;\n } catch (err) {\n if (err instanceof JiraApiError && err.status === 404) return null;\n throw err;\n }\n }\n let comment;\n try {\n comment = await this.api.createComment(key, body);\n } catch (err) {\n if (err instanceof JiraApiError && err.status === 404) return null;\n throw err;\n }\n return {\n id: comment.id,\n url: `${this.baseUrl}/browse/${key}?focusedCommentId=${comment.id}`,\n };\n }\n\n /**\n * Move an issue to a target state by applying a workflow transition. Jira\n * only exposes transitions that are currently legal, so a policy miss (no\n * transition reaches the target) returns `null` per the `Intake` contract —\n * infrastructure errors (network, auth) still throw.\n */\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n let issue: JiraIssue;\n try {\n issue = await this.api.getIssue(input.issueId);\n } catch (err) {\n if (err instanceof JiraApiError && err.status === 404) return null;\n throw err;\n }\n if (currentStatusMatches(issue, input.state)) {\n return this.#toIntakeIssue(issue);\n }\n const transitions = await this.api.listTransitions(issue.key);\n const transition = resolveTransition(transitions, input.state);\n if (!transition) return null;\n await this.api.applyTransition(issue.key, transition.id);\n const fresh = await this.api.getIssue(issue.key);\n return this.#toIntakeIssue(fresh);\n }\n\n #toIntakeIssue(issue: JiraIssue): IntakeIssue {\n const { fields } = issue;\n return {\n id: issue.id,\n identifier: issue.key,\n title: fields.summary ?? issue.key,\n url: `${this.baseUrl}/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 sourceId: fields.project?.id ?? null,\n labels: fields.labels ?? [],\n commentCount: null,\n createdAt: fields.created ?? '',\n updatedAt: fields.updated ?? '',\n };\n }\n\n // ── FactoryIntegration surface ───────────────────────────────────────────\n\n /**\n * The integration's HTTP surface: `/web/jira/*` Mastra `apiRoutes` (status,\n * projects + issues for Intake). No OAuth routes — credentials are\n * deployment-global. Handlers operate on this instance.\n */\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildJiraRoutes({\n jira: this,\n auth: ctx.auth,\n intake: ctx.storage.intake,\n ingestFactoryIssues: attachJiraRules(this, ctx),\n });\n }\n\n /**\n * Background reconciliation, matching the Platform integration: keeps filed\n * Jira cards' metadata fresh and replays close events through the rules\n * ingress. Registered only when reconciliation is enabled.\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 /**\n * Org-scoped agent tools: the read-only issue-detail tool for sessions whose\n * resource is a factory project (Jira credentials are deployment-global,\n * so no per-org connection gates apply). v1 exposes no mutating Jira tools.\n */\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildJiraAgentTools({ requestContext: args.requestContext, jira: this });\n }\n\n /** Non-secret config snapshot for system diagnostics/startup logs. */\n diagnostics(): Record<string, unknown> {\n return {\n configured: true,\n site: new URL(this.baseUrl).host,\n email: maskEmail(this.#config.email),\n };\n }\n}\n\n/** `ops@acme.test` → `o***@acme.test` — enough to identify, never the value. */\nfunction maskEmail(email: string): string {\n const at = email.indexOf('@');\n if (at <= 0) return '***';\n return `${email[0]}***${email.slice(at)}`;\n}\n\n/**\n * Build the intake JQL. Source ids and labels come from stored org config /\n * request input, so both are sanitized before interpolation: numeric project\n * ids pass through bare, project keys are restricted to a safe charset, and\n * label quotes/backslashes are stripped.\n */\nfunction buildIntakeJql(projectIds?: string[], labels?: string[]): string {\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 (safeProjects.length > 0) {\n clauses.push(`project IN (${safeProjects.join(', ')})`);\n }\n clauses.push('statusCategory != Done');\n const safeLabels = [...new Set((labels ?? []).map(label => label.trim().replace(/[\"\\\\]/g, '')).filter(Boolean))];\n if (safeLabels.length > 0) {\n clauses.push(`labels IN (${safeLabels.map(label => `\"${label}\"`).join(', ')})`);\n }\n return `${clauses.join(' AND ')} ORDER BY updated DESC`;\n}\n\n/** Whether the issue is already in the requested target state. */\nfunction currentStatusMatches(issue: JiraIssue, target: UpdateIntakeIssueInput['state']): boolean {\n const status = issue.fields.status;\n if (!status) return false;\n if (target.kind === 'byName') {\n return status.name.toLowerCase() === target.name.toLowerCase();\n }\n if (target.stateType === 'canceled') {\n return status.statusCategory?.key === 'done' && /cancel/i.test(status.name);\n }\n return status.statusCategory?.key === STATE_TYPE_TO_CATEGORY[target.stateType];\n}\n\n/** Resolve the target state against the issue's currently-legal transitions. */\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,8 +1,8 @@
1
1
  import { createGithubSubscriptionTools, parseCreatedPullRequest, subscribeCurrentSessionToPullRequest } from "../../github/session-subscriptions.js";
2
+ import { attachGithubReconciler, attachGithubRules } from "../../github/rules.js";
2
3
  import { PlatformApiClient, PlatformApiError, logPlatformInfo, logPlatformWarn, platformApiClientConfigFromEnv } from "../api-client.js";
3
4
  import { GithubAppIdentity } from "../../github/app-identity.js";
4
5
  import { resolveGithubRules } from "../../github/default-rules.js";
5
- import { attachGithubReconciler, attachGithubRules } from "../../github/rules.js";
6
6
  import { attachGithubIssueReconciler } from "../../github/issue-reconciler.js";
7
7
  import { reconcileInterval, reconciliationEnabled } from "../../github/reconciliation-config.js";
8
8
  import { parseAuthorizedBotsEnv } from "../../github/webhook.js";
@@ -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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"}
@@ -264,7 +264,13 @@ var PlatformJiraIntegration = class {
264
264
  if (error instanceof JiraApiError && error.status === 404) return null;
265
265
  throw error;
266
266
  }
267
- const comment = await resolved.context.api.createComment(key, input.body);
267
+ let comment;
268
+ try {
269
+ comment = await resolved.context.api.createComment(key, input.body);
270
+ } catch (error) {
271
+ if (error instanceof JiraApiError && error.status === 404) return null;
272
+ throw error;
273
+ }
268
274
  return {
269
275
  id: comment.id,
270
276
  url: `${resolved.context.siteUrl}/browse/${key}?focusedCommentId=${comment.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 const comment = await resolved.context.api.createComment(key, input.body);\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.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,8 +1,4 @@
1
- /**
2
- * Per-kind attention providers. Each kind owns its counts, its bounded page
3
- * scan, and its wire item shape; the route layer k-way merges provider pages
4
- * on `occurredAt desc` with one resumable cursor per kind.
5
- */
1
+ import type { BoardRegistry } from '../boards/index.js';
6
2
  import type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';
7
3
  import type { FactoryAttentionIdentity, FactoryAttentionKind, FactoryAttentionReceiptRecord, FactoryDeferredDecisionRecord, FactoryDispatchStatus, WorkItemRow, WorkItemsStorage } from '../storage/domains/work-items/base.js';
8
4
  export type FactoryAttentionView = 'open' | 'unread' | 'archived';
@@ -89,14 +85,17 @@ export interface DecisionAttentionSpec {
89
85
  occurredAt(decision: FactoryDeferredDecisionRecord): Date;
90
86
  title(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined): string;
91
87
  detail(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined): string;
92
- extra?(decision: FactoryDeferredDecisionRecord): Record<string, unknown>;
88
+ extra?(decision: FactoryDeferredDecisionRecord, context: {
89
+ overtaken: boolean;
90
+ }): Record<string, unknown>;
93
91
  matches(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined, search: string): boolean;
94
92
  }
95
93
  export declare class DecisionAttentionProvider implements AttentionProvider {
96
94
  #private;
97
95
  readonly kind: FactoryAttentionKind;
98
- constructor({ workItems }: {
96
+ constructor({ workItems, boards }: {
99
97
  workItems: WorkItemsStorage;
98
+ boards: BoardRegistry;
100
99
  }, spec: DecisionAttentionSpec);
101
100
  counts(scope: AttentionScope): Promise<AttentionCounts>;
102
101
  latest(scope: AttentionScope): Promise<AttentionLatest | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"attention-providers.d.ts","sourceRoot":"","sources":["../../src/routes/attention-providers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attention-providers.d.ts","sourceRoot":"","sources":["../../src/routes/attention-providers.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { factoryAttentionKey, factoryDecisionAttentionIdentity, factoryMentionAttentionIdentity, factorySupervisorFindingAttentionIdentity } from "../storage/domains/work-items/base.js";
2
2
  import { factoryDispatchFailureMetadata } from "../rules/dispatch-errors.js";
3
+ import { overtakenDecisionIds } from "../rules/decision-applicability.js";
3
4
  //#region src/routes/attention-providers.ts
4
5
  /**
5
6
  * Per-kind attention providers. Each kind owns its counts, its bounded page
@@ -91,8 +92,10 @@ var DecisionAttentionProvider = class {
91
92
  kind;
92
93
  #workItems;
93
94
  #spec;
94
- constructor({ workItems }, spec) {
95
+ #boards;
96
+ constructor({ workItems, boards }, spec) {
95
97
  this.#workItems = workItems;
98
+ this.#boards = boards;
96
99
  this.#spec = spec;
97
100
  this.kind = spec.kind;
98
101
  }
@@ -157,7 +160,11 @@ var DecisionAttentionProvider = class {
157
160
  }
158
161
  async page(scope, { view, search, before, limit }) {
159
162
  return collectPage(this.#scan(scope, before), limit, async (decisions) => {
160
- const [receiptByKey, itemById] = await Promise.all([this.#receiptsFor(scope, decisions), this.#linkedItems(scope, decisions)]);
163
+ const [receiptByKey, itemById, overtaken] = await Promise.all([
164
+ this.#receiptsFor(scope, decisions),
165
+ this.#linkedItems(scope, decisions),
166
+ overtakenDecisionIds(this.#workItems, this.#boards, scope, decisions)
167
+ ]);
161
168
  const entries = [];
162
169
  for (const decision of decisions) {
163
170
  const identity = this.#spec.identity(decision);
@@ -181,7 +188,7 @@ var DecisionAttentionProvider = class {
181
188
  title: this.#spec.title(decision, item),
182
189
  detail: this.#spec.detail(decision, item),
183
190
  decisionType: factoryDecisionType(decision),
184
- ...this.#spec.extra?.(decision),
191
+ ...this.#spec.extra?.(decision, { overtaken: overtaken.has(decision.id) }),
185
192
  occurredAt: occurredAt.toISOString(),
186
193
  read: receipt !== void 0,
187
194
  archived: receipt?.state === "archived",
@@ -224,9 +231,9 @@ const failedDecisionAttentionSpec = {
224
231
  occurredAt: (decision) => decision.completedAt ?? decision.updatedAt,
225
232
  title: (decision, item) => item?.title ?? factoryDispatchFailureMetadata(decision.failureCode).label,
226
233
  detail: (decision) => decision.lastError?.slice(0, 512) ?? factoryDispatchFailureMetadata(decision.failureCode).label,
227
- extra: (decision) => ({
234
+ extra: (decision, { overtaken }) => ({
228
235
  failureCode: decision.failureCode,
229
- canRetry: factoryDispatchFailureMetadata(decision.failureCode).canRetry
236
+ canRetry: !overtaken && factoryDispatchFailureMetadata(decision.failureCode).canRetry
230
237
  }),
231
238
  matches: (decision, item, search) => item?.title.toLowerCase().includes(search) === true || decision.lastError?.toLowerCase().includes(search) === true || factoryDecisionType(decision).toLowerCase().includes(search)
232
239
  };