devchain-cli 0.22.1 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/drizzle/0077_familiar_morbius.sql +6 -0
  2. package/dist/drizzle/0078_preserve_legacy_connection_ownership.sql +180 -0
  3. package/dist/drizzle/0079_premium_forgotten_one.sql +18 -0
  4. package/dist/drizzle/0080_lethal_thunderbolt_ross.sql +49 -0
  5. package/dist/drizzle/0081_early_kronos.sql +58 -0
  6. package/dist/drizzle/0082_huge_black_queen.sql +14 -0
  7. package/dist/drizzle/meta/0077_snapshot.json +6957 -0
  8. package/dist/drizzle/meta/0078_snapshot.json +6957 -0
  9. package/dist/drizzle/meta/0079_snapshot.json +7093 -0
  10. package/dist/drizzle/meta/0080_snapshot.json +7241 -0
  11. package/dist/drizzle/meta/0081_snapshot.json +7482 -0
  12. package/dist/drizzle/meta/0082_snapshot.json +7571 -0
  13. package/dist/drizzle/meta/_journal.json +42 -0
  14. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  15. package/dist/server/common/errors/error-types.d.ts +7 -0
  16. package/dist/server/common/errors/error-types.js +7 -1
  17. package/dist/server/common/errors/error-types.js.map +1 -1
  18. package/dist/server/modules/epic-time/controllers/agent-time-buffer.controller.d.ts +9 -0
  19. package/dist/server/modules/epic-time/controllers/agent-time-buffer.controller.js +90 -0
  20. package/dist/server/modules/epic-time/controllers/agent-time-buffer.controller.js.map +1 -0
  21. package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.d.ts +12 -0
  22. package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.js +322 -0
  23. package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.js.map +1 -0
  24. package/dist/server/modules/epic-time/epic-time-store.module.d.ts +2 -0
  25. package/dist/server/modules/epic-time/epic-time-store.module.js +23 -0
  26. package/dist/server/modules/epic-time/epic-time-store.module.js.map +1 -0
  27. package/dist/server/modules/epic-time/epic-time.module.js +10 -6
  28. package/dist/server/modules/epic-time/epic-time.module.js.map +1 -1
  29. package/dist/server/modules/epic-time/models/epic-time-daily-allocator.d.ts +30 -0
  30. package/dist/server/modules/epic-time/models/epic-time-daily-allocator.js +125 -0
  31. package/dist/server/modules/epic-time/models/epic-time-daily-allocator.js.map +1 -0
  32. package/dist/server/modules/epic-time/models/epic-time-local-day.d.ts +9 -0
  33. package/dist/server/modules/epic-time/models/epic-time-local-day.js +164 -0
  34. package/dist/server/modules/epic-time/models/epic-time-local-day.js.map +1 -0
  35. package/dist/server/modules/epic-time/models/epic-time.models.d.ts +147 -0
  36. package/dist/server/modules/epic-time/models/epic-time.models.js +2 -0
  37. package/dist/server/modules/epic-time/models/epic-time.models.js.map +1 -1
  38. package/dist/server/modules/epic-time/services/agent-time-accounting.service.d.ts +1 -0
  39. package/dist/server/modules/epic-time/services/agent-time-accounting.service.js +31 -8
  40. package/dist/server/modules/epic-time/services/agent-time-accounting.service.js.map +1 -1
  41. package/dist/server/modules/epic-time/services/epic-estimate-logging.service.d.ts +37 -0
  42. package/dist/server/modules/epic-time/services/epic-estimate-logging.service.js +716 -0
  43. package/dist/server/modules/epic-time/services/epic-estimate-logging.service.js.map +1 -0
  44. package/dist/server/modules/epic-time/services/epic-time.service.d.ts +13 -3
  45. package/dist/server/modules/epic-time/services/epic-time.service.js +162 -69
  46. package/dist/server/modules/epic-time/services/epic-time.service.js.map +1 -1
  47. package/dist/server/modules/epic-time/services/epic-time.store.d.ts +50 -3
  48. package/dist/server/modules/epic-time/services/epic-time.store.js +705 -146
  49. package/dist/server/modules/epic-time/services/epic-time.store.js.map +1 -1
  50. package/dist/server/modules/epics/controllers/epics.controller.d.ts +10 -1
  51. package/dist/server/modules/epics/controllers/epics.controller.js +108 -2
  52. package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
  53. package/dist/server/modules/epics/epics.module.js +8 -2
  54. package/dist/server/modules/epics/epics.module.js.map +1 -1
  55. package/dist/server/modules/epics/models/epic-relations.models.d.ts +34 -0
  56. package/dist/server/modules/epics/models/epic-relations.models.js +16 -0
  57. package/dist/server/modules/epics/models/epic-relations.models.js.map +1 -0
  58. package/dist/server/modules/epics/services/epic-relation-target-resolver.d.ts +8 -0
  59. package/dist/server/modules/epics/services/epic-relation-target-resolver.js +37 -0
  60. package/dist/server/modules/epics/services/epic-relation-target-resolver.js.map +1 -0
  61. package/dist/server/modules/epics/services/epic-relations.service.d.ts +52 -0
  62. package/dist/server/modules/epics/services/epic-relations.service.js +135 -0
  63. package/dist/server/modules/epics/services/epic-relations.service.js.map +1 -0
  64. package/dist/server/modules/epics/services/epics.service.d.ts +9 -2
  65. package/dist/server/modules/epics/services/epics.service.js +115 -11
  66. package/dist/server/modules/epics/services/epics.service.js.map +1 -1
  67. package/dist/server/modules/events/catalog/broadcast-registry.js +16 -0
  68. package/dist/server/modules/events/catalog/broadcast-registry.js.map +1 -1
  69. package/dist/server/modules/events/catalog/epic.relations.invalidated.d.ts +12 -0
  70. package/dist/server/modules/events/catalog/epic.relations.invalidated.js +13 -0
  71. package/dist/server/modules/events/catalog/epic.relations.invalidated.js.map +1 -0
  72. package/dist/server/modules/events/catalog/epic.time.scope.invalidated.d.ts +12 -0
  73. package/dist/server/modules/events/catalog/epic.time.scope.invalidated.js +13 -0
  74. package/dist/server/modules/events/catalog/epic.time.scope.invalidated.js.map +1 -0
  75. package/dist/server/modules/events/catalog/index.d.ts +27 -4
  76. package/dist/server/modules/events/catalog/index.js +6 -0
  77. package/dist/server/modules/events/catalog/index.js.map +1 -1
  78. package/dist/server/modules/events/catalog/integration.connection.created.d.ts +4 -1
  79. package/dist/server/modules/events/catalog/integration.connection.created.js +5 -2
  80. package/dist/server/modules/events/catalog/integration.connection.created.js.map +1 -1
  81. package/dist/server/modules/events/catalog/integration.connection.deleted.d.ts +4 -1
  82. package/dist/server/modules/events/catalog/integration.connection.deleted.js +5 -2
  83. package/dist/server/modules/events/catalog/integration.connection.deleted.js.map +1 -1
  84. package/dist/server/modules/events/catalog/integration.connection.legacy.d.ts +85 -0
  85. package/dist/server/modules/events/catalog/integration.connection.legacy.js +41 -0
  86. package/dist/server/modules/events/catalog/integration.connection.legacy.js.map +1 -0
  87. package/dist/server/modules/events/catalog/integration.connection.updated.d.ts +4 -1
  88. package/dist/server/modules/events/catalog/integration.connection.updated.js +5 -2
  89. package/dist/server/modules/events/catalog/integration.connection.updated.js.map +1 -1
  90. package/dist/server/modules/events/services/committed-event-replay.d.ts +10 -0
  91. package/dist/server/modules/events/services/committed-event-replay.js +30 -0
  92. package/dist/server/modules/events/services/committed-event-replay.js.map +1 -0
  93. package/dist/server/modules/events/services/committed-event.store.d.ts +1 -0
  94. package/dist/server/modules/events/services/committed-event.store.js +12 -7
  95. package/dist/server/modules/events/services/committed-event.store.js.map +1 -1
  96. package/dist/server/modules/events/services/durable-event-registry.service.d.ts +9 -2
  97. package/dist/server/modules/events/services/durable-event-registry.service.js.map +1 -1
  98. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.d.ts +7 -0
  99. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js +158 -50
  100. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js.map +1 -1
  101. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.d.ts +4 -0
  102. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js +73 -33
  103. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js.map +1 -1
  104. package/dist/server/modules/external-integrations/connections/integration-connections.controller.d.ts +6 -3
  105. package/dist/server/modules/external-integrations/connections/integration-connections.controller.js +67 -12
  106. package/dist/server/modules/external-integrations/connections/integration-connections.controller.js.map +1 -1
  107. package/dist/server/modules/external-integrations/connections/integration-connections.service.d.ts +35 -3
  108. package/dist/server/modules/external-integrations/connections/integration-connections.service.js +178 -17
  109. package/dist/server/modules/external-integrations/connections/integration-connections.service.js.map +1 -1
  110. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.d.ts +6 -3
  111. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js +51 -11
  112. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js.map +1 -1
  113. package/dist/server/modules/external-integrations/external-integrations.module.js +6 -1
  114. package/dist/server/modules/external-integrations/external-integrations.module.js.map +1 -1
  115. package/dist/server/modules/external-integrations/models/external-edit-session.models.d.ts +1 -0
  116. package/dist/server/modules/external-integrations/models/external-provider.models.d.ts +6 -2
  117. package/dist/server/modules/external-integrations/models/external-provider.models.js +2 -3
  118. package/dist/server/modules/external-integrations/models/external-provider.models.js.map +1 -1
  119. package/dist/server/modules/external-integrations/models/external-time-mutation.models.d.ts +33 -2
  120. package/dist/server/modules/external-integrations/models/external-time-mutation.models.js +14 -0
  121. package/dist/server/modules/external-integrations/models/external-time-mutation.models.js.map +1 -1
  122. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.d.ts +10 -9
  123. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js +81 -46
  124. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js.map +1 -1
  125. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.d.ts +22 -20
  126. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js +136 -64
  127. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js.map +1 -1
  128. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.d.ts +7 -0
  129. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js +289 -4
  130. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js.map +1 -1
  131. package/dist/server/modules/external-integrations/my-work/external-my-work.service.d.ts +12 -7
  132. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js +114 -54
  133. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js.map +1 -1
  134. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.d.ts +19 -6
  135. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js +346 -46
  136. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js.map +1 -1
  137. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.d.ts +2 -1
  138. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js +3 -2
  139. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js.map +1 -1
  140. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.d.ts +6 -1
  141. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js +19 -0
  142. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js.map +1 -1
  143. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.d.ts +15 -2
  144. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js +33 -6
  145. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js.map +1 -1
  146. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.d.ts +13 -6
  147. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js +212 -75
  148. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js.map +1 -1
  149. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.d.ts +13 -4
  150. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js +98 -0
  151. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js.map +1 -1
  152. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.d.ts +10 -4
  153. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js +57 -18
  154. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js.map +1 -1
  155. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +139 -0
  156. package/dist/server/modules/mcp/dtos/mcp.dto.js +41 -1
  157. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  158. package/dist/server/modules/mcp/services/handlers/epic-context.d.ts +2 -0
  159. package/dist/server/modules/mcp/services/handlers/epic-tools.d.ts +4 -0
  160. package/dist/server/modules/mcp/services/handlers/epic-tools.js +275 -0
  161. package/dist/server/modules/mcp/services/handlers/epic-tools.js.map +1 -1
  162. package/dist/server/modules/mcp/services/mcp-tool-binding.registry.d.ts +2 -1
  163. package/dist/server/modules/mcp/services/mcp-tool-binding.registry.js +13 -8
  164. package/dist/server/modules/mcp/services/mcp-tool-binding.registry.js.map +1 -1
  165. package/dist/server/modules/mcp/services/testing/mcp-tool-binding-registry.fixture.d.ts +1 -1
  166. package/dist/server/modules/mcp/services/testing/mcp-tool-binding-registry.fixture.js +1 -1
  167. package/dist/server/modules/mcp/services/testing/mcp-tool-binding-registry.fixture.js.map +1 -1
  168. package/dist/server/modules/mcp/tool-descriptors/binding-types.d.ts +2 -0
  169. package/dist/server/modules/mcp/tool-descriptors/binding-types.js.map +1 -1
  170. package/dist/server/modules/mcp/tool-descriptors/epic.bindings.js +6 -0
  171. package/dist/server/modules/mcp/tool-descriptors/epic.bindings.js.map +1 -1
  172. package/dist/server/modules/mcp/tool-descriptors/epic.metadata.js +132 -0
  173. package/dist/server/modules/mcp/tool-descriptors/epic.metadata.js.map +1 -1
  174. package/dist/server/modules/projects/controllers/projects.controller.js +1 -4
  175. package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
  176. package/dist/server/modules/projects/projects.module.js +2 -0
  177. package/dist/server/modules/projects/projects.module.js.map +1 -1
  178. package/dist/server/modules/projects/services/projects.service.d.ts +4 -1
  179. package/dist/server/modules/projects/services/projects.service.js +23 -2
  180. package/dist/server/modules/projects/services/projects.service.js.map +1 -1
  181. package/dist/server/modules/providers/adapters/claude.adapter.d.ts +4 -2
  182. package/dist/server/modules/providers/adapters/claude.adapter.js +31 -0
  183. package/dist/server/modules/providers/adapters/claude.adapter.js.map +1 -1
  184. package/dist/server/modules/providers/services/provider-mcp-ensure.service.d.ts +7 -0
  185. package/dist/server/modules/providers/services/provider-mcp-ensure.service.js +69 -4
  186. package/dist/server/modules/providers/services/provider-mcp-ensure.service.js.map +1 -1
  187. package/dist/server/modules/session-reader/data/pricing.json +452 -56
  188. package/dist/server/modules/session-reader/services/session-reader.service.d.ts +2 -0
  189. package/dist/server/modules/session-reader/services/session-reader.service.js +85 -9
  190. package/dist/server/modules/session-reader/services/session-reader.service.js.map +1 -1
  191. package/dist/server/modules/session-reader/services/transcript-path-validator.service.js +8 -2
  192. package/dist/server/modules/session-reader/services/transcript-path-validator.service.js.map +1 -1
  193. package/dist/server/modules/session-reader/services/transcript-watcher.service.js +44 -17
  194. package/dist/server/modules/session-reader/services/transcript-watcher.service.js.map +1 -1
  195. package/dist/server/modules/sessions/services/session-runtime/__test-utils__/pipeline-harness.d.ts +1 -0
  196. package/dist/server/modules/sessions/services/session-runtime/__test-utils__/pipeline-harness.js +1 -0
  197. package/dist/server/modules/sessions/services/session-runtime/__test-utils__/pipeline-harness.js.map +1 -1
  198. package/dist/server/modules/sessions/services/session-runtime/session-launch-pipeline.service.js +11 -1
  199. package/dist/server/modules/sessions/services/session-runtime/session-launch-pipeline.service.js.map +1 -1
  200. package/dist/server/modules/sessions/services/sessions.service.d.ts +3 -1
  201. package/dist/server/modules/sessions/services/sessions.service.js +36 -6
  202. package/dist/server/modules/sessions/services/sessions.service.js.map +1 -1
  203. package/dist/server/modules/sessions/sessions.module.js +2 -0
  204. package/dist/server/modules/sessions/sessions.module.js.map +1 -1
  205. package/dist/server/modules/sessions/utils/claude-config.d.ts +4 -1
  206. package/dist/server/modules/sessions/utils/claude-config.js +265 -60
  207. package/dist/server/modules/sessions/utils/claude-config.js.map +1 -1
  208. package/dist/server/modules/storage/db/schema.d.ts +1109 -98
  209. package/dist/server/modules/storage/db/schema.js +141 -3
  210. package/dist/server/modules/storage/db/schema.js.map +1 -1
  211. package/dist/server/modules/storage/db/sqlite-json.d.ts +1 -0
  212. package/dist/server/modules/storage/db/sqlite-json.js +4 -0
  213. package/dist/server/modules/storage/db/sqlite-json.js.map +1 -1
  214. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +58 -10
  215. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  216. package/dist/server/modules/storage/local/delegates/epic.delegate.d.ts +30 -5
  217. package/dist/server/modules/storage/local/delegates/epic.delegate.js +718 -14
  218. package/dist/server/modules/storage/local/delegates/epic.delegate.js.map +1 -1
  219. package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.d.ts +52 -0
  220. package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.js +659 -0
  221. package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.js.map +1 -0
  222. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.d.ts +2 -1
  223. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js +17 -4
  224. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js.map +1 -1
  225. package/dist/server/modules/storage/local/delegates/integration.delegate.d.ts +23 -8
  226. package/dist/server/modules/storage/local/delegates/integration.delegate.js +238 -52
  227. package/dist/server/modules/storage/local/delegates/integration.delegate.js.map +1 -1
  228. package/dist/server/modules/storage/local/delegates/project.delegate.d.ts +2 -0
  229. package/dist/server/modules/storage/local/delegates/project.delegate.js +180 -130
  230. package/dist/server/modules/storage/local/delegates/project.delegate.js.map +1 -1
  231. package/dist/server/modules/storage/local/local-storage.service.d.ts +45 -10
  232. package/dist/server/modules/storage/local/local-storage.service.js +99 -15
  233. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  234. package/dist/server/modules/storage/models/domain.models.d.ts +170 -0
  235. package/dist/server/modules/storage/models/domain.models.js +9 -0
  236. package/dist/server/modules/storage/models/domain.models.js.map +1 -1
  237. package/dist/server/templates/teams-dev.json +311 -301
  238. package/dist/server/tsconfig.tsbuildinfo +1 -1
  239. package/dist/server/ui/assets/{ExternalRichEditor-DMtnZIMZ.js → ExternalRichEditor-FwCAalXO.js} +1 -1
  240. package/dist/server/ui/assets/{ReviewDetailPage-BmyiM5IR.js → ReviewDetailPage-D8DVpsJm.js} +1 -1
  241. package/dist/server/ui/assets/{ReviewsPage-CQ87BMwT.js → ReviewsPage-DcWpRFbm.js} +1 -1
  242. package/dist/server/ui/assets/index-CV8ydm7w.css +32 -0
  243. package/dist/server/ui/assets/index-Cwe7LxcW.js +1197 -0
  244. package/dist/server/ui/assets/{useReviewSubscription-DjbEUzt4.js → useReviewSubscription-CYoSg-88.js} +1 -1
  245. package/dist/server/ui/index.html +2 -2
  246. package/dist/templates/teams-dev.json +311 -301
  247. package/package.json +20 -2
  248. package/dist/server/ui/assets/index-BX6Yze-A.js +0 -1177
  249. package/dist/server/ui/assets/index-qoQEv_u4.css +0 -32
