@mastra/factory 0.15.0 → 0.16.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 (212) hide show
  1. package/README.md +4 -3
  2. package/dist/boards/index.d.ts +1 -1
  3. package/dist/boards/index.d.ts.map +1 -1
  4. package/dist/boards/index.js +2 -2
  5. package/dist/boards/index.js.map +1 -1
  6. package/dist/boards/review.d.ts.map +1 -1
  7. package/dist/boards/review.js +4 -1
  8. package/dist/boards/review.js.map +1 -1
  9. package/dist/boards/semantics.d.ts +7 -0
  10. package/dist/boards/semantics.d.ts.map +1 -1
  11. package/dist/boards/semantics.js +15 -1
  12. package/dist/boards/semantics.js.map +1 -1
  13. package/dist/boards/transition-policy.d.ts +4 -0
  14. package/dist/boards/transition-policy.d.ts.map +1 -1
  15. package/dist/boards/transition-policy.js.map +1 -1
  16. package/dist/boards/work-transition-policy.d.ts.map +1 -1
  17. package/dist/boards/work-transition-policy.js +6 -1
  18. package/dist/boards/work-transition-policy.js.map +1 -1
  19. package/dist/boards/work.d.ts.map +1 -1
  20. package/dist/boards/work.js +37 -3
  21. package/dist/boards/work.js.map +1 -1
  22. package/dist/capabilities/intake.d.ts +4 -0
  23. package/dist/capabilities/intake.d.ts.map +1 -1
  24. package/dist/factory.d.ts +6 -3
  25. package/dist/factory.d.ts.map +1 -1
  26. package/dist/factory.js +66 -18
  27. package/dist/factory.js.map +1 -1
  28. package/dist/integrations/base.d.ts +6 -2
  29. package/dist/integrations/base.d.ts.map +1 -1
  30. package/dist/integrations/incidentio/agent-tools.d.ts +49 -0
  31. package/dist/integrations/incidentio/agent-tools.d.ts.map +1 -0
  32. package/dist/integrations/incidentio/agent-tools.js +90 -0
  33. package/dist/integrations/incidentio/agent-tools.js.map +1 -0
  34. package/dist/integrations/incidentio/claim.d.ts +8 -0
  35. package/dist/integrations/incidentio/claim.d.ts.map +1 -0
  36. package/dist/integrations/incidentio/claim.js +14 -0
  37. package/dist/integrations/incidentio/claim.js.map +1 -0
  38. package/dist/integrations/incidentio/default-rules.d.ts +46 -0
  39. package/dist/integrations/incidentio/default-rules.d.ts.map +1 -0
  40. package/dist/integrations/incidentio/default-rules.js +66 -0
  41. package/dist/integrations/incidentio/default-rules.js.map +1 -0
  42. package/dist/integrations/incidentio/integration.d.ts +48 -2
  43. package/dist/integrations/incidentio/integration.d.ts.map +1 -1
  44. package/dist/integrations/incidentio/integration.js +88 -2
  45. package/dist/integrations/incidentio/integration.js.map +1 -1
  46. package/dist/integrations/incidentio/issue-reconciler.d.ts +2 -0
  47. package/dist/integrations/incidentio/issue-reconciler.d.ts.map +1 -1
  48. package/dist/integrations/incidentio/issue-reconciler.js +38 -2
  49. package/dist/integrations/incidentio/issue-reconciler.js.map +1 -1
  50. package/dist/integrations/incidentio/routes.d.ts +71 -0
  51. package/dist/integrations/incidentio/routes.d.ts.map +1 -0
  52. package/dist/integrations/incidentio/routes.js +245 -0
  53. package/dist/integrations/incidentio/routes.js.map +1 -0
  54. package/dist/integrations/incidentio/rules.d.ts +55 -0
  55. package/dist/integrations/incidentio/rules.d.ts.map +1 -0
  56. package/dist/integrations/incidentio/rules.js +175 -0
  57. package/dist/integrations/incidentio/rules.js.map +1 -0
  58. package/dist/integrations/jira/adf.d.ts +26 -0
  59. package/dist/integrations/jira/adf.d.ts.map +1 -0
  60. package/dist/integrations/jira/adf.js +73 -0
  61. package/dist/integrations/jira/adf.js.map +1 -0
  62. package/dist/integrations/jira/agent-tools.d.ts +45 -0
  63. package/dist/integrations/jira/agent-tools.d.ts.map +1 -0
  64. package/dist/integrations/jira/agent-tools.js +87 -0
  65. package/dist/integrations/jira/agent-tools.js.map +1 -0
  66. package/dist/integrations/jira/api.d.ts +157 -0
  67. package/dist/integrations/jira/api.d.ts.map +1 -0
  68. package/dist/integrations/jira/api.js +141 -0
  69. package/dist/integrations/jira/api.js.map +1 -0
  70. package/dist/integrations/jira/claim.d.ts +9 -0
  71. package/dist/integrations/jira/claim.d.ts.map +1 -0
  72. package/dist/integrations/jira/claim.js +15 -0
  73. package/dist/integrations/jira/claim.js.map +1 -0
  74. package/dist/integrations/jira/default-rules.d.ts +47 -0
  75. package/dist/integrations/jira/default-rules.d.ts.map +1 -0
  76. package/dist/integrations/jira/default-rules.js +67 -0
  77. package/dist/integrations/jira/default-rules.js.map +1 -0
  78. package/dist/integrations/jira/integration.d.ts +114 -0
  79. package/dist/integrations/jira/integration.d.ts.map +1 -0
  80. package/dist/integrations/jira/integration.js +401 -0
  81. package/dist/integrations/jira/integration.js.map +1 -0
  82. package/dist/integrations/jira/issue-reconciler.d.ts +10 -0
  83. package/dist/integrations/jira/issue-reconciler.d.ts.map +1 -0
  84. package/dist/integrations/jira/issue-reconciler.js +70 -0
  85. package/dist/integrations/jira/issue-reconciler.js.map +1 -0
  86. package/dist/integrations/jira/reconciliation-config.d.ts +3 -0
  87. package/dist/integrations/jira/reconciliation-config.d.ts.map +1 -0
  88. package/dist/integrations/jira/reconciliation-config.js +17 -0
  89. package/dist/integrations/jira/reconciliation-config.js.map +1 -0
  90. package/dist/integrations/jira/routes.d.ts +54 -0
  91. package/dist/integrations/jira/routes.d.ts.map +1 -0
  92. package/dist/integrations/jira/routes.js +260 -0
  93. package/dist/integrations/jira/routes.js.map +1 -0
  94. package/dist/integrations/jira/rules.d.ts +57 -0
  95. package/dist/integrations/jira/rules.d.ts.map +1 -0
  96. package/dist/integrations/jira/rules.js +175 -0
  97. package/dist/integrations/jira/rules.js.map +1 -0
  98. package/dist/integrations/linear/claim.d.ts +8 -0
  99. package/dist/integrations/linear/claim.d.ts.map +1 -0
  100. package/dist/integrations/linear/claim.js +14 -0
  101. package/dist/integrations/linear/claim.js.map +1 -0
  102. package/dist/integrations/linear/default-rules.d.ts +1 -0
  103. package/dist/integrations/linear/default-rules.d.ts.map +1 -1
  104. package/dist/integrations/linear/default-rules.js +2 -0
  105. package/dist/integrations/linear/default-rules.js.map +1 -1
  106. package/dist/integrations/linear/integration.d.ts +24 -6
  107. package/dist/integrations/linear/integration.d.ts.map +1 -1
  108. package/dist/integrations/linear/integration.js +257 -37
  109. package/dist/integrations/linear/integration.js.map +1 -1
  110. package/dist/integrations/linear/routes.d.ts +10 -0
  111. package/dist/integrations/linear/routes.d.ts.map +1 -1
  112. package/dist/integrations/linear/routes.js +74 -12
  113. package/dist/integrations/linear/routes.js.map +1 -1
  114. package/dist/integrations/linear/rules.d.ts +1 -1
  115. package/dist/integrations/linear/rules.d.ts.map +1 -1
  116. package/dist/integrations/linear/rules.js +27 -2
  117. package/dist/integrations/linear/rules.js.map +1 -1
  118. package/dist/integrations/platform/connect/routes.d.ts +39 -0
  119. package/dist/integrations/platform/connect/routes.d.ts.map +1 -0
  120. package/dist/integrations/platform/connect/routes.js +129 -0
  121. package/dist/integrations/platform/connect/routes.js.map +1 -0
  122. package/dist/integrations/platform/github/event-worker.js +1 -1
  123. package/dist/integrations/platform/github/integration.js +1 -1
  124. package/dist/integrations/platform/incidentio/integration.d.ts +61 -4
  125. package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
  126. package/dist/integrations/platform/incidentio/integration.js +371 -22
  127. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  128. package/dist/integrations/platform/jira/agent-tools.d.ts +36 -0
  129. package/dist/integrations/platform/jira/agent-tools.d.ts.map +1 -0
  130. package/dist/integrations/platform/jira/agent-tools.js +85 -0
  131. package/dist/integrations/platform/jira/agent-tools.js.map +1 -0
  132. package/dist/integrations/platform/jira/integration.d.ts +65 -0
  133. package/dist/integrations/platform/jira/integration.d.ts.map +1 -0
  134. package/dist/integrations/platform/jira/integration.js +540 -0
  135. package/dist/integrations/platform/jira/integration.js.map +1 -0
  136. package/dist/integrations/platform/jira/routes.d.ts +55 -0
  137. package/dist/integrations/platform/jira/routes.d.ts.map +1 -0
  138. package/dist/integrations/platform/jira/routes.js +274 -0
  139. package/dist/integrations/platform/jira/routes.js.map +1 -0
  140. package/dist/integrations/platform/linear/integration.d.ts +34 -1
  141. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  142. package/dist/integrations/platform/linear/integration.js +271 -48
  143. package/dist/integrations/platform/linear/integration.js.map +1 -1
  144. package/dist/routes/intake.d.ts.map +1 -1
  145. package/dist/routes/intake.js +15 -5
  146. package/dist/routes/intake.js.map +1 -1
  147. package/dist/routes/oauth.js +2 -5
  148. package/dist/routes/oauth.js.map +1 -1
  149. package/dist/routes/surface.d.ts.map +1 -1
  150. package/dist/routes/surface.js +24 -4
  151. package/dist/routes/surface.js.map +1 -1
  152. package/dist/rules/dispatch-errors.d.ts.map +1 -1
  153. package/dist/rules/dispatch-errors.js +8 -0
  154. package/dist/rules/dispatch-errors.js.map +1 -1
  155. package/dist/rules/dispatcher.d.ts +1 -0
  156. package/dist/rules/dispatcher.d.ts.map +1 -1
  157. package/dist/rules/dispatcher.js +180 -106
  158. package/dist/rules/dispatcher.js.map +1 -1
  159. package/dist/rules/index.d.ts +2 -2
  160. package/dist/rules/index.d.ts.map +1 -1
  161. package/dist/rules/index.js +2 -2
  162. package/dist/rules/start-coordinator.d.ts.map +1 -1
  163. package/dist/rules/start-coordinator.js +4 -1
  164. package/dist/rules/start-coordinator.js.map +1 -1
  165. package/dist/rules/terminal-cleanup.d.ts +14 -1
  166. package/dist/rules/terminal-cleanup.d.ts.map +1 -1
  167. package/dist/rules/terminal-cleanup.js +20 -0
  168. package/dist/rules/terminal-cleanup.js.map +1 -1
  169. package/dist/rules/tools.d.ts +2 -0
  170. package/dist/rules/tools.d.ts.map +1 -1
  171. package/dist/rules/tools.js +30 -2
  172. package/dist/rules/tools.js.map +1 -1
  173. package/dist/rules/transition-service.d.ts +13 -0
  174. package/dist/rules/transition-service.d.ts.map +1 -1
  175. package/dist/rules/transition-service.js +11 -2
  176. package/dist/rules/transition-service.js.map +1 -1
  177. package/dist/rules/types.d.ts +72 -3
  178. package/dist/rules/types.d.ts.map +1 -1
  179. package/dist/rules/types.js +9 -1
  180. package/dist/rules/types.js.map +1 -1
  181. package/dist/rules/validation.d.ts.map +1 -1
  182. package/dist/rules/validation.js +13 -2
  183. package/dist/rules/validation.js.map +1 -1
  184. package/dist/session/run-audit.d.ts +9 -0
  185. package/dist/session/run-audit.d.ts.map +1 -1
  186. package/dist/session/run-audit.js +37 -6
  187. package/dist/session/run-audit.js.map +1 -1
  188. package/dist/skills/service.d.ts +12 -0
  189. package/dist/skills/service.d.ts.map +1 -1
  190. package/dist/skills/service.js +27 -1
  191. package/dist/skills/service.js.map +1 -1
  192. package/dist/storage/domains/intake/base.d.ts +11 -8
  193. package/dist/storage/domains/intake/base.d.ts.map +1 -1
  194. package/dist/storage/domains/intake/base.js +69 -24
  195. package/dist/storage/domains/intake/base.js.map +1 -1
  196. package/dist/storage/domains/work-items/base.d.ts +41 -2
  197. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  198. package/dist/storage/domains/work-items/base.js +159 -12
  199. package/dist/storage/domains/work-items/base.js.map +1 -1
  200. package/dist/supervisor/session-messaging.d.ts +23 -0
  201. package/dist/supervisor/session-messaging.d.ts.map +1 -0
  202. package/dist/supervisor/session-messaging.js +15 -0
  203. package/dist/supervisor/session-messaging.js.map +1 -0
  204. package/dist/supervisor/write-tools.d.ts +2 -1
  205. package/dist/supervisor/write-tools.d.ts.map +1 -1
  206. package/dist/supervisor/write-tools.js +11 -7
  207. package/dist/supervisor/write-tools.js.map +1 -1
  208. package/dist/work-item-branch.d.ts +1 -1
  209. package/dist/work-item-branch.d.ts.map +1 -1
  210. package/dist/work-item-branch.js +4 -2
  211. package/dist/work-item-branch.js.map +1 -1
  212. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"default-rules.js","names":[],"sources":["../../../src/integrations/linear/default-rules.ts"],"sourcesContent":["import type { FactoryLinearEventName, FactoryLinearRuleContext, FactoryRuleHandler } from '../../rules/types.js';\n\nexport type LinearRuleOverrides = Partial<\n Record<FactoryLinearEventName, FactoryRuleHandler<FactoryLinearRuleContext> | null | undefined>\n>;\nexport type LinearEventRules = Readonly<\n Record<FactoryLinearEventName, FactoryRuleHandler<FactoryLinearRuleContext> | null>\n>;\n\nfunction linearIssueObserved(context: FactoryLinearRuleContext) {\n if (context.item) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-triage`,\n // A source bound to a custom board lands on that board's initial phase;\n // otherwise Work auto-triages the new issue.\n board: context.intake?.board ?? 'work',\n source: 'linear-issue',\n sourceKey: `linear:${context.issue.identifier}`,\n title: `${context.issue.identifier}: ${context.issue.title}`,\n url: context.issue.url,\n stage: context.intake?.initialPhase ?? 'triage',\n metadata: {\n linearIssueId: context.issue.id,\n identifier: context.issue.identifier,\n sourceCreatedAt: context.issue.createdAt,\n linearState: context.issue.state,\n linearStateType: context.issue.stateType,\n linearPriority: context.issue.priorityLabel,\n linearAssignee: context.issue.assignee,\n linearCreator: context.issue.creator,\n linearTeam: context.issue.team,\n labels: [...context.issue.labels] as string[],\n ...(context.issue.assignee ? { assignee: context.issue.assignee } : {}),\n ...(context.issue.creator ? { creator: context.issue.creator, author: context.issue.creator } : {}),\n },\n } as const;\n}\n\nfunction linearIssueClosed(context: FactoryLinearRuleContext) {\n if (!context.item || context.item.source !== 'linear-issue') return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Only terminal state types trigger close.\n const stateType = context.issue.stateType;\n if (stateType !== 'completed' && stateType !== 'canceled') return;\n const canceled = stateType === 'canceled';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text: `Linear issue ${context.issue.identifier} was ${canceled ? 'canceled' : 'completed'}; this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nexport const defaultLinearRules = Object.freeze({\n issueObserved: linearIssueObserved,\n issueClosed: linearIssueClosed,\n} satisfies LinearEventRules);\n\nexport function resolveLinearRules(overrides?: LinearRuleOverrides): LinearEventRules {\n if (\n overrides !== undefined &&\n (overrides === null ||\n typeof overrides !== 'object' ||\n Array.isArray(overrides) ||\n ![Object.prototype, null].includes(Object.getPrototypeOf(overrides)))\n ) {\n throw new Error('Linear rules must be a plain object.');\n }\n const rules: Record<string, FactoryRuleHandler<FactoryLinearRuleContext> | null> = { ...defaultLinearRules };\n for (const key of Reflect.ownKeys(overrides ?? {})) {\n if (typeof key !== 'string' || !Object.hasOwn(defaultLinearRules, key)) {\n throw new Error(`Unknown Linear rule event: ${String(key)}.`);\n }\n const handler = overrides?.[key as FactoryLinearEventName];\n if (handler !== undefined && handler !== null && typeof handler !== 'function') {\n throw new Error(`Linear rule ${key} must be a function, null, or undefined.`);\n }\n if (handler !== undefined) rules[key] = handler;\n }\n return Object.freeze(rules) as LinearEventRules;\n}\n"],"mappings":";AASA,SAAS,oBAAoB,SAAmC;CAC9D,IAAI,QAAQ,MAAM;CAClB,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EAGtC,OAAO,QAAQ,QAAQ,SAAS;EAChC,QAAQ;EACR,WAAW,UAAU,QAAQ,MAAM;EACnC,OAAO,GAAG,QAAQ,MAAM,WAAW,IAAI,QAAQ,MAAM;EACrD,KAAK,QAAQ,MAAM;EACnB,OAAO,QAAQ,QAAQ,gBAAgB;EACvC,UAAU;GACR,eAAe,QAAQ,MAAM;GAC7B,YAAY,QAAQ,MAAM;GAC1B,iBAAiB,QAAQ,MAAM;GAC/B,aAAa,QAAQ,MAAM;GAC3B,iBAAiB,QAAQ,MAAM;GAC/B,gBAAgB,QAAQ,MAAM;GAC9B,gBAAgB,QAAQ,MAAM;GAC9B,eAAe,QAAQ,MAAM;GAC7B,YAAY,QAAQ,MAAM;GAC1B,QAAQ,CAAC,GAAG,QAAQ,MAAM,MAAM;GAChC,GAAI,QAAQ,MAAM,WAAW,EAAE,UAAU,QAAQ,MAAM,SAAS,IAAI,CAAC;GACrE,GAAI,QAAQ,MAAM,UAAU;IAAE,SAAS,QAAQ,MAAM;IAAS,QAAQ,QAAQ,MAAM;GAAQ,IAAI,CAAC;EACnG;CACF;AACF;AAEA,SAAS,kBAAkB,SAAmC;CAC5D,IAAI,CAAC,QAAQ,QAAQ,QAAQ,KAAK,WAAW,gBAAgB;CAC7D,IAAI,QAAQ,UAAU,QAAQ;CAE9B,IAAI,QAAQ,KAAK,OAAO,MAAK,UAAS,UAAU,UAAU,UAAU,UAAU,GAAG;CAEjF,MAAM,YAAY,QAAQ,MAAM;CAChC,IAAI,cAAc,eAAe,cAAc,YAAY;CAC3D,MAAM,WAAW,cAAc;CAC/B,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,OAAO,WAAW,aAAa;EAC/B,SAAS,EACP,MAAM,gBAAgB,QAAQ,MAAM,WAAW,OAAO,WAAW,aAAa,YAAY,gCAAgC,WAAW,aAAa,OAAO,GAC3J;CACF;AACF;AAEA,MAAa,qBAAqB,OAAO,OAAO;CAC9C,eAAe;CACf,aAAa;AACf,CAA4B;AAE5B,SAAgB,mBAAmB,WAAmD;CACpF,IACE,cAAc,KAAA,MACb,cAAc,QACb,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,KACvB,CAAC,CAAC,OAAO,WAAW,IAAI,CAAC,CAAC,SAAS,OAAO,eAAe,SAAS,CAAC,IAErE,MAAM,IAAI,MAAM,sCAAsC;CAExD,MAAM,QAA6E,EAAE,GAAG,mBAAmB;CAC3G,KAAK,MAAM,OAAO,QAAQ,QAAQ,aAAa,CAAC,CAAC,GAAG;EAClD,IAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,OAAO,oBAAoB,GAAG,GACnE,MAAM,IAAI,MAAM,8BAA8B,OAAO,GAAG,EAAE,EAAE;EAE9D,MAAM,UAAU,YAAY;EAC5B,IAAI,YAAY,KAAA,KAAa,YAAY,QAAQ,OAAO,YAAY,YAClE,MAAM,IAAI,MAAM,eAAe,IAAI,yCAAyC;EAE9E,IAAI,YAAY,KAAA,GAAW,MAAM,OAAO;CAC1C;CACA,OAAO,OAAO,OAAO,KAAK;AAC5B"}
