@mastra/github-signals 0.2.4 → 0.2.5-alpha.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @mastra/github-signals
2
2
 
3
+ ## 0.2.5-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed GitHub polling so stopping a poll prevents in-flight work from sending notifications or writing subscription state. ([#20593](https://github.com/mastra-ai/mastra/pull/20593))
8
+
9
+ - Updated dependencies [[`cdd5c33`](https://github.com/mastra-ai/mastra/commit/cdd5c33ac6c7118a9f139e6dc0e14e6a8ae31658), [`d7cf7fa`](https://github.com/mastra-ai/mastra/commit/d7cf7fafc1ae1b50bd8462dd0e6c671a8606db93), [`0f9a448`](https://github.com/mastra-ai/mastra/commit/0f9a448502157e59f7b76f24360ad497168f5ef8), [`289f4ce`](https://github.com/mastra-ai/mastra/commit/289f4ce16e3293370440172132c52ee787cbc09f), [`4f16ff8`](https://github.com/mastra-ai/mastra/commit/4f16ff824bf2f9b0ddc93f210477c10c8a4fb1ab), [`1c67d85`](https://github.com/mastra-ai/mastra/commit/1c67d85e9da8285662f4dbbf47e0378c3fee0747), [`ba24be6`](https://github.com/mastra-ai/mastra/commit/ba24be662439c331ab23a600041f93803c89eca8), [`842b5fe`](https://github.com/mastra-ai/mastra/commit/842b5fe22b6a7fa811bd14e48eb9af523ac989f2), [`80bdf3a`](https://github.com/mastra-ai/mastra/commit/80bdf3ae16ade6ff63bde0cb16fa2df8ab7dd4dd), [`9ba1247`](https://github.com/mastra-ai/mastra/commit/9ba12470c77f1c03642d720ce67e517e878f666e), [`fd96298`](https://github.com/mastra-ai/mastra/commit/fd96298a8367622f4ebfcaa97b5b6c1fbbd14564), [`6a84954`](https://github.com/mastra-ai/mastra/commit/6a84954a2667f85b6d59da652dab1bbff007ccb0), [`52d8ef0`](https://github.com/mastra-ai/mastra/commit/52d8ef03801f1deb7ee48532fc4190dd4a33916c), [`cdd5c33`](https://github.com/mastra-ai/mastra/commit/cdd5c33ac6c7118a9f139e6dc0e14e6a8ae31658), [`efd5c81`](https://github.com/mastra-ai/mastra/commit/efd5c81cc25fde3c2ddd86fc1178deb4ec176e19), [`0976933`](https://github.com/mastra-ai/mastra/commit/0976933142333ec78451feef265b68bcb45aa5e7), [`242b945`](https://github.com/mastra-ai/mastra/commit/242b94558777bfbdeb42cbfea84afff0b6ad0633), [`fea5cae`](https://github.com/mastra-ai/mastra/commit/fea5caedc7e2cfea51784a15e015952692027abf), [`4b59f78`](https://github.com/mastra-ai/mastra/commit/4b59f786cbc9a7d1ef07a07517dbd4b96865e99d), [`7010c5d`](https://github.com/mastra-ai/mastra/commit/7010c5d15728bf9c5dfe4fb6b1bf80ce23bf143a)]:
10
+ - @mastra/core@1.58.0-alpha.3
11
+
3
12
  ## 0.2.4
4
13
 
5
14
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -740,6 +740,8 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
740
740
  #syncClient;
741
741
  #repositoryResolver;
742
742
  #polling = /* @__PURE__ */ new Map();
743
+ #pollingThreadGenerations = /* @__PURE__ */ new Map();
744
+ #pollingGeneration = 0;
743
745
  #permissionCache = /* @__PURE__ */ new Map();
744
746
  #agent;
745
747
  #agentOptions = {};
@@ -813,6 +815,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
813
815
  const key = this.#pollingKey(input);
814
816
  for (const [pollingKey, state] of this.#polling.entries()) {
815
817
  if (pollingKey === key) continue;
818
+ this.#invalidatePollingThread(pollingKey);
816
819
  clearInterval(state.timer);
817
820
  this.#polling.delete(pollingKey);
818
821
  }
@@ -836,6 +839,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
836
839
  }
837
840
  stopPollingForThread(input) {
838
841
  const key = this.#pollingKey(input);
842
+ this.#invalidatePollingThread(key);
839
843
  const state = this.#polling.get(key);
840
844
  if (!state) return;
841
845
  clearInterval(state.timer);
@@ -851,6 +855,8 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
851
855
  return this.#options.pollIntervalMs ?? 3e5;
852
856
  }
853
857
  stopAllPolling() {
858
+ this.#pollingGeneration++;
859
+ this.#pollingThreadGenerations.clear();
854
860
  for (const state of this.#polling.values()) clearInterval(state.timer);
855
861
  this.#polling.clear();
856
862
  }
@@ -1044,6 +1050,9 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1044
1050
  #pollingKey(input) {
1045
1051
  return `${input.resourceId}:${input.threadId}`;
1046
1052
  }
1053
+ #invalidatePollingThread(key) {
1054
+ this.#pollingThreadGenerations.set(key, (this.#pollingThreadGenerations.get(key) ?? 0) + 1);
1055
+ }
1047
1056
  #getNotificationAgent(_input) {