@@ -70,24 +70,26 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
70
70
  }
71
71
  else if (event.name === 'integration.connection.created' ||
72
72
  event.name === 'integration.connection.updated') {
73
- const provider = event.payload.provider;
74
- results = await this.reconcileProvider(provider);
73
+ results =
74
+ 'projectId' in event.payload
75
+ ? await this.reconcileProjectConnection(event.payload)
76
+ : await this.reconcileLegacyConnection(event.payload.connectionId, event.payload.provider);
75
77
  }
76
78
  else if (event.name === 'integration.connection.deleted') {
77
- const provider = event.payload.provider;
78
- results = await this.projectDisconnected(provider);
79
+ results = await this.connectionDisconnected(event.payload.provider, event.payload.connectionId);
79
80
  }
80
81
  const retryCount = results.filter((result) => result.outcome === 'retry').length;
81
82
  if (retryCount > 0) {
82
83
  throw new Error(`Managed subtask reconciliation has ${retryCount} retryable projection(s).`);
83
84
  }
84
85
  }
85
- async reconcileEpic(epicId, providerFilter) {
86
+ async reconcileEpic(epicId, providerFilter, connectionFilter) {
86
87
  const allExisting = await this.storage.listExternalManagedSubtaskLinksForEpicSnapshot(epicId);
87
- const existing = allExisting.filter((row) => !providerFilter || row.provider === providerFilter);
88
+ const existing = allExisting.filter((row) => (!providerFilter || row.provider === providerFilter) &&
89
+ (!connectionFilter || row.connectionIdSnapshot === connectionFilter.id));
88
90
  const epic = await this.loadEpicOrNull(epicId);
89
91
  const desired = epic?.parentId
90
- ? await this.desiredProjections(epic, providerFilter)
92
+ ? await this.desiredProjections(epic, providerFilter, connectionFilter)
91
93
  : new Map();
92
94
  const results = [];
93
95
  const obsolete = existing.filter((row) => !desired.has(row.parentSourceLinkIdSnapshot));
@@ -182,50 +184,118 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
182
184
  }
183
185
  return results;
184
186
  }