1
+ {"version":3,"file":"default-rules.js","names":[],"sources":["../../../src/integrations/linear/default-rules.ts"],"sourcesContent":["import type { FactoryLinearEventName, FactoryLinearRuleContext, FactoryRuleHandler } from '../../rules/types.js';\nimport { linearClaimKey } from './claim.js';\n\nexport type LinearRuleOverrides = Partial<\n Record<FactoryLinearEventName, FactoryRuleHandler<FactoryLinearRuleContext> | null | undefined>\n>;\nexport type LinearEventRules = Readonly<\n Record<FactoryLinearEventName, FactoryRuleHandler<FactoryLinearRuleContext> | null>\n>;\n\nfunction linearIssueObserved(context: FactoryLinearRuleContext) {\n if (context.item) return;\n return {\n type: 'upsertLinkedWorkItem',\n idempotencyKey: `${context.ingress.id}:issue-triage`,\n // A source bound to a custom board lands on that board's initial phase;\n // otherwise Work auto-triages the new issue.\n board: context.intake?.board ?? 'work',\n source: 'linear-issue',\n sourceKey: `linear:${context.issue.identifier}`,\n claimKey: linearClaimKey(context.issue.id),\n title: `${context.issue.identifier}: ${context.issue.title}`,\n url: context.issue.url,\n stage: context.intake?.initialPhase ?? 'triage',\n metadata: {\n linearIssueId: context.issue.id,\n identifier: context.issue.identifier,\n sourceCreatedAt: context.issue.createdAt,\n linearState: context.issue.state,\n linearStateType: context.issue.stateType,\n linearPriority: context.issue.priorityLabel,\n linearAssignee: context.issue.assignee,\n linearCreator: context.issue.creator,\n linearTeam: context.issue.team,\n labels: [...context.issue.labels] as string[],\n ...(context.issue.assignee ? { assignee: context.issue.assignee } : {}),\n ...(context.issue.creator ? { creator: context.issue.creator, author: context.issue.creator } : {}),\n },\n } as const;\n}\n\nfunction linearIssueClosed(context: FactoryLinearRuleContext) {\n if (!context.item || context.item.source !== 'linear-issue') return;\n if (context.board !== 'work') return;\n // Already off the board: nothing to reconcile.\n if (context.item.stages.some(stage => stage === 'done' || stage === 'canceled')) return;\n // Only terminal state types trigger close.\n const stateType = context.issue.stateType;\n if (stateType !== 'completed' && stateType !== 'canceled') return;\n const canceled = stateType === 'canceled';\n return {\n type: 'transition',\n idempotencyKey: `${context.ingress.id}:issue-closed`,\n board: 'work',\n stage: canceled ? 'canceled' : 'done',\n message: {\n text: `Linear issue ${context.issue.identifier} was ${canceled ? 'canceled' : 'completed'}; this Work card was moved to ${canceled ? 'Canceled' : 'Done'}.`,\n },\n } as const;\n}\n\nexport const defaultLinearRules = Object.freeze({\n issueObserved: linearIssueObserved,\n issueClosed: linearIssueClosed,\n} satisfies LinearEventRules);\n\nexport function resolveLinearRules(overrides?: LinearRuleOverrides): LinearEventRules {\n if (\n overrides !== undefined &&\n (overrides === null ||\n typeof overrides !== 'object' ||\n Array.isArray(overrides) ||\n ![Object.prototype, null].includes(Object.getPrototypeOf(overrides)))\n ) {\n throw new Error('Linear rules must be a plain object.');\n }\n const rules: Record<string, FactoryRuleHandler<FactoryLinearRuleContext> | null> = { ...defaultLinearRules };\n for (const key of Reflect.ownKeys(overrides ?? {})) {\n if (typeof key !== 'string' || !Object.hasOwn(defaultLinearRules, key)) {\n throw new Error(`Unknown Linear rule event: ${String(key)}.`);\n }\n const handler = overrides?.[key as FactoryLinearEventName];\n if (handler !== undefined && handler !== null && typeof handler !== 'function') {\n throw new Error(`Linear rule ${key} must be a function, null, or undefined.`);\n }\n if (handler !== undefined) rules[key] = handler;\n }\n return Object.freeze(rules) as LinearEventRules;\n}\n"],"mappings":";;AAUA,SAAS,oBAAoB,SAAmC;CAC9D,IAAI,QAAQ,MAAM;CAClB,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EAGtC,OAAO,QAAQ,QAAQ,SAAS;EAChC,QAAQ;EACR,WAAW,UAAU,QAAQ,MAAM;EACnC,UAAU,eAAe,QAAQ,MAAM,EAAE;EACzC,OAAO,GAAG,QAAQ,MAAM,WAAW,IAAI,QAAQ,MAAM;EACrD,KAAK,QAAQ,MAAM;EACnB,OAAO,QAAQ,QAAQ,gBAAgB;EACvC,UAAU;GACR,eAAe,QAAQ,MAAM;GAC7B,YAAY,QAAQ,MAAM;GAC1B,iBAAiB,QAAQ,MAAM;GAC/B,aAAa,QAAQ,MAAM;GAC3B,iBAAiB,QAAQ,MAAM;GAC/B,gBAAgB,QAAQ,MAAM;GAC9B,gBAAgB,QAAQ,MAAM;GAC9B,eAAe,QAAQ,MAAM;GAC7B,YAAY,QAAQ,MAAM;GAC1B,QAAQ,CAAC,GAAG,QAAQ,MAAM,MAAM;GAChC,GAAI,QAAQ,MAAM,WAAW,EAAE,UAAU,QAAQ,MAAM,SAAS,IAAI,CAAC;GACrE,GAAI,QAAQ,MAAM,UAAU;IAAE,SAAS,QAAQ,MAAM;IAAS,QAAQ,QAAQ,MAAM;GAAQ,IAAI,CAAC;EACnG;CACF;AACF;AAEA,SAAS,kBAAkB,SAAmC;CAC5D,IAAI,CAAC,QAAQ,QAAQ,QAAQ,KAAK,WAAW,gBAAgB;CAC7D,IAAI,QAAQ,UAAU,QAAQ;CAE9B,IAAI,QAAQ,KAAK,OAAO,MAAK,UAAS,UAAU,UAAU,UAAU,UAAU,GAAG;CAEjF,MAAM,YAAY,QAAQ,MAAM;CAChC,IAAI,cAAc,eAAe,cAAc,YAAY;CAC3D,MAAM,WAAW,cAAc;CAC/B,OAAO;EACL,MAAM;EACN,gBAAgB,GAAG,QAAQ,QAAQ,GAAG;EACtC,OAAO;EACP,OAAO,WAAW,aAAa;EAC/B,SAAS,EACP,MAAM,gBAAgB,QAAQ,MAAM,WAAW,OAAO,WAAW,aAAa,YAAY,gCAAgC,WAAW,aAAa,OAAO,GAC3J;CACF;AACF;AAEA,MAAa,qBAAqB,OAAO,OAAO;CAC9C,eAAe;CACf,aAAa;AACf,CAA4B;AAE5B,SAAgB,mBAAmB,WAAmD;CACpF,IACE,cAAc,KAAA,MACb,cAAc,QACb,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,KACvB,CAAC,CAAC,OAAO,WAAW,IAAI,CAAC,CAAC,SAAS,OAAO,eAAe,SAAS,CAAC,IAErE,MAAM,IAAI,MAAM,sCAAsC;CAExD,MAAM,QAA6E,EAAE,GAAG,mBAAmB;CAC3G,KAAK,MAAM,OAAO,QAAQ,QAAQ,aAAa,CAAC,CAAC,GAAG;EAClD,IAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,OAAO,oBAAoB,GAAG,GACnE,MAAM,IAAI,MAAM,8BAA8B,OAAO,GAAG,EAAE,EAAE;EAE9D,MAAM,UAAU,YAAY;EAC5B,IAAI,YAAY,KAAA,KAAa,YAAY,QAAQ,OAAO,YAAY,YAClE,MAAM,IAAI,MAAM,eAAe,IAAI,yCAAyC;EAE9E,IAAI,YAAY,KAAA,GAAW,MAAM,OAAO;CAC1C;CACA,OAAO,OAAO,OAAO,KAAK;AAC5B"}
@@ -57,7 +57,11 @@ export interface LinearWorkspace {
57
57
  }