1048
1057
  if (this.#agent) return this.#agent;
1049
1058
  const agentId = _input?.agentId ?? this.#options.agentId;
@@ -1063,6 +1072,9 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1063
1072
  const key = this.#pollingKey(input);
1064
1073
  const state = this.#polling.get(key);
1065
1074
  if (state?.running) return 0;
1075
+ const generation = this.#pollingGeneration;
1076
+ const threadGeneration = this.#pollingThreadGenerations.get(key) ?? 0;
1077
+ const isCurrentGeneration = () => this.#pollingGeneration === generation && (this.#pollingThreadGenerations.get(key) ?? 0) === threadGeneration;
1066
1078
  if (state) state.running = true;
1067
1079
  this.#notifyPollingChanged({
1068
1080
  threadId: input.threadId,
@@ -1071,6 +1083,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1071
1083
  });
1072
1084
  try {
1073
1085
  const { threadStore, loadedThread } = await this.#loadThread(input);
1086
+ if (!isCurrentGeneration()) return 0;
1074
1087
  const githubMetadata = getGithubMetadata(loadedThread.metadata);
1075
1088
  if (githubMetadata.subscriptions.length === 0) {
1076
1089
  this.stopPollingForThread(input);
@@ -1087,6 +1100,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1087
1100
  includeComments: options.includeComments
1088
1101
  };
1089
1102
  const syncResult = await this.#syncClient.syncPullRequest(syncInput);
1103
+ if (!isCurrentGeneration()) return 0;
1090
1104
  let snapshot;
1091
1105
  let snapshotError;
1092
1106
  if (syncResult.ok) try {
@@ -1094,7 +1108,9 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1094
1108
  } catch (error) {
1095
1109
  snapshotError = error instanceof Error ? error.message : String(error);
1096
1110
  }
1097
- if (snapshot) snapshot = await this.#filterUnauthorizedLatestComment(subscription.owner, subscription.repo, snapshot);
1111
+ if (!isCurrentGeneration()) return 0;
1112
+ if (snapshot) snapshot = await this.#filterUnauthorizedLatestComment(subscription.owner, subscription.repo, snapshot, isCurrentGeneration);
1113
+ if (!isCurrentGeneration()) return 0;
1098
1114
  const nextSubscription = {
1099
1115
  ...subscription,
1100
1116
  updatedAt: now,
@@ -1117,15 +1133,17 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1117
1133
  const legacyAggregateChanged = previousContentHash && snapshot?.contentHash && previousContentHash !== snapshot.contentHash && !previousThreadContentHash && !previousHeadSha;
1118
1134
  const changed = isFirstObservation || syncResult.ok && snapshot && (legacyAggregateChanged || latestCommentChanged || previousThreadContentHash && snapshot.threadContentHash && previousThreadContentHash !== snapshot.threadContentHash && !existingBotCommentEdited || previousHeadSha && snapshot.headSha && previousHeadSha !== snapshot.headSha || subscription.lastObservedState && snapshot.state && subscription.lastObservedState !== snapshot.state || hasMeaningfulMergeableStateChange(subscription.lastObservedMergeableState, snapshot.mergeableState) || subscription.lastObservedCiState && snapshot.ciState && subscription.lastObservedCiState !== snapshot.ciState || subscription.lastObservedReviewStateHash && snapshot.reviewStateHash && subscription.lastObservedReviewStateHash !== snapshot.reviewStateHash);
1119
1135
  let shouldKeepSubscription = true;
1120
- if (changed && snapshot) {
1136
+ if (changed && snapshot && isCurrentGeneration()) {
1121
1137
  const notifications = await this.#sendActivityNotifications({
1122
1138
  polling: input,
1123
1139
  subscription,
1124
1140
  snapshot,
1125
1141
  previousGithubUpdatedAt,
1126
1142
  previousContentHash,
1127
- latestCommentChanged
1143
+ latestCommentChanged,
1144
+ isCurrentGeneration
1128
1145
  });
1146
+ if (!isCurrentGeneration()) return 0;
1129
1147
  const primaryNotification = notifications[0];
1130
1148
  if (primaryNotification) {
1131
1149
  nextSubscription.lastNotificationAt = now;
@@ -1137,6 +1155,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1137
1155
  }
1138
1156
  if (shouldKeepSubscription) subscriptions.push(nextSubscription);
1139
1157
  }
1158
+ if (!isCurrentGeneration()) return 0;
1140
1159
  await threadStore.saveThread({ thread: {
1141
1160
  ...loadedThread,
1142
1161
  id: input.threadId,
@@ -1145,6 +1164,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1145
1164
  updatedAt: /* @__PURE__ */ new Date(),
1146
1165
  metadata: setGithubMetadata(loadedThread.metadata, { subscriptions })
1147
1166
  } });
1167
+ if (!isCurrentGeneration()) return 0;
1148
1168
  this.#notifySubscriptionsChanged({
1149
1169
  threadId: input.threadId,
1150
1170
  resourceId: input.resourceId,
@@ -1152,16 +1172,16 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1152
1172
  });