185
- async reconcileManagedLink(id) {
187
+ async reconcileProjectConnection(input) {
188
+ const connection = await this.storage.getIntegrationConnectionById(input.connectionId);
189
+ if (!connection ||
190
+ connection.projectId !== input.projectId ||
191
+ connection.provider !== input.provider) {
192
+ return [];
193
+ }
194
+ return this.reconcileConnection(connection);
195
+ }
196
+ async reconcileLegacyConnection(legacySourceConnectionId, provider) {
197
+ const connections = (await this.storage.listIntegrationConnectionsByLegacySourceConnectionId(legacySourceConnectionId)).filter((connection) => connection.provider === provider);
198
+ const results = [];
199
+ for (const connection of connections) {
200
+ results.push(...(await this.reconcileConnection(connection)));
201
+ }
202
+ return results;
203
+ }
204
+ async reconcileConnection(connection) {
205
+ const results = [];
206
+ const existing = (await this.storage.listExternalManagedSubtaskLinksByConnection(connection.id)).filter((row) => row.provider === connection.provider);
207
+ const processedEpicIds = new Set();
208
+ for (const row of existing) {
209
+ if (processedEpicIds.has(row.epicIdSnapshot)) {
210
+ continue;
211
+ }
212
+ processedEpicIds.add(row.epicIdSnapshot);
213
+ results.push(...(await this.reconcileEpic(row.epicIdSnapshot, connection.provider, connection)));
214
+ }
215
+ if (!connection.projectId) {
216
+ return results;
217
+ }
218
+ let offset = 0;
219
+ while (true) {
220
+ const page = await this.storage.listProjectEpics(connection.projectId, {
221
+ type: 'all',
222
+ limit: 500,
223
+ offset,
224
+ });
225
+ for (const epic of page.items) {
226
+ if (epic.parentId && !processedEpicIds.has(epic.id)) {
227
+ processedEpicIds.add(epic.id);
228
+ results.push(...(await this.reconcileEpic(epic.id, connection.provider, connection)));
229
+ }
230
+ }
231
+ offset += page.items.length;
232
+ if (page.items.length === 0 || offset >= page.total) {
233
+ break;
234
+ }
235
+ }
236
+ return results;
237
+ }
238
+ async reconcileManagedLink(id, expectedConnectionId) {
239
+ return this.withProjectionLock(id, () => this.reconcileManagedLinkUnlocked(id, expectedConnectionId));
240
+ }
241
+ async verifyManagedLink(id, expectedConnectionId) {
186
242
  return this.withProjectionLock(id, async () => {
187
243
  const row = await this.storage.getExternalManagedSubtaskLink(id);
188
- const epic = await this.loadEpicOrNull(row.epicIdSnapshot);
189
- const desired = epic?.parentId
190
- ? await this.desiredProjections(epic, row.provider)
191
- : new Map();
192
- return this.reconcileExisting(row, epic, desired.get(row.parentSourceLinkIdSnapshot) ?? null);
244
+ this.assertExpectedConnection(row, expectedConnectionId);
245
+ if (row.operationPhase === 'dispatch_admitted' ||
246
+ row.operationPhase === 'outcome_unknown' ||
247
+ row.operationPhase === 'needs_attention') {
248
+ await this.storage.updateExternalManagedSubtaskLink(id, {
249
+ operationPhase: 'outcome_unknown',
250
+ retryAt: null,
251
+ });
252
+ }
253
+ return this.reconcileManagedLinkUnlocked(id, expectedConnectionId);
193
254
  });
194
255
  }
195
- async verifyManagedLink(id) {
196
- const row = await this.storage.getExternalManagedSubtaskLink(id);
197
- if (row.operationPhase === 'dispatch_admitted' ||
198
- row.operationPhase === 'outcome_unknown' ||
199
- row.operationPhase === 'needs_attention') {
256
+ async retryManagedLink(id, expectedConnectionId) {
257
+ return this.withProjectionLock(id, async () => {
258
+ const row = await this.storage.getExternalManagedSubtaskLink(id);
259
+ this.assertExpectedConnection(row, expectedConnectionId);
260
+ const decision = (0, managed_subtask_recovery_policy_1.managedSubtaskRetryDecision)(row);
261
+ if (!decision.allowed) {
262
+ throw new error_types_1.ConflictError(decision.reason === 'verification_required'
263
+ ? 'Unknown managed-subtask outcomes must be verified before retry.'
264
+ : 'Managed-subtask state does not allow Retry.', {
265
+ managedLinkId: id,
266
+ reason: decision.reason,
267
+ });
268
+ }
200
269
  await this.storage.updateExternalManagedSubtaskLink(id, {
201
- operationPhase: 'outcome_unknown',
270
+ operationPhase: 'pre_dispatch',
271
+ safeErrorCode: null,
202
272
  retryAt: null,
203
273
  });
204
- }
205
- return this.reconcileManagedLink(id);
274
+ return this.reconcileManagedLinkUnlocked(id, expectedConnectionId);
275
+ });
206
276
  }
207
- async retryManagedLink(id) {
277
+ async reconcileManagedLinkUnlocked(id, expectedConnectionId) {
208
278
  const row = await this.storage.getExternalManagedSubtaskLink(id);
209
- const decision = (0, managed_subtask_recovery_policy_1.managedSubtaskRetryDecision)(row);
210
- if (!decision.allowed) {
211
- throw new error_types_1.ConflictError(decision.reason === 'verification_required'
212
- ? 'Unknown managed-subtask outcomes must be verified before retry.'
213
- : 'Managed-subtask state does not allow Retry.', {
214
- managedLinkId: id,
215
- reason: decision.reason,
216
- });
217
- }
218
- await this.storage.updateExternalManagedSubtaskLink(id, {
219
- operationPhase: 'pre_dispatch',
220
- safeErrorCode: null,
221
- retryAt: null,
222
- });
223
- return this.reconcileManagedLink(id);
279
+ this.assertExpectedConnection(row, expectedConnectionId);
280
+ const [epic, connection] = await Promise.all([
281
+ this.loadEpicOrNull(row.epicIdSnapshot),
282
+ this.storage.getIntegrationConnectionById(row.connectionIdSnapshot),
283
+ ]);
284
+ const allowUnassignedConnection = Boolean(expectedConnectionId && !connection?.projectId);
285
+ const desired = epic?.parentId && connection
286
+ ? await this.desiredProjections(epic, row.provider, connection, allowUnassignedConnection)
287
+ : new Map();
288
+ return this.reconcileExisting(row, epic, desired.get(row.parentSourceLinkIdSnapshot) ?? null, allowUnassignedConnection);
224
289
  }
225
- async reconcileExisting(original, epic, desired) {
290
+ async reconcileExisting(original, epic, desired, allowUnassignedConnection = false) {
226
291
  let row = await this.storage.getExternalManagedSubtaskLink(original.id);
227
- const connection = await this.storage.getIntegrationConnection(row.provider);
228
- if (!connection?.subtaskSyncEnabled) {
292
+ const connection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
293
+ if (!connection ||
294
+ connection.provider !== row.provider ||
295
+ (epic &&
296
+ connection.projectId !== epic.projectId &&
297
+ !(allowUnassignedConnection && connection.projectId === null)) ||
298
+ !connection.subtaskSyncEnabled) {
229
299
  return this.result(row, 'paused', connection ? 'sync_disabled' : 'connection_missing');
230
300
  }
231
301
  row = await this.refreshConnectionFence(row, connection);
@@ -235,6 +305,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
235
305
  row.safeErrorCode?.startsWith('provider_') === true));
236
306
  if ((!epic || !desired) && safeAbsentProjection) {
237
307
  await this.storage.removeExternalManagedSubtaskLink(row.id);
308
+ await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
238
309
  return this.result(row, 'deleted');
239
310
  }
240
311
  if (row.operationPhase === 'needs_attention' || row.tombstoneState === 'orphan_risk') {
@@ -308,6 +379,19 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
308
379
  }
309
380
  return this.updateProjection(row, epic, desired);
310
381
  }
382
+ async publishEpicTimeScopeHint(epicId) {
383
+ if (!epicId)
384
+ return;
385
+ try {
386
+ const epic = await this.storage.getEpic(epicId);
387
+ const project = await this.storage.getProject(epic.projectId);
388
+ await this.events.publish('epic.time.scope.invalidated', {
389
+ workspaceId: project.workspaceId,
390
+ });
391
+ }
392
+ catch {
393
+ }
394
+ }
311
395
  async createProjection(row, epic, desired) {
312
396
  const admission = await this.admitMutation(row);
313
397
  if (!admission) {
@@ -317,8 +401,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
317
401
  const snapshot = await admission.capability.create(admission.credentials, admission.context, {
318
402
  parentRemoteTaskId: row.parentRemoteTaskId,
319
403
  ownershipToken: row.ownershipToken,
320
- title: epic.title,
321
- description: epic.description,
404
+ title: desired.content.title,
405
+ description: desired.content.description,
322
406
  });
323
407
  if (!(await this.fenceStillCurrent(admission.connection))) {
324
408
  await this.markUnknown(row, 'connection_changed_after_dispatch');
@@ -333,6 +417,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
333
417
  confirmedFingerprint: row.desiredFingerprint,
334
418
  sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, admission.credentials),
335
419
  });
420
+ await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
336
421
  return this.result(confirmed.managedLink, 'confirmed');
337
422
  }
338
423
  catch (error) {
@@ -356,8 +441,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
356
441
  remoteTaskId: row.remoteTaskId,
357
442
  expectedParentRemoteTaskId: row.parentRemoteTaskId,
358
443
  ownershipToken: row.ownershipToken,
359
- title: epic.title,
360
- description: epic.description,
444
+ title: desired.content.title,
445
+ description: desired.content.description,
361
446
  });
362
447
  const snapshot = await admission.capability.readExact(admission.credentials, admission.context, row.remoteTaskId);
363
448
  if (!(await this.fenceStillCurrent(admission.connection))) {
@@ -368,7 +453,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
368
453
  return this.keepUnknownForRetry(row, 'remote_task_missing_after_update');
369
454
  }
370
455
  this.assertOwnedSnapshot(row, snapshot);
371
- if (!(0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, epic)) {
456
+ if (!(0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, desired.content)) {
372
457
  return this.keepUnknownForRetry(row, 'remote_verification_mismatch');
373
458
  }
374
459
  const confirmed = await this.storage.confirmExternalManagedSubtaskLink({
@@ -379,6 +464,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
379
464
  confirmedFingerprint: row.desiredFingerprint,
380
465
  sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, admission.credentials),
381
466
  });
467
+ await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
382
468
  return this.result(confirmed.managedLink, 'confirmed');
383
469
  }
