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