58
58
  export interface LinearIssue {
59
59
  id: string;
60
- projectId: string;
60
+ projectId: string | null;
61
+ /** Backend workspace identity when source ids span multiple workspaces. */
62
+ workspaceId?: string;
63
+ /** Raw Linear team id for exact team-source attribution. */
64
+ teamId: string | null;
61
65
  /** Human key like `ENG-123`. */
62
66
  identifier: string;
63
67
  title: string;
@@ -94,14 +98,22 @@ export interface LinearProject {
94
98
  /** Teams the project belongs to (the Settings picker groups by these). */
95
99
  teams: LinearProjectTeam[];
96
100
  }
101
+ /** A Linear team, selectable as an intake source in its own right. */
102
+ export interface LinearTeam {
103
+ id: string;
104
+ /** Short team key, e.g. `ENG`. */
105
+ key: string;
106
+ name: string;
107
+ /** Opaque intake source identifier. Optional on the wire for compatibility. */
108
+ sourceId?: string;
109
+ }
97
110
  export interface LinearIssueComment {
98
111
  author: string | null;
99
112
  body: string;
100
113
  createdAt: string;
101
114
  }
102
115
  /** Full issue payload for agent context: everything in {@link LinearIssue} plus description and discussion. */
103
- export interface LinearIssueDetail extends Omit<LinearIssue, 'projectId'> {
104
- projectId: string | null;
116
+ export interface LinearIssueDetail extends LinearIssue {
105
117
  /** Markdown body of the issue, or `null` when empty. */
106
118
  description: string | null;
107
119
  /** Discussion comments, oldest first. */
@@ -201,20 +213,26 @@ export declare class LinearIntegration implements FactoryIntegration {
201
213
  fetchWorkspace(accessToken: string): Promise<LinearWorkspace>;
202
214
  /** List the workspace's projects (for the Settings intake-source picker). */
203
215
  listProjects(accessToken: string): Promise<LinearProject[]>;
216
+ /** List the workspace's teams (for the Settings intake-source picker). */
217
+ listTeams(accessToken: string): Promise<LinearTeam[]>;
218
+ sourceMatchesIssue(sourceId: string, issue: Pick<LinearIssueDetail, 'projectId' | 'teamId'>): boolean;
204
219
  /**
205
220
  * List one page of the workspace's active issues (triage/backlog/unstarted/
206
221
  * started — completed and canceled are excluded), most recently updated
207
222
  * first. When `projectIds` is provided, only issues from those projects are
208
- * returned.
223
+ * returned. When `teamIds` is provided, only issues in those teams are
224
+ * returned, including issues that belong to no project. Callers list team
225
+ * sources and project sources in separate calls so the two filters are not
226
+ * AND-ed together.
209
227
  */
210
- listActiveIssues(accessToken: string, after?: string, projectIds?: string[], labels?: string[]): Promise<LinearIssuePage>;
228
+ listActiveIssues(accessToken: string, after?: string, projectIds?: string[], labels?: string[], teamIds?: string[]): Promise<LinearIssuePage>;
211
229
  /**
212
230
  * Fetch one issue with its description and comments. `idOrIdentifier`
213
231
  * accepts both the Linear UUID and the human key (`ENG-123`). Returns
214
232
  * `null` when the issue doesn't exist (Linear reports it as an "Entity not
215
233
  * found" error).
216
234
  */
217
- fetchIssueDetail(accessToken: string, idOrIdentifier: string): Promise<LinearIssueDetail | null>;
235
+ fetchIssueDetail(accessToken: string, idOrIdentifier: string, _sourceIds?: string[], _routedSourceIds?: string[]): Promise<LinearIssueDetail | null>;
218
236
  /**
219
237
  * Post a comment on an issue. `idOrIdentifier` accepts both the Linear UUID
220
238
  * and the human key (`ENG-123`) — the identifier is resolved to a UUID
@@ -1 +1 @@
1
- {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/integration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/integration.ts"],"names":[],"mappings":"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"}
@@ -5,12 +5,35 @@ import { attachLinearIssueReconciler } from "./issue-reconciler.js";
5
5
  import { linearIssueReconciliationEnabled, linearIssueReconciliationInterval } from "./reconciliation-config.js";
6
6
  import { buildLinearRoutes } from "./routes.js";
7
7
  import { buildLinearAgentTools } from "./agent-tools.js";
8
+ import { createHash } from "crypto";
8
9
  //#region src/integrations/linear/integration.ts
10
+ /**
11
+ * `LinearIntegration` — the self-contained Linear integration.
12
+ *
13
+ * Implements the system-wide `FactoryIntegration` contract
14
+ * (`../factory-integration.ts`): the deploy entry reads the Linear OAuth env
15
+ * vars ONCE, constructs an instance with explicit credentials, and passes it
16
+ * to `MastraFactory`. Everything Linear-flavored the system does — the OAuth
17
+ * connect/callback flow, workspace/project/issue reads for Intake, and the
18
+ * agent's issue tools — flows through this instance. No other module reads
19
+ * `LINEAR_*` env vars.
20
+ *
21
+ * The class owns:
22
+ * - OAuth: the user-facing authorize URL, code exchange, and refresh-token
23
+ * rotation against Linear's `/oauth/token` endpoint.
24
+ * - GraphQL reads/writes: viewer workspace, projects, active issues for
25
+ * Intake, full issue detail (description + discussion), issue comments.
26
+ * - The HTTP surface (`routes()`) and per-request agent tools
27
+ * (`agentTools()`), delegating to `./routes.ts` / `./agent-tools.ts` with
28
+ * `this` as the API client.
29
+ */
9
30
  const LINEAR_GRAPHQL_URL = "https://api.linear.app/graphql";
10
31
  const LINEAR_TOKEN_URL = "https://api.linear.app/oauth/token";
11
32
  const LINEAR_AUTHORIZE_URL = "https://linear.app/oauth/authorize";
12
33
  const LINEAR_ISSUES_PAGE_SIZE = 30;
13
34
  const ISSUE_COMMENTS_PAGE_SIZE = 50;
35
+ const LINEAR_TEAMS_PAGE_SIZE = 100;
36
+ const LINEAR_TEAMS_MAX_PAGES = 20;
14
37
  /** Hard stop for comment pagination so a misbehaving cursor can't loop forever. */
15
38
  const ISSUE_COMMENTS_MAX_PAGES = 20;
16
39
  /** Refresh this many ms before the recorded expiry to absorb clock skew. */
@@ -236,13 +259,19 @@ var LinearIntegration = class {
236
259
  const connection = await this.loadConnection(orgId);
237
260
  if (!connection) return [];
238
261
  const accessToken = await this.getFreshAccessToken(connection);
239
- return (await this.listProjects(accessToken)).map((project) => ({
262
+ const [projects, teams] = await Promise.all([this.listProjects(accessToken), this.listTeams(accessToken)]);
263
+ return [...projects.map((project) => ({
240
264
  id: project.id,
241
265
  name: project.name,
242
266
  type: "project"
243
- }));
267
+ })), ...teams.map((team) => ({
268
+ id: encodeSelfManagedTeamSourceId(team.id),
269
+ name: team.name,
270
+ type: "team",
271
+ metadata: { teamKey: team.key }
272
+ }))];
244
273
  },
245
- listItems: async ({ orgId, sourceIds, cursor }) => {
274
+ listItems: async ({ orgId, sourceIds, attributionSourceIds, cursor }) => {
246
275
  if (sourceIds.length === 0) return {
247
276
  items: [],
248
277
  nextCursor: null
@@ -253,28 +282,39 @@ var LinearIntegration = class {
253
282
  nextCursor: null
254
283
  };
255
284
  const accessToken = await this.getFreshAccessToken(connection);
256
- const page = await this.listActiveIssues(accessToken, cursor, sourceIds);
285
+ const page = await this.#listIntakeIssues({
286
+ connection: {
287
+ type: "oauth",
288
+ accessToken
289
+ },
290
+ sourceIds,
291
+ attributionSourceIds,
292
+ cursor
293
+ });
257
294
  return {
258
- items: page.issues.map((issue) => ({
259
- source: {
260
- type: "issue",
261
- externalId: issue.id,
262
- url: issue.url
263
- },
264
- sourceId: issue.projectId,
265
- title: `${issue.identifier}: ${issue.title}`,
266
- status: issue.state,
267
- labels: issue.labels,
268
- assignee: issue.assignee,
269
- createdAt: issue.createdAt,
270
- updatedAt: issue.updatedAt,
271
- metadata: {
272
- identifier: issue.identifier,
273
- stateType: issue.stateType,
274
- priority: issue.priorityLabel,
275
- team: issue.team
276
- }
277
- })),
295
+ items: page.issues.flatMap((issue) => {
296
+ if (!issue.sourceId) return [];
297
+ return [{
298
+ source: {
299
+ type: "issue",
300
+ externalId: issue.id,
301
+ url: issue.url
302
+ },
303
+ sourceId: issue.sourceId,
304
+ title: `${issue.identifier}: ${issue.title}`,
305
+ status: issue.state ?? "",
306
+ labels: issue.labels,
307
+ assignee: issue.assignee,
308
+ createdAt: issue.createdAt,
309
+ updatedAt: issue.updatedAt,
310
+ metadata: {
311
+ identifier: issue.identifier,
312
+ stateType: issue.stateType,
313
+ priority: issue.priority,
314
+ team: issue.source
315
+ }
316
+ }];
317
+ }),
278
318
  nextCursor: page.nextCursor
279
319
  };
280
320
  },
@@ -388,10 +428,52 @@ var LinearIntegration = class {
388
428
  }
389
429
  async #listIntakeIssues(input) {
390
430
  const accessToken = getLinearAccessToken(input.connection);
391
- const result = await this.listActiveIssues(accessToken, input.cursor, input.sourceIds, input.labels);
431
+ if (input.sourceIds.length === 0) return {
432
+ issues: [],
433
+ nextCursor: null
434
+ };
435
+ const attributionSourceIds = input.attributionSourceIds ?? input.sourceIds;
436
+ const { projectIds, teamIds } = splitSelfManagedSourceIds(input.sourceIds);
437
+ const cursors = decodeSelfManagedListCursor(input.cursor, input.sourceIds, attributionSourceIds);
438
+ if (teamIds.length === 0) {
439
+ if (cursors.projects === null) return {
440
+ issues: [],
441
+ nextCursor: null
442
+ };
443
+ const result = await this.listActiveIssues(accessToken, cursors.projects, projectIds, input.labels);
444
+ return {
445
+ issues: result.issues.map((issue) => linearIssueToIntakeIssue(issue)),
446
+ nextCursor: encodeSelfManagedListCursor({
447
+ projects: result.nextCursor,
448
+ teams: null
449
+ }, input.sourceIds, attributionSourceIds)
450
+ };
451
+ }
452
+ const projectResult = projectIds.length > 0 && cursors.projects !== null ? await this.listActiveIssues(accessToken, cursors.projects, projectIds, input.labels) : {
453
+ issues: [],
454
+ nextCursor: null
455
+ };
456
+ const teamResult = cursors.teams !== null ? await this.listActiveIssues(accessToken, cursors.teams, void 0, input.labels, teamIds) : {
457
+ issues: [],
458
+ nextCursor: null
459
+ };
460
+ const projectIssues = projectResult.issues.map((issue) => linearIssueToIntakeIssue(issue));
461
+ const selectedTeamSourceById = new Map(teamIds.map((teamId) => [teamId, encodeSelfManagedTeamSourceId(teamId)]));
462
+ const selectedProjectIds = new Set(splitSelfManagedSourceIds(attributionSourceIds).projectIds);
463
+ const teamIssues = teamResult.issues.filter((issue) => issue.projectId === null || !selectedProjectIds.has(issue.projectId)).map((issue) => {
464
+ const teamSourceId = issue.teamId ? selectedTeamSourceById.get(issue.teamId) : void 0;
465
+ if (!teamSourceId) throw new Error("Linear returned an issue outside the selected teams.");
466
+ return {
467
+ ...linearIssueToIntakeIssue(issue),
468
+ sourceId: teamSourceId
469
+ };
470
+ });
392
471
  return {
393
- issues: result.issues.map((issue) => linearIssueToIntakeIssue(issue)),
394
- nextCursor: result.nextCursor
472
+ issues: dedupeSelfManagedIssues([...projectIssues, ...teamIssues]),
473
+ nextCursor: encodeSelfManagedListCursor({
474
+ projects: projectResult.nextCursor,
475
+ teams: teamResult.nextCursor
476
+ }, input.sourceIds, attributionSourceIds)
395
477
  };
396
478
  }
397
479
  async #getIntakeIssue(input) {
@@ -454,23 +536,57 @@ var LinearIntegration = class {
454
536
  }))
455
537
  }));
