mastracode 0.21.2 → 0.22.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 +38 -0
- package/dist/HarnessCompat.d.ts +21 -2
- package/dist/HarnessCompat.d.ts.map +1 -1
- package/dist/agents/prompts/tool-guidance.d.ts.map +1 -1
- package/dist/agents/tools.d.ts +2 -1
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/{chunk-HFGEUXMI.js → chunk-7ZTY5SBK.js} +1154 -13
- package/dist/chunk-7ZTY5SBK.js.map +1 -0
- package/dist/{chunk-MNOWL5GQ.cjs → chunk-B4IKAUT7.cjs} +3533 -2776
- package/dist/chunk-B4IKAUT7.cjs.map +1 -0
- package/dist/{chunk-YRX4OMQN.js → chunk-BFO3NTQO.js} +2671 -1914
- package/dist/chunk-BFO3NTQO.js.map +1 -0
- package/dist/{chunk-A4TDK7QP.cjs → chunk-DFC5V3P7.cjs} +267 -103
- package/dist/chunk-DFC5V3P7.cjs.map +1 -0
- package/dist/{chunk-DJBUYADN.cjs → chunk-EAUXUMEB.cjs} +1157 -12
- package/dist/chunk-EAUXUMEB.cjs.map +1 -0
- package/dist/{chunk-AWYGW5ZV.cjs → chunk-JHYTJMKT.cjs} +7 -3
- package/dist/chunk-JHYTJMKT.cjs.map +1 -0
- package/dist/{chunk-TTAAM2XR.js → chunk-UOFNLVKF.js} +7 -3
- package/dist/chunk-UOFNLVKF.js.map +1 -0
- package/dist/{chunk-J4DYNVTI.js → chunk-Y5AUV6T3.js} +202 -38
- package/dist/chunk-Y5AUV6T3.js.map +1 -0
- package/dist/cli.cjs +27 -19
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +12 -4
- package/dist/cli.js.map +1 -1
- package/dist/github-signals/index.d.ts +217 -0
- package/dist/github-signals/index.d.ts.map +1 -0
- package/dist/headless.d.ts.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/onboarding/settings.d.ts +2 -0
- package/dist/onboarding/settings.d.ts.map +1 -1
- package/dist/permissions-RL7PPO42.cjs +40 -0
- package/dist/{permissions-GWY37IEK.cjs.map → permissions-RL7PPO42.cjs.map} +1 -1
- package/dist/permissions-TJGRCT4O.js +3 -0
- package/dist/{permissions-EDE3D2DA.js.map → permissions-TJGRCT4O.js.map} +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/schema.d.ts +7 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/tool-names.d.ts +1 -0
- package/dist/tool-names.d.ts.map +1 -1
- package/dist/tui/command-dispatch.d.ts.map +1 -1
- package/dist/tui/commands/github.d.ts +3 -0
- package/dist/tui/commands/github.d.ts.map +1 -0
- package/dist/tui/commands/index.d.ts +1 -0
- package/dist/tui/commands/index.d.ts.map +1 -1
- package/dist/tui/commands/settings.d.ts.map +1 -1
- package/dist/tui/components/help-overlay.d.ts.map +1 -1
- package/dist/tui/components/notification-summary.d.ts +12 -0
- package/dist/tui/components/notification-summary.d.ts.map +1 -0
- package/dist/tui/components/notification.d.ts +14 -0
- package/dist/tui/components/notification.d.ts.map +1 -0
- package/dist/tui/components/reactive-signal.d.ts +11 -0
- package/dist/tui/components/reactive-signal.d.ts.map +1 -0
- package/dist/tui/components/settings.d.ts +2 -0
- package/dist/tui/components/settings.d.ts.map +1 -1
- package/dist/tui/components/state-signal.d.ts +13 -0
- package/dist/tui/components/state-signal.d.ts.map +1 -0
- package/dist/tui/event-dispatch.d.ts.map +1 -1
- package/dist/tui/handlers/message.d.ts.map +1 -1
- package/dist/tui/mastra-tui.d.ts.map +1 -1
- package/dist/tui/render-messages.d.ts.map +1 -1
- package/dist/tui/setup.d.ts.map +1 -1
- package/dist/tui/state.d.ts +14 -0
- package/dist/tui/state.d.ts.map +1 -1
- package/dist/tui/status-line.d.ts.map +1 -1
- package/dist/tui.cjs +19 -19
- package/dist/tui.js +2 -2
- package/package.json +10 -10
- package/dist/chunk-A4TDK7QP.cjs.map +0 -1
- package/dist/chunk-AWYGW5ZV.cjs.map +0 -1
- package/dist/chunk-DJBUYADN.cjs.map +0 -1
- package/dist/chunk-HFGEUXMI.js.map +0 -1
- package/dist/chunk-J4DYNVTI.js.map +0 -1
- package/dist/chunk-MNOWL5GQ.cjs.map +0 -1
- package/dist/chunk-TTAAM2XR.js.map +0 -1
- package/dist/chunk-YRX4OMQN.js.map +0 -1
- package/dist/permissions-EDE3D2DA.js +0 -3
- package/dist/permissions-GWY37IEK.cjs +0 -40
|
@@ -9,6 +9,13 @@ var openaiCompatible = require('@ai-sdk/openai-compatible');
|
|
|
9
9
|
var schemaCompat = require('@mastra/schema-compat');
|
|
10
10
|
var openai = require('@ai-sdk/openai');
|
|
11
11
|
var llm = require('@mastra/core/llm');
|
|
12
|
+
var child_process = require('child_process');
|
|
13
|
+
var crypto = require('crypto');
|
|
14
|
+
var promises = require('fs/promises');
|
|
15
|
+
var os = require('os');
|
|
16
|
+
var util = require('util');
|
|
17
|
+
var tools = require('@mastra/core/tools');
|
|
18
|
+
var z = require('zod');
|
|
12
19
|
var chalk = require('chalk');
|
|
13
20
|
|
|
14
21
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -33,6 +40,7 @@ function _interopNamespace(e) {
|
|
|
33
40
|
|
|
34
41
|
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
35
42
|
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
43
|
+
var z__default = /*#__PURE__*/_interopDefault(z);
|
|
36
44
|
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
37
45
|
|
|
38
46
|
var MEMORY_GATEWAY_PROVIDER = "mastra-gateway";
|
|
@@ -89,11 +97,22 @@ var DEFAULTS = {
|
|
|
89
97
|
stagehand: { env: "LOCAL" }
|
|
90
98
|
},
|
|
91
99
|
shellPassthrough: { mode: "default" },
|
|
92
|
-
signals: { unixSocketPubSub: false },
|
|
100
|
+
signals: { unixSocketPubSub: false, experimentalGithubSignals: false },
|
|
93
101
|
observability: { resources: {}, localTracing: false }
|
|
94
102
|
};
|
|
95
103
|
var THINKING_LEVEL_VALUES = ["off", "low", "medium", "high", "xhigh"];
|
|
96
104
|
var QUIET_MODE_MAX_TOOL_PREVIEW_LINES_MAX = 8;
|
|
105
|
+
var loadedSignalSettings = /* @__PURE__ */ new WeakMap();
|
|
106
|
+
function cloneSignalSettings(signals) {
|
|
107
|
+
return { ...signals };
|
|
108
|
+
}
|
|
109
|
+
function rememberLoadedSettings(settings) {
|
|
110
|
+
loadedSignalSettings.set(settings, cloneSignalSettings(settings.signals));
|
|
111
|
+
return settings;
|
|
112
|
+
}
|
|
113
|
+
function signalSettingsEqual(left, right) {
|
|
114
|
+
return left.unixSocketPubSub === right.unixSocketPubSub && left.experimentalGithubSignals === right.experimentalGithubSignals;
|
|
115
|
+
}
|
|
97
116
|
function parseThinkingLevel(value) {
|
|
98
117
|
return typeof value === "string" && THINKING_LEVEL_VALUES.includes(value) ? value : DEFAULTS.preferences.thinkingLevel;
|
|
99
118
|
}
|
|
@@ -110,6 +129,13 @@ function parsePreferences(rawPreferences) {
|
|
|
110
129
|
quietModeMaxToolPreviewLines: parseQuietModeMaxToolPreviewLines(raw.quietModeMaxToolPreviewLines)
|
|
111
130
|
};
|
|
112
131
|
}
|
|
132
|
+
function parseSignalSettings(rawSignals) {
|
|
133
|
+
const raw = rawSignals && typeof rawSignals === "object" ? rawSignals : {};
|
|
134
|
+
return {
|
|
135
|
+
unixSocketPubSub: typeof raw.unixSocketPubSub === "boolean" ? raw.unixSocketPubSub : DEFAULTS.signals.unixSocketPubSub,
|
|
136
|
+
experimentalGithubSignals: typeof raw.experimentalGithubSignals === "boolean" ? raw.experimentalGithubSignals : DEFAULTS.signals.experimentalGithubSignals
|
|
137
|
+
};
|
|
138
|
+
}
|
|
113
139
|
function hasQuietModePreferenceSelected(rawOnboarding) {
|
|
114
140
|
return Boolean(
|
|
115
141
|
rawOnboarding && typeof rawOnboarding === "object" && Object.prototype.hasOwnProperty.call(rawOnboarding, "quietModePreferenceSelected")
|
|
@@ -266,9 +292,7 @@ function migrateFromAuth(settingsPath) {
|
|
|
266
292
|
lsp: raw.lsp && typeof raw.lsp === "object" ? raw.lsp : void 0,
|
|
267
293
|
browser: parseBrowserSettings(raw.browser),
|
|
268
294
|
shellPassthrough: parseShellPassthroughSettings(raw.shellPassthrough),
|
|
269
|
-
signals:
|
|
270
|
-
unixSocketPubSub: raw.signals && typeof raw.signals === "object" && typeof raw.signals.unixSocketPubSub === "boolean" ? raw.signals.unixSocketPubSub : DEFAULTS.signals.unixSocketPubSub
|
|
271
|
-
},
|
|
295
|
+
signals: parseSignalSettings(raw.signals),
|
|
272
296
|
observability: parseObservabilitySettings(raw.observability)
|
|
273
297
|
};
|
|
274
298
|
applyQuietModePreferenceRollout(settings, raw.onboarding);
|
|
@@ -343,7 +367,7 @@ function migrateLegacyVariedPack(settings) {
|
|
|
343
367
|
}
|
|
344
368
|
function loadSettings(filePath = getSettingsPath()) {
|
|
345
369
|
migrateFromAuth(filePath);
|
|
346
|
-
if (!fs.existsSync(filePath)) return getNewInstallDefaults();
|
|
370
|
+
if (!fs.existsSync(filePath)) return rememberLoadedSettings(getNewInstallDefaults());
|
|
347
371
|
try {
|
|
348
372
|
const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
349
373
|
const settings = {
|
|
@@ -365,9 +389,7 @@ function loadSettings(filePath = getSettingsPath()) {
|
|
|
365
389
|
lsp: raw.lsp && typeof raw.lsp === "object" ? raw.lsp : void 0,
|
|
366
390
|
browser: parseBrowserSettings(raw.browser),
|
|
367
391
|
shellPassthrough: parseShellPassthroughSettings(raw.shellPassthrough),
|
|
368
|
-
signals:
|
|
369
|
-
unixSocketPubSub: raw.signals && typeof raw.signals === "object" && typeof raw.signals.unixSocketPubSub === "boolean" ? raw.signals.unixSocketPubSub : DEFAULTS.signals.unixSocketPubSub
|
|
370
|
-
},
|
|
392
|
+
signals: parseSignalSettings(raw.signals),
|
|
371
393
|
observability: parseObservabilitySettings(raw.observability)
|
|
372
394
|
};
|
|
373
395
|
let settingsChanged = false;
|
|
@@ -385,9 +407,9 @@ function loadSettings(filePath = getSettingsPath()) {
|
|
|
385
407
|
if (settingsChanged) {
|
|
386
408
|
saveSettings(settings, filePath);
|
|
387
409
|
}
|
|
388
|
-
return settings;
|
|
410
|
+
return rememberLoadedSettings(settings);
|
|
389
411
|
} catch {
|
|
390
|
-
return structuredClone(DEFAULTS);
|
|
412
|
+
return rememberLoadedSettings(structuredClone(DEFAULTS));
|
|
391
413
|
}
|
|
392
414
|
}
|
|
393
415
|
var THREAD_ACTIVE_MODEL_PACK_ID_KEY = "activeModelPackId";
|
|
@@ -456,11 +478,29 @@ function resolveOmRoleModel(settings, role, builtinOmPacks) {
|
|
|
456
478
|
if (pack) return pack.modelId;
|
|
457
479
|
return omModelOverride;
|
|
458
480
|
}
|
|
481
|
+
function getSignalSettingsForSave(settings, filePath) {
|
|
482
|
+
const loadedSignals = loadedSignalSettings.get(settings);
|
|
483
|
+
if (!loadedSignals || !signalSettingsEqual(settings.signals, loadedSignals) || !fs.existsSync(filePath)) {
|
|
484
|
+
return settings.signals;
|
|
485
|
+
}
|
|
486
|
+
try {
|
|
487
|
+
const currentRaw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
488
|
+
const currentSignals = parseSignalSettings(currentRaw.signals);
|
|
489
|
+
if (!signalSettingsEqual(currentSignals, loadedSignals)) {
|
|
490
|
+
return currentSignals;
|
|
491
|
+
}
|
|
492
|
+
} catch {
|
|
493
|
+
}
|
|
494
|
+
return settings.signals;
|
|
495
|
+
}
|
|
459
496
|
function saveSettings(settings, filePath = getSettingsPath()) {
|
|
460
497
|
const dir = path.dirname(filePath);
|
|
461
498
|
if (!fs.existsSync(dir)) {
|
|
462
499
|
fs.mkdirSync(dir, { recursive: true });
|
|
463
500
|
}
|
|
501
|
+
const signals = getSignalSettingsForSave(settings, filePath);
|
|
502
|
+
settings.signals = signals;
|
|
503
|
+
loadedSignalSettings.set(settings, cloneSignalSettings(signals));
|
|
464
504
|
fs.writeFileSync(filePath, JSON.stringify(settings, null, 2), "utf-8");
|
|
465
505
|
}
|
|
466
506
|
var PROFILE_PROVIDER_MARKER = ".mastra-provider";
|
|
@@ -1245,6 +1285,1084 @@ function getDynamicModel({ requestContext }) {
|
|
|
1245
1285
|
const thinkingLevel = harnessContext?.state?.thinkingLevel;
|
|
1246
1286
|
return resolveModel(modelId, { thinkingLevel, requestContext });
|
|
1247
1287
|
}
|
|
1288
|
+
var execFileAsync = util.promisify(child_process.execFile);
|
|
1289
|
+
var GITHUB_SUBSCRIBE_PR_TAG = "github-subscribe-pr";
|
|
1290
|
+
var GITHUB_UNSUBSCRIBE_PR_TAG = "github-unsubscribe-pr";
|
|
1291
|
+
var GITHUB_SYNC_STATUS_TAG = "github-sync-status";
|
|
1292
|
+
var GITHUB_SIGNALS_METADATA_KEY = "githubSignals";
|
|
1293
|
+
var createGithubTool = tools.createTool;
|
|
1294
|
+
function isPlainObject(value) {
|
|
1295
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1296
|
+
}
|
|
1297
|
+
function readString(value) {
|
|
1298
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1299
|
+
}
|
|
1300
|
+
function readNumber(value) {
|
|
1301
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
|
|
1302
|
+
if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
|
|
1303
|
+
return void 0;
|
|
1304
|
+
}
|
|
1305
|
+
function stableJson(value) {
|
|
1306
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
1307
|
+
if (isPlainObject(value)) {
|
|
1308
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
1309
|
+
}
|
|
1310
|
+
return JSON.stringify(value);
|
|
1311
|
+
}
|
|
1312
|
+
function snapshotHash(value) {
|
|
1313
|
+
return crypto.createHash("sha256").update(stableJson(value)).digest("hex");
|
|
1314
|
+
}
|
|
1315
|
+
function resolveHomePath(path2) {
|
|
1316
|
+
return path2.startsWith("~/") ? path.join(os.homedir(), path2.slice(2)) : path2;
|
|
1317
|
+
}
|
|
1318
|
+
async function getGitcrawlDbPath() {
|
|
1319
|
+
if (process.env.GITCRAWL_DB_PATH) return resolveHomePath(process.env.GITCRAWL_DB_PATH);
|
|
1320
|
+
const configPath = process.env.GITCRAWL_CONFIG_PATH ?? path.join(os.homedir(), ".config", "gitcrawl", "config.toml");
|
|
1321
|
+
try {
|
|
1322
|
+
const config = await promises.readFile(resolveHomePath(configPath), "utf8");
|
|
1323
|
+
const match = /^\s*db_path\s*=\s*['\"]([^'\"]+)['\"]/m.exec(config);
|
|
1324
|
+
if (match?.[1]) return resolveHomePath(match[1]);
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
1327
|
+
return path.join(os.homedir(), ".config", "gitcrawl", "gitcrawl.db");
|
|
1328
|
+
}
|
|
1329
|
+
async function queryGitcrawlDb(sql) {
|
|
1330
|
+
const dbPath = await getGitcrawlDbPath();
|
|
1331
|
+
const { stdout } = await execFileAsync("sqlite3", ["-json", dbPath, sql], { maxBuffer: 10 * 1024 * 1024 });
|
|
1332
|
+
return JSON.parse(stdout || "[]");
|
|
1333
|
+
}
|
|
1334
|
+
function sqlString(value) {
|
|
1335
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
1336
|
+
}
|
|
1337
|
+
function getSignalMetadata(message) {
|
|
1338
|
+
if (message.role !== "signal") return void 0;
|
|
1339
|
+
const signal = message.content.metadata?.signal;
|
|
1340
|
+
return isPlainObject(signal) ? signal : void 0;
|
|
1341
|
+
}
|
|
1342
|
+
function getGithubMetadata(threadMetadata) {
|
|
1343
|
+
const mastra2 = isPlainObject(threadMetadata?.mastra) ? threadMetadata.mastra : {};
|
|
1344
|
+
const githubSignals = isPlainObject(mastra2[GITHUB_SIGNALS_METADATA_KEY]) ? mastra2[GITHUB_SIGNALS_METADATA_KEY] : {};
|
|
1345
|
+
const rawSubscriptions = Array.isArray(githubSignals.subscriptions) ? githubSignals.subscriptions : [];
|
|
1346
|
+
const subscriptions = [];
|
|
1347
|
+
for (const rawSubscription of rawSubscriptions) {
|
|
1348
|
+
if (!isPlainObject(rawSubscription)) continue;
|
|
1349
|
+
const owner = readString(rawSubscription.owner);
|
|
1350
|
+
const repo = readString(rawSubscription.repo);
|
|
1351
|
+
const number = readNumber(rawSubscription.number);
|
|
1352
|
+
const subscribedAt = readString(rawSubscription.subscribedAt);
|
|
1353
|
+
const updatedAt = readString(rawSubscription.updatedAt);
|
|
1354
|
+
const lastSubscribeSignalId = readString(rawSubscription.lastSubscribeSignalId);
|
|
1355
|
+
if (!owner || !repo || !number || !subscribedAt || !updatedAt || !lastSubscribeSignalId) continue;
|
|
1356
|
+
subscriptions.push({
|
|
1357
|
+
owner,
|
|
1358
|
+
repo,
|
|
1359
|
+
number,
|
|
1360
|
+
subscribedAt,
|
|
1361
|
+
updatedAt,
|
|
1362
|
+
lastSubscribeSignalId,
|
|
1363
|
+
...readString(rawSubscription.lastSyncAt) ? { lastSyncAt: readString(rawSubscription.lastSyncAt) } : {},
|
|
1364
|
+
...rawSubscription.lastSyncStatus === "success" || rawSubscription.lastSyncStatus === "error" || rawSubscription.lastSyncStatus === "skipped" ? { lastSyncStatus: rawSubscription.lastSyncStatus } : {},
|
|
1365
|
+
...readString(rawSubscription.lastSyncError) ? { lastSyncError: readString(rawSubscription.lastSyncError) } : {},
|
|
1366
|
+
...readString(rawSubscription.lastObservedGithubUpdatedAt) ? { lastObservedGithubUpdatedAt: readString(rawSubscription.lastObservedGithubUpdatedAt) } : {},
|
|
1367
|
+
...readString(rawSubscription.lastObservedContentHash) ? { lastObservedContentHash: readString(rawSubscription.lastObservedContentHash) } : {},
|
|
1368
|
+
...readString(rawSubscription.lastObservedThreadContentHash) ? { lastObservedThreadContentHash: readString(rawSubscription.lastObservedThreadContentHash) } : {},
|
|
1369
|
+
...readString(rawSubscription.lastObservedHeadSha) ? { lastObservedHeadSha: readString(rawSubscription.lastObservedHeadSha) } : {},
|
|
1370
|
+
...readString(rawSubscription.lastObservedState) ? { lastObservedState: readString(rawSubscription.lastObservedState) } : {},
|
|
1371
|
+
...readString(rawSubscription.lastObservedMergeableState) ? { lastObservedMergeableState: readString(rawSubscription.lastObservedMergeableState) } : {},
|
|
1372
|
+
...readString(rawSubscription.lastObservedCiState) ? { lastObservedCiState: readString(rawSubscription.lastObservedCiState) } : {},
|
|
1373
|
+
...readString(rawSubscription.lastObservedReviewStateHash) ? { lastObservedReviewStateHash: readString(rawSubscription.lastObservedReviewStateHash) } : {},
|
|
1374
|
+
...readString(rawSubscription.lastNotificationAt) ? { lastNotificationAt: readString(rawSubscription.lastNotificationAt) } : {},
|
|
1375
|
+
...readString(rawSubscription.lastNotificationKind) ? { lastNotificationKind: readString(rawSubscription.lastNotificationKind) } : {},
|
|
1376
|
+
...rawSubscription.lastNotificationPriority === "medium" || rawSubscription.lastNotificationPriority === "high" ? { lastNotificationPriority: rawSubscription.lastNotificationPriority } : {},
|
|
1377
|
+
...readString(rawSubscription.lastNotificationSummary) ? { lastNotificationSummary: readString(rawSubscription.lastNotificationSummary) } : {}
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
subscriptions,
|
|
1382
|
+
...githubSignals.subscriptionHintShown === true ? { subscriptionHintShown: true } : {}
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
function setGithubMetadata(threadMetadata, githubSignals) {
|
|
1386
|
+
const existing = threadMetadata ?? {};
|
|
1387
|
+
const mastra2 = isPlainObject(existing.mastra) ? existing.mastra : {};
|
|
1388
|
+
const existingGithubSignals = isPlainObject(mastra2[GITHUB_SIGNALS_METADATA_KEY]) ? mastra2[GITHUB_SIGNALS_METADATA_KEY] : {};
|
|
1389
|
+
return {
|
|
1390
|
+
...existing,
|
|
1391
|
+
mastra: {
|
|
1392
|
+
...mastra2,
|
|
1393
|
+
[GITHUB_SIGNALS_METADATA_KEY]: {
|
|
1394
|
+
...existingGithubSignals,
|
|
1395
|
+
...githubSignals
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
function getFailingChecks(snapshot) {
|
|
1401
|
+
return (snapshot.checks ?? []).filter((check) => check.conclusion === "failure" || check.conclusion === "timed_out");
|
|
1402
|
+
}
|
|
1403
|
+
function getPendingChecks(snapshot) {
|
|
1404
|
+
return (snapshot.checks ?? []).filter((check) => check.status && check.status !== "completed");
|
|
1405
|
+
}
|
|
1406
|
+
function getPrLabel(subscription, snapshot) {
|
|
1407
|
+
const pr = `${subscription.owner}/${subscription.repo}#${subscription.number}`;
|
|
1408
|
+
return snapshot?.title ? `${pr}: ${snapshot.title}` : pr;
|
|
1409
|
+
}
|
|
1410
|
+
function getMergedNotificationSummary(label) {
|
|
1411
|
+
return `${label} was merged. This thread has been automatically unsubscribed from this PR. Resubscribe if you still need updates.`;
|
|
1412
|
+
}
|
|
1413
|
+
function getCheckUpdatedTime(check) {
|
|
1414
|
+
const value = check.updatedAt ? Date.parse(check.updatedAt) : Number.NaN;
|
|
1415
|
+
return Number.isFinite(value) ? value : 0;
|
|
1416
|
+
}
|
|
1417
|
+
function getCheckKey(check) {
|
|
1418
|
+
return `${check.name || "check"}:${check.detailsUrl || check.workflowName || ""}`;
|
|
1419
|
+
}
|
|
1420
|
+
function normalizeGithubChecksForSnapshot(input) {
|
|
1421
|
+
const latestCheckUpdatedAt = input.checkRows.reduce(
|
|
1422
|
+
(latest, check) => Math.max(latest, getCheckUpdatedTime(check)),
|
|
1423
|
+
0
|
|
1424
|
+
);
|
|
1425
|
+
const rows = [
|
|
1426
|
+
...input.checkRows,
|
|
1427
|
+
...input.workflowRows.filter(
|
|
1428
|
+
(workflow) => input.checkRows.length === 0 || getCheckUpdatedTime(workflow) >= latestCheckUpdatedAt
|
|
1429
|
+
)
|
|
1430
|
+
];
|
|
1431
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1432
|
+
for (const row of rows) {
|
|
1433
|
+
const key = getCheckKey(row);
|
|
1434
|
+
const existing = byKey.get(key);
|
|
1435
|
+
if (!existing) {
|
|
1436
|
+
byKey.set(key, row);
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
const rowTime = getCheckUpdatedTime(row);
|
|
1440
|
+
const existingTime = getCheckUpdatedTime(existing);
|
|
1441
|
+
if (rowTime > existingTime || rowTime === existingTime && existing.source === "workflow" && row.source === "check") {
|
|
1442
|
+
byKey.set(key, row);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return [...byKey.values()].map(({ source: _source, ...check }) => check).sort((a, b) => `${a.name}:${a.detailsUrl ?? ""}`.localeCompare(`${b.name}:${b.detailsUrl ?? ""}`));
|
|
1446
|
+
}
|
|
1447
|
+
function isBotOnlyActivity(snapshot) {
|
|
1448
|
+
return snapshot.latestCommentIsBot === true && (!snapshot.ciState || snapshot.ciState === "unknown");
|
|
1449
|
+
}
|
|
1450
|
+
function stringifyEvidence(value) {
|
|
1451
|
+
if (typeof value === "string") return value;
|
|
1452
|
+
try {
|
|
1453
|
+
return JSON.stringify(value);
|
|
1454
|
+
} catch {
|
|
1455
|
+
return "";
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
function detectPrWorkEvidence(input) {
|
|
1459
|
+
const evidence = [
|
|
1460
|
+
input.text ?? "",
|
|
1461
|
+
...(input.toolCalls ?? []).map((toolCall) => `${toolCall.toolName} ${stringifyEvidence(toolCall.args)}`)
|
|
1462
|
+
].join("\n");
|
|
1463
|
+
if (!evidence.trim()) return void 0;
|
|
1464
|
+
const url = /github\.com\/([^\s/#]+)\/([^\s/#]+)\/pull\/(\d+)/i.exec(evidence);
|
|
1465
|
+
if (url?.[1] && url[2] && url[3]) return { owner: url[1], repo: url[2], number: Number(url[3]) };
|
|
1466
|
+
const repoRef = /\b([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(\d+)\b/.exec(evidence);
|
|
1467
|
+
if (repoRef?.[1] && repoRef[2] && repoRef[3])
|
|
1468
|
+
return { owner: repoRef[1], repo: repoRef[2], number: Number(repoRef[3]) };
|
|
1469
|
+
const ghCommand = /\bgh\s+(?:pr\s+(?:view|checks|status|comment|diff|checkout)|run\s+(?:rerun|view))\b/i.test(
|
|
1470
|
+
evidence
|
|
1471
|
+
);
|
|
1472
|
+
if (!ghCommand) return void 0;
|
|
1473
|
+
const numberMatch = /(?:^|\s)#?(\d{2,})(?:\s|$)/.exec(evidence);
|
|
1474
|
+
return numberMatch?.[1] ? { number: Number(numberMatch[1]) } : void 0;
|
|
1475
|
+
}
|
|
1476
|
+
function classifyGithubActivityNotification(input) {
|
|
1477
|
+
const pr = `${input.subscription.owner}/${input.subscription.repo}#${input.subscription.number}`;
|
|
1478
|
+
const label = getPrLabel(input.subscription, input.snapshot);
|
|
1479
|
+
if (input.snapshot.state && input.subscription.lastObservedState !== input.snapshot.state) {
|
|
1480
|
+
if (input.snapshot.state === "merged")
|
|
1481
|
+
return {
|
|
1482
|
+
kind: "pull-request-merged",
|
|
1483
|
+
priority: "high",
|
|
1484
|
+
summary: getMergedNotificationSummary(label)
|
|
1485
|
+
};
|
|
1486
|
+
if (input.snapshot.state === "closed")
|
|
1487
|
+
return { kind: "pull-request-closed", priority: "high", summary: `${label} was closed` };
|
|
1488
|
+
if (input.subscription.lastObservedState && input.snapshot.state === "open")
|
|
1489
|
+
return { kind: "pull-request-reopened", priority: "medium", summary: `${label} was reopened` };
|
|
1490
|
+
}
|
|
1491
|
+
const failingChecks = getFailingChecks(input.snapshot);
|
|
1492
|
+
if (input.snapshot.ciState === "failure" && input.subscription.lastObservedCiState !== "failure") {
|
|
1493
|
+
const names = failingChecks.slice(0, 3).map((check) => check.name).join(", ");
|
|
1494
|
+
return {
|
|
1495
|
+
kind: "pull-request-ci-failure",
|
|
1496
|
+
priority: "high",
|
|
1497
|
+
summary: `${pr} has failing CI${names ? `: ${names}` : ""}`
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
if (input.snapshot.mergeableState === "dirty" && input.subscription.lastObservedMergeableState !== "dirty") {
|
|
1501
|
+
return {
|
|
1502
|
+
kind: "pull-request-conflict",
|
|
1503
|
+
priority: "high",
|
|
1504
|
+
summary: `${pr} has merge conflicts${input.snapshot.title ? `: ${input.snapshot.title}` : ""}`
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
if (input.snapshot.mergeableState && input.subscription.lastObservedMergeableState === "dirty" && input.snapshot.mergeableState !== "dirty") {
|
|
1508
|
+
return {
|
|
1509
|
+
kind: "pull-request-conflict-resolved",
|
|
1510
|
+
priority: "medium",
|
|
1511
|
+
summary: `${pr} merge conflicts were resolved`
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
if (input.snapshot.mergeableState === "dirty") return void 0;
|
|
1515
|
+
if (input.snapshot.ciState === "success" && input.subscription.lastObservedCiState && input.subscription.lastObservedCiState !== "success") {
|
|
1516
|
+
return { kind: "pull-request-ci-recovered", priority: "medium", summary: `${pr} CI recovered` };
|
|
1517
|
+
}
|
|
1518
|
+
if (input.snapshot.reviewStateHash && input.subscription.lastObservedReviewStateHash && input.snapshot.reviewStateHash !== input.subscription.lastObservedReviewStateHash && (input.snapshot.unresolvedReviewThreads ?? 0) > 0) {
|
|
1519
|
+
return {
|
|
1520
|
+
kind: "pull-request-review-activity",
|
|
1521
|
+
priority: "medium",
|
|
1522
|
+
summary: `${pr} has ${input.snapshot.unresolvedReviewThreads} unresolved review thread${input.snapshot.unresolvedReviewThreads === 1 ? "" : "s"}`
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
const pendingChecks = getPendingChecks(input.snapshot);
|
|
1526
|
+
if (input.snapshot.ciState === "pending" && input.subscription.lastObservedCiState !== "pending" && pendingChecks.length > 0) {
|
|
1527
|
+
const names = pendingChecks.slice(0, 3).map((check) => check.name).join(", ");
|
|
1528
|
+
return {
|
|
1529
|
+
kind: "pull-request-ci-pending",
|
|
1530
|
+
priority: "medium",
|
|
1531
|
+
summary: `${pr} has CI still running${names ? `: ${names}` : ""}`
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
if (input.snapshot.ciState === "pending" && input.subscription.lastObservedCiState === "pending") return void 0;
|
|
1535
|
+
if (isBotOnlyActivity(input.snapshot)) return void 0;
|
|
1536
|
+
return {
|
|
1537
|
+
kind: "pull-request-activity",
|
|
1538
|
+
priority: "medium",
|
|
1539
|
+
summary: `${pr} has new activity${input.snapshot.title ? `: ${input.snapshot.title}` : ""}`
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
function classifyGithubBaselineNotification(input) {
|
|
1543
|
+
const pr = `${input.subscription.owner}/${input.subscription.repo}#${input.subscription.number}`;
|
|
1544
|
+
const failingChecks = getFailingChecks(input.snapshot);
|
|
1545
|
+
const reviewCount = input.snapshot.unresolvedReviewThreads ?? 0;
|
|
1546
|
+
const high = input.snapshot.ciState === "failure" || input.snapshot.mergeableState === "dirty";
|
|
1547
|
+
const details = [
|
|
1548
|
+
input.snapshot.state ? `state: ${input.snapshot.state}` : void 0,
|
|
1549
|
+
input.snapshot.ciState && input.snapshot.ciState !== "unknown" ? `CI: ${input.snapshot.ciState}` : void 0,
|
|
1550
|
+
input.snapshot.mergeableState ? `mergeability: ${input.snapshot.mergeableState}` : void 0,
|
|
1551
|
+
reviewCount > 0 ? `${reviewCount} unresolved review thread${reviewCount === 1 ? "" : "s"}` : void 0,
|
|
1552
|
+
failingChecks.length > 0 ? `failing: ${failingChecks.slice(0, 3).map((check) => check.name).join(", ")}` : void 0
|
|
1553
|
+
].filter(Boolean);
|
|
1554
|
+
return {
|
|
1555
|
+
kind: "pull-request-baseline",
|
|
1556
|
+
priority: high ? "high" : "medium",
|
|
1557
|
+
summary: `${pr} subscribed${input.snapshot.title ? `: ${input.snapshot.title}` : ""}${details.length ? ` (${details.join("; ")})` : ""}`
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
function applySnapshotCursor(subscription, snapshot) {
|
|
1561
|
+
if (snapshot.githubUpdatedAt) subscription.lastObservedGithubUpdatedAt = snapshot.githubUpdatedAt;
|
|
1562
|
+
if (snapshot.contentHash) subscription.lastObservedContentHash = snapshot.contentHash;
|
|
1563
|
+
if (snapshot.threadContentHash) subscription.lastObservedThreadContentHash = snapshot.threadContentHash;
|
|
1564
|
+
if (snapshot.headSha) subscription.lastObservedHeadSha = snapshot.headSha;
|
|
1565
|
+
if (snapshot.state) subscription.lastObservedState = snapshot.state;
|
|
1566
|
+
if (snapshot.mergeableState) subscription.lastObservedMergeableState = snapshot.mergeableState;
|
|
1567
|
+
if (snapshot.ciState) subscription.lastObservedCiState = snapshot.ciState;
|
|
1568
|
+
if (snapshot.reviewStateHash) subscription.lastObservedReviewStateHash = snapshot.reviewStateHash;
|
|
1569
|
+
}
|
|
1570
|
+
function parseGitHubRemoteUrl(remoteUrl) {
|
|
1571
|
+
const trimmed = remoteUrl.trim().replace(/\.git$/, "");
|
|
1572
|
+
const httpsMatch = /^https:\/\/github\.com\/([^/]+)\/([^/]+)$/.exec(trimmed);
|
|
1573
|
+
if (httpsMatch?.[1] && httpsMatch[2]) return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
1574
|
+
const sshMatch = /^git@github\.com:([^/]+)\/([^/]+)$/.exec(trimmed);
|
|
1575
|
+
if (sshMatch?.[1] && sshMatch[2]) return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
1576
|
+
return void 0;
|
|
1577
|
+
}
|
|
1578
|
+
var GitRemoteRepositoryResolver = class {
|
|
1579
|
+
async resolveRepository(input) {
|
|
1580
|
+
try {
|
|
1581
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
|
|
1582
|
+
cwd: input.cwd,
|
|
1583
|
+
signal: input.abortSignal
|
|
1584
|
+
});
|
|
1585
|
+
return parseGitHubRemoteUrl(stdout);
|
|
1586
|
+
} catch {
|
|
1587
|
+
return void 0;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
var GitcrawlSyncClient = class {
|
|
1592
|
+
#command;
|
|
1593
|
+
constructor(options = {}) {
|
|
1594
|
+
this.#command = options.command ?? "gitcrawl";
|
|
1595
|
+
}
|
|
1596
|
+
async syncPullRequest(input) {
|
|
1597
|
+
try {
|
|
1598
|
+
const args = [
|
|
1599
|
+
"sync",
|
|
1600
|
+
`${input.owner}/${input.repo}`,
|
|
1601
|
+
"--numbers",
|
|
1602
|
+
String(input.number),
|
|
1603
|
+
...input.includeComments === false ? [] : ["--include-comments"],
|
|
1604
|
+
"--with",
|
|
1605
|
+
"pr-details",
|
|
1606
|
+
"--json"
|
|
1607
|
+
];
|
|
1608
|
+
const { stdout, stderr } = await execFileAsync(this.#command, args, {
|
|
1609
|
+
cwd: input.cwd,
|
|
1610
|
+
signal: input.abortSignal,
|
|
1611
|
+
maxBuffer: 10 * 1024 * 1024
|
|
1612
|
+
});
|
|
1613
|
+
return { ok: true, stdout, stderr };
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
async getPullRequestSnapshot(input) {
|
|
1619
|
+
try {
|
|
1620
|
+
const { stdout } = await execFileAsync(
|
|
1621
|
+
this.#command,
|
|
1622
|
+
["threads", `${input.owner}/${input.repo}`, "--numbers", String(input.number), "--json"],
|
|
1623
|
+
{
|
|
1624
|
+
cwd: input.cwd,
|
|
1625
|
+
signal: input.abortSignal,
|
|
1626
|
+
maxBuffer: 10 * 1024 * 1024
|
|
1627
|
+
}
|
|
1628
|
+
);
|
|
1629
|
+
const parsed = JSON.parse(stdout);
|
|
1630
|
+
const thread = parsed.threads?.find((item) => readNumber(item.number) === input.number);
|
|
1631
|
+
if (!thread) return void 0;
|
|
1632
|
+
const owner = sqlString(input.owner);
|
|
1633
|
+
const repo = sqlString(input.repo);
|
|
1634
|
+
const number = input.number;
|
|
1635
|
+
const [threadDetails] = await queryGitcrawlDb(`select t.state, t.closed_at_gh, t.merged_at_gh
|
|
1636
|
+
from threads t
|
|
1637
|
+
join repositories r on r.id=t.repo_id
|
|
1638
|
+
where r.owner=${owner} and r.name=${repo} and t.number=${number}
|
|
1639
|
+
limit 1`);
|
|
1640
|
+
const [details] = await queryGitcrawlDb(`select d.head_sha, d.head_ref, d.mergeable_state,
|
|
1641
|
+
json_extract(d.raw_json, '$.merged_at') as merged_at
|
|
1642
|
+
from pull_request_details d
|
|
1643
|
+
join threads t on t.id=d.thread_id
|
|
1644
|
+
join repositories r on r.id=t.repo_id
|
|
1645
|
+
where r.owner=${owner} and r.name=${repo} and t.number=${number}
|
|
1646
|
+
limit 1`);
|
|
1647
|
+
const headSha = readString(details?.head_sha);
|
|
1648
|
+
const checkRows = await queryGitcrawlDb(`select c.name, c.status, c.conclusion, c.workflow_name, c.details_url,
|
|
1649
|
+
coalesce(c.completed_at, c.started_at, c.fetched_at) as updated_at
|
|
1650
|
+
from pull_request_checks c
|
|
1651
|
+
join threads t on t.id=c.thread_id
|
|
1652
|
+
join repositories r on r.id=t.repo_id
|
|
1653
|
+
where r.owner=${owner} and r.name=${repo} and t.number=${number}${headSha ? ` and json_extract(c.raw_json, '$.head_sha')=${sqlString(headSha)}` : ""}`);
|
|
1654
|
+
const workflowRows = details?.head_sha ? await queryGitcrawlDb(`select workflow_name, status, conclusion, html_url, updated_at_gh
|
|
1655
|
+
from github_workflow_runs w
|
|
1656
|
+
join repositories r on r.id=w.repo_id
|
|
1657
|
+
where r.owner=${owner} and r.name=${repo} and w.head_sha=${sqlString(details.head_sha)}`) : [];
|
|
1658
|
+
const [reviewState] = await queryGitcrawlDb(`select count(*) as unresolved_count,
|
|
1659
|
+
max(coalesce(first_comment_updated_at, first_comment_created_at, fetched_at)) as latest_review_thread_at
|
|
1660
|
+
from pull_request_review_threads rt
|
|
1661
|
+
join threads t on t.id=rt.thread_id
|
|
1662
|
+
join repositories r on r.id=t.repo_id
|
|
1663
|
+
where r.owner=${owner} and r.name=${repo} and t.number=${number} and rt.is_resolved=0`);
|
|
1664
|
+
const [latestComment] = await queryGitcrawlDb(`select c.author_login, c.author_type, c.is_bot
|
|
1665
|
+
from comments c
|
|
1666
|
+
join threads t on t.id=c.thread_id
|
|
1667
|
+
join repositories r on r.id=t.repo_id
|
|
1668
|
+
where r.owner=${owner} and r.name=${repo} and t.number=${number}
|
|
1669
|
+
order by coalesce(c.updated_at_gh, c.created_at_gh) desc
|
|
1670
|
+
limit 1`);
|
|
1671
|
+
const checks = normalizeGithubChecksForSnapshot({
|
|
1672
|
+
checkRows: checkRows.map((row) => ({
|
|
1673
|
+
source: "check",
|
|
1674
|
+
name: readString(row.name) ?? "check",
|
|
1675
|
+
status: readString(row.status),
|
|
1676
|
+
conclusion: readString(row.conclusion),
|
|
1677
|
+
workflowName: readString(row.workflow_name),
|
|
1678
|
+
detailsUrl: readString(row.details_url),
|
|
1679
|
+
updatedAt: readString(row.updated_at)
|
|
1680
|
+
})),
|
|
1681
|
+
workflowRows: workflowRows.map((row) => ({
|
|
1682
|
+
source: "workflow",
|
|
1683
|
+
name: readString(row.workflow_name) ?? "workflow",
|
|
1684
|
+
status: readString(row.status),
|
|
1685
|
+
conclusion: readString(row.conclusion),
|
|
1686
|
+
workflowName: readString(row.workflow_name),
|
|
1687
|
+
detailsUrl: readString(row.html_url),
|
|
1688
|
+
updatedAt: readString(row.updated_at_gh)
|
|
1689
|
+
}))
|
|
1690
|
+
});
|
|
1691
|
+
const ciState = checks.some((check) => check.conclusion === "failure" || check.conclusion === "timed_out") ? "failure" : checks.some((check) => check.status && check.status !== "completed") ? "pending" : checks.length > 0 ? "success" : "unknown";
|
|
1692
|
+
const threadContentHash = readString(thread.content_hash);
|
|
1693
|
+
const unresolvedReviewThreads = Number(reviewState?.unresolved_count ?? 0);
|
|
1694
|
+
const reviewStateHash = snapshotHash({
|
|
1695
|
+
unresolvedReviewThreads,
|
|
1696
|
+
latestReviewThreadAt: reviewState?.latest_review_thread_at
|
|
1697
|
+
});
|
|
1698
|
+
const contentHash = snapshotHash({
|
|
1699
|
+
threadContentHash,
|
|
1700
|
+
state: thread.state,
|
|
1701
|
+
headSha: details?.head_sha,
|
|
1702
|
+
mergeableState: details?.mergeable_state,
|
|
1703
|
+
ciState,
|
|
1704
|
+
reviewStateHash,
|
|
1705
|
+
checks: checks.map((check) => ({
|
|
1706
|
+
name: check.name,
|
|
1707
|
+
status: check.status,
|
|
1708
|
+
conclusion: check.conclusion,
|
|
1709
|
+
detailsUrl: check.detailsUrl,
|
|
1710
|
+
updatedAt: check.updatedAt
|
|
1711
|
+
}))
|
|
1712
|
+
});
|
|
1713
|
+
return {
|
|
1714
|
+
title: readString(thread.title),
|
|
1715
|
+
state: readString(details?.merged_at) || readString(threadDetails?.merged_at_gh) ? "merged" : readString(threadDetails?.state) ?? readString(thread.state),
|
|
1716
|
+
htmlUrl: readString(thread.html_url),
|
|
1717
|
+
githubUpdatedAt: readString(thread.updated_at_gh),
|
|
1718
|
+
closedAt: readString(threadDetails?.closed_at_gh),
|
|
1719
|
+
mergedAt: readString(details?.merged_at) ?? readString(threadDetails?.merged_at_gh),
|
|
1720
|
+
threadContentHash,
|
|
1721
|
+
contentHash,
|
|
1722
|
+
headSha: readString(details?.head_sha),
|
|
1723
|
+
headRef: readString(details?.head_ref),
|
|
1724
|
+
mergeableState: readString(details?.mergeable_state),
|
|
1725
|
+
checks,
|
|
1726
|
+
ciState,
|
|
1727
|
+
unresolvedReviewThreads,
|
|
1728
|
+
reviewStateHash,
|
|
1729
|
+
latestReviewThreadAt: readString(reviewState?.latest_review_thread_at),
|
|
1730
|
+
latestCommentAuthor: readString(latestComment?.author_login),
|
|
1731
|
+
latestCommentAuthorType: readString(latestComment?.author_type),
|
|
1732
|
+
latestCommentIsBot: latestComment?.is_bot === 1
|
|
1733
|
+
};
|
|
1734
|
+
} catch {
|
|
1735
|
+
return void 0;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
var GithubSignals = class {
|
|
1740
|
+
id = "github-signals";
|
|
1741
|
+
name = "GitHub Signals";
|
|
1742
|
+
mastra;
|
|
1743
|
+
static signals = {
|
|
1744
|
+
subscribeToPR(input) {
|
|
1745
|
+
const normalized = typeof input === "number" ? { number: input } : input;
|
|
1746
|
+
return {
|
|
1747
|
+
type: "reactive",
|
|
1748
|
+
tagName: GITHUB_SUBSCRIBE_PR_TAG,
|
|
1749
|
+
contents: `Subscribe to GitHub PR #${normalized.number}`,
|
|
1750
|
+
attributes: {
|
|
1751
|
+
...normalized.owner ? { owner: normalized.owner } : {},
|
|
1752
|
+
...normalized.repo ? { repo: normalized.repo } : {},
|
|
1753
|
+
number: normalized.number
|
|
1754
|
+
},
|
|
1755
|
+
metadata: {
|
|
1756
|
+
github: {
|
|
1757
|
+
action: "subscribeToPR",
|
|
1758
|
+
...normalized
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
};
|
|
1762
|
+
},
|
|
1763
|
+
unsubscribeFromPR(input) {
|
|
1764
|
+
const normalized = typeof input === "number" ? { number: input } : input;
|
|
1765
|
+
return {
|
|
1766
|
+
type: "reactive",
|
|
1767
|
+
tagName: GITHUB_UNSUBSCRIBE_PR_TAG,
|
|
1768
|
+
contents: `Unsubscribe from GitHub PR #${normalized.number}`,
|
|
1769
|
+
attributes: {
|
|
1770
|
+
...normalized.owner ? { owner: normalized.owner } : {},
|
|
1771
|
+
...normalized.repo ? { repo: normalized.repo } : {},
|
|
1772
|
+
number: normalized.number
|
|
1773
|
+
},
|
|
1774
|
+
metadata: {
|
|
1775
|
+
github: {
|
|
1776
|
+
action: "unsubscribeFromPR",
|
|
1777
|
+
...normalized
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
#options;
|
|
1784
|
+
#syncClient;
|
|
1785
|
+
#repositoryResolver;
|
|
1786
|
+
#polling = /* @__PURE__ */ new Map();
|
|
1787
|
+
#agent;
|
|
1788
|
+
#agentOptions = {};
|
|
1789
|
+
#subscriptionsChangedHandler;
|
|
1790
|
+
constructor(options = {}) {
|
|
1791
|
+
this.#options = options;
|
|
1792
|
+
this.#syncClient = options.syncClient ?? new GitcrawlSyncClient({ command: options.gitcrawlCommand });
|
|
1793
|
+
this.#repositoryResolver = options.repositoryResolver ?? new GitRemoteRepositoryResolver();
|
|
1794
|
+
}
|
|
1795
|
+
addAgent(agent, options = {}) {
|
|
1796
|
+
this.#agent = agent;
|
|
1797
|
+
this.#agentOptions = options;
|
|
1798
|
+
}
|
|
1799
|
+
onSubscriptionsChanged(handler) {
|
|
1800
|
+
this.#subscriptionsChangedHandler = handler;
|
|
1801
|
+
}
|
|
1802
|
+
__registerMastra(mastra2) {
|
|
1803
|
+
this.mastra = mastra2;
|
|
1804
|
+
}
|
|
1805
|
+
async syncThreadNow(input) {
|
|
1806
|
+
return this.#pollThread(input, { includeComments: true });
|
|
1807
|
+
}
|
|
1808
|
+
async subscribeThreadToPR(input) {
|
|
1809
|
+
const pr = typeof input.pr === "number" ? { number: input.pr } : input.pr;
|
|
1810
|
+
return this.#subscribe({
|
|
1811
|
+
id: `github-command-subscribe-${crypto.randomUUID()}`,
|
|
1812
|
+
...pr,
|
|
1813
|
+
threadId: input.threadId,
|
|
1814
|
+
resourceId: input.resourceId
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
async unsubscribeThreadFromPR(input) {
|
|
1818
|
+
const pr = typeof input.pr === "number" ? { number: input.pr } : input.pr;
|
|
1819
|
+
return this.#unsubscribe({
|
|
1820
|
+
id: `github-command-unsubscribe-${crypto.randomUUID()}`,
|
|
1821
|
+
...pr,
|
|
1822
|
+
threadId: input.threadId,
|
|
1823
|
+
resourceId: input.resourceId
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
async startPollingForThread(input, options = {}) {
|
|
1827
|
+
const subscriptions = await this.#getThreadSubscriptions(input);
|
|
1828
|
+
if (subscriptions.length === 0) {
|
|
1829
|
+
this.stopPollingForThread(input);
|
|
1830
|
+
return false;
|
|
1831
|
+
}
|
|
1832
|
+
const key = this.#pollingKey(input);
|
|
1833
|
+
for (const [pollingKey, state] of this.#polling.entries()) {
|
|
1834
|
+
if (pollingKey === key) continue;
|
|
1835
|
+
clearInterval(state.timer);
|
|
1836
|
+
this.#polling.delete(pollingKey);
|
|
1837
|
+
}
|
|
1838
|
+
if (this.#polling.has(key)) return true;
|
|
1839
|
+
let scheduledPollCount = options.pollImmediately ? 1 : 0;
|
|
1840
|
+
const runPoll = (pollOptions = {}) => {
|
|
1841
|
+
void this.#pollThread(input, pollOptions).catch((error) => {
|
|
1842
|
+
console.warn("GitHub PR polling failed:", error);
|
|
1843
|
+
});
|
|
1844
|
+
};
|
|
1845
|
+
const timer = setInterval(() => {
|
|
1846
|
+
scheduledPollCount += 1;
|
|
1847
|
+
runPoll({ includeComments: scheduledPollCount % 2 === 1 });
|
|
1848
|
+
}, this.#options.pollIntervalMs ?? 3e5);
|
|
1849
|
+
if (options.pollImmediately) runPoll({ includeComments: true });
|
|
1850
|
+
timer.unref?.();
|
|
1851
|
+
this.#polling.set(key, { ...input, timer, running: false });
|
|
1852
|
+
return true;
|
|
1853
|
+
}
|
|
1854
|
+
stopPollingForThread(input) {
|
|
1855
|
+
const key = this.#pollingKey(input);
|
|
1856
|
+
const state = this.#polling.get(key);
|
|
1857
|
+
if (!state) return;
|
|
1858
|
+
clearInterval(state.timer);
|
|
1859
|
+
this.#polling.delete(key);
|
|
1860
|
+
}
|
|
1861
|
+
isPollingThread(input) {
|
|
1862
|
+
return this.#polling.has(this.#pollingKey(input));
|
|
1863
|
+
}
|
|
1864
|
+
getPollIntervalMs() {
|
|
1865
|
+
return this.#options.pollIntervalMs ?? 3e5;
|
|
1866
|
+
}
|
|
1867
|
+
stopAllPolling() {
|
|
1868
|
+
for (const state of this.#polling.values()) clearInterval(state.timer);
|
|
1869
|
+
this.#polling.clear();
|
|
1870
|
+
}
|
|
1871
|
+
async pollThreadNow(input) {
|
|
1872
|
+
return this.#pollThread(input, { includeComments: true });
|
|
1873
|
+
}
|
|
1874
|
+
async processInputStep(args) {
|
|
1875
|
+
const tools = this.#createTools(args);
|
|
1876
|
+
if (args.stepNumber !== 0) return { tools };
|
|
1877
|
+
const signal = this.#findLatestGithubSignal(args.messages);
|
|
1878
|
+
if (!signal) return { tools };
|
|
1879
|
+
const threadContext = this.#getThreadContext(args);
|
|
1880
|
+
if (signal.tagName === GITHUB_UNSUBSCRIBE_PR_TAG) {
|
|
1881
|
+
const result2 = await this.#unsubscribe({ ...signal, ...threadContext, abortSignal: args.abortSignal });
|
|
1882
|
+
await this.#sendStatus(args, result2, {
|
|
1883
|
+
status: result2.removed ? "unsubscribed" : "not_subscribed",
|
|
1884
|
+
action: "unsubscribeFromPR",
|
|
1885
|
+
message: result2.removed ? `Unsubscribed from ${result2.owner}/${result2.repo}#${result2.number}.` : `No GitHub subscription found for ${result2.owner}/${result2.repo}#${result2.number}.`
|
|
1886
|
+
});
|
|
1887
|
+
return { tools };
|
|
1888
|
+
}
|
|
1889
|
+
const result = await this.#subscribe({ ...signal, ...threadContext, abortSignal: args.abortSignal });
|
|
1890
|
+
if (result.alreadyProcessed) return { tools };
|
|
1891
|
+
await this.#sendStatus(args, result, {
|
|
1892
|
+
status: result.syncResult?.ok === false ? "sync_error" : "subscribed",
|
|
1893
|
+
action: "subscribeToPR",
|
|
1894
|
+
message: result.syncResult?.ok === false ? `Subscribed to ${result.owner}/${result.repo}#${result.number}, but gitcrawl sync failed: ${result.syncResult.error}` : `Subscribed to ${result.owner}/${result.repo}#${result.number}.`
|
|
1895
|
+
});
|
|
1896
|
+
return { tools };
|
|
1897
|
+
}
|
|
1898
|
+
async processOutputStep(args) {
|
|
1899
|
+
const evidence = detectPrWorkEvidence({ text: args.text, toolCalls: args.toolCalls });
|
|
1900
|
+
if (!evidence) return args.messages;
|
|
1901
|
+
const threadContext = this.#getThreadContext(args);
|
|
1902
|
+
if (!threadContext.threadId || !threadContext.resourceId) return args.messages;
|
|
1903
|
+
const { threadStore, loadedThread } = await this.#loadThread(threadContext);
|
|
1904
|
+
const githubMetadata = getGithubMetadata(loadedThread.metadata);
|
|
1905
|
+
if (githubMetadata.subscriptionHintShown || githubMetadata.subscriptions.length > 0) return args.messages;
|
|
1906
|
+
let repository;
|
|
1907
|
+
try {
|
|
1908
|
+
repository = await this.#resolveRepository({
|
|
1909
|
+
id: "github-subscription-hint",
|
|
1910
|
+
owner: evidence.owner,
|
|
1911
|
+
repo: evidence.repo,
|
|
1912
|
+
number: evidence.number
|
|
1913
|
+
});
|
|
1914
|
+
} catch {
|
|
1915
|
+
return args.messages;
|
|
1916
|
+
}
|
|
1917
|
+
await threadStore.saveThread({
|
|
1918
|
+
thread: {
|
|
1919
|
+
...loadedThread,
|
|
1920
|
+
id: threadContext.threadId,
|
|
1921
|
+
resourceId: threadContext.resourceId,
|
|
1922
|
+
createdAt: loadedThread.createdAt ?? /* @__PURE__ */ new Date(),
|
|
1923
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
1924
|
+
metadata: setGithubMetadata(loadedThread.metadata, { ...githubMetadata, subscriptionHintShown: true })
|
|
1925
|
+
}
|
|
1926
|
+
});
|
|
1927
|
+
await args.sendSignal?.({
|
|
1928
|
+
type: "reactive",
|
|
1929
|
+
tagName: "system-reminder",
|
|
1930
|
+
contents: `Looks like you're working with ${repository.owner}/${repository.repo}#${evidence.number}. Use /github subscribe ${evidence.number} or the github_subscribe_pr tool to follow updates.`,
|
|
1931
|
+
attributes: { type: "github-subscription-hint" },
|
|
1932
|
+
metadata: {
|
|
1933
|
+
github: {
|
|
1934
|
+
action: "subscriptionHint",
|
|
1935
|
+
owner: repository.owner,
|
|
1936
|
+
repo: repository.repo,
|
|
1937
|
+
number: evidence.number
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
});
|
|
1941
|
+
return args.messages;
|
|
1942
|
+
}
|
|
1943
|
+
async #resolveThreadStore() {
|
|
1944
|
+
if (this.#options.threadStore) return this.#options.threadStore;
|
|
1945
|
+
const storage = this.mastra?.getStorage?.();
|
|
1946
|
+
const memoryStore = storage?.getStore ? await storage.getStore("memory") : void 0;
|
|
1947
|
+
return memoryStore;
|
|
1948
|
+
}
|
|
1949
|
+
#getThreadContext(args) {
|
|
1950
|
+
const memoryContext = args.requestContext?.get("MastraMemory");
|
|
1951
|
+
return { threadId: memoryContext?.thread?.id, resourceId: memoryContext?.resourceId };
|
|
1952
|
+
}
|
|
1953
|
+
#createTools(args) {
|
|
1954
|
+
const threadContext = this.#getThreadContext(args);
|
|
1955
|
+
return {
|
|
1956
|
+
...args.tools,
|
|
1957
|
+
github_subscribe_pr: createGithubTool({
|
|
1958
|
+
id: "github_subscribe_pr",
|
|
1959
|
+
description: "Subscribe this thread to a GitHub pull request. Syncs only the requested PR with gitcrawl and stores the subscription on the thread.",
|
|
1960
|
+
inputSchema: z__default.default.object({
|
|
1961
|
+
number: z__default.default.number().int().positive(),
|
|
1962
|
+
owner: z__default.default.string().optional(),
|
|
1963
|
+
repo: z__default.default.string().optional()
|
|
1964
|
+
}),
|
|
1965
|
+
execute: async (input) => {
|
|
1966
|
+
const result = await this.#subscribe({
|
|
1967
|
+
id: `github-tool-subscribe-${crypto.randomUUID()}`,
|
|
1968
|
+
owner: input.owner,
|
|
1969
|
+
repo: input.repo,
|
|
1970
|
+
number: input.number,
|
|
1971
|
+
threadId: threadContext.threadId,
|
|
1972
|
+
resourceId: threadContext.resourceId
|
|
1973
|
+
});
|
|
1974
|
+
return {
|
|
1975
|
+
subscribed: true,
|
|
1976
|
+
owner: result.owner,
|
|
1977
|
+
repo: result.repo,
|
|
1978
|
+
number: result.number,
|
|
1979
|
+
syncStatus: result.syncResult?.ok === false ? "error" : result.syncResult ? "success" : void 0,
|
|
1980
|
+
message: result.syncResult?.ok === false ? `Subscribed to ${result.owner}/${result.repo}#${result.number}, but gitcrawl sync failed: ${result.syncResult.error}` : `Subscribed to ${result.owner}/${result.repo}#${result.number}.`
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
}),
|
|
1984
|
+
github_unsubscribe_pr: createGithubTool({
|
|
1985
|
+
id: "github_unsubscribe_pr",
|
|
1986
|
+
description: "Unsubscribe this thread from a GitHub pull request.",
|
|
1987
|
+
inputSchema: z__default.default.object({
|
|
1988
|
+
number: z__default.default.number().int().positive(),
|
|
1989
|
+
owner: z__default.default.string().optional(),
|
|
1990
|
+
repo: z__default.default.string().optional()
|
|
1991
|
+
}),
|
|
1992
|
+
execute: async (input) => {
|
|
1993
|
+
const result = await this.#unsubscribe({
|
|
1994
|
+
id: `github-tool-unsubscribe-${crypto.randomUUID()}`,
|
|
1995
|
+
owner: input.owner,
|
|
1996
|
+
repo: input.repo,
|
|
1997
|
+
number: input.number,
|
|
1998
|
+
threadId: threadContext.threadId,
|
|
1999
|
+
resourceId: threadContext.resourceId
|
|
2000
|
+
});
|
|
2001
|
+
return {
|
|
2002
|
+
unsubscribed: result.removed ?? false,
|
|
2003
|
+
owner: result.owner,
|
|
2004
|
+
repo: result.repo,
|
|
2005
|
+
number: result.number,
|
|
2006
|
+
remainingSubscriptions: result.remainingSubscriptions,
|
|
2007
|
+
message: result.removed ? `Unsubscribed from ${result.owner}/${result.repo}#${result.number}.` : `No GitHub subscription found for ${result.owner}/${result.repo}#${result.number}.`
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
})
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
async #resolveRepository(input) {
|
|
2014
|
+
const resolvedRepository = input.owner && input.repo ? { owner: input.owner, repo: input.repo } : this.#options.owner && this.#options.repo ? { owner: this.#options.owner, repo: this.#options.repo } : await this.#repositoryResolver.resolveRepository({
|
|
2015
|
+
cwd: this.#options.cwd,
|
|
2016
|
+
abortSignal: input.abortSignal
|
|
2017
|
+
});
|
|
2018
|
+
if (!resolvedRepository?.owner || !resolvedRepository.repo) {
|
|
2019
|
+
throw new Error(
|
|
2020
|
+
"GitHub PR subscription requires owner and repo. Run inside a GitHub repo or pass owner and repo."
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
return resolvedRepository;
|
|
2024
|
+
}
|
|
2025
|
+
async #loadThread(input) {
|
|
2026
|
+
const threadStore = await this.#resolveThreadStore();
|
|
2027
|
+
if (!threadStore) throw new Error("GitHub PR subscription requires memory-backed thread storage.");
|
|
2028
|
+
if (!input.threadId || !input.resourceId)
|
|
2029
|
+
throw new Error("GitHub PR subscription requires threadId and resourceId.");
|
|
2030
|
+
const loadedThread = await threadStore.getThreadById({ threadId: input.threadId, resourceId: input.resourceId }) ?? void 0;
|
|
2031
|
+
if (!loadedThread) throw new Error(`Could not load thread ${input.threadId}.`);
|
|
2032
|
+
return { threadStore, loadedThread };
|
|
2033
|
+
}
|
|
2034
|
+
#pollingKey(input) {
|
|
2035
|
+
return `${input.resourceId}:${input.threadId}`;
|
|
2036
|
+
}
|
|
2037
|
+
#getNotificationAgent(_input) {
|
|
2038
|
+
if (this.#agent) return this.#agent;
|
|
2039
|
+
const agentId = _input?.agentId ?? this.#options.agentId;
|
|
2040
|
+
return agentId ? this.mastra?.getAgentById?.(agentId) : void 0;
|
|
2041
|
+
}
|
|
2042
|
+
async #getThreadSubscriptions(input) {
|
|
2043
|
+
const { loadedThread } = await this.#loadThread(input);
|
|
2044
|
+
return getGithubMetadata(loadedThread.metadata).subscriptions;
|
|
2045
|
+
}
|
|
2046
|
+
#notifySubscriptionsChanged(input) {
|
|
2047
|
+
this.#subscriptionsChangedHandler?.(input);
|
|
2048
|
+
}
|
|
2049
|
+
async #pollThread(input, options = {}) {
|
|
2050
|
+
const key = this.#pollingKey(input);
|
|
2051
|
+
const state = this.#polling.get(key);
|
|
2052
|
+
if (state?.running) {
|
|
2053
|
+
return 0;
|
|
2054
|
+
}
|
|
2055
|
+
if (state) state.running = true;
|
|
2056
|
+
try {
|
|
2057
|
+
const { threadStore, loadedThread } = await this.#loadThread(input);
|
|
2058
|
+
const githubMetadata = getGithubMetadata(loadedThread.metadata);
|
|
2059
|
+
if (githubMetadata.subscriptions.length === 0) {
|
|
2060
|
+
this.stopPollingForThread(input);
|
|
2061
|
+
return 0;
|
|
2062
|
+
}
|
|
2063
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2064
|
+
const subscriptions = [];
|
|
2065
|
+
for (const subscription of githubMetadata.subscriptions) {
|
|
2066
|
+
const syncInput = {
|
|
2067
|
+
owner: subscription.owner,
|
|
2068
|
+
repo: subscription.repo,
|
|
2069
|
+
number: subscription.number,
|
|
2070
|
+
cwd: this.#options.cwd,
|
|
2071
|
+
includeComments: options.includeComments
|
|
2072
|
+
};
|
|
2073
|
+
const syncResult = await this.#syncClient.syncPullRequest(syncInput);
|
|
2074
|
+
const snapshot = syncResult.ok ? await this.#syncClient.getPullRequestSnapshot?.(syncInput) : void 0;
|
|
2075
|
+
const nextSubscription = {
|
|
2076
|
+
...subscription,
|
|
2077
|
+
updatedAt: now,
|
|
2078
|
+
lastSyncAt: now,
|
|
2079
|
+
lastSyncStatus: syncResult.ok ? "success" : "error"
|
|
2080
|
+
};
|
|
2081
|
+
if (syncResult.error) nextSubscription.lastSyncError = syncResult.error;
|
|
2082
|
+
else delete nextSubscription.lastSyncError;
|
|
2083
|
+
const previousGithubUpdatedAt = subscription.lastObservedGithubUpdatedAt;
|
|
2084
|
+
const previousContentHash = subscription.lastObservedContentHash;
|
|
2085
|
+
const previousThreadContentHash = subscription.lastObservedThreadContentHash;
|
|
2086
|
+
const previousHeadSha = subscription.lastObservedHeadSha;
|
|
2087
|
+
if (snapshot) applySnapshotCursor(nextSubscription, snapshot);
|
|
2088
|
+
const isFirstObservation = syncResult.ok && snapshot && !previousGithubUpdatedAt && !previousContentHash;
|
|
2089
|
+
const legacyAggregateChanged = previousContentHash && snapshot?.contentHash && previousContentHash !== snapshot.contentHash && !previousThreadContentHash && !previousHeadSha;
|
|
2090
|
+
const changed = isFirstObservation || syncResult.ok && snapshot && (legacyAggregateChanged || previousThreadContentHash && snapshot.threadContentHash && previousThreadContentHash !== snapshot.threadContentHash || previousHeadSha && snapshot.headSha && previousHeadSha !== snapshot.headSha || subscription.lastObservedState && snapshot.state && subscription.lastObservedState !== snapshot.state || subscription.lastObservedMergeableState && snapshot.mergeableState && subscription.lastObservedMergeableState !== snapshot.mergeableState || subscription.lastObservedCiState && snapshot.ciState && subscription.lastObservedCiState !== snapshot.ciState || subscription.lastObservedReviewStateHash && snapshot.reviewStateHash && subscription.lastObservedReviewStateHash !== snapshot.reviewStateHash);
|
|
2091
|
+
let shouldKeepSubscription = true;
|
|
2092
|
+
if (changed) {
|
|
2093
|
+
const notification = await this.#sendActivityNotification({
|
|
2094
|
+
polling: input,
|
|
2095
|
+
subscription,
|
|
2096
|
+
snapshot,
|
|
2097
|
+
previousGithubUpdatedAt,
|
|
2098
|
+
previousContentHash
|
|
2099
|
+
});
|
|
2100
|
+
if (notification) {
|
|
2101
|
+
nextSubscription.lastNotificationAt = now;
|
|
2102
|
+
nextSubscription.lastNotificationKind = notification.kind;
|
|
2103
|
+
nextSubscription.lastNotificationPriority = notification.priority;
|
|
2104
|
+
nextSubscription.lastNotificationSummary = notification.summary;
|
|
2105
|
+
shouldKeepSubscription = notification.kind !== "pull-request-merged";
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
if (shouldKeepSubscription) subscriptions.push(nextSubscription);
|
|
2109
|
+
}
|
|
2110
|
+
await threadStore.saveThread({
|
|
2111
|
+
thread: {
|
|
2112
|
+
...loadedThread,
|
|
2113
|
+
id: input.threadId,
|
|
2114
|
+
resourceId: input.resourceId,
|
|
2115
|
+
createdAt: loadedThread.createdAt ?? /* @__PURE__ */ new Date(),
|
|
2116
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
2117
|
+
metadata: setGithubMetadata(loadedThread.metadata, { subscriptions })
|
|
2118
|
+
}
|
|
2119
|
+
});
|
|
2120
|
+
this.#notifySubscriptionsChanged({ threadId: input.threadId, resourceId: input.resourceId, subscriptions });
|
|
2121
|
+
if (subscriptions.length === 0) this.stopPollingForThread(input);
|
|
2122
|
+
return subscriptions.length;
|
|
2123
|
+
} catch (error) {
|
|
2124
|
+
throw error;
|
|
2125
|
+
} finally {
|
|
2126
|
+
const latestState = this.#polling.get(key);
|
|
2127
|
+
if (latestState) latestState.running = false;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
async #sendGithubNotification(input) {
|
|
2131
|
+
const failingChecks = getFailingChecks(input.snapshot);
|
|
2132
|
+
const pendingChecks = getPendingChecks(input.snapshot);
|
|
2133
|
+
const notificationInput = {
|
|
2134
|
+
source: "github",
|
|
2135
|
+
kind: input.notification.kind,
|
|
2136
|
+
priority: input.notification.priority,
|
|
2137
|
+
summary: input.notification.summary,
|
|
2138
|
+
dedupeKey: `github:${input.subscription.owner}/${input.subscription.repo}#${input.subscription.number}:${input.dedupeSuffix}`,
|
|
2139
|
+
coalesceKey: `github:${input.subscription.owner}/${input.subscription.repo}#${input.subscription.number}:${input.notification.kind}`,
|
|
2140
|
+
attributes: {
|
|
2141
|
+
owner: input.subscription.owner,
|
|
2142
|
+
repo: input.subscription.repo,
|
|
2143
|
+
number: input.subscription.number,
|
|
2144
|
+
...input.snapshot.title ? { title: input.snapshot.title } : {},
|
|
2145
|
+
...input.snapshot.state ? { state: input.snapshot.state } : {},
|
|
2146
|
+
...input.snapshot.htmlUrl ? { url: input.snapshot.htmlUrl } : {},
|
|
2147
|
+
...input.snapshot.githubUpdatedAt ? { githubUpdatedAt: input.snapshot.githubUpdatedAt } : {},
|
|
2148
|
+
...input.previousGithubUpdatedAt ? { previousGithubUpdatedAt: input.previousGithubUpdatedAt } : {},
|
|
2149
|
+
...input.snapshot.mergeableState ? { mergeableState: input.snapshot.mergeableState } : {},
|
|
2150
|
+
...input.snapshot.ciState ? { ciState: input.snapshot.ciState } : {},
|
|
2151
|
+
...input.snapshot.unresolvedReviewThreads !== void 0 ? { unresolvedReviewThreads: input.snapshot.unresolvedReviewThreads } : {},
|
|
2152
|
+
...failingChecks.length > 0 ? { failingChecks: failingChecks.map((check) => check.name).join(", ") } : {},
|
|
2153
|
+
...pendingChecks.length > 0 ? { pendingChecks: pendingChecks.map((check) => check.name).join(", ") } : {}
|
|
2154
|
+
},
|
|
2155
|
+
metadata: {
|
|
2156
|
+
github: {
|
|
2157
|
+
owner: input.subscription.owner,
|
|
2158
|
+
repo: input.subscription.repo,
|
|
2159
|
+
number: input.subscription.number,
|
|
2160
|
+
title: input.snapshot.title,
|
|
2161
|
+
state: input.snapshot.state,
|
|
2162
|
+
htmlUrl: input.snapshot.htmlUrl,
|
|
2163
|
+
githubUpdatedAt: input.snapshot.githubUpdatedAt,
|
|
2164
|
+
previousGithubUpdatedAt: input.previousGithubUpdatedAt,
|
|
2165
|
+
contentHash: input.snapshot.contentHash,
|
|
2166
|
+
previousContentHash: input.previousContentHash,
|
|
2167
|
+
threadContentHash: input.snapshot.threadContentHash,
|
|
2168
|
+
headSha: input.snapshot.headSha,
|
|
2169
|
+
headRef: input.snapshot.headRef,
|
|
2170
|
+
mergeableState: input.snapshot.mergeableState,
|
|
2171
|
+
ciState: input.snapshot.ciState,
|
|
2172
|
+
closedAt: input.snapshot.closedAt,
|
|
2173
|
+
mergedAt: input.snapshot.mergedAt,
|
|
2174
|
+
unresolvedReviewThreads: input.snapshot.unresolvedReviewThreads,
|
|
2175
|
+
reviewStateHash: input.snapshot.reviewStateHash,
|
|
2176
|
+
latestReviewThreadAt: input.snapshot.latestReviewThreadAt,
|
|
2177
|
+
latestCommentAuthor: input.snapshot.latestCommentAuthor,
|
|
2178
|
+
latestCommentAuthorType: input.snapshot.latestCommentAuthorType,
|
|
2179
|
+
latestCommentIsBot: input.snapshot.latestCommentIsBot,
|
|
2180
|
+
failingChecks,
|
|
2181
|
+
pendingChecks
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
const streamOptions = await this.#agentOptions.getNotificationStreamOptions?.(input.target);
|
|
2186
|
+
await input.agent?.sendNotificationSignal?.(
|
|
2187
|
+
notificationInput,
|
|
2188
|
+
streamOptions ? { ...input.target, ifIdle: { streamOptions } } : input.target
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
async #sendBaselineNotification(input) {
|
|
2192
|
+
const agent = this.#getNotificationAgent({});
|
|
2193
|
+
if (!agent?.sendNotificationSignal) return;
|
|
2194
|
+
await this.#sendGithubNotification({
|
|
2195
|
+
agent,
|
|
2196
|
+
subscription: input.subscription,
|
|
2197
|
+
snapshot: input.snapshot,
|
|
2198
|
+
notification: classifyGithubBaselineNotification({ subscription: input.subscription, snapshot: input.snapshot }),
|
|
2199
|
+
target: { resourceId: input.resourceId, threadId: input.threadId },
|
|
2200
|
+
dedupeSuffix: `baseline:${input.subscription.lastSubscribeSignalId}`
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
async #sendActivityNotification(input) {
|
|
2204
|
+
const agent = this.#getNotificationAgent(input.polling);
|
|
2205
|
+
if (!agent?.sendNotificationSignal) return void 0;
|
|
2206
|
+
const notification = classifyGithubActivityNotification({
|
|
2207
|
+
subscription: input.subscription,
|
|
2208
|
+
snapshot: input.snapshot
|
|
2209
|
+
});
|
|
2210
|
+
if (!notification) return void 0;
|
|
2211
|
+
await this.#sendGithubNotification({
|
|
2212
|
+
agent,
|
|
2213
|
+
subscription: input.subscription,
|
|
2214
|
+
snapshot: input.snapshot,
|
|
2215
|
+
notification,
|
|
2216
|
+
target: { resourceId: input.polling.resourceId, threadId: input.polling.threadId },
|
|
2217
|
+
dedupeSuffix: input.snapshot.contentHash ?? input.snapshot.githubUpdatedAt ?? String(Date.now()),
|
|
2218
|
+
previousGithubUpdatedAt: input.previousGithubUpdatedAt,
|
|
2219
|
+
previousContentHash: input.previousContentHash
|
|
2220
|
+
});
|
|
2221
|
+
return notification;
|
|
2222
|
+
}
|
|
2223
|
+
async #subscribe(input) {
|
|
2224
|
+
const { owner, repo } = await this.#resolveRepository(input);
|
|
2225
|
+
const { threadStore, loadedThread } = await this.#loadThread(input);
|
|
2226
|
+
const githubMetadata = getGithubMetadata(loadedThread.metadata);
|
|
2227
|
+
const existingIndex = githubMetadata.subscriptions.findIndex(
|
|
2228
|
+
(subscription2) => subscription2.owner === owner && subscription2.repo === repo && subscription2.number === input.number
|
|
2229
|
+
);
|
|
2230
|
+
const existing = existingIndex >= 0 ? githubMetadata.subscriptions[existingIndex] : void 0;
|
|
2231
|
+
if (existing?.lastSubscribeSignalId === input.id) {
|
|
2232
|
+
return { owner, repo, number: input.number, subscription: existing, alreadyProcessed: true };
|
|
2233
|
+
}
|
|
2234
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2235
|
+
const subscription = {
|
|
2236
|
+
owner,
|
|
2237
|
+
repo,
|
|
2238
|
+
number: input.number,
|
|
2239
|
+
subscribedAt: existing?.subscribedAt ?? now,
|
|
2240
|
+
updatedAt: now,
|
|
2241
|
+
lastSubscribeSignalId: input.id,
|
|
2242
|
+
...existing?.lastSyncAt ? { lastSyncAt: existing.lastSyncAt } : {},
|
|
2243
|
+
...existing?.lastSyncStatus ? { lastSyncStatus: existing.lastSyncStatus } : {},
|
|
2244
|
+
...existing?.lastSyncError ? { lastSyncError: existing.lastSyncError } : {},
|
|
2245
|
+
...existing?.lastObservedGithubUpdatedAt ? { lastObservedGithubUpdatedAt: existing.lastObservedGithubUpdatedAt } : {},
|
|
2246
|
+
...existing?.lastObservedContentHash ? { lastObservedContentHash: existing.lastObservedContentHash } : {},
|
|
2247
|
+
...existing?.lastObservedThreadContentHash ? { lastObservedThreadContentHash: existing.lastObservedThreadContentHash } : {},
|
|
2248
|
+
...existing?.lastObservedHeadSha ? { lastObservedHeadSha: existing.lastObservedHeadSha } : {},
|
|
2249
|
+
...existing?.lastObservedState ? { lastObservedState: existing.lastObservedState } : {},
|
|
2250
|
+
...existing?.lastObservedMergeableState ? { lastObservedMergeableState: existing.lastObservedMergeableState } : {},
|
|
2251
|
+
...existing?.lastObservedCiState ? { lastObservedCiState: existing.lastObservedCiState } : {},
|
|
2252
|
+
...existing?.lastObservedReviewStateHash ? { lastObservedReviewStateHash: existing.lastObservedReviewStateHash } : {}
|
|
2253
|
+
};
|
|
2254
|
+
let syncResult;
|
|
2255
|
+
let baselineSnapshot;
|
|
2256
|
+
if (this.#options.syncOnSubscribe !== false) {
|
|
2257
|
+
const syncInput = {
|
|
2258
|
+
owner,
|
|
2259
|
+
repo,
|
|
2260
|
+
number: input.number,
|
|
2261
|
+
cwd: this.#options.cwd,
|
|
2262
|
+
abortSignal: input.abortSignal
|
|
2263
|
+
};
|
|
2264
|
+
syncResult = await this.#syncClient.syncPullRequest(syncInput);
|
|
2265
|
+
subscription.lastSyncAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2266
|
+
subscription.lastSyncStatus = syncResult.ok ? "success" : "error";
|
|
2267
|
+
if (syncResult.error) subscription.lastSyncError = syncResult.error;
|
|
2268
|
+
else delete subscription.lastSyncError;
|
|
2269
|
+
const snapshot = syncResult.ok ? await this.#syncClient.getPullRequestSnapshot?.(syncInput) : void 0;
|
|
2270
|
+
baselineSnapshot = snapshot;
|
|
2271
|
+
if (snapshot) applySnapshotCursor(subscription, snapshot);
|
|
2272
|
+
} else {
|
|
2273
|
+
subscription.lastSyncStatus = "skipped";
|
|
2274
|
+
}
|
|
2275
|
+
const subscriptions = [subscription];
|
|
2276
|
+
await threadStore.saveThread({
|
|
2277
|
+
thread: {
|
|
2278
|
+
...loadedThread,
|
|
2279
|
+
id: input.threadId,
|
|
2280
|
+
resourceId: input.resourceId,
|
|
2281
|
+
createdAt: loadedThread.createdAt ?? /* @__PURE__ */ new Date(),
|
|
2282
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
2283
|
+
metadata: setGithubMetadata(loadedThread.metadata, { subscriptions })
|
|
2284
|
+
}
|
|
2285
|
+
});
|
|
2286
|
+
this.#notifySubscriptionsChanged({ threadId: input.threadId, resourceId: input.resourceId, subscriptions });
|
|
2287
|
+
if (baselineSnapshot) {
|
|
2288
|
+
await this.#sendBaselineNotification({
|
|
2289
|
+
threadId: input.threadId,
|
|
2290
|
+
resourceId: input.resourceId,
|
|
2291
|
+
subscription,
|
|
2292
|
+
snapshot: baselineSnapshot
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
await this.startPollingForThread({ threadId: input.threadId, resourceId: input.resourceId });
|
|
2296
|
+
return { owner, repo, number: input.number, subscription, syncResult };
|
|
2297
|
+
}
|
|
2298
|
+
async #unsubscribe(input) {
|
|
2299
|
+
const { owner, repo } = await this.#resolveRepository(input);
|
|
2300
|
+
const { threadStore, loadedThread } = await this.#loadThread(input);
|
|
2301
|
+
const githubMetadata = getGithubMetadata(loadedThread.metadata);
|
|
2302
|
+
const subscriptions = githubMetadata.subscriptions.filter(
|
|
2303
|
+
(subscription) => !(subscription.owner === owner && subscription.repo === repo && subscription.number === input.number)
|
|
2304
|
+
);
|
|
2305
|
+
const removed = subscriptions.length !== githubMetadata.subscriptions.length;
|
|
2306
|
+
if (removed) {
|
|
2307
|
+
await threadStore.saveThread({
|
|
2308
|
+
thread: {
|
|
2309
|
+
...loadedThread,
|
|
2310
|
+
id: input.threadId,
|
|
2311
|
+
resourceId: input.resourceId,
|
|
2312
|
+
createdAt: loadedThread.createdAt ?? /* @__PURE__ */ new Date(),
|
|
2313
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
2314
|
+
metadata: setGithubMetadata(loadedThread.metadata, { subscriptions })
|
|
2315
|
+
}
|
|
2316
|
+
});
|
|
2317
|
+
this.#notifySubscriptionsChanged({ threadId: input.threadId, resourceId: input.resourceId, subscriptions });
|
|
2318
|
+
if (subscriptions.length === 0)
|
|
2319
|
+
this.stopPollingForThread({ threadId: input.threadId, resourceId: input.resourceId });
|
|
2320
|
+
}
|
|
2321
|
+
return { owner, repo, number: input.number, removed, remainingSubscriptions: subscriptions.length };
|
|
2322
|
+
}
|
|
2323
|
+
#findLatestGithubSignal(messages) {
|
|
2324
|
+
const message = messages.at(-1);
|
|
2325
|
+
if (!message) return void 0;
|
|
2326
|
+
const signal = getSignalMetadata(message);
|
|
2327
|
+
if (!signal || signal.tagName !== GITHUB_SUBSCRIBE_PR_TAG && signal.tagName !== GITHUB_UNSUBSCRIBE_PR_TAG) {
|
|
2328
|
+
return void 0;
|
|
2329
|
+
}
|
|
2330
|
+
const attributes = isPlainObject(signal.attributes) ? signal.attributes : {};
|
|
2331
|
+
const metadata = isPlainObject(signal.metadata) ? signal.metadata : {};
|
|
2332
|
+
const github = isPlainObject(metadata.github) ? metadata.github : {};
|
|
2333
|
+
const number = readNumber(attributes.number) ?? readNumber(github.number);
|
|
2334
|
+
if (!number) return void 0;
|
|
2335
|
+
return {
|
|
2336
|
+
tagName: String(signal.tagName),
|
|
2337
|
+
id: readString(signal.id) ?? message.id,
|
|
2338
|
+
owner: readString(attributes.owner) ?? readString(github.owner),
|
|
2339
|
+
repo: readString(attributes.repo) ?? readString(github.repo),
|
|
2340
|
+
number
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
async #sendStatus(args, signal, status) {
|
|
2344
|
+
await args.sendSignal?.({
|
|
2345
|
+
type: "reactive",
|
|
2346
|
+
tagName: GITHUB_SYNC_STATUS_TAG,
|
|
2347
|
+
contents: status.message,
|
|
2348
|
+
attributes: {
|
|
2349
|
+
status: status.status,
|
|
2350
|
+
owner: signal.owner,
|
|
2351
|
+
repo: signal.repo,
|
|
2352
|
+
number: signal.number
|
|
2353
|
+
},
|
|
2354
|
+
metadata: {
|
|
2355
|
+
github: {
|
|
2356
|
+
action: status.action,
|
|
2357
|
+
status: status.status,
|
|
2358
|
+
owner: signal.owner,
|
|
2359
|
+
repo: signal.repo,
|
|
2360
|
+
number: signal.number
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
1248
2366
|
|
|
1249
2367
|
// src/onboarding/packs.ts
|
|
1250
2368
|
function getAvailableModePacks(access, savedCustomPacks = []) {
|
|
@@ -1369,6 +2487,30 @@ var mastraBrand = {
|
|
|
1369
2487
|
// #ff4758 too intense
|
|
1370
2488
|
yellow: "#e7e67b"
|
|
1371
2489
|
};
|
|
2490
|
+
var extendedColors = {
|
|
2491
|
+
// Teals / Cyans
|
|
2492
|
+
teal: "#14b8a6",
|
|
2493
|
+
cyan: "#06b6d4",
|
|
2494
|
+
lightCyan: "#22d3ee",
|
|
2495
|
+
// Blues
|
|
2496
|
+
skyBlue: "#38bdf8",
|
|
2497
|
+
indigo: "#6366f1",
|
|
2498
|
+
// Purples / Violets
|
|
2499
|
+
violet: "#a855f7",
|
|
2500
|
+
lavender: "#c084fc",
|
|
2501
|
+
fuchsia: "#d946ef",
|
|
2502
|
+
// Pinks / Roses
|
|
2503
|
+
rose: "#f472b6",
|
|
2504
|
+
coral: "#fb7185",
|
|
2505
|
+
// Warm Tones
|
|
2506
|
+
amber: "#fb923c",
|
|
2507
|
+
lime: "#a3e635",
|
|
2508
|
+
gold: "#facc15",
|
|
2509
|
+
// Earthy / Muted
|
|
2510
|
+
stone: "#a8a29e",
|
|
2511
|
+
warmGray: "#78716c",
|
|
2512
|
+
copper: "#b45309"
|
|
2513
|
+
};
|
|
1372
2514
|
var darkSurface = {
|
|
1373
2515
|
bg: "#020202",
|
|
1374
2516
|
antiGrid: "#0d0d0d",
|
|
@@ -1866,6 +3008,8 @@ function releaseAllThreadLocks() {
|
|
|
1866
3008
|
exports.BOX_INDENT = BOX_INDENT;
|
|
1867
3009
|
exports.BOX_INDENT_STR = BOX_INDENT_STR;
|
|
1868
3010
|
exports.CHAT_INDENT = CHAT_INDENT;
|
|
3011
|
+
exports.GITHUB_SIGNALS_METADATA_KEY = GITHUB_SIGNALS_METADATA_KEY;
|
|
3012
|
+
exports.GithubSignals = GithubSignals;
|
|
1869
3013
|
exports.MEMORY_GATEWAY_DEFAULT_URL = MEMORY_GATEWAY_DEFAULT_URL;
|
|
1870
3014
|
exports.MEMORY_GATEWAY_PROVIDER = MEMORY_GATEWAY_PROVIDER;
|
|
1871
3015
|
exports.OBSERVABILITY_AUTH_PREFIX = OBSERVABILITY_AUTH_PREFIX;
|
|
@@ -1878,6 +3022,7 @@ exports.applyThemeMode = applyThemeMode;
|
|
|
1878
3022
|
exports.checkProfileProviderMismatch = checkProfileProviderMismatch;
|
|
1879
3023
|
exports.createBrowserFromSettings = createBrowserFromSettings;
|
|
1880
3024
|
exports.ensureTerminalGlyphContrast = ensureTerminalGlyphContrast;
|
|
3025
|
+
exports.extendedColors = extendedColors;
|
|
1881
3026
|
exports.getAvailableModePacks = getAvailableModePacks;
|
|
1882
3027
|
exports.getAvailableOmPacks = getAvailableOmPacks;
|
|
1883
3028
|
exports.getCopilotModelCatalog = getCopilotModelCatalog;
|
|
@@ -1908,5 +3053,5 @@ exports.setProfileProvider = setProfileProvider;
|
|
|
1908
3053
|
exports.theme = theme;
|
|
1909
3054
|
exports.tintHex = tintHex;
|
|
1910
3055
|
exports.toCustomProviderModelId = toCustomProviderModelId;
|
|
1911
|
-
//# sourceMappingURL=chunk-
|
|
1912
|
-
//# sourceMappingURL=chunk-
|
|
3056
|
+
//# sourceMappingURL=chunk-EAUXUMEB.cjs.map
|
|
3057
|
+
//# sourceMappingURL=chunk-EAUXUMEB.cjs.map
|