@mastra/factory 0.16.0-alpha.6 → 0.16.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +2 -1
  2. package/dist/boards/work.d.ts.map +1 -1
  3. package/dist/boards/work.js +20 -3
  4. package/dist/boards/work.js.map +1 -1
  5. package/dist/factory.d.ts +5 -3
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +13 -5
  8. package/dist/factory.js.map +1 -1
  9. package/dist/integrations/jira/adf.d.ts +26 -0
  10. package/dist/integrations/jira/adf.d.ts.map +1 -0
  11. package/dist/integrations/jira/adf.js +73 -0
  12. package/dist/integrations/jira/adf.js.map +1 -0
  13. package/dist/integrations/jira/agent-tools.d.ts +45 -0
  14. package/dist/integrations/jira/agent-tools.d.ts.map +1 -0
  15. package/dist/integrations/jira/agent-tools.js +87 -0
  16. package/dist/integrations/jira/agent-tools.js.map +1 -0
  17. package/dist/integrations/jira/api.d.ts +157 -0
  18. package/dist/integrations/jira/api.d.ts.map +1 -0
  19. package/dist/integrations/jira/api.js +141 -0
  20. package/dist/integrations/jira/api.js.map +1 -0
  21. package/dist/integrations/jira/claim.d.ts +9 -0
  22. package/dist/integrations/jira/claim.d.ts.map +1 -0
  23. package/dist/integrations/jira/claim.js +15 -0
  24. package/dist/integrations/jira/claim.js.map +1 -0
  25. package/dist/integrations/jira/default-rules.d.ts +47 -0
  26. package/dist/integrations/jira/default-rules.d.ts.map +1 -0
  27. package/dist/integrations/jira/default-rules.js +67 -0
  28. package/dist/integrations/jira/default-rules.js.map +1 -0
  29. package/dist/integrations/jira/integration.d.ts +114 -0
  30. package/dist/integrations/jira/integration.d.ts.map +1 -0
  31. package/dist/integrations/jira/integration.js +401 -0
  32. package/dist/integrations/jira/integration.js.map +1 -0
  33. package/dist/integrations/jira/issue-reconciler.d.ts +10 -0
  34. package/dist/integrations/jira/issue-reconciler.d.ts.map +1 -0
  35. package/dist/integrations/jira/issue-reconciler.js +70 -0
  36. package/dist/integrations/jira/issue-reconciler.js.map +1 -0
  37. package/dist/integrations/jira/reconciliation-config.d.ts +3 -0
  38. package/dist/integrations/jira/reconciliation-config.d.ts.map +1 -0
  39. package/dist/integrations/jira/reconciliation-config.js +17 -0
  40. package/dist/integrations/jira/reconciliation-config.js.map +1 -0
  41. package/dist/integrations/jira/routes.d.ts +54 -0
  42. package/dist/integrations/jira/routes.d.ts.map +1 -0
  43. package/dist/integrations/jira/routes.js +260 -0
  44. package/dist/integrations/jira/routes.js.map +1 -0
  45. package/dist/integrations/jira/rules.d.ts +57 -0
  46. package/dist/integrations/jira/rules.d.ts.map +1 -0
  47. package/dist/integrations/jira/rules.js +175 -0
  48. package/dist/integrations/jira/rules.js.map +1 -0
  49. package/dist/integrations/platform/connect/routes.d.ts +39 -0
  50. package/dist/integrations/platform/connect/routes.d.ts.map +1 -0
  51. package/dist/integrations/platform/connect/routes.js +123 -0
  52. package/dist/integrations/platform/connect/routes.js.map +1 -0
  53. package/dist/integrations/platform/github/event-worker.js +1 -1
  54. package/dist/integrations/platform/github/integration.js +1 -1
  55. package/dist/integrations/platform/jira/agent-tools.d.ts +36 -0
  56. package/dist/integrations/platform/jira/agent-tools.d.ts.map +1 -0
  57. package/dist/integrations/platform/jira/agent-tools.js +85 -0
  58. package/dist/integrations/platform/jira/agent-tools.js.map +1 -0
  59. package/dist/integrations/platform/jira/integration.d.ts +65 -0
  60. package/dist/integrations/platform/jira/integration.d.ts.map +1 -0
  61. package/dist/integrations/platform/jira/integration.js +540 -0
  62. package/dist/integrations/platform/jira/integration.js.map +1 -0
  63. package/dist/integrations/platform/jira/routes.d.ts +55 -0
  64. package/dist/integrations/platform/jira/routes.d.ts.map +1 -0
  65. package/dist/integrations/platform/jira/routes.js +274 -0
  66. package/dist/integrations/platform/jira/routes.js.map +1 -0
  67. package/dist/routes/surface.d.ts.map +1 -1
  68. package/dist/routes/surface.js +24 -4
  69. package/dist/routes/surface.js.map +1 -1
  70. package/dist/rules/dispatcher.d.ts.map +1 -1
  71. package/dist/rules/dispatcher.js +1 -1
  72. package/dist/rules/dispatcher.js.map +1 -1
  73. package/dist/rules/index.d.ts +2 -2
  74. package/dist/rules/index.d.ts.map +1 -1
  75. package/dist/rules/index.js +2 -2
  76. package/dist/rules/types.d.ts +30 -3
  77. package/dist/rules/types.d.ts.map +1 -1
  78. package/dist/rules/types.js +5 -1
  79. package/dist/rules/types.js.map +1 -1
  80. package/dist/rules/validation.d.ts.map +1 -1
  81. package/dist/rules/validation.js +1 -0
  82. package/dist/rules/validation.js.map +1 -1
  83. package/dist/work-item-branch.d.ts +1 -1
  84. package/dist/work-item-branch.d.ts.map +1 -1
  85. package/dist/work-item-branch.js +3 -2
  86. package/dist/work-item-branch.js.map +1 -1
  87. package/package.json +8 -8
