@mastra/factory 0.5.1-alpha.0 → 0.6.0-alpha.10

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 (163) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/dist/auth.d.ts +17 -0
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +19 -1
  5. package/dist/auth.js.map +1 -1
  6. package/dist/capabilities/intake.d.ts +1 -0
  7. package/dist/capabilities/intake.d.ts.map +1 -1
  8. package/dist/capabilities/version-control.d.ts +3 -0
  9. package/dist/capabilities/version-control.d.ts.map +1 -1
  10. package/dist/factory.d.ts +10 -0
  11. package/dist/factory.d.ts.map +1 -1
  12. package/dist/factory.js +17 -4
  13. package/dist/factory.js.map +1 -1
  14. package/dist/integrations/base.d.ts +6 -0
  15. package/dist/integrations/base.d.ts.map +1 -1
  16. package/dist/integrations/github/integration.d.ts +14 -1
  17. package/dist/integrations/github/integration.d.ts.map +1 -1
  18. package/dist/integrations/github/integration.js +61 -3
  19. package/dist/integrations/github/integration.js.map +1 -1
  20. package/dist/integrations/github/issue-reconciler.d.ts +24 -0
  21. package/dist/integrations/github/issue-reconciler.d.ts.map +1 -0
  22. package/dist/integrations/github/issue-reconciler.js +104 -0
  23. package/dist/integrations/github/issue-reconciler.js.map +1 -0
  24. package/dist/integrations/github/reconcile-worker.d.ts +8 -0
  25. package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
  26. package/dist/integrations/github/reconcile-worker.js +38 -1
  27. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  28. package/dist/integrations/github/routes.d.ts.map +1 -1
  29. package/dist/integrations/github/routes.js +65 -52
  30. package/dist/integrations/github/routes.js.map +1 -1
  31. package/dist/integrations/github/rules.d.ts +37 -2
  32. package/dist/integrations/github/rules.d.ts.map +1 -1
  33. package/dist/integrations/github/rules.js +150 -14
  34. package/dist/integrations/github/rules.js.map +1 -1
  35. package/dist/integrations/github/sandbox-release.d.ts +16 -1
  36. package/dist/integrations/github/sandbox-release.d.ts.map +1 -1
  37. package/dist/integrations/github/sandbox-release.js +42 -1
  38. package/dist/integrations/github/sandbox-release.js.map +1 -1
  39. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  40. package/dist/integrations/github/sandbox.js +74 -8
  41. package/dist/integrations/github/sandbox.js.map +1 -1
  42. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  43. package/dist/integrations/github/session-subscriptions.js +16 -12
  44. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  45. package/dist/integrations/issue-reconcile-worker.d.ts +19 -0
  46. package/dist/integrations/issue-reconcile-worker.d.ts.map +1 -0
  47. package/dist/integrations/issue-reconcile-worker.js +98 -0
  48. package/dist/integrations/issue-reconcile-worker.js.map +1 -0
  49. package/dist/integrations/issue-reconciler.d.ts +47 -0
  50. package/dist/integrations/issue-reconciler.d.ts.map +1 -0
  51. package/dist/integrations/issue-reconciler.js +104 -0
  52. package/dist/integrations/issue-reconciler.js.map +1 -0
  53. package/dist/integrations/linear/integration.d.ts +4 -0
  54. package/dist/integrations/linear/integration.d.ts.map +1 -1
  55. package/dist/integrations/linear/integration.js +19 -2
  56. package/dist/integrations/linear/integration.js.map +1 -1
  57. package/dist/integrations/linear/issue-reconciler.d.ts +6 -0
  58. package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -0
  59. package/dist/integrations/linear/issue-reconciler.js +64 -0
  60. package/dist/integrations/linear/issue-reconciler.js.map +1 -0
  61. package/dist/integrations/linear/routes.d.ts.map +1 -1
  62. package/dist/integrations/linear/routes.js +1 -0
  63. package/dist/integrations/linear/routes.js.map +1 -1
  64. package/dist/integrations/linear/rules.d.ts +2 -0
  65. package/dist/integrations/linear/rules.d.ts.map +1 -1
  66. package/dist/integrations/linear/rules.js +5 -2
  67. package/dist/integrations/linear/rules.js.map +1 -1
  68. package/dist/integrations/platform/github/event-worker.d.ts +7 -0
  69. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
  70. package/dist/integrations/platform/github/event-worker.js +24 -0
  71. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  72. package/dist/integrations/platform/github/integration.d.ts +13 -3
  73. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  74. package/dist/integrations/platform/github/integration.js +42 -1
  75. package/dist/integrations/platform/github/integration.js.map +1 -1
  76. package/dist/integrations/platform/linear/event-worker.d.ts +105 -0
  77. package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -0
  78. package/dist/integrations/platform/linear/event-worker.js +380 -0
  79. package/dist/integrations/platform/linear/event-worker.js.map +1 -0
  80. package/dist/integrations/platform/linear/integration.d.ts +9 -0
  81. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  82. package/dist/integrations/platform/linear/integration.js +37 -1
  83. package/dist/integrations/platform/linear/integration.js.map +1 -1
  84. package/dist/integrations/slack/integration.d.ts.map +1 -1
  85. package/dist/integrations/slack/integration.js +3 -2
  86. package/dist/integrations/slack/integration.js.map +1 -1
  87. package/dist/integrations/slack/slack.d.ts +37 -110
  88. package/dist/integrations/slack/slack.d.ts.map +1 -1
  89. package/dist/integrations/slack/slack.js +49 -44
  90. package/dist/integrations/slack/slack.js.map +1 -1
  91. package/dist/routes/config.d.ts +25 -0
  92. package/dist/routes/config.d.ts.map +1 -1
  93. package/dist/routes/config.js +63 -1
  94. package/dist/routes/config.js.map +1 -1
  95. package/dist/routes/fs.d.ts +16 -2
  96. package/dist/routes/fs.d.ts.map +1 -1
  97. package/dist/routes/fs.js +47 -5
  98. package/dist/routes/fs.js.map +1 -1
  99. package/dist/routes/intake.d.ts.map +1 -1
  100. package/dist/routes/intake.js +13 -5
  101. package/dist/routes/intake.js.map +1 -1
  102. package/dist/routes/surface.d.ts +13 -1
  103. package/dist/routes/surface.d.ts.map +1 -1
  104. package/dist/routes/surface.js +20 -9
  105. package/dist/routes/surface.js.map +1 -1
  106. package/dist/rules/binding-context.d.ts +28 -1
  107. package/dist/rules/binding-context.d.ts.map +1 -1
  108. package/dist/rules/binding-context.js +68 -3
  109. package/dist/rules/binding-context.js.map +1 -1
  110. package/dist/rules/defaults.d.ts.map +1 -1
  111. package/dist/rules/defaults.js +71 -5
  112. package/dist/rules/defaults.js.map +1 -1
  113. package/dist/rules/start-coordinator.d.ts.map +1 -1
  114. package/dist/rules/start-coordinator.js +8 -27
  115. package/dist/rules/start-coordinator.js.map +1 -1
  116. package/dist/rules/tools.d.ts +2 -0
  117. package/dist/rules/tools.d.ts.map +1 -1
  118. package/dist/rules/tools.js +15 -6
  119. package/dist/rules/tools.js.map +1 -1
  120. package/dist/rules/types.d.ts +17 -2
  121. package/dist/rules/types.d.ts.map +1 -1
  122. package/dist/rules/types.js +2 -1
  123. package/dist/rules/types.js.map +1 -1
  124. package/dist/session/factory-session.d.ts +116 -0
  125. package/dist/session/factory-session.d.ts.map +1 -0
  126. package/dist/session/factory-session.js +162 -0
  127. package/dist/session/factory-session.js.map +1 -0
  128. package/dist/session/filesystem-capture.d.ts +26 -0
  129. package/dist/session/filesystem-capture.d.ts.map +1 -0
  130. package/dist/session/filesystem-capture.js +75 -0
  131. package/dist/session/filesystem-capture.js.map +1 -0
  132. package/dist/storage/domains/audit/domain.d.ts +9 -2
  133. package/dist/storage/domains/audit/domain.d.ts.map +1 -1
  134. package/dist/storage/domains/audit/domain.js +106 -4
  135. package/dist/storage/domains/audit/domain.js.map +1 -1
  136. package/dist/storage/domains/filesystem/base.d.ts +26 -0
  137. package/dist/storage/domains/filesystem/base.d.ts.map +1 -0
  138. package/dist/storage/domains/filesystem/base.js +74 -0
  139. package/dist/storage/domains/filesystem/base.js.map +1 -0
  140. package/dist/storage/domains/projects/base.d.ts +1 -0
  141. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  142. package/dist/storage/domains/projects/base.js +3 -0
  143. package/dist/storage/domains/projects/base.js.map +1 -1
  144. package/dist/storage/domains/source-control/base.d.ts +2 -0
  145. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  146. package/dist/storage/domains/source-control/base.js +6 -0
  147. package/dist/storage/domains/source-control/base.js.map +1 -1
  148. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  149. package/dist/storage/domains/source-control/inmemory.js +3 -0
  150. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  151. package/dist/storage/domains/work-items/base.d.ts +11 -0
  152. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  153. package/dist/storage/domains/work-items/base.js +28 -0
  154. package/dist/storage/domains/work-items/base.js.map +1 -1
  155. package/dist/workspace.d.ts.map +1 -1
  156. package/dist/workspace.js +99 -45
  157. package/dist/workspace.js.map +1 -1
  158. package/factory-skills/factory-triage/SKILL.md +45 -12
  159. package/package.json +6 -6
  160. package/dist/integrations/github/factory-session.d.ts +0 -12
  161. package/dist/integrations/github/factory-session.d.ts.map +0 -1
  162. package/dist/integrations/github/factory-session.js +0 -37
  163. package/dist/integrations/github/factory-session.js.map +0 -1