384
470
  catch (error) {
@@ -388,6 +474,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
388
474
  async deleteProjection(row) {
389
475
  if (!row.remoteTaskId) {
390
476
  await this.storage.removeExternalManagedSubtaskLink(row.id);
477
+ await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
391
478
  return this.result(row, 'deleted');
392
479
  }
393
480
  const remoteTaskId = row.remoteTaskId;
@@ -419,6 +506,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
419
506
  return this.result(row, 'retry', 'connection_changed_after_dispatch');
420
507
  }
421
508
  await this.storage.removeExternalManagedSubtaskLink(row.id);
509
+ await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
422
510
  return this.result(row, 'deleted');
423
511
  }
424
512
  catch (error) {
@@ -429,7 +517,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
429
517
  if (!row.remoteTaskId) {
430
518
  return this.resolveUnknownCreate(row, epic, desired);
431
519
  }
432
- const access = await this.captureReadAccess(row.provider);
520
+ const access = await this.captureReadAccess(row);
433
521
  if (!access) {
434
522
  return this.result(row, 'retry', 'verification_unavailable');
435
523
  }
@@ -442,7 +530,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
442
530
  return this.markNeedsAttention(row, 'remote_task_missing_after_unknown_update');
443
531
  }
444
532
  this.assertOwnedSnapshot(row, snapshot);