@@ -0,0 +1,540 @@
1
+ import { PlatformApiClient, logPlatformInfo, platformApiClientConfigFromEnv } from "../api-client.js";
2
+ import { IssueReconcileWorker } from "../../issue-reconcile-worker.js";
3
+ import { adfToText } from "../../jira/adf.js";
4
+ import { JiraApiClient, JiraApiError } from "../../jira/api.js";
5
+ import { resolveJiraRules } from "../../jira/default-rules.js";
6
+ import { attachJiraRules } from "../../jira/rules.js";
7
+ import { attachJiraIssueReconciler } from "../../jira/issue-reconciler.js";
8
+ import { jiraReconciliationEnabled, jiraReconciliationInterval } from "../../jira/reconciliation-config.js";
9
+ import { buildPlatformJiraAgentTools } from "./agent-tools.js";
10
+ import { buildPlatformJiraRoutes } from "./routes.js";
11
+ //#region src/integrations/platform/jira/integration.ts
12
+ const ISSUE_COMMENTS_MAX_PAGES = 20;
13
+ /** Hard stop for project pagination so a misbehaving `isLast` can't loop forever. */
14
+ const PROJECT_SEARCH_MAX_PAGES = 50;
15
+ const ISSUE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*-\d+$/;
16
+ const PLATFORM_JIRA_PROVIDER_CONFIG_KEY = "jira";
17
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18
+ const JIRA_CONNECTION_TOKEN_PREFIX = "jira-connection:";
19
+ const JIRA_ISSUE_REF_PREFIX = "jira-issue:";
20
+ const JIRA_SOURCE_PREFIX = "jira-project:";
21
+ const STATE_TYPE_TO_CATEGORY = {
22
+ unstarted: "new",
23
+ started: "indeterminate",
24
+ completed: "done"
25
+ };
26
+ function stateTypeFromCategory(key) {
27
+ switch (key) {
28
+ case "new": return "unstarted";
29
+ case "indeterminate": return "started";
30
+ case "done": return "completed";
31
+ default: return null;
32
+ }
33
+ }
34
+ var PlatformJiraIntegration = class {
35
+ id = "jira";
36
+ #clientConfig;
37
+ #platformClient;
38
+ #endpointHost;
39
+ #cloudIdByConnectionId = /* @__PURE__ */ new Map();
40
+ #siteUrlByConnectionId = /* @__PURE__ */ new Map();
41
+ #projects;
42
+ #auth;
43
+ #orgIdByResourceId = /* @__PURE__ */ new Map();
44
+ #rules;
45
+ get rules() {
46
+ return this.#rules;
47
+ }
48
+ constructor(config = {}) {
49
+ this.#rules = resolveJiraRules(config.rules);
50
+ this.#clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();
51
+ this.#platformClient = new PlatformApiClient(this.#clientConfig);
52
+ this.#endpointHost = new URL(this.#clientConfig.baseUrl).host;
53
+ }
54
+ initialize({ projects, auth }) {
55
+ this.#projects = projects;
56
+ this.#auth = auth;
57
+ logPlatformInfo("Platform Jira integration initialized", { endpointHost: this.#endpointHost });
58
+ }
59
+ get projects() {
60
+ if (!this.#projects) throw new Error("PlatformJiraIntegration is not initialized — the factory binds storage during prepare().");
61
+ return this.#projects;
62
+ }
63
+ get authEnabled() {
64
+ return this.#auth?.enabled() ?? false;
65
+ }
66
+ async resolveOrgId(resourceId) {
67
+ const cached = this.#orgIdByResourceId.get(resourceId);
68
+ if (cached !== void 0) return cached;
69
+ if (!UUID_PATTERN.test(resourceId)) {
70
+ this.#orgIdByResourceId.set(resourceId, null);
71
+ return null;
72
+ }
73
+ try {
74
+ await this.projects.ensureReady();
75
+ const orgId = (await this.projects.getById({ id: resourceId }))?.orgId ?? null;
76
+ this.#orgIdByResourceId.set(resourceId, orgId);
77
+ return orgId;
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+ clearCaches() {
83
+ this.#orgIdByResourceId.clear();
84
+ this.#cloudIdByConnectionId.clear();
85
+ this.#siteUrlByConnectionId.clear();
86
+ }
87
+ async listConnections() {
88
+ const providerKey = encodeURIComponent(PLATFORM_JIRA_PROVIDER_CONFIG_KEY);
89
+ return (await this.#platformClient.request("GET", `/v2/connections?providerKey=${providerKey}`)).connections;
90
+ }
91
+ async hasActiveConnections() {
92
+ return (await this.#activeConnections()).length > 0;
93
+ }
94
+ intake = {
95
+ resolveIntakeDispatch: (input) => this.#resolveIntakeDispatch(input),
96
+ listSources: () => this.#listSources(),
97
+ listItems: (input) => this.#listItems(input),
98
+ listIssues: (input) => this.#listIssues(input),
99
+ getIssue: (input) => this.#getIssue(input),
100
+ createComment: (input) => this.#createComment(input),
101
+ updateIssue: (input) => this.#updateIssue(input)
102
+ };
103
+ async #resolveIntakeDispatch({ externalSource }) {
104
+ if (externalSource.type !== "issue") return null;
105
+ const reference = decodeIssueReference(externalSource.externalId);
106
+ if (!reference) return null;
107
+ return {
108
+ connection: jiraConnection(reference.connectionId),
109
+ ...reference.projectId ? { sourceId: encodeSourceId(reference.connectionId, reference.projectId) } : {},
110
+ issueId: reference.issueId
111
+ };
112
+ }
113
+ async #listSources() {
114
+ const sources = [];
115
+ for (const connection of await this.#activeConnections()) {
116
+ const context = await this.#connectionContext(connection);
117
+ let startAt = 0;
118
+ for (let pageCount = 0; pageCount < PROJECT_SEARCH_MAX_PAGES; pageCount++) {
119
+ const page = await context.api.listProjects({ startAt });
120
+ for (const project of page.values) sources.push({
121
+ id: encodeSourceId(connection.id, project.id),
122
+ name: project.name,
123
+ type: "project",
124
+ metadata: {
125
+ key: project.key,
126
+ connectionId: connection.id,
127
+ site: new URL(context.siteUrl).host
128
+ }
129
+ });
130
+ if (page.isLast || page.values.length === 0) break;
131
+ startAt += page.values.length;
132
+ }
133
+ }
134
+ return sources;
135
+ }
136
+ async #listItems({ sourceIds, cursor }) {
137
+ if (sourceIds.length === 0) return {
138
+ items: [],
139
+ nextCursor: null
140
+ };
141
+ const page = await this.listActiveIssues(cursor, sourceIds);
142
+ return {
143
+ items: page.issues.map((issue) => ({
144
+ source: {
145
+ type: "issue",
146
+ externalId: issue.externalId,
147
+ url: issue.url
148
+ },
149
+ sourceId: issue.sourceId,
150
+ title: `${issue.identifier}: ${issue.title}`,
151
+ status: issue.state ?? void 0,
152
+ labels: issue.labels,
153
+ assignee: issue.assignee,
154
+ createdAt: issue.createdAt,
155
+ updatedAt: issue.updatedAt,
156
+ metadata: {
157
+ identifier: issue.identifier,
158
+ issueRef: issue.externalId,
159
+ state: issue.state,
160
+ stateType: issue.stateType,
161
+ priority: issue.priority,
162
+ project: issue.source,
163
+ site: issue.site,
164
+ assignee: issue.assignee,
165
+ assignees: issue.assignees ?? [],
166
+ creator: issue.author,
167
+ author: issue.author,
168
+ labels: issue.labels,
169
+ createdAt: issue.createdAt,
170
+ updatedAt: issue.updatedAt
171
+ }
172
+ })),
173
+ nextCursor: page.nextCursor
174
+ };
175
+ }
176
+ async #listIssues(input) {
177
+ const page = await this.listActiveIssues(input.cursor, input.sourceIds, input.labels);
178
+ return {
179
+ issues: page.issues,
180
+ nextCursor: page.nextCursor
181
+ };
182
+ }
183
+ async listActiveIssues(cursor, sourceIds = [], labels) {
184
+ const grouped = groupSources(sourceIds);
185
+ if (grouped.length === 0) return {
186
+ issues: [],
187
+ nextCursor: null
188
+ };
189
+ const decodedCursor = decodePageCursor(cursor);
190
+ if (decodedCursor.group >= grouped.length) return {
191
+ issues: [],
192
+ nextCursor: null
193
+ };
194
+ for (let groupIndex = decodedCursor.group; groupIndex < grouped.length; groupIndex++) {
195
+ const group = grouped[groupIndex];
196
+ const context = await this.#connectionContextById(group.connectionId);
197
+ const jql = buildIntakeJql(group.projectIds, labels);
198
+ if (jql === null) continue;
199
+ const page = await context.api.searchIssues({
200
+ jql,
201
+ ...groupIndex === decodedCursor.group && decodedCursor.jira ? { nextPageToken: decodedCursor.jira } : {}
202
+ });
203
+ const nextCursor = page.nextPageToken ? encodePageCursor({
204
+ group: groupIndex,
205
+ jira: page.nextPageToken
206
+ }) : groupIndex + 1 < grouped.length ? encodePageCursor({ group: groupIndex + 1 }) : null;
207
+ return {
208
+ issues: page.issues.map((issue) => {
209
+ const projectId = issue.fields.project?.id ?? group.projectIds[0] ?? "";
210
+ return {
211
+ ...this.#toIntakeIssue(issue, context.siteUrl),
212
+ sourceId: encodeSourceId(group.connectionId, projectId),
213
+ externalId: encodeIssueReference({
214
+ connectionId: group.connectionId,
215
+ issueId: issue.key,
216
+ projectId
217
+ }),
218
+ site: new URL(context.siteUrl).host
219
+ };
220
+ }),
221
+ nextCursor
222
+ };
223
+ }
224
+ return {
225
+ issues: [],
226
+ nextCursor: null
227
+ };
228
+ }
229
+ async #getIssue(input) {
230
+ const { context, issue } = await this.#findIssue(input) ?? {};
231
+ if (!context || !issue) return null;
232
+ const { comments, total } = await this.#listAllComments(context.api, issue.key);
233
+ return {
234
+ ...this.#toIntakeIssue(issue, context.siteUrl),
235
+ commentCount: total,
236
+ description: issue.fields.description ? adfToText(issue.fields.description) || null : null,
237
+ comments: comments.map((comment) => ({
238
+ author: comment.author?.displayName ?? null,
239
+ body: adfToText(comment.body),
240
+ createdAt: comment.created
241
+ }))
242
+ };
243
+ }
244
+ async #listAllComments(api, keyOrId) {
245
+ const comments = [];
246
+ let total = 0;
247
+ for (let page = 0; page < ISSUE_COMMENTS_MAX_PAGES; page++) {
248
+ const result = await api.listComments(keyOrId, { startAt: comments.length });
249
+ comments.push(...result.comments);
250
+ total = result.total;
251
+ if (result.comments.length === 0 || comments.length >= total) break;
252
+ }
253
+ return {
254
+ comments,
255
+ total
256
+ };
257
+ }
258
+ async #createComment(input) {
259
+ const resolved = await this.#resolveRequest(input);
260
+ let key = resolved.issueId;
261
+ if (!ISSUE_KEY_PATTERN.test(key)) try {
262
+ key = (await resolved.context.api.getIssue(key)).key;
263
+ } catch (error) {
264
+ if (error instanceof JiraApiError && error.status === 404) return null;
265
+ throw error;
266
+ }
267
+ const comment = await resolved.context.api.createComment(key, input.body);
268
+ return {
269
+ id: comment.id,
270
+ url: `${resolved.context.siteUrl}/browse/${key}?focusedCommentId=${comment.id}`
271
+ };
272
+ }
273
+ async #updateIssue(input) {
274
+ const resolved = await this.#resolveRequest(input);
275
+ let issue;
276
+ try {
277
+ issue = await resolved.context.api.getIssue(resolved.issueId);
278
+ } catch (error) {
279
+ if (error instanceof JiraApiError && error.status === 404) return null;
280
+ throw error;
281
+ }
282
+ if (currentStatusMatches(issue, input.state)) return this.#toIntakeIssue(issue, resolved.context.siteUrl);
283
+ const transition = resolveTransition(await resolved.context.api.listTransitions(issue.key), input.state);
284
+ if (!transition) return null;
285
+ await resolved.context.api.applyTransition(issue.key, transition.id);
286
+ return this.#toIntakeIssue(await resolved.context.api.getIssue(issue.key), resolved.context.siteUrl);
287
+ }
288
+ async #resolveRequest(input) {
289
+ const issueReference = decodeIssueReference(input.issueId);
290
+ const source = input.sourceId ? decodeSourceId(input.sourceId) : null;
291
+ const connectionId = issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);
292
+ const issueId = issueReference?.issueId ?? input.issueId;
293
+ if (connectionId) return {
294
+ context: await this.#connectionContextById(connectionId),
295
+ issueId
296
+ };
297
+ const active = await this.#activeConnections();
298
+ if (active.length !== 1) throw new JiraApiError("Jira issue reference must identify a connection when multiple sites are connected.", 400);
299
+ return {
300
+ context: await this.#connectionContext(active[0]),
301
+ issueId
302
+ };
303
+ }
304
+ /**
305
+ * Locate an issue for a read. A connection-scoped reference resolves
306
+ * directly; a plain issue key (the agent tool's normal input) is looked up
307
+ * across every active connection so multi-site organizations can still
308
+ * fetch by key — the first site that knows the key wins, which is safe
309
+ * because Jira project-key prefixes make cross-site collisions unlikely and
310
+ * the tool is read-only.
311
+ */
312
+ async #findIssue(input) {
313
+ const issueReference = decodeIssueReference(input.issueId);
314
+ const source = input.sourceId ? decodeSourceId(input.sourceId) : null;
315
+ const connectionId = issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);
316
+ const issueId = issueReference?.issueId ?? input.issueId;
317
+ const candidates = [];
318
+ if (connectionId) candidates.push(await this.#connectionContextById(connectionId));
319
+ else {
320
+ const active = await this.#activeConnections();
321
+ if (active.length === 0) throw new JiraApiError("Jira connection is unavailable or requires reauthentication.", 401);
322
+ for (const connection of active) candidates.push(await this.#connectionContext(connection));
323
+ }
324
+ let firstError;
325
+ for (const context of candidates) try {
326
+ return {
327
+ context,
328
+ issue: await context.api.getIssue(issueId)
329
+ };
330
+ } catch (error) {
331
+ if (error instanceof JiraApiError && error.status === 404) continue;
332
+ firstError ??= error;
333
+ }
334
+ if (firstError !== void 0) throw firstError;
335
+ return null;
336
+ }
337
+ #toIntakeIssue(issue, siteUrl) {
338
+ const { fields } = issue;
339
+ return {
340
+ id: issue.id,
341
+ identifier: issue.key,
342
+ title: fields.summary ?? issue.key,
343
+ url: `${siteUrl}/browse/${issue.key}`,
344
+ author: fields.reporter?.displayName ?? null,
345
+ state: fields.status?.name ?? null,
346
+ stateType: stateTypeFromCategory(fields.status?.statusCategory?.key),
347
+ priority: fields.priority?.name ?? null,
348
+ assignee: fields.assignee?.displayName ?? null,
349
+ source: fields.project?.key ?? null,
350
+ labels: fields.labels ?? [],
351
+ commentCount: null,
352
+ createdAt: fields.created ?? "",
353
+ updatedAt: fields.updated ?? ""
354
+ };
355
+ }
356
+ async #activeConnections() {
357
+ return (await this.listConnections()).filter((connection) => connection.status === "active");
358
+ }
359
+ async #connectionContextById(connectionId) {
360
+ const connection = (await this.#activeConnections()).find((candidate) => candidate.id === connectionId);
361
+ if (!connection) throw new JiraApiError("Jira connection is unavailable or requires reauthentication.", 401);
362
+ return this.#connectionContext(connection);
363
+ }
364
+ async #cloudId(connectionId) {
365
+ const cached = this.#cloudIdByConnectionId.get(connectionId);
366
+ if (cached) return cached;
367
+ const cloudId = (await this.#platformClient.request("GET", `/v2/connections/${encodeURIComponent(connectionId)}/context`)).connection_config?.cloudId;
368
+ if (typeof cloudId !== "string" || !UUID_PATTERN.test(cloudId)) throw new JiraApiError("Platform Jira connection context is missing a valid cloudId.", 502);
369
+ this.#cloudIdByConnectionId.set(connectionId, cloudId);
370
+ return cloudId;
371
+ }
372
+ async #connectionContext(connection) {
373
+ const proxyBaseUrl = `${this.#clientConfig.baseUrl.replace(/\/+$/, "")}/v2/connections/${encodeURIComponent(connection.id)}/proxy`;
374
+ const cloudId = await this.#cloudId(connection.id);
375
+ const api = new JiraApiClient({
376
+ baseUrl: `${proxyBaseUrl}/ex/jira/${encodeURIComponent(cloudId)}`,
377
+ accessToken: this.#clientConfig.accessToken,
378
+ ...this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}
379
+ });
380
+ let siteUrl = this.#siteUrlByConnectionId.get(connection.id);
381
+ if (!siteUrl) {
382
+ siteUrl = connection.accountLabel ? normalizeSiteUrl(connection.accountLabel) : normalizeSiteUrl((await api.getServerInfo()).baseUrl);
383
+ this.#siteUrlByConnectionId.set(connection.id, siteUrl);
384
+ }
385
+ return {
386
+ connection,
387
+ api,
388
+ siteUrl
389
+ };
390
+ }
391
+ workers(ctx) {
392
+ if (!jiraReconciliationEnabled()) return [];
393
+ const reconcile = attachJiraIssueReconciler(this, ctx);
394
+ if (!reconcile) return [];
395
+ const intervalMs = jiraReconciliationInterval();
396
+ return [new IssueReconcileWorker({
397
+ integrationId: this.id,
398
+ reconcile,
399
+ ...intervalMs ? { intervalMs } : {}
400
+ })];
401
+ }
402
+ routes(ctx) {
403
+ return buildPlatformJiraRoutes({
404
+ jira: this,
405
+ auth: ctx.auth,
406
+ intake: ctx.storage.intake,
407
+ appDbConfigured: Boolean(ctx.factoryStorage),
408
+ ingestFactoryIssues: attachJiraRules(this, ctx)
409
+ });
410
+ }
411
+ async agentTools(args) {
412
+ return buildPlatformJiraAgentTools({
413
+ requestContext: args.requestContext,
414
+ jira: this
415
+ });
416
+ }
417
+ diagnostics() {
418
+ return {
419
+ configured: true,
420
+ mode: "platform",
421
+ endpointHost: this.#endpointHost
422
+ };
423
+ }
424
+ };
425
+ function jiraConnection(connectionId) {
426
+ return {
427
+ type: "oauth",
428
+ accessToken: `${JIRA_CONNECTION_TOKEN_PREFIX}${connectionId}`
429
+ };
430
+ }
431
+ function connectionIdFromConnection(connection) {
432
+ if (connection.type !== "oauth" || !connection.accessToken.startsWith(JIRA_CONNECTION_TOKEN_PREFIX)) return null;
433
+ return connection.accessToken.slice(16) || null;
434
+ }
435
+ function encodeSourceId(connectionId, projectId) {
436
+ return `${JIRA_SOURCE_PREFIX}${Buffer.from(JSON.stringify({
437
+ connectionId,
438
+ projectId
439
+ })).toString("base64url")}`;
440
+ }
441
+ function decodeSourceId(sourceId) {
442
+ if (!sourceId.startsWith(JIRA_SOURCE_PREFIX)) return null;
443
+ try {
444
+ const parsed = JSON.parse(Buffer.from(sourceId.slice(13), "base64url").toString("utf8"));
445
+ return typeof parsed.connectionId === "string" && typeof parsed.projectId === "string" ? {
446
+ connectionId: parsed.connectionId,
447
+ projectId: parsed.projectId
448
+ } : null;
449
+ } catch {
450
+ return null;
451
+ }
452
+ }
453
+ function encodeIssueReference(reference) {
454
+ return `${JIRA_ISSUE_REF_PREFIX}${Buffer.from(JSON.stringify(reference)).toString("base64url")}`;
455
+ }
456
+ function decodeIssueReference(value) {
457
+ if (!value.startsWith(JIRA_ISSUE_REF_PREFIX)) return null;
458
+ try {
459
+ const parsed = JSON.parse(Buffer.from(value.slice(11), "base64url").toString("utf8"));
460
+ if (typeof parsed.connectionId !== "string" || typeof parsed.issueId !== "string") return null;
461
+ return {
462
+ connectionId: parsed.connectionId,
463
+ issueId: parsed.issueId,
464
+ ...typeof parsed.projectId === "string" ? { projectId: parsed.projectId } : {}
465
+ };
466
+ } catch {
467
+ return null;
468
+ }
469
+ }
470
+ function groupSources(sourceIds) {
471
+ const groups = /* @__PURE__ */ new Map();
472
+ for (const sourceId of sourceIds) {
473
+ const source = decodeSourceId(sourceId);
474
+ if (!source) continue;
475
+ const projectIds = groups.get(source.connectionId) ?? /* @__PURE__ */ new Set();
476
+ projectIds.add(source.projectId);
477
+ groups.set(source.connectionId, projectIds);
478
+ }
479
+ return [...groups.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([connectionId, projectIds]) => ({
480
+ connectionId,
481
+ projectIds: [...projectIds].sort()
482
+ }));
483
+ }
484
+ function encodePageCursor(cursor) {
485
+ return Buffer.from(JSON.stringify(cursor)).toString("base64url");
486
+ }
487
+ function decodePageCursor(cursor) {
488
+ if (!cursor) return { group: 0 };
489
+ try {
490
+ const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
491
+ if (!Number.isInteger(parsed.group) || parsed.group < 0) throw new Error();
492
+ return {
493
+ group: parsed.group,
494
+ ...typeof parsed.jira === "string" ? { jira: parsed.jira } : {}
495
+ };
496
+ } catch {
497
+ throw new JiraApiError("Jira pagination cursor is invalid.", 400);
498
+ }
499
+ }
500
+ function normalizeSiteUrl(value) {
501
+ const candidate = /^https?:\/\//i.test(value) ? value : `https://${value}`;
502
+ let url;
503
+ try {
504
+ url = new URL(candidate);
505
+ } catch {
506
+ throw new JiraApiError("Jira site URL is invalid.", 502);
507
+ }
508
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new JiraApiError("Jira site URL is invalid.", 502);
509
+ return url.origin;
510
+ }
511
+ function buildIntakeJql(projectIds, labels) {
512
+ const clauses = [];
513
+ const safeProjects = (projectIds ?? []).map((id) => id.trim()).filter((id) => /^[A-Za-z0-9_]+$/.test(id)).map((id) => /^\d+$/.test(id) ? id : `"${id}"`);
514
+ if (projectIds && safeProjects.length === 0) return null;
515
+ if (safeProjects.length > 0) clauses.push(`project IN (${safeProjects.join(", ")})`);
516
+ clauses.push("statusCategory != Done");
517
+ const safeLabels = [...new Set((labels ?? []).map((label) => label.trim().replace(/["\\]/g, "")).filter(Boolean))];
518
+ if (safeLabels.length > 0) clauses.push(`labels IN (${safeLabels.map((label) => `"${label}"`).join(", ")})`);
519
+ return `${clauses.join(" AND ")} ORDER BY updated DESC`;
520
+ }
521
+ function currentStatusMatches(issue, target) {
522
+ const status = issue.fields.status;
523
+ if (!status) return false;
524
+ if (target.kind === "byName") return status.name.toLowerCase() === target.name.toLowerCase();
525
+ if (target.stateType === "canceled") return status.statusCategory?.key === "done" && /cancel/i.test(status.name);
526
+ return status.statusCategory?.key === STATE_TYPE_TO_CATEGORY[target.stateType];
527
+ }
528
+ function resolveTransition(transitions, target) {
529
+ if (target.kind === "byName") {
530
+ const wanted = target.name.toLowerCase();
531
+ return transitions.find((transition) => transition.to?.name.toLowerCase() === wanted) ?? null;
532
+ }
533
+ if (target.stateType === "canceled") return transitions.find((transition) => transition.to?.statusCategory?.key === "done" && /cancel/i.test(transition.to.name)) ?? null;
534
+ const category = STATE_TYPE_TO_CATEGORY[target.stateType];
535
+ return transitions.find((transition) => transition.to?.statusCategory?.key === category) ?? null;
536
+ }
537
+ //#endregion
538
+ export { PlatformJiraIntegration, decodeIssueReference, decodeSourceId, encodeIssueReference, encodeSourceId };
539
+
540
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +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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"}