@@ -0,0 +1,98 @@
1
+ import { randomUUID } from "crypto";
2
+ import { NoopLeaseProvider, isLeaseProvider } from "@mastra/core/events";
3
+ import { MastraWorker } from "@mastra/core/worker";
4
+ //#region src/integrations/issue-reconcile-worker.ts
5
+ const DEFAULT_ISSUE_RECONCILE_INTERVAL_MS = 5 * 6e4;
6
+ const MIN_LEASE_TTL_MS = 3e4;
7
+ var IssueReconcileWorker = class extends MastraWorker {
8
+ name;
9
+ #integrationId;
10
+ #reconcile;
11
+ #intervalMs;
12
+ #leaseTtlMs;
13
+ #leaseKey;
14
+ #leaseOwner = randomUUID();
15
+ #running = false;
16
+ #timer;
17
+ #inFlight;
18
+ #leaseProvider = NoopLeaseProvider;
19
+ constructor(config) {
20
+ super();
21
+ this.#integrationId = config.integrationId;
22
+ this.name = `${config.integrationId}-issue-reconcile`;
23
+ this.#leaseKey = `${config.integrationId}:issue-reconcile`;
24
+ this.#reconcile = config.reconcile;
25
+ this.#intervalMs = config.intervalMs ?? 3e5;
26
+ if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) throw new Error(`${config.integrationId} issue reconcile interval must be a positive number.`);
27
+ this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);
28
+ }
29
+ async init(deps) {
30
+ await super.init(deps);
31
+ this.#leaseProvider = getLeaseProvider(deps.pubsub);
32
+ }
33
+ async start() {
34
+ if (this.#running) return;
35
+ if (!this.deps) throw new Error("IssueReconcileWorker: call init() before start()");
36
+ this.#running = true;
37
+ this.deps.logger.info(`${this.#integrationId} issue reconcile worker started`, { intervalMs: this.#intervalMs });
38
+ this.#schedule(0);
39
+ }
40
+ async stop() {
41
+ if (!this.#running) return;
42
+ this.#running = false;
43
+ if (this.#timer) clearTimeout(this.#timer);
44
+ this.#timer = void 0;
45
+ await this.#inFlight;
46
+ }
47
+ get isRunning() {
48
+ return this.#running;
49
+ }
50
+ #schedule(delayMs) {
51
+ if (!this.#running) return;
52
+ this.#timer = setTimeout(() => {
53
+ this.#timer = void 0;
54
+ const run = this.#tick().finally(() => {
55
+ this.#inFlight = void 0;
56
+ this.#schedule(this.#intervalMs);
57
+ });
58
+ this.#inFlight = run;
59
+ }, delayMs);
60
+ this.#timer.unref?.();
61
+ }
62
+ async #tick() {
63
+ if (!(await this.#leaseProvider.acquireLease(this.#leaseKey, this.#leaseOwner, this.#leaseTtlMs).catch(() => ({ acquired: false }))).acquired) return;
64
+ const renewalTimer = setInterval(() => {
65
+ this.#leaseProvider.renewLease(this.#leaseKey, this.#leaseOwner, this.#leaseTtlMs).catch((error) => {
66
+ this.deps?.logger.warn(`${this.#integrationId} issue reconcile lease renewal failed`, { error: error instanceof Error ? error.message : String(error) });
67
+ });
68
+ }, Math.max(1e3, Math.floor(this.#leaseTtlMs / 3)));
69
+ renewalTimer.unref?.();
70
+ try {
71
+ const startedAt = Date.now();
72
+ const { errors, ...counts } = await this.#reconcile();
73
+ const context = {
74
+ ...counts,
75
+ durationMs: Date.now() - startedAt
76
+ };
77
+ if (counts.failed > 0) this.deps?.logger.warn(`${this.#integrationId} issue reconcile sweep completed with failures`, {
78
+ ...context,
79
+ errors
80
+ });
81
+ else this.deps?.logger.debug(`${this.#integrationId} issue reconcile sweep completed`, context);
82
+ } catch (error) {
83
+ this.deps?.logger.warn(`${this.#integrationId} issue reconcile sweep failed`, { error: error instanceof Error ? error.message : String(error) });
84
+ } finally {
85
+ clearInterval(renewalTimer);
86
+ await this.#leaseProvider.releaseLease(this.#leaseKey, this.#leaseOwner).catch(() => void 0);
87
+ }
88
+ }
89
+ };
90
+ function getLeaseProvider(pubsub) {
91
+ const getProvider = pubsub.getLeaseProvider;
92
+ if (typeof getProvider === "function") return getProvider.call(pubsub) ?? NoopLeaseProvider;
93
+ return isLeaseProvider(pubsub) ? pubsub : pubsub.lease ?? NoopLeaseProvider;
94
+ }
95
+ //#endregion
96
+ export { DEFAULT_ISSUE_RECONCILE_INTERVAL_MS, IssueReconcileWorker };
97
+
98
+ //# sourceMappingURL=issue-reconcile-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-reconcile-worker.js","names":["#integrationId","#reconcile","#intervalMs","#leaseTtlMs","#leaseKey","#leaseOwner","#leaseProvider","#running","#schedule","#timer","#inFlight","#tick"],"sources":["../../src/integrations/issue-reconcile-worker.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { isLeaseProvider, NoopLeaseProvider } from '@mastra/core/events';\nimport type { LeaseProvider, PubSub } from '@mastra/core/events';\nimport { MastraWorker } from '@mastra/core/worker';\nimport type { WorkerDeps } from '@mastra/core/worker';\n\nimport type { IssueReconciler } from './issue-reconciler.js';\n\nexport const DEFAULT_ISSUE_RECONCILE_INTERVAL_MS = 5 * 60_000;\nconst MIN_LEASE_TTL_MS = 30_000;\n\nexport interface IssueReconcileWorkerConfig {\n integrationId: string;\n reconcile: IssueReconciler;\n intervalMs?: number;\n}\n\nexport class IssueReconcileWorker extends MastraWorker {\n readonly name: string;\n\n readonly #integrationId: string;\n readonly #reconcile: IssueReconciler;\n readonly #intervalMs: number;\n readonly #leaseTtlMs: number;\n readonly #leaseKey: string;\n readonly #leaseOwner = randomUUID();\n\n #running = false;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #inFlight: Promise<void> | undefined;\n #leaseProvider: LeaseProvider = NoopLeaseProvider;\n\n constructor(config: IssueReconcileWorkerConfig) {\n super();\n this.#integrationId = config.integrationId;\n this.name = `${config.integrationId}-issue-reconcile`;\n this.#leaseKey = `${config.integrationId}:issue-reconcile`;\n this.#reconcile = config.reconcile;\n this.#intervalMs = config.intervalMs ?? DEFAULT_ISSUE_RECONCILE_INTERVAL_MS;\n if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {\n throw new Error(`${config.integrationId} issue reconcile interval must be a positive number.`);\n }\n this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);\n }\n\n async init(deps: WorkerDeps): Promise<void> {\n await super.init(deps);\n this.#leaseProvider = getLeaseProvider(deps.pubsub);\n }\n\n async start(): Promise<void> {\n if (this.#running) return;\n if (!this.deps) throw new Error('IssueReconcileWorker: call init() before start()');\n this.#running = true;\n this.deps.logger.info(`${this.#integrationId} issue reconcile worker started`, { intervalMs: this.#intervalMs });\n this.#schedule(0);\n }\n\n async stop(): Promise<void> {\n if (!this.#running) return;\n this.#running = false;\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n await this.#inFlight;\n }\n\n get isRunning(): boolean {\n return this.#running;\n }\n\n #schedule(delayMs: number): void {\n if (!this.#running) return;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n const run = this.#tick().finally(() => {\n this.#inFlight = undefined;\n this.#schedule(this.#intervalMs);\n });\n this.#inFlight = run;\n }, delayMs);\n this.#timer.unref?.();\n }\n\n async #tick(): Promise<void> {\n const lease = await this.#leaseProvider\n .acquireLease(this.#leaseKey, this.#leaseOwner, this.#leaseTtlMs)\n .catch(() => ({ acquired: false }));\n if (!lease.acquired) return;\n\n // A sweep that traverses many projects/orgs can outrun the lease TTL;\n // renew periodically so a replica can't grab the expired lease and start\n // an overlapping sweep partway through this one.\n const renewalTimer = setInterval(\n () => {\n void this.#leaseProvider\n .renewLease(this.#leaseKey, this.#leaseOwner, this.#leaseTtlMs)\n .catch(error => {\n this.deps?.logger.warn(`${this.#integrationId} issue reconcile lease renewal failed`, {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n Math.max(1_000, Math.floor(this.#leaseTtlMs / 3)),\n );\n renewalTimer.unref?.();\n\n try {\n const startedAt = Date.now();\n const { errors, ...counts } = await this.#reconcile();\n const context = { ...counts, durationMs: Date.now() - startedAt };\n if (counts.failed > 0) {\n this.deps?.logger.warn(`${this.#integrationId} issue reconcile sweep completed with failures`, {\n ...context,\n errors,\n });\n } else {\n this.deps?.logger.debug(`${this.#integrationId} issue reconcile sweep completed`, context);\n }\n } catch (error) {\n this.deps?.logger.warn(`${this.#integrationId} issue reconcile sweep failed`, {\n error: error instanceof Error ? error.message : String(error),\n });\n } finally {\n clearInterval(renewalTimer);\n await this.#leaseProvider.releaseLease(this.#leaseKey, this.#leaseOwner).catch(() => undefined);\n }\n }\n}\n\nfunction getLeaseProvider(pubsub: PubSub): LeaseProvider {\n const getProvider = (pubsub as PubSub & { getLeaseProvider?: () => LeaseProvider | undefined }).getLeaseProvider;\n if (typeof getProvider === 'function') return getProvider.call(pubsub) ?? NoopLeaseProvider;\n return isLeaseProvider(pubsub)\n ? pubsub\n : ((pubsub as PubSub & { lease?: LeaseProvider }).lease ?? NoopLeaseProvider);\n}\n"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { Intake, IntakeIssue } from '../capabilities/intake.js';
2
+ import type { FactoryProject, FactoryProjectsStorage } from '../storage/domains/projects/base.js';
3
+ import type { WorkItemRow, WorkItemsStorage } from '../storage/domains/work-items/base.js';
4
+ export interface IssueReconcileSummary {
5
+ projects: number;
6
+ checked: number;
7
+ updated: number;
8
+ closed: number;
9
+ missing: number;
10
+ failed: number;
11
+ errors: Array<{
12
+ projectId: string;
13
+ workItemId?: string;
14
+ error: string;
15
+ }>;
16
+ }
17
+ /**
18
+ * Scope passed in by the caller. Providers that reconcile per-repository (like
19
+ * GitHub, sharing target discovery with the PR reconciler) pass a target set
20
+ * and a `matches(item, scope)` predicate. Providers with no repository concept
21
+ * (Linear) pass no scope and every issue item is considered.
22
+ */
23
+ export interface IssueReconcileScope<TScope> {
24
+ scopes: TScope[];
25
+ matches(item: WorkItemRow, scope: TScope): boolean;
26
+ }
27
+ export interface IssueReconcilerOptions<TScope = void> {
28
+ integrationId: string;
29
+ intake: Intake;
30
+ projects: Pick<FactoryProjectsStorage, 'listAll'>;
31
+ storage: WorkItemsStorage;
32
+ externalSource?(item: WorkItemRow): {
33
+ type: string;
34
+ externalId: string;
35
+ };
36
+ issueId(item: WorkItemRow): string | undefined;
37
+ metadata(item: WorkItemRow, issue: IntakeIssue): Record<string, unknown>;
38
+ /**
39
+ * Called when a closed issue (stateType 'completed' or 'canceled') is detected
40
+ * on a non-terminal work item. Use this to replay the close event through
41
+ * the provider's rules ingress.
42
+ */
43
+ onClosed?(item: WorkItemRow, issue: IntakeIssue, project: FactoryProject): Promise<void>;
44
+ }
45
+ export type IssueReconciler<TScope = void> = TScope extends void ? () => Promise<IssueReconcileSummary> : (scope: IssueReconcileScope<TScope>) => Promise<IssueReconcileSummary>;
46
+ export declare function createIssueReconciler<TScope = void>(options: IssueReconcilerOptions<TScope>): IssueReconciler<TScope>;
47
+ //# sourceMappingURL=issue-reconciler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-reconciler.d.ts","sourceRoot":"","sources":["../../src/integrations/issue-reconciler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClG,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE3F,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB,CAAC,MAAM;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACpD;AAED,MAAM,WAAW,sBAAsB,CAAC,MAAM,GAAG,IAAI;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,cAAc,CAAC,CAAC,IAAI,EAAE,WAAW,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzE;;;;OAIG;IACH,QAAQ,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1F;AAED,MAAM,MAAM,eAAe,CAAC,MAAM,GAAG,IAAI,IAAI,MAAM,SAAS,IAAI,GAC5D,MAAM,OAAO,CAAC,qBAAqB,CAAC,GACpC,CAAC,KAAK,EAAE,mBAAmB,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;AAsC3E,wBAAgB,qBAAqB,CAAC,MAAM,GAAG,IAAI,EACjD,OAAO,EAAE,sBAAsB,CAAC,MAAM,CAAC,GACtC,eAAe,CAAC,MAAM,CAAC,CA8EzB"}
@@ -0,0 +1,104 @@
1
+ //#region src/integrations/issue-reconciler.ts
2
+ function sameValue(left, right) {
3
+ if (Array.isArray(right)) {
4
+ if (!Array.isArray(left)) return right.length === 0;
5
+ const leftStrings = left.filter((value) => typeof value === "string").slice().sort();
6
+ const rightStrings = right.filter((value) => typeof value === "string").slice().sort();
7
+ return leftStrings.length === rightStrings.length && leftStrings.every((value, index) => value === rightStrings[index]);
8
+ }
9
+ return left === right;
10
+ }
11
+ function metadataMatches(current, desired) {
12
+ return Object.entries(desired).every(([key, value]) => sameValue(current?.[key], value));
13
+ }
14
+ /**
15
+ * Drop `undefined` entries so we never spread them into stored metadata. An
16
+ * `undefined` field on the desired patch means "no signal from the live issue"
17
+ * — we must preserve whatever was already stored, not clobber it.
18
+ */
19
+ function withoutUndefined(record) {
20
+ const out = {};
21
+ for (const [key, value] of Object.entries(record)) if (value !== void 0) out[key] = value;
22
+ return out;
23
+ }
24
+ function issueItems(project, items, integrationId) {
25
+ return items.filter((item) => item.factoryProjectId === project.id && item.externalSource?.integrationId === integrationId && item.externalSource.type === "issue");
26
+ }
27
+ function createIssueReconciler(options) {
28
+ const run = async (scope) => {
29
+ const summary = {
30
+ projects: 0,
31
+ checked: 0,
32
+ updated: 0,
33
+ closed: 0,
34
+ missing: 0,
35
+ failed: 0,
36
+ errors: []
37
+ };
38
+ if (scope && scope.scopes.length === 0) return summary;
39
+ const projects = await options.projects.listAll();
40
+ for (const project of projects) {
41
+ const candidates = issueItems(project, await options.storage.list({
42
+ orgId: project.orgId,
43
+ factoryProjectId: project.id
44
+ }), options.integrationId);
45
+ const items = scope ? candidates.filter((item) => scope.scopes.some((target) => scope.matches(item, target))) : candidates;
46
+ if (items.length === 0) continue;
47
+ summary.projects += 1;
48
+ for (const item of items) {
49
+ summary.checked += 1;
50
+ try {
51
+ const resolved = await options.intake.resolveIntakeDispatch?.({
52
+ orgId: project.orgId,
53
+ externalSource: options.externalSource?.(item) ?? item.externalSource
54
+ });
55
+ if (!resolved) {
56
+ summary.missing += 1;
57
+ continue;
58
+ }
59
+ const issueId = options.issueId(item) ?? resolved.issueId;
60
+ const issue = await options.intake.getIssue({
61
+ ...resolved,
62
+ issueId
63
+ });
64
+ if (!issue) {
65
+ summary.missing += 1;
66
+ continue;
67
+ }
68
+ const isClosed = issue.stateType === "completed" || issue.stateType === "canceled";
69
+ const stage = item.stages[0];
70
+ if (isClosed && !(stage === "done" || stage === "canceled") && options.onClosed) {
71
+ await options.onClosed(item, issue, project);
72
+ summary.closed += 1;
73
+ continue;
74
+ }
75
+ const metadata = withoutUndefined(options.metadata(item, issue));
76
+ if (metadataMatches(item.metadata, metadata)) continue;
77
+ await options.storage.update({
78
+ orgId: project.orgId,
79
+ id: item.id,
80
+ userId: "factory-rule-dispatcher",
81
+ patch: { metadata: {
82
+ ...item.metadata ?? {},
83
+ ...metadata
84
+ } }
85
+ });
86
+ summary.updated += 1;
87
+ } catch (error) {
88
+ summary.failed += 1;
89
+ summary.errors.push({
90
+ projectId: project.id,
91
+ workItemId: item.id,
92
+ error: error instanceof Error ? error.message : String(error)
93
+ });
94
+ }
95
+ }
96
+ }
97
+ return summary;
98
+ };
99
+ return run;
100
+ }
101
+ //#endregion
102
+ export { createIssueReconciler };
103
+
104
+ //# sourceMappingURL=issue-reconciler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-reconciler.js","names":[],"sources":["../../src/integrations/issue-reconciler.ts"],"sourcesContent":["import type { Intake, IntakeIssue } from '../capabilities/intake.js';\nimport type { FactoryProject, FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { WorkItemRow, WorkItemsStorage } from '../storage/domains/work-items/base.js';\n\nexport interface IssueReconcileSummary {\n projects: number;\n checked: number;\n updated: number;\n closed: number;\n missing: number;\n failed: number;\n errors: Array<{ projectId: string; workItemId?: string; error: string }>;\n}\n\n/**\n * Scope passed in by the caller. Providers that reconcile per-repository (like\n * GitHub, sharing target discovery with the PR reconciler) pass a target set\n * and a `matches(item, scope)` predicate. Providers with no repository concept\n * (Linear) pass no scope and every issue item is considered.\n */\nexport interface IssueReconcileScope<TScope> {\n scopes: TScope[];\n matches(item: WorkItemRow, scope: TScope): boolean;\n}\n\nexport interface IssueReconcilerOptions<TScope = void> {\n integrationId: string;\n intake: Intake;\n projects: Pick<FactoryProjectsStorage, 'listAll'>;\n storage: WorkItemsStorage;\n externalSource?(item: WorkItemRow): { type: string; externalId: string };\n issueId(item: WorkItemRow): string | undefined;\n metadata(item: WorkItemRow, issue: IntakeIssue): Record<string, unknown>;\n /**\n * Called when a closed issue (stateType 'completed' or 'canceled') is detected\n * on a non-terminal work item. Use this to replay the close event through\n * the provider's rules ingress.\n */\n onClosed?(item: WorkItemRow, issue: IntakeIssue, project: FactoryProject): Promise<void>;\n}\n\nexport type IssueReconciler<TScope = void> = TScope extends void\n ? () => Promise<IssueReconcileSummary>\n : (scope: IssueReconcileScope<TScope>) => Promise<IssueReconcileSummary>;\n\nfunction sameValue(left: unknown, right: unknown): boolean {\n if (Array.isArray(right)) {\n if (!Array.isArray(left)) return right.length === 0;\n const leftStrings = left.filter((value): value is string => typeof value === 'string').slice().sort();\n const rightStrings = right.filter((value): value is string => typeof value === 'string').slice().sort();\n return leftStrings.length === rightStrings.length && leftStrings.every((value, index) => value === rightStrings[index]);\n }\n return left === right;\n}\n\nfunction metadataMatches(current: Record<string, unknown> | null, desired: Record<string, unknown>): boolean {\n return Object.entries(desired).every(([key, value]) => sameValue(current?.[key], value));\n}\n\n/**\n * Drop `undefined` entries so we never spread them into stored metadata. An\n * `undefined` field on the desired patch means \"no signal from the live issue\"\n * — we must preserve whatever was already stored, not clobber it.\n */\nfunction withoutUndefined(record: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(record)) {\n if (value !== undefined) out[key] = value;\n }\n return out;\n}\n\nfunction issueItems(project: FactoryProject, items: WorkItemRow[], integrationId: string): WorkItemRow[] {\n return items.filter(\n item =>\n item.factoryProjectId === project.id &&\n item.externalSource?.integrationId === integrationId &&\n item.externalSource.type === 'issue',\n );\n}\n\nexport function createIssueReconciler<TScope = void>(\n options: IssueReconcilerOptions<TScope>,\n): IssueReconciler<TScope> {\n const run = async (scope?: IssueReconcileScope<TScope>): Promise<IssueReconcileSummary> => {\n const summary: IssueReconcileSummary = {\n projects: 0,\n checked: 0,\n updated: 0,\n closed: 0,\n missing: 0,\n failed: 0,\n errors: [],\n };\n\n // A scoped sweep with zero targets is a no-op: the caller is telling us\n // there are no configured repositories to consider.\n if (scope && scope.scopes.length === 0) return summary;\n\n const projects = await options.projects.listAll();\n for (const project of projects) {\n const all = await options.storage.list({ orgId: project.orgId, factoryProjectId: project.id });\n const candidates = issueItems(project, all, options.integrationId);\n const items = scope\n ? candidates.filter(item => scope.scopes.some(target => scope.matches(item, target)))\n : candidates;\n if (items.length === 0) continue;\n summary.projects += 1;\n\n for (const item of items) {\n summary.checked += 1;\n try {\n const resolved = await options.intake.resolveIntakeDispatch?.({\n orgId: project.orgId,\n externalSource: options.externalSource?.(item) ?? item.externalSource!,\n });\n if (!resolved) {\n summary.missing += 1;\n continue;\n }\n const issueId = options.issueId(item) ?? resolved.issueId;\n const issue = await options.intake.getIssue({ ...resolved, issueId });\n if (!issue) {\n summary.missing += 1;\n continue;\n }\n\n // Close detection for Linear: replay through rules ingress if closed\n const isClosed = issue.stateType === 'completed' || issue.stateType === 'canceled';\n const stage = item.stages[0];\n const isTerminal = stage === 'done' || stage === 'canceled';\n if (isClosed && !isTerminal && options.onClosed) {\n await options.onClosed(item, issue, project);\n summary.closed += 1;\n continue; // Skip metadata patch for closed issues\n }\n\n const metadata = withoutUndefined(options.metadata(item, issue));\n if (metadataMatches(item.metadata, metadata)) continue;\n await options.storage.update({\n orgId: project.orgId,\n id: item.id,\n userId: 'factory-rule-dispatcher',\n patch: { metadata: { ...(item.metadata ?? {}), ...metadata } },\n });\n summary.updated += 1;\n } catch (error) {\n summary.failed += 1;\n summary.errors.push({\n projectId: project.id,\n workItemId: item.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n return summary;\n };\n\n return run as IssueReconciler<TScope>;\n}\n"],"mappings":"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"}
@@ -20,6 +20,7 @@
20
20
  */
21
21
  import type { RequestContext } from '@mastra/core/request-context';
22
22
  import type { ApiRoute } from '@mastra/core/server';
23
+ import type { MastraWorker } from '@mastra/core/worker';
23
24
  import type { Intake } from '../../capabilities/intake.js';
24
25
  import type { RouteAuth } from '../../routes/route.js';
25
26
  import type { IntegrationStorageHandle } from '../../storage/domains/integrations/base.js';
@@ -64,6 +65,8 @@ export interface LinearIssue {
64
65
  stateType: string;
65
66
  priorityLabel: string;
66
67
  assignee: string | null;
68
+ /** Display name of the Linear user who created the issue, when Linear returns one. */
69
+ creator: string | null;
67
70
  team: string | null;
68
71
  labels: string[];
69
72
  createdAt: string;
@@ -214,6 +217,7 @@ export declare class LinearIntegration implements FactoryIntegration {
214
217
  * issue doesn't exist.
215
218
  */
216
219
  createIssueComment(accessToken: string, idOrIdentifier: string, body: string): Promise<LinearCreatedComment | null>;
220
+ workers(ctx: IntegrationContext): MastraWorker[];
217
221
  /**
218
222
  * The integration's HTTP surface: `/web/linear/*` + `/auth/linear/*` Mastra
219
223
  * `apiRoutes` (status, OAuth connect/callback, projects + issues for
@@ -1 +1 @@
1
- {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,KAAK,EAGV,MAAM,EAKP,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAC3F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAI3F,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAM1G,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,mEAAmE;IACnE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,+GAA+G;AAC/G,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,wDAAwD;IACxD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAeD,2EAA2E;AAC3E,qBAAa,yBAA0B,SAAQ,KAAK;;CAInD;AAED,kEAAkE;AAClE,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAmGD,qBAAa,iBAAkB,YAAW,kBAAkB;;IAC1D,wDAAwD;IACxD,QAAQ,CAAC,EAAE,YAAY;IAMvB,2GAA2G;IAC3G,UAAU,CAAC,EACT,OAAO,EACP,QAAQ,EACR,IAAI,GACL,EAAE;QACD,OAAO,EAAE,wBAAwB,CAAC;QAClC,QAAQ,EAAE,sBAAsB,CAAC;QACjC,IAAI,EAAE,SAAS,CAAC;KACjB,GAAG,IAAI;IAMR,IAAI,OAAO,IAAI,mBAAmB,CAKjC;IAED,+EAA+E;IAC/E,IAAI,QAAQ,IAAI,sBAAsB,CAKrC;IAED;;;OAGG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAID,sEAAsE;IAChE,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAmBxE,4DAA4D;IACtD,gBAAgB,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BzE;;;;OAIG;IACH,eAAe,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO;IAYzD;;;;;OAKG;IACG,mBAAmB,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;IAmD3E;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAapE;;;;OAIG;IACH,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAY9C,uFAAuF;IACjF,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuB9D,gEAAgE;IAChE,WAAW,IAAI,IAAI;IAKnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CA2CrB;IAmBF;;;OAGG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAK9B,MAAM,EAAE,uBAAuB;IAW3C;;;;OAIG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;IAa7D,iEAAiE;IAC3D,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAInF;;;;;OAKG;IACG,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAwCvE,wEAAwE;IAClE,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAoFnE,6EAA6E;IACvE,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAsBjE;;;;;OAKG;IACG,gBAAgB,CACpB,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,EAAE,EACrB,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,eAAe,CAAC;IAyF3B;;;;;OAKG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA8DtG;;;;;OAKG;IACG,kBAAkB,CACtB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IA6BvC;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAW3C;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIrF,sEAAsE;IACtE,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAKvC"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/integration.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
- import { buildLinearRoutes } from "./routes.js";
1
+ import { IssueReconcileWorker } from "../issue-reconcile-worker.js";
2
2
  import { attachLinearRules } from "./rules.js";
3
+ import { attachLinearIssueReconciler } from "./issue-reconciler.js";
4
+ import { buildLinearRoutes } from "./routes.js";
3
5
  import { buildLinearAgentTools } from "./agent-tools.js";
4
6
  //#region src/integrations/linear/integration.ts
5
7
  const LINEAR_GRAPHQL_URL = "https://api.linear.app/graphql";
@@ -474,6 +476,7 @@ var LinearIntegration = class {
474
476
  state { name type }
475
477
  project { id }
476
478
  assignee { name }
479
+ creator { name }
477
480
  team { key }
478
481
  labels { nodes { name } }
479
482
  }
@@ -496,6 +499,7 @@ var LinearIntegration = class {
496
499
  stateType: node.state.type,
497
500
  priorityLabel: node.priorityLabel,
498
501
  assignee: node.assignee?.name ?? null,
502
+ creator: node.creator?.name ?? null,
499
503
  team: node.team?.key ?? null,
500
504
  labels: node.labels.nodes.map((label) => label.name),
501
505
  createdAt: node.createdAt,
@@ -549,6 +553,7 @@ var LinearIntegration = class {
549
553
  state { name type }
550
554
  project { id }
551
555
  assignee { name }
556
+ creator { name }
552
557
  team { key }
553
558
  labels { nodes { name } }
554
559
  comments(first: $commentsFirst) {
@@ -578,6 +583,7 @@ var LinearIntegration = class {
578
583
  stateType: issue.state.type,
579
584
  priorityLabel: issue.priorityLabel,
580
585
  assignee: issue.assignee?.name ?? null,
586
+ creator: issue.creator?.name ?? null,
581
587
  team: issue.team?.key ?? null,
582
588
  labels: issue.labels.nodes.map((label) => label.name),
583
589
  createdAt: issue.createdAt,
@@ -614,6 +620,17 @@ var LinearIntegration = class {
614
620
  if (!data.commentCreate.success || !data.commentCreate.comment) throw new Error("Linear did not accept the comment.");
615
621
  return data.commentCreate.comment;
616
622
  }
623
+ workers(ctx) {
624
+ if (process.env.MASTRACODE_LINEAR_RECONCILE_ENABLED?.trim().toLowerCase() === "false") return [];
625
+ const reconcile = attachLinearIssueReconciler(this, ctx);
626
+ if (!reconcile) return [];
627
+ const intervalMs = Number(process.env.MASTRACODE_LINEAR_RECONCILE_INTERVAL_MS);
628
+ return [new IssueReconcileWorker({
629
+ integrationId: this.id,
630
+ reconcile,
631
+ ...Number.isSafeInteger(intervalMs) && intervalMs > 0 ? { intervalMs } : {}
632
+ })];
633
+ }
617
634
  /**
618
635
  * The integration's HTTP surface: `/web/linear/*` + `/auth/linear/*` Mastra
619
636
  * `apiRoutes` (status, OAuth connect/callback, projects + issues for
@@ -654,7 +671,7 @@ function linearIssueToIntakeIssue(issue) {
654
671
  identifier: issue.identifier,
655
672
  title: issue.title,
656
673
  url: issue.url,
657
- author: null,
674
+ author: issue.creator,
658
675
  state: issue.state,
659
676
  stateType: issue.stateType,
660
677
  priority: issue.priorityLabel,