445
- if ((0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, epic)) {
533
+ if ((0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, desired.content)) {
446
534
  const confirmed = await this.storage.confirmExternalManagedSubtaskLink({
447
535
  managedLinkId: row.id,
448
536
  remoteTaskId: snapshot.remoteTaskId,
@@ -451,6 +539,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
451
539
  confirmedFingerprint: row.desiredFingerprint,
452
540
  sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, access.credentials),
453
541
  });
542
+ await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
454
543
  return this.result(confirmed.managedLink, 'confirmed');
455
544
  }
456
545
  const retryable = await this.storage.updateExternalManagedSubtaskLink(row.id, {
@@ -468,7 +557,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
468
557
  }
469
558
  }
470
559
  async resolveUnknownCreate(row, epic, desired) {
471
- const access = await this.captureReadAccess(row.provider);
560
+ const access = await this.captureReadAccess(row);
472
561
  if (!access) {
473
562
  return this.result(row, 'retry', 'verification_unavailable');
474
563
  }
@@ -485,7 +574,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
485
574
  if (matches.length === 1) {
486
575
  const snapshot = matches[0];
487
576
  this.assertOwnedSnapshot(row, snapshot);
488
- adoptedMatchesDesired = (0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, epic);
577
+ adoptedMatchesDesired = (0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, desired.content);
489
578
  adoptedFingerprint = adoptedMatchesDesired
490
579
  ? row.desiredFingerprint
491
580
  : this.projectionFingerprint(snapshot.title, snapshot.description, row.parentSourceLinkIdSnapshot, row.parentRemoteTaskId);
@@ -497,6 +586,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
497
586
  confirmedFingerprint: adoptedFingerprint,
498
587
  sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, access.credentials),
499
588
  });