1153
1173
  if (subscriptions.length === 0) this.stopPollingForThread(input);
1154
1174
  return subscriptions.length;
1155
- } catch (error) {
1156
- throw error;
1157
1175
  } finally {
1158
1176
  const latestState = this.#polling.get(key);
1159
- if (latestState) latestState.running = false;
1160
- this.#notifyPollingChanged({
1161
- threadId: input.threadId,
1162
- resourceId: input.resourceId,
1163
- running: false
1164
- });
1177
+ if (isCurrentGeneration()) {
1178
+ if (latestState) latestState.running = false;
1179
+ this.#notifyPollingChanged({
1180
+ threadId: input.threadId,
1181
+ resourceId: input.resourceId,
1182
+ running: false
1183
+ });
1184
+ }
1165
1185
  }
1166
1186
  }
1167
1187
  #createGithubNotificationInput(input) {
@@ -1253,18 +1273,19 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1253
1273
  dedupeSuffix: `baseline:${input.subscription.lastSubscribeSignalId}`
1254
1274
  });
1255
1275
  }
1256
- async #isAuthorizedAuthor(owner, repo, user, metadata = {}) {
1276
+ async #isAuthorizedAuthor(owner, repo, user, metadata = {}, isCurrentGeneration) {
1257
1277
  if (!user) return false;
1258
1278
  const normalizedUser = user.toLowerCase();