456
538
  }
539
+ /** List the workspace's teams (for the Settings intake-source picker). */
540
+ async listTeams(accessToken) {
541
+ const teams = [];
542
+ let after;
543
+ for (let page = 0; page < LINEAR_TEAMS_MAX_PAGES; page += 1) {
544
+ const data = await linearGraphql(accessToken, `query Teams($first: Int!, $after: String) {
545
+ teams(first: $first, after: $after) {
546
+ nodes { id key name }
547
+ pageInfo { hasNextPage endCursor }
548
+ }
549
+ }`, {
550
+ first: LINEAR_TEAMS_PAGE_SIZE,
551
+ ...after ? { after } : {}
552
+ });
553
+ teams.push(...data.teams.nodes);
554
+ if (!data.teams.pageInfo.hasNextPage || !data.teams.pageInfo.endCursor) break;
555
+ if (data.teams.pageInfo.endCursor === after) throw invalidLinearCursor();
556
+ after = data.teams.pageInfo.endCursor;
557
+ }
558
+ return teams.map((team) => ({
559
+ id: team.id,
560
+ key: team.key,
561
+ name: team.name,
562
+ sourceId: encodeSelfManagedTeamSourceId(team.id)
563
+ }));
564
+ }
565
+ sourceMatchesIssue(sourceId, issue) {
566
+ return isSelfManagedTeamSourceId(sourceId) ? issue.teamId === decodeSelfManagedTeamSourceId(sourceId) : issue.projectId === sourceId;
567
+ }
457
568
  /**
458
569
  * List one page of the workspace's active issues (triage/backlog/unstarted/
459
570
  * started — completed and canceled are excluded), most recently updated
460
571
  * first. When `projectIds` is provided, only issues from those projects are
461
- * returned.
572
+ * returned. When `teamIds` is provided, only issues in those teams are
573
+ * returned, including issues that belong to no project. Callers list team
574
+ * sources and project sources in separate calls so the two filters are not
575
+ * AND-ed together.
462
576
  */