589
+ await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
500
590
  adopted = confirmed.managedLink;
501
591
  }
502
592
  if (!adopted && matches.length === 0 && children.complete) {
@@ -520,7 +610,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
520
610
  return this.updateProjection(adopted, epic, desired);
521
611
  }
522
612
  async resolveUnknownCreateForRemoval(row, tombstoneState) {
523
- const access = await this.captureReadAccess(row.provider);
613
+ const access = await this.captureReadAccess(row);
524
614
  if (!access) {
525
615
  return this.result(row, 'retry', 'verification_unavailable');
526
616
  }
@@ -533,6 +623,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
533
623
  item.parentRemoteTaskId === row.parentRemoteTaskId);
534
624
  if (matches.length === 0 && children.complete) {
535
625
  await this.storage.removeExternalManagedSubtaskLink(row.id);
626
+ await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
536
627
  return this.result(row, 'deleted');
537
628
  }
538
629
  if (matches.length !== 1) {
@@ -561,9 +652,10 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
561
652
  async resolveUnknownDelete(row) {
562
653
  if (!row.remoteTaskId) {
563
654
  await this.storage.removeExternalManagedSubtaskLink(row.id);
655
+ await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
564
656
  return this.result(row, 'deleted');
565
657
  }
566
- const access = await this.captureReadAccess(row.provider);
658
+ const access = await this.captureReadAccess(row);
567
659
  if (!access) {
568
660
  return this.result(row, 'retry', 'verification_unavailable');
569
661
  }
@@ -574,6 +666,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
574
666
  }
575
667
  if (!snapshot) {
576
668
  await this.storage.removeExternalManagedSubtaskLink(row.id);
669
+ await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
577
670
  return this.result(row, 'deleted');
578
671
  }
579
672
  this.assertOwnedSnapshot(row, snapshot);
@@ -595,7 +688,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
595
688
  if (!row.remoteTaskId) {
596
689
  return this.createProjection(row, epic, desired);
597
690
  }
598
- const access = await this.captureReadAccess(row.provider);
691
+ const access = await this.captureReadAccess(row);
599
692
  if (!access) {
600
693
  return this.result(row, 'retry', 'verification_unavailable');
601
694
  }
@@ -616,6 +709,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
616
709
  confirmedFingerprint: row.desiredFingerprint,
617
710
  sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, access.credentials),