1259
1279
  if (metadata.isBot === true || metadata.authorType?.toLowerCase() === "bot" || normalizedUser.endsWith("[bot]")) {
1260
1280
  if ((this.#options.ignoredBots ?? []).some((bot) => bot.toLowerCase() === normalizedUser)) return false;
1261
1281
  return (this.#options.authorizedBots ?? DEFAULT_AUTHORIZED_BOTS).some((bot) => bot.toLowerCase() === normalizedUser);
1262
1282
  }
1263
- const permission = await this.#loadAuthorPermission(owner, repo, user);
1283
+ const permission = await this.#loadAuthorPermission(owner, repo, user, isCurrentGeneration);
1284
+ if (isCurrentGeneration && !isCurrentGeneration()) return false;
1264
1285
  const authorizedPermissions = this.#options.authorizedPermissions ?? DEFAULT_AUTHORIZED_PERMISSIONS;
1265
1286
  return !!permission && authorizedPermissions.includes(permission);
1266
1287
  }
1267
- async #filterUnauthorizedLatestComment(owner, repo, snapshot) {
1288
+ async #filterUnauthorizedLatestComment(owner, repo, snapshot, isCurrentGeneration) {
1268
1289
  const comments = snapshot.latestComments?.length ? snapshot.latestComments : [{
1269
1290
  author: snapshot.latestCommentAuthor,
1270
1291
  authorType: snapshot.latestCommentAuthorType,
@@ -1276,10 +1297,11 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1276
1297
  if (!comments.some((comment) => comment.author)) return snapshot;
1277
1298
  if (!comments.some((comment) => comment.body || comment.url || comment.updatedAt)) return snapshot;
1278
1299
  for (const comment of comments) {
1300
+ if (isCurrentGeneration && !isCurrentGeneration()) return snapshot;
1279
1301
  if (!await this.#isAuthorizedAuthor(owner, repo, comment.author, {
1280
1302
  authorType: comment.authorType,
1281
1303
  isBot: comment.isBot
1282
- })) continue;
1304
+ }, isCurrentGeneration)) continue;
1283
1305
  return {
1284
1306
  ...snapshot,
1285
1307
  latestCommentAuthor: comment.author,
@@ -1300,7 +1322,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1300
1322
  latestCommentUpdatedAt: void 0
1301
1323
  };
1302
1324
  }
1303
- async #loadAuthorPermission(owner, repo, user) {
1325
+ async #loadAuthorPermission(owner, repo, user, isCurrentGeneration) {
1304
1326
  const cacheKey = `${owner}/${repo}:${user.toLowerCase()}`;
1305
1327
  const cached = this.#permissionCache.get(cacheKey);
1306
1328
  if (cached && cached.expiresAt > Date.now()) return cached.permission;
@@ -1325,10 +1347,11 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1325
1347
  "none"
1326
1348
  ].includes(raw) ? raw : void 0;
1327
1349
  }
1328
- if (permission) this.#permissionCache.set(cacheKey, {
1350
+ if (permission && (!isCurrentGeneration || isCurrentGeneration())) this.#permissionCache.set(cacheKey, {
1329
1351
  permission,
1330
1352
  expiresAt: Date.now() + PERMISSION_CACHE_TTL_MS
1331
1353
  });
1354
+ if (isCurrentGeneration && !isCurrentGeneration()) return void 0;
1332
1355
  return permission;
1333
1356
  } catch {
1334
1357
  this.#permissionCache.delete(cacheKey);
@@ -1351,10 +1374,12 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1351
1374
  for (const notification of notifications.sort(compareGithubActivityNotifications)) {
1352
1375
  if (!notification) continue;
1353
1376
  if (AUTHOR_GATED_NOTIFICATION_KINDS.has(notification.kind)) {
1354
- if (!await this.#isAuthorizedAuthor(input.subscription.owner, input.subscription.repo, input.snapshot.latestCommentAuthor, {
1377
+ const authorized = await this.#isAuthorizedAuthor(input.subscription.owner, input.subscription.repo, input.snapshot.latestCommentAuthor, {
1355
1378
  authorType: input.snapshot.latestCommentAuthorType,
1356
1379
  isBot: input.snapshot.latestCommentIsBot
1357
- })) continue;
1380
+ }, input.isCurrentGeneration);
1381
+ if (!input.isCurrentGeneration()) return [];
1382
+ if (!authorized) continue;
1358
1383
  }
1359
1384
  notificationInputs.push(this.#createGithubNotificationInput({
1360
1385
  subscription: input.subscription,
@@ -1371,11 +1396,14 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
1371
1396
  resourceId: input.polling.resourceId,
1372
1397
  threadId: input.polling.threadId
1373
1398
  };
1399
+ if (!input.isCurrentGeneration()) return [];
1374
1400
  const streamOptions = await this.#agentOptions.getNotificationStreamOptions?.(target);
1401
+ if (!input.isCurrentGeneration()) return [];
1375
1402
  await agent.sendNotificationSignal(notificationInputs, streamOptions ? {
1376
1403
  ...target,
1377
1404
  ifIdle: { streamOptions }
1378
1405
  } : target);
1406
+ if (!input.isCurrentGeneration()) return [];
1379
1407
  }
1380
1408
  return sent;
1381
1409
  }