463
- async listActiveIssues(accessToken, after, projectIds, labels) {
577
+ async listActiveIssues(accessToken, after, projectIds, labels, teamIds) {
464
578
  const normalizedLabels = [...new Set((labels ?? []).map((label) => label.trim()).filter(Boolean))];
465
579
  const projectFilter = projectIds?.length ? ", project: { id: { in: $projectIds } }" : "";
466
580
  const projectVar = projectIds?.length ? ", $projectIds: [ID!]" : "";
581
+ const teamFilter = teamIds?.length ? ", team: { id: { in: $teamIds } }" : "";
582
+ const teamVar = teamIds?.length ? ", $teamIds: [ID!]" : "";
467
583
  const labelFilter = normalizedLabels.length > 0 ? ", labels: { name: { in: $labels } }" : "";
468
- const { nodes, pageInfo } = (await linearGraphql(accessToken, `query Intake($first: Int!, $after: String${projectVar}${normalizedLabels.length > 0 ? ", $labels: [String!]" : ""}) {
584
+ const { nodes, pageInfo } = (await linearGraphql(accessToken, `query Intake($first: Int!, $after: String${projectVar}${teamVar}${normalizedLabels.length > 0 ? ", $labels: [String!]" : ""}) {
469
585
  issues(
470
586
  first: $first
471
587
  after: $after
472
588
  orderBy: updatedAt
473
- filter: { state: { type: { in: ["triage", "backlog", "unstarted", "started"] } }${projectFilter}${labelFilter} }
589
+ filter: { state: { type: { in: ["triage", "backlog", "unstarted", "started"] } }${projectFilter}${teamFilter}${labelFilter} }
474
590
  ) {
475
591
  nodes {
476
592
  id
@@ -484,7 +600,7 @@ var LinearIntegration = class {
484
600
  project { id }
485
601
  assignee { name }
486
602
  creator { name }
487
- team { key }
603
+ team { id key }
488
604
  labels { nodes { name } }
489
605
  }
490
606
  pageInfo { hasNextPage endCursor }
@@ -493,12 +609,13 @@ var LinearIntegration = class {
493
609
  first: LINEAR_ISSUES_PAGE_SIZE,
494
610
  after: after ?? null,
495
611
  ...projectIds?.length ? { projectIds } : {},
612
+ ...teamIds?.length ? { teamIds } : {},
496
613
  ...normalizedLabels.length > 0 ? { labels: normalizedLabels } : {}
497
614
  })).issues;
498
615
  return {
499
616
  issues: nodes.map((node) => ({
500
617
  id: node.id,
501
- projectId: node.project.id,
618
+ projectId: node.project?.id ?? null,
502
619
  identifier: node.identifier,
503
620
  title: node.title,
504
621
  url: node.url,
@@ -510,7 +627,8 @@ var LinearIntegration = class {
510
627
  team: node.team?.key ?? null,
511
628
  labels: node.labels.nodes.map((label) => label.name),
512
629
  createdAt: node.createdAt,
513
- updatedAt: node.updatedAt
630
+ updatedAt: node.updatedAt,
631
+ teamId: node.team?.id ?? null
514
632
  })),
515
633
  nextCursor: pageInfo.hasNextPage ? pageInfo.endCursor : null
516
634
  };
@@ -534,6 +652,7 @@ var LinearIntegration = class {
534
652
  })).issue?.comments;
535
653
  if (!comments) break;
536
654
  nodes.push(...comments.nodes);
655
+ if (comments.pageInfo.hasNextPage && comments.pageInfo.endCursor === endCursor) throw invalidLinearCursor();
537
656
  ({hasNextPage, endCursor} = comments.pageInfo);
538
657
  }
539
658
  return nodes;
@@ -544,7 +663,7 @@ var LinearIntegration = class {
544
663
  * `null` when the issue doesn't exist (Linear reports it as an "Entity not
545
664
  * found" error).
546
665
  */
547
- async fetchIssueDetail(accessToken, idOrIdentifier) {
666
+ async fetchIssueDetail(accessToken, idOrIdentifier, _sourceIds, _routedSourceIds) {
548
667
  let data;
549
668
  try {
550
669
  data = await linearGraphql(accessToken, `query IssueDetail($id: String!, $commentsFirst: Int!) {
@@ -561,7 +680,7 @@ var LinearIntegration = class {
561
680
  project { id }
562
681
  assignee { name }
563
682
  creator { name }
564
- team { key }
683
+ team { id key }
565
684
  labels { nodes { name } }
566
685
  comments(first: $commentsFirst) {
567
686
  nodes { body createdAt user { name } }
@@ -582,6 +701,7 @@ var LinearIntegration = class {
582
701
  return {
583
702
  id: issue.id,
584
703
  projectId: issue.project?.id ?? null,
704
+ teamId: issue.team?.id ?? null,
585
705
  identifier: issue.identifier,
586
706
  title: issue.title,
587
707
  description: issue.description?.trim() ? issue.description : null,
@@ -651,7 +771,9 @@ var LinearIntegration = class {
651
771
  baseUrl: ctx.baseUrl,
652
772
  intake: ctx.storage.intake,
653
773
  projects: ctx.storage.projects,
654
- ingestFactoryIssues: attachLinearRules(this, ctx)
774
+ ingestFactoryIssues: attachLinearRules(this, ctx),
775
+ workItems: ctx.runtime?.workItems,
776
+ boards: ctx.runtime?.boards
655
777
  });
656
778
  }
657
779
  /**
@@ -673,6 +795,104 @@ function getLinearAccessToken(connection) {
673
795
  if (connection.type !== "oauth") throw new Error("Linear capabilities require an OAuth connection.");
674
796
  return connection.accessToken;
675
797
  }
798
+ /**
799
+ * Self-managed source ids. Projects use their raw Linear project id (no prefix,
800
+ * preserving existing intake bindings). Teams are prefixed so the two never
801
+ * collide and callers can tell them apart.
802
+ */
803
+ const SELF_MANAGED_TEAM_SOURCE_PREFIX = "linear-team:";
804
+ function encodeSelfManagedTeamSourceId(teamId) {
805
+ return `${SELF_MANAGED_TEAM_SOURCE_PREFIX}${teamId}`;
806
+ }
807
+ function isSelfManagedTeamSourceId(sourceId) {
808
+ return sourceId.startsWith(SELF_MANAGED_TEAM_SOURCE_PREFIX);
809
+ }
810
+ function decodeSelfManagedTeamSourceId(sourceId) {
811
+ return sourceId.slice(12);
812
+ }
813
+ /**
814
+ * Split selected source ids into raw project ids and raw team ids. A team
815
+ * source id carries the `linear-team:` prefix; everything else is a project id.
816
+ */
817
+ function splitSelfManagedSourceIds(sourceIds) {
818
+ const projectIds = [];
819
+ const teamIds = [];
820
+ for (const sourceId of sourceIds) if (isSelfManagedTeamSourceId(sourceId)) teamIds.push(decodeSelfManagedTeamSourceId(sourceId));
821
+ else projectIds.push(sourceId);
822
+ return {
823
+ projectIds,
824
+ teamIds
825
+ };
826
+ }
827
+ function linearSourceSetFingerprint(sourceIds, attributionSourceIds) {
828
+ const scope = {
829
+ sourceIds: [...new Set(sourceIds)].sort(),
830
+ attributionSourceIds: [...new Set(attributionSourceIds)].sort()
831
+ };
832
+ return createHash("sha256").update(JSON.stringify(scope)).digest("base64url");
833
+ }
834
+ function invalidLinearCursor() {
835
+ return Object.assign(/* @__PURE__ */ new Error("Linear cursor is invalid or stale."), { code: "invalid_cursor" });
836
+ }
837
+ function isCursorValue(value) {
838
+ return value === null || typeof value === "string";
839
+ }
840
+ function decodeSelfManagedListCursor(cursor, sourceIds, attributionSourceIds) {
841
+ const { projectIds, teamIds } = splitSelfManagedSourceIds(sourceIds);
842
+ if (!cursor) return {
843
+ projects: projectIds.length > 0 ? void 0 : null,
844
+ teams: teamIds.length > 0 ? void 0 : null
845
+ };
846
+ try {
847
+ const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
848
+ if (parsed.v !== 1 || parsed.sourceSet !== linearSourceSetFingerprint(sourceIds, attributionSourceIds) || !isCursorValue(parsed.projects) || !isCursorValue(parsed.teams)) throw invalidLinearCursor();
849
+ return {
850
+ projects: parsed.projects,
851
+ teams: parsed.teams
852
+ };
853
+ } catch {
854
+ throw invalidLinearCursor();
855
+ }
856
+ }
857
+ function encodeSelfManagedListCursor(state, sourceIds, attributionSourceIds) {
858
+ if (state.projects === null && state.teams === null) return null;
859
+ return Buffer.from(JSON.stringify({
860
+ v: 1,
861
+ sourceSet: linearSourceSetFingerprint(sourceIds, attributionSourceIds),
862
+ ...state
863
+ })).toString("base64url");
864
+ }
865
+ /**
866
+ * Deduplicate issues that surfaced from both a selected team and one of its
867
+ * selected projects. Keeps one entry per Linear issue UUID.
868
+ *
869
+ * Tie-breaker — MOST-SPECIFIC-WINS: an issue in a selected project is
870
+ * attributed to that project source (routes to the project's board); the team
871
+ * source covers only the remainder (projectless issues and issues in projects
872
+ * that were not separately selected). Change the comparison here to change the
873
+ * routing policy — this is the single precedence point for the self-managed
874
+ * integration, mirroring the platform-backed one.
875
+ */
876
+ function dedupeSelfManagedIssues(issues) {
877
+ const isProjectSource = (sourceId) => typeof sourceId === "string" && sourceId.length > 0 && !isSelfManagedTeamSourceId(sourceId);
878
+ const winners = /* @__PURE__ */ new Map();
879
+ for (const issue of issues) {
880
+ const existing = winners.get(issue.id);
881
+ if (!existing) {
882
+ winners.set(issue.id, issue);
883
+ continue;
884
+ }
885
+ if (isProjectSource(issue.sourceId) && !isProjectSource(existing.sourceId)) winners.set(issue.id, issue);
886
+ }
887
+ const seen = /* @__PURE__ */ new Set();
888
+ const ordered = [];
889
+ for (const issue of issues) {
890
+ if (seen.has(issue.id)) continue;
891
+ seen.add(issue.id);
892
+ ordered.push(winners.get(issue.id));
893
+ }
894
+ return ordered;
895
+ }
676
896
  function linearIssueToIntakeIssue(issue) {
677
897
  return {
678
898
  id: issue.id,