618
711
  });
712
+ await this.publishEpicTimeScopeHint(repaired.managedLink.epicIdSnapshot);
619
713
  return this.result(repaired.managedLink, 'confirmed', 'recognition_repaired');
620
714
  }
621
715
  catch (error) {
@@ -629,7 +723,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
629
723
  if (!row.remoteTaskId) {
630
724
  return this.markNeedsAttention(row, 'remote_identity_missing');
631
725
  }
632
- const access = await this.captureReadAccess(row.provider);
726
+ const access = await this.captureReadAccess(row);
633
727
  if (!access) {
634
728
  return this.result(row, 'retry', 'ownership_verification_unavailable');
635
729
  }
@@ -652,22 +746,29 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
652
746
  }
653
747
  }
654
748
  async admitMutation(row) {
749
+ const scopedConnection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
750
+ if (!scopedConnection || scopedConnection.provider !== row.provider) {
751
+ return null;
752
+ }
655
753
  try {
656
- return await this.providerGate.run(row.provider, async () => {
657
- const connection = await this.storage.getIntegrationConnection(row.provider);
754
+ return await this.providerGate.run(this.connectionOperationScope(scopedConnection), async () => {
755
+ const connection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
658
756
  if (!connection?.subtaskSyncEnabled ||
757
+ connection.projectId !== scopedConnection.projectId ||
659
758
  connection.id !== row.connectionIdSnapshot ||
660
759
  connection.generation !== row.connectionGeneration ||
661
760
  connection.syncSettingRevision !== row.syncSettingRevision) {
662
761
  return null;
663
762
  }
664
763
  const adapter = this.providers.get(row.provider);
665
- const credentials = await this.storage.getIntegrationConnectionCredentials(row.provider);
666
- const rechecked = await this.storage.getIntegrationConnection(row.provider);
764
+ const credentials = await this.storage.getIntegrationConnectionCredentialsById(row.connectionIdSnapshot);
765
+ const rechecked = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
667
766
  if (!adapter.subtaskSync ||
668
767
  !credentials ||
669
768
  credentials.provider !== row.provider ||
670
769
  !rechecked?.subtaskSyncEnabled ||
770
+ rechecked.projectId !== connection.projectId ||
771
+ rechecked.provider !== row.provider ||
671
772
  rechecked.id !== connection.id ||
672
773
  rechecked.generation !== connection.generation ||
673
774
  rechecked.syncSettingRevision !== connection.syncSettingRevision) {
@@ -698,18 +799,26 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
698
799
  throw error;
699
800
  }
700
801
  }
701
- async captureReadAccess(provider) {
802
+ async captureReadAccess(row) {
803
+ const scopedConnection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
804
+ if (!scopedConnection || scopedConnection.provider !== row.provider) {
805
+ return null;
806
+ }
702
807
  try {
703
- return await this.providerGate.run(provider, async () => {
704
- const connection = await this.storage.getIntegrationConnection(provider);
705
- const adapter = this.providers.get(provider);
706
- const credentials = await this.storage.getIntegrationConnectionCredentials(provider);
707
- const rechecked = await this.storage.getIntegrationConnection(provider);
708
- if (!connection?.subtaskSyncEnabled ||
808
+ return await this.providerGate.run(this.connectionOperationScope(scopedConnection), async () => {
809
+ const connection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
810
+ const adapter = this.providers.get(row.provider);
811
+ const credentials = await this.storage.getIntegrationConnectionCredentialsById(row.connectionIdSnapshot);
812
+ const rechecked = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
813
+ if (connection?.provider !== row.provider ||
814
+ connection.projectId !== scopedConnection.projectId ||
815
+ !connection.subtaskSyncEnabled ||
709
816
  !adapter.subtaskSync ||
710
817
  !credentials ||
711
- credentials.provider !== provider ||
818
+ credentials.provider !== row.provider ||
712
819
  !rechecked?.subtaskSyncEnabled ||
820
+ rechecked.projectId !== connection.projectId ||
821
+ rechecked.provider !== row.provider ||
713
822
  rechecked.id !== connection.id ||
714
823
  rechecked.generation !== connection.generation ||
715
824
  rechecked.syncSettingRevision !== connection.syncSettingRevision) {
@@ -743,7 +852,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
743
852
  syncSettingRevision: connection.syncSettingRevision,
744
853
  });
745
854
  }
746
- const access = await this.captureReadAccess(row.provider);
855
+ const access = await this.captureReadAccess(row);
747
856
  if (!access) {
748
857
  return this.storage.updateExternalManagedSubtaskLink(row.id, {
749
858
  operationPhase: 'pre_dispatch',
@@ -792,18 +901,35 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
792
901
  });
793
902
  }
794
903
  }
795
- async desiredProjections(epic, providerFilter) {
904
+ async desiredProjections(epic, providerFilter, connectionFilter, allowUnassignedConnection = false) {
796
905
  const result = new Map();
797
906
  if (!epic.parentId) {
798
907
  return result;
799
908
  }
909
+ const workspaceProjects = await this.storage.getProjectWorkspaceSnapshot(epic.projectId);
910
+ const project = workspaceProjects.find((candidate) => candidate.id === epic.projectId);
911
+ if (!project) {
912
+ throw new error_types_1.NotFoundError('Project', epic.projectId);
913
+ }
914
+ const content = (0, managed_subtask_content_1.redactManagedSubtaskContent)(epic, project.rootPath, undefined, {
915
+ owningProjectId: project.id,
916
+ projects: workspaceProjects,
917
+ });
800
918
  const sources = await this.storage.listExternalTaskLinksForEpic(epic.parentId);
801
919
  for (const source of sources) {
802
920
  if (providerFilter && source.provider !== providerFilter) {
803
921
  continue;
804
922
  }
805
- const connection = await this.storage.getIntegrationConnection(source.provider);
923
+ if (connectionFilter && source.connectionId !== connectionFilter.id) {
924
+ continue;
925
+ }
926
+ let connection = connectionFilter ?? null;
927
+ if (!connection && source.connectionId) {
928
+ connection = await this.storage.getIntegrationConnectionById(source.connectionId);
929
+ }
806
930
  if (!connection?.subtaskSyncEnabled ||
931
+ (connection.projectId !== epic.projectId &&
932
+ !(allowUnassignedConnection && connection.projectId === null)) ||
807
933
  source.connectionId !== connection.id ||
808
934
  !this.providers.get(source.provider).subtaskSync) {
809
935
  continue;
@@ -814,10 +940,11 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
814
940
  }
815
941
  result.set(source.id, {
816
942
  epic,
943
+ content,
817
944
  parentSource: source,
818
945
  connection,
819
946
  workAreaRemoteId,
820
- desiredFingerprint: this.desiredFingerprint(epic, source),
947
+ desiredFingerprint: this.desiredFingerprint(content, source),
821
948
  });
822
949
  }
823
950
  return result;
@@ -836,7 +963,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
836
963
  parentRemoteTaskId: desired.parentSource.remoteTaskId,
837
964
  connectionGeneration: desired.connection.generation,
838
965
  syncSettingRevision: desired.connection.syncSettingRevision,
839
- ownershipToken: (0, node_crypto_1.randomUUID)(),
966
+ ownershipToken: (0, node_crypto_1.randomUUID)().slice(0, 8),
840
967
  desiredVersion: desired.epic.version,
841
968
  desiredFingerprint: desired.desiredFingerprint,
842
969
  });
@@ -849,8 +976,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
849
976
  throw error;
850
977
  }
851
978
  }
852
- async projectDisconnected(provider) {
853
- const rows = await this.storage.listExternalManagedSubtaskLinksByProvider(provider);
979
+ async connectionDisconnected(provider, connectionIdSnapshot) {
980
+ const rows = (await this.storage.listExternalManagedSubtaskLinksByConnection(connectionIdSnapshot)).filter((row) => row.provider === provider);
854
981
  return Promise.all(rows.map(async (row) => {
855
982
  const updated = await this.storage.updateExternalManagedSubtaskLink(row.id, {
856
983
  safeErrorCode: 'connection_missing',
@@ -983,7 +1110,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
983
1110
  : 'ownership_mismatch';
984
1111
  }
985
1112
  async fenceStillCurrent(expected) {
986
- const current = await this.storage.getIntegrationConnection(expected.provider);
1113
+ const current = await this.storage.getIntegrationConnectionById(expected.id);
987
1114
  return (current?.subtaskSyncEnabled === true &&
988
1115
  current.id === expected.id &&
989
1116
  current.generation === expected.generation &&
@@ -1015,8 +1142,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
1015
1142
  const candidate = `${new URL(credentials.siteUrl).origin}/browse/${encodeURIComponent(snapshot.remoteKey)}`;
1016
1143
  return (0, external_task_source_1.normalizeExternalTaskSourceUrl)(provider, candidate);
1017
1144
  }
1018
- desiredFingerprint(epic, parentSource) {
1019
- return this.projectionFingerprint(epic.title, epic.description, parentSource.id, parentSource.remoteTaskId);
1145
+ desiredFingerprint(content, parentSource) {
1146
+ return this.projectionFingerprint(content.title, content.description, parentSource.id, parentSource.remoteTaskId);
1020
1147
  }
1021
1148
  projectionFingerprint(title, description, parentSourceLinkId, parentRemoteTaskId) {
1022
1149
  return (0, node_crypto_1.createHash)('sha256')
@@ -1038,9 +1165,19 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
1038
1165
  connectionGeneration: connection.generation,
1039
1166
  };
1040
1167
  }
1168
+ connectionOperationScope(connection) {
1169
+ return connection.projectId
1170
+ ? { projectId: connection.projectId, provider: connection.provider }
1171
+ : { connectionId: connection.id, provider: connection.provider };
1172
+ }
1041
1173
  retryIsInFuture(retryAt) {
1042
1174
  return Boolean(retryAt && Date.parse(retryAt) > Date.now());
1043
1175
  }
1176
+ assertExpectedConnection(row, expectedConnectionId) {
1177
+ if (expectedConnectionId && row.connectionIdSnapshot !== expectedConnectionId) {
1178
+ throw new error_types_1.NotFoundError('Managed subtask link', row.id);
1179
+ }
1180
+ }
1044
1181
  async loadEpicOrNull(id) {
1045
1182
  try {
1046
1183
  return await this.storage.getEpic(id);