@knowsuchagency/fulcrum 3.2.1 → 3.3.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/server/index.js CHANGED
@@ -1147055,8 +1147055,10 @@ You have access to Fulcrum's MCP tools. Use them proactively to help users.
1147055
1147055
  - Use \`search\` with \`memoryTags: ["actionable"]\` to review tracked items
1147056
1147056
 
1147057
1147057
  **Unified Search:**
1147058
- - \`search\` - Cross-entity FTS5 full-text search across tasks, projects, messages, events, memories, and conversations
1147059
- - Filter by entity type: \`entities: ["tasks", "projects", "messages", "events", "memories", "conversations"]\`
1147058
+ - \`search\` - Cross-entity FTS5 full-text search across tasks, projects, messages, events, memories, conversations, and gmail
1147059
+ - Filter by entity type: \`entities: ["tasks", "projects", "messages", "events", "memories", "conversations", "gmail"]\`
1147060
+ - **Gmail is opt-in** \u2014 not included in default searches to avoid latency/rate-limit impact. Must be explicitly requested via \`entities: ["gmail"]\`
1147061
+ - Gmail-specific filters: \`gmailFrom\`, \`gmailTo\`, \`gmailAfter\`, \`gmailBefore\`
1147060
1147062
  - Entity-specific filters: \`taskStatus\`, \`projectStatus\`, \`messageChannel\`, \`messageDirection\`, \`eventFrom\`, \`eventTo\`, \`memoryTags\`, \`conversationRole\`, \`conversationProvider\`, \`conversationProjectId\`
1147061
1147063
  - Conversations search indexes AI assistant chat messages (excludes system prompts) with session context
1147062
1147064
  - Results sorted by relevance score with BM25 ranking
@@ -1147398,7 +1147400,7 @@ Fulcrum is your digital concierge - a personal command center where you track ev
1147398
1147400
  - list_projects, create_project
1147399
1147401
  - execute_command (run any CLI command)
1147400
1147402
  - send_notification
1147401
- - search (unified FTS5 search across tasks, projects, messages, events, memories, conversations)
1147403
+ - search (unified FTS5 search across tasks, projects, messages, events, memories, conversations; gmail is opt-in via \`entities: ["gmail"]\`)
1147402
1147404
  - memory_file_read, memory_file_update (master memory file - always in prompt)
1147403
1147405
  - memory_store (ephemeral knowledge snippets with tags)
1147404
1147406
  - message (send to WhatsApp/Discord/Telegram/Slack/Gmail - user-only, concierge mode)
@@ -1147597,7 +1147599,7 @@ function addMessage(sessionId, message) {
1147597
1147599
  return db2.select().from(chatMessages).where(eq(chatMessages.id, id)).get();
1147598
1147600
  }
1147599
1147601
  function getMessages(sessionId) {
1147600
- return db2.select().from(chatMessages).where(eq(chatMessages.sessionId, sessionId)).orderBy(chatMessages.createdAt).all();
1147602
+ return db2.select().from(chatMessages).where(eq(chatMessages.sessionId, sessionId)).orderBy(chatMessages.createdAt, sql`rowid`).all();
1147601
1147603
  }
1147602
1147604
  function buildBaselinePrompt(condensed = false) {
1147603
1147605
  const settings = getSettings();
@@ -1188385,13 +1188387,13 @@ var require_core = __commonJS((exports) => {
1188385
1188387
  return metaOpts;
1188386
1188388
  }
1188387
1188389
  var noLogs = { log() {}, warn() {}, error() {} };
1188388
- function getLogger(logger6) {
1188389
- if (logger6 === false)
1188390
+ function getLogger(logger7) {
1188391
+ if (logger7 === false)
1188390
1188392
  return noLogs;
1188391
- if (logger6 === undefined)
1188393
+ if (logger7 === undefined)
1188392
1188394
  return console;
1188393
- if (logger6.log && logger6.warn && logger6.error)
1188394
- return logger6;
1188395
+ if (logger7.log && logger7.warn && logger7.error)
1188396
+ return logger7;
1188395
1188397
  throw new Error("logger must implement log, warn and error methods");
1188396
1188398
  }
1188397
1188399
  var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
@@ -1194018,13 +1194020,13 @@ var require_core3 = __commonJS((exports) => {
1194018
1194020
  return metaOpts;
1194019
1194021
  }
1194020
1194022
  var noLogs = { log() {}, warn() {}, error() {} };
1194021
- function getLogger(logger6) {
1194022
- if (logger6 === false)
1194023
+ function getLogger(logger7) {
1194024
+ if (logger7 === false)
1194023
1194025
  return noLogs;
1194024
- if (logger6 === undefined)
1194026
+ if (logger7 === undefined)
1194025
1194027
  return console;
1194026
- if (logger6.log && logger6.warn && logger6.error)
1194027
- return logger6;
1194028
+ if (logger7.log && logger7.warn && logger7.error)
1194029
+ return logger7;
1194028
1194030
  throw new Error("logger must implement log, warn and error methods");
1194029
1194031
  }
1194030
1194032
  var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
@@ -1200416,10 +1200418,10 @@ class CaldavAccountManager {
1200416
1200418
  }
1200417
1200419
  async startAll() {
1200418
1200420
  const accounts = db2.select().from(caldavAccounts).where(eq(caldavAccounts.enabled, true)).all();
1200419
- logger6.info("Starting all CalDAV accounts", { count: accounts.length });
1200421
+ logger7.info("Starting all CalDAV accounts", { count: accounts.length });
1200420
1200422
  for (const account2 of accounts) {
1200421
1200423
  await this.startAccount(account2.id).catch((err) => {
1200422
- logger6.error("Failed to start account", {
1200424
+ logger7.error("Failed to start account", {
1200423
1200425
  accountId: account2.id,
1200424
1200426
  name: account2.name,
1200425
1200427
  error: err instanceof Error ? err.message : String(err)
@@ -1200432,7 +1200434,7 @@ class CaldavAccountManager {
1200432
1200434
  this.stopAccount(accountId);
1200433
1200435
  }
1200434
1200436
  this.connections.clear();
1200435
- logger6.info("All CalDAV accounts stopped");
1200437
+ logger7.info("All CalDAV accounts stopped");
1200436
1200438
  }
1200437
1200439
  async startAccount(accountId) {
1200438
1200440
  const account2 = db2.select().from(caldavAccounts).where(eq(caldavAccounts.id, accountId)).get();
@@ -1200440,7 +1200442,7 @@ class CaldavAccountManager {
1200440
1200442
  throw new Error(`Account not found: ${accountId}`);
1200441
1200443
  }
1200442
1200444
  if (!account2.enabled) {
1200443
- logger6.info("Account is disabled, skipping", { accountId, name: account2.name });
1200445
+ logger7.info("Account is disabled, skipping", { accountId, name: account2.name });
1200444
1200446
  return;
1200445
1200447
  }
1200446
1200448
  this.stopAccount(accountId);
@@ -1200456,11 +1200458,11 @@ class CaldavAccountManager {
1200456
1200458
  try {
1200457
1200459
  conn.client = await this.connect(account2);
1200458
1200460
  this.scheduleSync(accountId, account2.syncIntervalMinutes ?? 15);
1200459
- logger6.info("Started CalDAV account", { accountId, name: account2.name });
1200461
+ logger7.info("Started CalDAV account", { accountId, name: account2.name });
1200460
1200462
  } catch (err) {
1200461
1200463
  conn.lastSyncError = err instanceof Error ? err.message : String(err);
1200462
1200464
  this.updateAccountError(accountId, conn.lastSyncError);
1200463
- logger6.error("Failed to connect account", { accountId, error: conn.lastSyncError });
1200465
+ logger7.error("Failed to connect account", { accountId, error: conn.lastSyncError });
1200464
1200466
  this.scheduleRetry(accountId);
1200465
1200467
  }
1200466
1200468
  }
@@ -1200502,7 +1200504,7 @@ class CaldavAccountManager {
1200502
1200504
  for (const [accountId, conn] of this.connections) {
1200503
1200505
  if (conn.client && !conn.isSyncing) {
1200504
1200506
  promises.push(this.syncAccount(accountId).catch((err) => {
1200505
- logger6.error("Sync failed for account", {
1200507
+ logger7.error("Sync failed for account", {
1200506
1200508
  accountId,
1200507
1200509
  error: err instanceof Error ? err.message : String(err)
1200508
1200510
  });
@@ -1200562,7 +1200564,7 @@ class CaldavAccountManager {
1200562
1200564
  defaultAccountType: "caldav"
1200563
1200565
  });
1200564
1200566
  await client4.login();
1200565
- logger6.info("Connected to CalDAV server (Basic)", { accountId: account2.id, serverUrl: account2.serverUrl });
1200567
+ logger7.info("Connected to CalDAV server (Basic)", { accountId: account2.id, serverUrl: account2.serverUrl });
1200566
1200568
  return client4;
1200567
1200569
  }
1200568
1200570
  async connectOAuth(account2) {
@@ -1200597,9 +1200599,9 @@ class CaldavAccountManager {
1200597
1200599
  credentials.expiration = newTokens.expiration;
1200598
1200600
  try {
1200599
1200601
  db2.update(caldavAccounts).set({ oauthTokens: newTokens, updatedAt: new Date().toISOString() }).where(eq(caldavAccounts.id, accountId)).run();
1200600
- logger6.info("Persisted refreshed OAuth tokens", { accountId });
1200602
+ logger7.info("Persisted refreshed OAuth tokens", { accountId });
1200601
1200603
  } catch (err) {
1200602
- logger6.error("Failed to persist refreshed OAuth tokens", {
1200604
+ logger7.error("Failed to persist refreshed OAuth tokens", {
1200603
1200605
  accountId,
1200604
1200606
  error: err instanceof Error ? err.message : String(err)
1200605
1200607
  });
@@ -1200610,7 +1200612,7 @@ class CaldavAccountManager {
1200610
1200612
  defaultAccountType: "caldav"
1200611
1200613
  });
1200612
1200614
  await client4.login();
1200613
- logger6.info("Connected to CalDAV server (Google OAuth)", { accountId: account2.id, serverUrl: account2.serverUrl });
1200615
+ logger7.info("Connected to CalDAV server (Google OAuth)", { accountId: account2.id, serverUrl: account2.serverUrl });
1200614
1200616
  return client4;
1200615
1200617
  }
1200616
1200618
  scheduleSync(accountId, intervalMinutes) {
@@ -1200622,7 +1200624,7 @@ class CaldavAccountManager {
1200622
1200624
  }
1200623
1200625
  conn.syncInterval = setInterval(() => {
1200624
1200626
  this.syncAccount(accountId).catch((err) => {
1200625
- logger6.error("Periodic sync failed", {
1200627
+ logger7.error("Periodic sync failed", {
1200626
1200628
  accountId,
1200627
1200629
  error: err instanceof Error ? err.message : String(err)
1200628
1200630
  });
@@ -1200635,7 +1200637,7 @@ class CaldavAccountManager {
1200635
1200637
  return;
1200636
1200638
  conn.retryCount++;
1200637
1200639
  const delay2 = Math.min(1000 * Math.pow(2, conn.retryCount), MAX_RETRY_DELAY);
1200638
- logger6.info("Scheduling account retry", { accountId, retryCount: conn.retryCount, delayMs: delay2 });
1200640
+ logger7.info("Scheduling account retry", { accountId, retryCount: conn.retryCount, delayMs: delay2 });
1200639
1200641
  setTimeout(async () => {
1200640
1200642
  const account2 = db2.select().from(caldavAccounts).where(eq(caldavAccounts.id, accountId)).get();
1200641
1200643
  if (!account2 || !account2.enabled)
@@ -1200651,7 +1200653,7 @@ class CaldavAccountManager {
1200651
1200653
  } catch (err) {
1200652
1200654
  conn.lastSyncError = err instanceof Error ? err.message : String(err);
1200653
1200655
  this.updateAccountError(accountId, conn.lastSyncError);
1200654
- logger6.error("Account retry failed", { accountId, error: conn.lastSyncError });
1200656
+ logger7.error("Account retry failed", { accountId, error: conn.lastSyncError });
1200655
1200657
  this.scheduleRetry(accountId);
1200656
1200658
  }
1200657
1200659
  }, delay2);
@@ -1200709,10 +1200711,10 @@ class CaldavAccountManager {
1200709
1200711
  if (!seenUrls.has(local.remoteUrl)) {
1200710
1200712
  db2.delete(caldavEvents2).where(eq(caldavEvents2.calendarId, local.id)).run();
1200711
1200713
  db2.delete(caldavCalendars).where(eq(caldavCalendars.id, local.id)).run();
1200712
- logger6.info("Removed deleted calendar", { displayName: local.displayName, accountId });
1200714
+ logger7.info("Removed deleted calendar", { displayName: local.displayName, accountId });
1200713
1200715
  }
1200714
1200716
  }
1200715
- logger6.info("Account sync complete", { accountId, calendars: remoteCalendars.length });
1200717
+ logger7.info("Account sync complete", { accountId, calendars: remoteCalendars.length });
1200716
1200718
  }
1200717
1200719
  async syncCalendarEvents(calendarId, remoteCal, client4) {
1200718
1200720
  const { caldavEvents: caldavEvents2 } = await Promise.resolve().then(() => (init_db2(), exports_db));
@@ -1200779,13 +1200781,13 @@ class CaldavAccountManager {
1200779
1200781
  }
1200780
1200782
  }
1200781
1200783
  }
1200782
- var logger6, GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token", MAX_RETRY_DELAY, accountManager;
1200784
+ var logger7, GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token", MAX_RETRY_DELAY, accountManager;
1200783
1200785
  var init_caldav_account_manager = __esm(() => {
1200784
1200786
  init_tsdav_esm();
1200785
1200787
  init_drizzle_orm();
1200786
1200788
  init_db2();
1200787
1200789
  init_logger3();
1200788
- logger6 = createLogger("CalDAV:AccountManager");
1200790
+ logger7 = createLogger("CalDAV:AccountManager");
1200789
1200791
  MAX_RETRY_DELAY = 5 * 60 * 1000;
1200790
1200792
  accountManager = new CaldavAccountManager;
1200791
1200793
  });
@@ -1200808,7 +1200810,7 @@ class GoogleCalendarManager {
1200808
1200810
  try {
1200809
1200811
  await this.syncAccount(accountId);
1200810
1200812
  } catch (err) {
1200811
- logger7.error("Initial Google Calendar sync failed", {
1200813
+ logger8.error("Initial Google Calendar sync failed", {
1200812
1200814
  accountId,
1200813
1200815
  error: err instanceof Error ? err.message : String(err)
1200814
1200816
  });
@@ -1200816,13 +1200818,13 @@ class GoogleCalendarManager {
1200816
1200818
  const intervalMs = (account2.syncIntervalMinutes ?? 15) * 60 * 1000;
1200817
1200819
  conn.syncInterval = setInterval(() => {
1200818
1200820
  this.syncAccount(accountId).catch((err) => {
1200819
- logger7.error("Periodic Google Calendar sync failed", {
1200821
+ logger8.error("Periodic Google Calendar sync failed", {
1200820
1200822
  accountId,
1200821
1200823
  error: err instanceof Error ? err.message : String(err)
1200822
1200824
  });
1200823
1200825
  });
1200824
1200826
  }, intervalMs);
1200825
- logger7.info("Started Google Calendar account", { accountId, name: account2.name });
1200827
+ logger8.info("Started Google Calendar account", { accountId, name: account2.name });
1200826
1200828
  }
1200827
1200829
  stopAccount(accountId) {
1200828
1200830
  const conn = this.connections.get(accountId);
@@ -1200840,10 +1200842,10 @@ class GoogleCalendarManager {
1200840
1200842
  }
1200841
1200843
  async startAll() {
1200842
1200844
  const accounts = db2.select().from(googleAccounts).where(eq(googleAccounts.calendarEnabled, true)).all();
1200843
- logger7.info("Starting Google Calendar accounts", { count: accounts.length });
1200845
+ logger8.info("Starting Google Calendar accounts", { count: accounts.length });
1200844
1200846
  for (const account2 of accounts) {
1200845
1200847
  await this.startAccount(account2.id).catch((err) => {
1200846
- logger7.error("Failed to start Google Calendar account", {
1200848
+ logger8.error("Failed to start Google Calendar account", {
1200847
1200849
  accountId: account2.id,
1200848
1200850
  error: err instanceof Error ? err.message : String(err)
1200849
1200851
  });
@@ -1200905,7 +1200907,7 @@ class GoogleCalendarManager {
1200905
1200907
  if (!seenRemoteUrls.has(local.remoteUrl)) {
1200906
1200908
  db2.delete(caldavEvents).where(eq(caldavEvents.calendarId, local.id)).run();
1200907
1200909
  db2.delete(caldavCalendars).where(eq(caldavCalendars.id, local.id)).run();
1200908
- logger7.info("Removed deleted Google calendar", {
1200910
+ logger8.info("Removed deleted Google calendar", {
1200909
1200911
  displayName: local.displayName,
1200910
1200912
  accountId
1200911
1200913
  });
@@ -1200919,7 +1200921,7 @@ class GoogleCalendarManager {
1200919
1200921
  if (conn) {
1200920
1200922
  conn.lastSyncError = null;
1200921
1200923
  }
1200922
- logger7.info("Google Calendar sync complete", {
1200924
+ logger8.info("Google Calendar sync complete", {
1200923
1200925
  accountId,
1200924
1200926
  calendars: remoteCalendars.length
1200925
1200927
  });
@@ -1201136,14 +1201138,14 @@ class GoogleCalendarManager {
1201136
1201138
  return this.connections.get(accountId)?.lastSyncError ?? null;
1201137
1201139
  }
1201138
1201140
  }
1201139
- var import_googleapis3, logger7, googleCalendarManager;
1201141
+ var import_googleapis3, logger8, googleCalendarManager;
1201140
1201142
  var init_google_calendar_manager = __esm(() => {
1201141
1201143
  init_drizzle_orm();
1201142
1201144
  init_db2();
1201143
1201145
  init_google_oauth();
1201144
1201146
  init_logger3();
1201145
1201147
  import_googleapis3 = __toESM(require_src12(), 1);
1201146
- logger7 = createLogger("Google:Calendar");
1201148
+ logger8 = createLogger("Google:Calendar");
1201147
1201149
  googleCalendarManager = new GoogleCalendarManager;
1201148
1201150
  });
1201149
1201151
 
@@ -1201157,12 +1201159,12 @@ async function executeAllRules() {
1201157
1201159
  const rules = db2.select().from(caldavCopyRules).where(eq(caldavCopyRules.enabled, true)).all();
1201158
1201160
  if (rules.length === 0)
1201159
1201161
  return;
1201160
- logger8.info("Executing copy rules", { count: rules.length });
1201162
+ logger9.info("Executing copy rules", { count: rules.length });
1201161
1201163
  for (const rule of rules) {
1201162
1201164
  try {
1201163
1201165
  await executeSingleRule(rule.id);
1201164
1201166
  } catch (err) {
1201165
- logger8.error("Copy rule failed", {
1201167
+ logger9.error("Copy rule failed", {
1201166
1201168
  ruleId: rule.id,
1201167
1201169
  error: err instanceof Error ? err.message : String(err)
1201168
1201170
  });
@@ -1201176,7 +1201178,7 @@ async function executeSingleRule(ruleId) {
1201176
1201178
  const sourceCal = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, rule.sourceCalendarId)).get();
1201177
1201179
  const destCal = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, rule.destCalendarId)).get();
1201178
1201180
  if (!sourceCal || !destCal) {
1201179
- logger8.warn("Copy rule references missing calendar", {
1201181
+ logger9.warn("Copy rule references missing calendar", {
1201180
1201182
  ruleId,
1201181
1201183
  sourceExists: !!sourceCal,
1201182
1201184
  destExists: !!destCal
@@ -1201186,7 +1201188,7 @@ async function executeSingleRule(ruleId) {
1201186
1201188
  const isGoogleDest = !!destCal.googleAccountId;
1201187
1201189
  const destClient = destCal.accountId ? accountManager.getClient(destCal.accountId) : null;
1201188
1201190
  if (!isGoogleDest && !destClient) {
1201189
- logger8.warn("Destination account not connected", {
1201191
+ logger9.warn("Destination account not connected", {
1201190
1201192
  ruleId,
1201191
1201193
  destCalendarId: destCal.id,
1201192
1201194
  destAccountId: destCal.accountId
@@ -1201272,7 +1201274,7 @@ async function executeSingleRule(ruleId) {
1201272
1201274
  }).run();
1201273
1201275
  created++;
1201274
1201276
  } catch (err) {
1201275
- logger8.error("Failed to copy event", {
1201277
+ logger9.error("Failed to copy event", {
1201276
1201278
  ruleId,
1201277
1201279
  sourceEventId: sourceEvent.id,
1201278
1201280
  error: err instanceof Error ? err.message : String(err)
@@ -1201336,7 +1201338,7 @@ async function executeSingleRule(ruleId) {
1201336
1201338
  db2.update(caldavCopiedEvents).set({ sourceEtag: sourceEvent.etag, updatedAt: now }).where(eq(caldavCopiedEvents.id, existingCopy.id)).run();
1201337
1201339
  updated++;
1201338
1201340
  } catch (err) {
1201339
- logger8.error("Failed to update copied event", {
1201341
+ logger9.error("Failed to update copied event", {
1201340
1201342
  ruleId,
1201341
1201343
  sourceEventId: sourceEvent.id,
1201342
1201344
  destEventId: existingCopy.destEventId,
@@ -1201347,11 +1201349,11 @@ async function executeSingleRule(ruleId) {
1201347
1201349
  }
1201348
1201350
  db2.update(caldavCopyRules).set({ lastExecutedAt: now, updatedAt: now }).where(eq(caldavCopyRules.id, ruleId)).run();
1201349
1201351
  if (created > 0 || updated > 0) {
1201350
- logger8.info("Copy rule executed", { ruleId, created, updated });
1201352
+ logger9.info("Copy rule executed", { ruleId, created, updated });
1201351
1201353
  }
1201352
1201354
  return { created, updated };
1201353
1201355
  }
1201354
- var logger8;
1201356
+ var logger9;
1201355
1201357
  var init_copy_engine = __esm(() => {
1201356
1201358
  init_drizzle_orm();
1201357
1201359
  init_db2();
@@ -1201359,7 +1201361,7 @@ var init_copy_engine = __esm(() => {
1201359
1201361
  init_ical_helpers();
1201360
1201362
  init_caldav_account_manager();
1201361
1201363
  init_google_calendar_manager();
1201362
- logger8 = createLogger("CalDAV:CopyEngine");
1201364
+ logger9 = createLogger("CalDAV:CopyEngine");
1201363
1201365
  });
1201364
1201366
 
1201365
1201367
  // node_modules/@hono/node-server/dist/index.mjs
@@ -1208351,6 +1208353,8 @@ async function updateTaskStatus(taskId, newStatus, newPosition) {
1208351
1208353
  // server/services/search-service.ts
1208352
1208354
  init_db2();
1208353
1208355
  init_drizzle_orm();
1208356
+ init_schema();
1208357
+ init_logger3();
1208354
1208358
  var ALL_ENTITIES = ["tasks", "projects", "messages", "events", "memories", "conversations"];
1208355
1208359
  async function search(options) {
1208356
1208360
  const entities = options.entities?.length ? options.entities : ALL_ENTITIES;
@@ -1208369,6 +1208373,8 @@ async function search(options) {
1208369
1208373
  return searchMemories2(options.query, { tags: options.memoryTags }, limit);
1208370
1208374
  case "conversations":
1208371
1208375
  return searchConversations(options.query, { role: options.conversationRole, provider: options.conversationProvider, projectId: options.conversationProjectId }, limit);
1208376
+ case "gmail":
1208377
+ return searchGmail(options.query, { from: options.gmailFrom, to: options.gmailTo, after: options.gmailAfter, before: options.gmailBefore }, limit);
1208372
1208378
  }
1208373
1208379
  });
1208374
1208380
  const resultSets = await Promise.all(searches);
@@ -1208572,6 +1208578,55 @@ async function searchConversations(query, filters, limit) {
1208572
1208578
  }
1208573
1208579
  }));
1208574
1208580
  }
1208581
+ var logger6 = createLogger("SearchService");
1208582
+ async function searchGmail(query, filters, limit) {
1208583
+ const accounts = db2.select().from(googleAccounts).where(eq(googleAccounts.gmailEnabled, true)).all();
1208584
+ if (!accounts.length)
1208585
+ return [];
1208586
+ const parts = [query];
1208587
+ if (filters.from)
1208588
+ parts.push(`from:${filters.from}`);
1208589
+ if (filters.to)
1208590
+ parts.push(`to:${filters.to}`);
1208591
+ if (filters.after)
1208592
+ parts.push(`after:${filters.after}`);
1208593
+ if (filters.before)
1208594
+ parts.push(`before:${filters.before}`);
1208595
+ const gmailQuery = parts.join(" ");
1208596
+ const { listMessages: listMessages2 } = await Promise.resolve().then(() => (init_gmail_service(), exports_gmail_service));
1208597
+ const results = await Promise.allSettled(accounts.map(async (account) => {
1208598
+ const messages2 = await listMessages2(account.id, {
1208599
+ query: gmailQuery,
1208600
+ maxResults: limit
1208601
+ });
1208602
+ return messages2.map((msg) => ({
1208603
+ entityType: "gmail",
1208604
+ id: msg.id,
1208605
+ title: msg.subject || "(no subject)",
1208606
+ snippet: msg.snippet || "",
1208607
+ score: 0.9,
1208608
+ metadata: {
1208609
+ threadId: msg.threadId,
1208610
+ from: msg.from,
1208611
+ to: msg.to,
1208612
+ date: msg.date,
1208613
+ labels: msg.labels,
1208614
+ accountId: account.id,
1208615
+ accountEmail: account.email,
1208616
+ gmailLink: `https://mail.google.com/mail/u/0/#inbox/${msg.id}`
1208617
+ }
1208618
+ }));
1208619
+ }));
1208620
+ const allResults = [];
1208621
+ for (const result of results) {
1208622
+ if (result.status === "fulfilled") {
1208623
+ allResults.push(...result.value);
1208624
+ } else {
1208625
+ logger6.warn("Gmail search failed for an account", { error: String(result.reason) });
1208626
+ }
1208627
+ }
1208628
+ return allResults.slice(0, limit);
1208629
+ }
1208575
1208630
  function reindexTaskFTS(taskId) {
1208576
1208631
  db2.run(sql`
1208577
1208632
  UPDATE tasks SET updated_at = updated_at WHERE id = ${taskId}
@@ -1212956,20 +1213011,20 @@ var VERSION4 = "7.0.6";
1212956
1213011
  var noop2 = () => {};
1212957
1213012
  var consoleWarn = console.warn.bind(console);
1212958
1213013
  var consoleError = console.error.bind(console);
1212959
- function createLogger2(logger6 = {}) {
1212960
- if (typeof logger6.debug !== "function") {
1212961
- logger6.debug = noop2;
1213014
+ function createLogger2(logger7 = {}) {
1213015
+ if (typeof logger7.debug !== "function") {
1213016
+ logger7.debug = noop2;
1212962
1213017
  }
1212963
- if (typeof logger6.info !== "function") {
1212964
- logger6.info = noop2;
1213018
+ if (typeof logger7.info !== "function") {
1213019
+ logger7.info = noop2;
1212965
1213020
  }
1212966
- if (typeof logger6.warn !== "function") {
1212967
- logger6.warn = consoleWarn;
1213021
+ if (typeof logger7.warn !== "function") {
1213022
+ logger7.warn = consoleWarn;
1212968
1213023
  }
1212969
- if (typeof logger6.error !== "function") {
1212970
- logger6.error = consoleError;
1213024
+ if (typeof logger7.error !== "function") {
1213025
+ logger7.error = consoleError;
1212971
1213026
  }
1212972
- return logger6;
1213027
+ return logger7;
1212973
1213028
  }
1212974
1213029
  var userAgentTrail = `octokit-core.js/${VERSION4} ${getUserAgent2()}`;
1212975
1213030
 
@@ -1251854,9 +1251909,9 @@ var toolRegistry = [
1251854
1251909
  },
1251855
1251910
  {
1251856
1251911
  name: "search",
1251857
- description: "Search across all Fulcrum entities (tasks, projects, messages, events, memories) using full-text search",
1251912
+ description: "Search across all Fulcrum entities (tasks, projects, messages, events, memories, gmail) using full-text search",
1251858
1251913
  category: "core",
1251859
- keywords: ["search", "find", "query", "fts", "full-text", "task", "project", "message", "event", "memory", "recall", "knowledge"],
1251914
+ keywords: ["search", "find", "query", "fts", "full-text", "task", "project", "message", "event", "memory", "recall", "knowledge", "gmail", "email", "google"],
1251860
1251915
  defer_loading: false
1251861
1251916
  },
1251862
1251917
  {
@@ -1253645,9 +1253700,9 @@ var registerMessagingTools = (server2, client3) => {
1253645
1253700
  };
1253646
1253701
 
1253647
1253702
  // cli/src/mcp/tools/search.ts
1253648
- var EntityTypeSchema = exports_external.enum(["tasks", "projects", "messages", "events", "memories", "conversations"]);
1253703
+ var EntityTypeSchema = exports_external.enum(["tasks", "projects", "messages", "events", "memories", "conversations", "gmail"]);
1253649
1253704
  var registerSearchTools = (server2, client3) => {
1253650
- server2.tool("search", 'Search across all Fulcrum entities (tasks, projects, messages, calendar events, memories, conversations) using full-text search. Supports boolean operators (AND, OR, NOT), phrase matching ("quoted phrases"), and prefix matching (term*). Returns results ranked by relevance.', {
1253705
+ server2.tool("search", 'Search across all Fulcrum entities (tasks, projects, messages, calendar events, memories, conversations) using full-text search. Supports boolean operators (AND, OR, NOT), phrase matching ("quoted phrases"), and prefix matching (term*). Returns results ranked by relevance. Gmail search is opt-in: include "gmail" in entities to search Gmail via API (not included in default searches).', {
1253651
1253706
  query: exports_external.string().describe('FTS5 search query. Supports: AND, OR, NOT operators, "quoted phrases", prefix* matching. Example: "kubernetes deployment" OR k8s'),
1253652
1253707
  entities: exports_external.optional(exports_external.array(EntityTypeSchema)).describe("Entity types to search. Defaults to all: tasks, projects, messages, events, memories, conversations"),
1253653
1253708
  limit: exports_external.optional(exports_external.number()).describe("Maximum results per entity type (default: 10)"),
@@ -1253660,8 +1253715,12 @@ var registerSearchTools = (server2, client3) => {
1253660
1253715
  memoryTags: exports_external.optional(exports_external.array(exports_external.string())).describe("Filter memories by tags"),
1253661
1253716
  conversationRole: exports_external.optional(exports_external.string()).describe('Filter conversations by role (e.g., "user", "assistant")'),
1253662
1253717
  conversationProvider: exports_external.optional(exports_external.string()).describe('Filter conversations by provider (e.g., "claude", "opencode")'),
1253663
- conversationProjectId: exports_external.optional(exports_external.string()).describe("Filter conversations by project ID")
1253664
- }, async ({ query, entities, limit: limit2, taskStatus, projectStatus, messageChannel, messageDirection, eventFrom, eventTo, memoryTags, conversationRole, conversationProvider, conversationProjectId }) => {
1253718
+ conversationProjectId: exports_external.optional(exports_external.string()).describe("Filter conversations by project ID"),
1253719
+ gmailFrom: exports_external.optional(exports_external.string()).describe('Filter Gmail results by sender (e.g., "user@example.com")'),
1253720
+ gmailTo: exports_external.optional(exports_external.string()).describe('Filter Gmail results by recipient (e.g., "user@example.com")'),
1253721
+ gmailAfter: exports_external.optional(exports_external.string()).describe("Filter Gmail results after this date (YYYY/MM/DD format)"),
1253722
+ gmailBefore: exports_external.optional(exports_external.string()).describe("Filter Gmail results before this date (YYYY/MM/DD format)")
1253723
+ }, async ({ query, entities, limit: limit2, taskStatus, projectStatus, messageChannel, messageDirection, eventFrom, eventTo, memoryTags, conversationRole, conversationProvider, conversationProjectId, gmailFrom, gmailTo, gmailAfter, gmailBefore }) => {
1253665
1253724
  try {
1253666
1253725
  const results = await client3.search({
1253667
1253726
  query,
@@ -1253676,7 +1253735,11 @@ var registerSearchTools = (server2, client3) => {
1253676
1253735
  memoryTags,
1253677
1253736
  conversationRole,
1253678
1253737
  conversationProvider,
1253679
- conversationProjectId
1253738
+ conversationProjectId,
1253739
+ gmailFrom,
1253740
+ gmailTo,
1253741
+ gmailAfter,
1253742
+ gmailBefore
1253680
1253743
  });
1253681
1253744
  return formatSuccess(results);
1253682
1253745
  } catch (err) {
@@ -1254516,6 +1254579,14 @@ class FulcrumClient {
1254516
1254579
  params.set("conversationProvider", input.conversationProvider);
1254517
1254580
  if (input.conversationProjectId)
1254518
1254581
  params.set("conversationProjectId", input.conversationProjectId);
1254582
+ if (input.gmailFrom)
1254583
+ params.set("gmailFrom", input.gmailFrom);
1254584
+ if (input.gmailTo)
1254585
+ params.set("gmailTo", input.gmailTo);
1254586
+ if (input.gmailAfter)
1254587
+ params.set("gmailAfter", input.gmailAfter);
1254588
+ if (input.gmailBefore)
1254589
+ params.set("gmailBefore", input.gmailBefore);
1254519
1254590
  return this.fetch(`/api/search?${params.toString()}`);
1254520
1254591
  }
1254521
1254592
  }
@@ -1254531,7 +1254602,7 @@ mcpRoutes.all("/", async (c) => {
1254531
1254602
  });
1254532
1254603
  const server2 = new McpServer({
1254533
1254604
  name: "fulcrum",
1254534
- version: "3.2.1"
1254605
+ version: "3.3.0"
1254535
1254606
  });
1254536
1254607
  const client3 = new FulcrumClient(`http://localhost:${port}`);
1254537
1254608
  registerTools(server2, client3);
@@ -1255855,7 +1255926,7 @@ init_logger3();
1255855
1255926
  init_ical_helpers();
1255856
1255927
  init_caldav_account_manager();
1255857
1255928
  init_google_calendar_manager();
1255858
- var logger9 = createLogger("CalDAV");
1255929
+ var logger10 = createLogger("CalDAV");
1255859
1255930
  function migrateFromSettings() {
1255860
1255931
  const settings = getSettings();
1255861
1255932
  const caldavSettings = settings.caldav;
@@ -1255871,7 +1255942,7 @@ function migrateFromSettings() {
1255871
1255942
  return;
1255872
1255943
  return;
1255873
1255944
  }
1255874
- logger9.info("Migrating CalDAV credentials from settings.json to database", {
1255945
+ logger10.info("Migrating CalDAV credentials from settings.json to database", {
1255875
1255946
  authType: caldavSettings.authType,
1255876
1255947
  orphanedCalendars: orphanedCalendars.length
1255877
1255948
  });
@@ -1255896,28 +1255967,28 @@ function migrateFromSettings() {
1255896
1255967
  for (const cal of orphanedCalendars) {
1255897
1255968
  db2.update(caldavCalendars).set({ accountId, updatedAt: now }).where(eq(caldavCalendars.id, cal.id)).run();
1255898
1255969
  }
1255899
- logger9.info("Migration complete", { accountId, migratedCalendars: orphanedCalendars.length });
1255970
+ logger10.info("Migration complete", { accountId, migratedCalendars: orphanedCalendars.length });
1255900
1255971
  }
1255901
1255972
  async function startCaldavSync() {
1255902
1255973
  const settings = getSettings();
1255903
1255974
  if (!settings.caldav?.enabled) {
1255904
- logger9.info("CalDAV disabled, skipping sync start");
1255975
+ logger10.info("CalDAV disabled, skipping sync start");
1255905
1255976
  return;
1255906
1255977
  }
1255907
1255978
  migrateFromSettings();
1255908
1255979
  await accountManager.startAll();
1255909
1255980
  await accountManager.syncAll().catch((err) => {
1255910
- logger9.error("Initial sync failed", { error: err instanceof Error ? err.message : String(err) });
1255981
+ logger10.error("Initial sync failed", { error: err instanceof Error ? err.message : String(err) });
1255911
1255982
  });
1255912
1255983
  await executeCopyRules().catch((err) => {
1255913
- logger9.error("Copy rules execution failed after sync", {
1255984
+ logger10.error("Copy rules execution failed after sync", {
1255914
1255985
  error: err instanceof Error ? err.message : String(err)
1255915
1255986
  });
1255916
1255987
  });
1255917
1255988
  }
1255918
1255989
  function stopCaldavSync() {
1255919
1255990
  accountManager.stopAll();
1255920
- logger9.info("CalDAV sync stopped");
1255991
+ logger10.info("CalDAV sync stopped");
1255921
1255992
  }
1255922
1255993
  function getCaldavStatus() {
1255923
1255994
  const accounts = accountManager.getStatus();
@@ -1255960,7 +1256031,7 @@ async function createAccount2(input) {
1255960
1256031
  updatedAt: now
1255961
1256032
  };
1255962
1256033
  db2.insert(caldavAccounts).values(account2).run();
1255963
- logger9.info("Created CalDAV account", { id, name: input.name });
1256034
+ logger10.info("Created CalDAV account", { id, name: input.name });
1255964
1256035
  return account2;
1255965
1256036
  }
1255966
1256037
  async function updateAccount(id, updates) {
@@ -1255988,7 +1256059,7 @@ async function deleteAccount(id) {
1255988
1256059
  }
1255989
1256060
  db2.delete(caldavCalendars).where(eq(caldavCalendars.accountId, id)).run();
1255990
1256061
  db2.delete(caldavAccounts).where(eq(caldavAccounts.id, id)).run();
1255991
- logger9.info("Deleted CalDAV account", { id });
1256062
+ logger10.info("Deleted CalDAV account", { id });
1255992
1256063
  }
1255993
1256064
  async function enableAccount(id) {
1255994
1256065
  db2.update(caldavAccounts).set({ enabled: true, updatedAt: new Date().toISOString() }).where(eq(caldavAccounts.id, id)).run();
@@ -1256024,7 +1256095,7 @@ async function testAccountConnection(config3) {
1256024
1256095
  async function syncAccount(id) {
1256025
1256096
  await accountManager.syncAccount(id);
1256026
1256097
  await executeCopyRules().catch((err) => {
1256027
- logger9.error("Copy rules failed after account sync", {
1256098
+ logger10.error("Copy rules failed after account sync", {
1256028
1256099
  error: err instanceof Error ? err.message : String(err)
1256029
1256100
  });
1256030
1256101
  });
@@ -1256130,7 +1256201,7 @@ function listCalendars(accountId) {
1256130
1256201
  async function syncCalendars2() {
1256131
1256202
  await accountManager.syncAll();
1256132
1256203
  await executeCopyRules().catch((err) => {
1256133
- logger9.error("Copy rules failed after sync", {
1256204
+ logger10.error("Copy rules failed after sync", {
1256134
1256205
  error: err instanceof Error ? err.message : String(err)
1256135
1256206
  });
1256136
1256207
  });
@@ -1256221,7 +1256292,7 @@ async function createEvent(input) {
1256221
1256292
  updatedAt: now
1256222
1256293
  };
1256223
1256294
  db2.insert(caldavEvents).values(event).run();
1256224
- logger9.info("Created CalDAV event", { id, summary: input.summary });
1256295
+ logger10.info("Created CalDAV event", { id, summary: input.summary });
1256225
1256296
  return event;
1256226
1256297
  }
1256227
1256298
  async function updateEvent(id, updates) {
@@ -1256271,7 +1256342,7 @@ async function updateEvent(id, updates) {
1256271
1256342
  rawIcal: updatedIcal,
1256272
1256343
  updatedAt: now
1256273
1256344
  }).where(eq(caldavEvents.id, id)).run();
1256274
- logger9.info("Updated CalDAV event", { id, summary: updates.summary ?? event.summary });
1256345
+ logger10.info("Updated CalDAV event", { id, summary: updates.summary ?? event.summary });
1256275
1256346
  return db2.select().from(caldavEvents).where(eq(caldavEvents.id, id)).get();
1256276
1256347
  }
1256277
1256348
  async function deleteEvent(id) {
@@ -1256295,7 +1256366,7 @@ async function deleteEvent(id) {
1256295
1256366
  }
1256296
1256367
  });
1256297
1256368
  db2.delete(caldavEvents).where(eq(caldavEvents.id, id)).run();
1256298
- logger9.info("Deleted CalDAV event", { id, summary: event.summary });
1256369
+ logger10.info("Deleted CalDAV event", { id, summary: event.summary });
1256299
1256370
  }
1256300
1256371
  function listCopyRules() {
1256301
1256372
  return db2.select().from(caldavCopyRules).all();
@@ -1256314,7 +1256385,7 @@ function createCopyRule(input) {
1256314
1256385
  updatedAt: now
1256315
1256386
  };
1256316
1256387
  db2.insert(caldavCopyRules).values(rule).run();
1256317
- logger9.info("Created copy rule", { id, source: input.sourceCalendarId, dest: input.destCalendarId });
1256388
+ logger10.info("Created copy rule", { id, source: input.sourceCalendarId, dest: input.destCalendarId });
1256318
1256389
  return rule;
1256319
1256390
  }
1256320
1256391
  function updateCopyRule(id, updates) {
@@ -1256327,7 +1256398,7 @@ function updateCopyRule(id, updates) {
1256327
1256398
  function deleteCopyRule(id) {
1256328
1256399
  db2.delete(caldavCopiedEvents).where(eq(caldavCopiedEvents.ruleId, id)).run();
1256329
1256400
  db2.delete(caldavCopyRules).where(eq(caldavCopyRules.id, id)).run();
1256330
- logger9.info("Deleted copy rule", { id });
1256401
+ logger10.info("Deleted copy rule", { id });
1256331
1256402
  }
1256332
1256403
  async function executeCopyRule(ruleId) {
1256333
1256404
  const { executeSingleRule: executeSingleRule2 } = await Promise.resolve().then(() => (init_copy_engine(), exports_copy_engine));
@@ -1256770,13 +1256841,13 @@ init_db2();
1256770
1256841
  init_settings();
1256771
1256842
  init_google_oauth();
1256772
1256843
  init_logger3();
1256773
- var logger10 = createLogger("GoogleOAuth:Routes");
1256844
+ var logger11 = createLogger("GoogleOAuth:Routes");
1256774
1256845
  var app25 = new Hono2;
1256775
1256846
  app25.get("/authorize", (c) => {
1256776
1256847
  const accountName = c.req.query("accountName");
1256777
1256848
  const accountId = c.req.query("accountId");
1256778
1256849
  const clientOrigin = c.req.query("origin");
1256779
- logger10.info("OAuth authorize request", {
1256850
+ logger11.info("OAuth authorize request", {
1256780
1256851
  accountName,
1256781
1256852
  accountId,
1256782
1256853
  clientOrigin,
@@ -1256800,7 +1256871,7 @@ app25.get("/authorize", (c) => {
1256800
1256871
  accountName: accountName || "Google Account",
1256801
1256872
  redirectUri
1256802
1256873
  });
1256803
- logger10.info("OAuth authorize: generating auth URL", {
1256874
+ logger11.info("OAuth authorize: generating auth URL", {
1256804
1256875
  baseUrl,
1256805
1256876
  redirectUri,
1256806
1256877
  stateJson: state,
@@ -1256809,18 +1256880,18 @@ app25.get("/authorize", (c) => {
1256809
1256880
  const authUrl = generateAuthUrl(client4, redirectUri, state);
1256810
1256881
  try {
1256811
1256882
  const parsed = new URL(authUrl);
1256812
- logger10.info("OAuth authorize: generated auth URL", {
1256883
+ logger11.info("OAuth authorize: generated auth URL", {
1256813
1256884
  authUrl,
1256814
1256885
  redirect_uri_in_url: parsed.searchParams.get("redirect_uri"),
1256815
1256886
  client_id_in_url: parsed.searchParams.get("client_id"),
1256816
1256887
  scope_in_url: parsed.searchParams.get("scope")
1256817
1256888
  });
1256818
1256889
  } catch {
1256819
- logger10.info("OAuth authorize: generated auth URL (unparseable)", { authUrl });
1256890
+ logger11.info("OAuth authorize: generated auth URL (unparseable)", { authUrl });
1256820
1256891
  }
1256821
1256892
  return c.json({ authUrl });
1256822
1256893
  } catch (err) {
1256823
- logger10.error("OAuth authorize failed", {
1256894
+ logger11.error("OAuth authorize failed", {
1256824
1256895
  error: err instanceof Error ? err.message : String(err)
1256825
1256896
  });
1256826
1256897
  return c.json({ error: err instanceof Error ? err.message : "Failed to generate auth URL" }, 400);
@@ -1256831,7 +1256902,7 @@ app25.get("/callback", async (c) => {
1256831
1256902
  const error46 = c.req.query("error");
1256832
1256903
  const stateParam = c.req.query("state");
1256833
1256904
  const fullUrl = c.req.url;
1256834
- logger10.info("OAuth callback received", {
1256905
+ logger11.info("OAuth callback received", {
1256835
1256906
  hasCode: !!code,
1256836
1256907
  codePrefix: code?.slice(0, 20) + "...",
1256837
1256908
  error: error46,
@@ -1256842,11 +1256913,11 @@ app25.get("/callback", async (c) => {
1256842
1256913
  refererHeader: c.req.header("referer")
1256843
1256914
  });
1256844
1256915
  if (error46) {
1256845
- logger10.warn("OAuth callback: Google returned error", { error: error46 });
1256916
+ logger11.warn("OAuth callback: Google returned error", { error: error46 });
1256846
1256917
  return c.html(`<html><body><h2>Authorization Failed</h2><p>${error46}</p><script>setTimeout(()=>window.close(),3000)</script></body></html>`);
1256847
1256918
  }
1256848
1256919
  if (!code) {
1256849
- logger10.warn("OAuth callback: missing authorization code");
1256920
+ logger11.warn("OAuth callback: missing authorization code");
1256850
1256921
  return c.html("<html><body><h2>Missing authorization code</h2><script>setTimeout(()=>window.close(),3000)</script></body></html>", 400);
1256851
1256922
  }
1256852
1256923
  let state = {
@@ -1256858,9 +1256929,9 @@ app25.get("/callback", async (c) => {
1256858
1256929
  state = JSON.parse(stateParam);
1256859
1256930
  }
1256860
1256931
  } catch {
1256861
- logger10.warn("OAuth callback: failed to parse state param", { stateParam });
1256932
+ logger11.warn("OAuth callback: failed to parse state param", { stateParam });
1256862
1256933
  }
1256863
- logger10.info("OAuth callback: parsed state", {
1256934
+ logger11.info("OAuth callback: parsed state", {
1256864
1256935
  accountId: state.accountId,
1256865
1256936
  accountName: state.accountName,
1256866
1256937
  redirectUri: state.redirectUri
@@ -1256871,7 +1256942,7 @@ app25.get("/callback", async (c) => {
1256871
1256942
  throw new Error("Missing redirect URI in state \u2014 cannot match token exchange");
1256872
1256943
  }
1256873
1256944
  const client4 = createOAuth2Client();
1256874
- logger10.info("OAuth callback: exchanging code for tokens", {
1256945
+ logger11.info("OAuth callback: exchanging code for tokens", {
1256875
1256946
  redirectUri,
1256876
1256947
  clientId: client4._clientId,
1256877
1256948
  codePrefix: code.slice(0, 20) + "..."
@@ -1256893,7 +1256964,7 @@ app25.get("/callback", async (c) => {
1256893
1256964
  email: email4 ?? undefined,
1256894
1256965
  updatedAt: now
1256895
1256966
  }).where(eq(googleAccounts.id, state.accountId)).run();
1256896
- logger10.info("Re-authorized Google account", {
1256967
+ logger11.info("Re-authorized Google account", {
1256897
1256968
  accountId: state.accountId,
1256898
1256969
  email: email4
1256899
1256970
  });
@@ -1256913,7 +1256984,7 @@ app25.get("/callback", async (c) => {
1256913
1256984
  createdAt: now,
1256914
1256985
  updatedAt: now
1256915
1256986
  }).run();
1256916
- logger10.info("Created Google account", {
1256987
+ logger11.info("Created Google account", {
1256917
1256988
  accountId,
1256918
1256989
  name: state.accountName,
1256919
1256990
  email: email4
@@ -1256921,7 +1256992,7 @@ app25.get("/callback", async (c) => {
1256921
1256992
  }
1256922
1256993
  return c.html("<html><body><h2>Authorization Successful</h2><p>You can close this window.</p><script>setTimeout(()=>window.close(),2000)</script></body></html>");
1256923
1256994
  } catch (err) {
1256924
- logger10.error("OAuth callback failed", {
1256995
+ logger11.error("OAuth callback failed", {
1256925
1256996
  error: err instanceof Error ? err.message : String(err)
1256926
1256997
  });
1256927
1256998
  return c.html(`<html><body><h2>Authorization Failed</h2><p>${err instanceof Error ? err.message : String(err)}</p><script>setTimeout(()=>window.close(),5000)</script></body></html>`, 500);
@@ -1256936,7 +1257007,7 @@ init_google_calendar_manager();
1256936
1257007
  init_logger3();
1256937
1257008
  init_settings();
1256938
1257009
  init_channel_manager();
1256939
- var logger11 = createLogger("Google:CalendarService");
1257010
+ var logger12 = createLogger("Google:CalendarService");
1256940
1257011
  function listGoogleAccounts() {
1256941
1257012
  return db2.select().from(googleAccounts).all();
1256942
1257013
  }
@@ -1256956,19 +1257027,19 @@ async function deleteGoogleAccount(id) {
1256956
1257027
  }
1256957
1257028
  db2.delete(caldavCalendars).where(eq(caldavCalendars.googleAccountId, id)).run();
1256958
1257029
  db2.delete(googleAccounts).where(eq(googleAccounts.id, id)).run();
1256959
- logger11.info("Deleted Google account", { accountId: id });
1257030
+ logger12.info("Deleted Google account", { accountId: id });
1256960
1257031
  }
1256961
1257032
  async function enableGoogleCalendar(id) {
1256962
1257033
  const now = new Date().toISOString();
1256963
1257034
  db2.update(googleAccounts).set({ calendarEnabled: true, updatedAt: now }).where(eq(googleAccounts.id, id)).run();
1256964
1257035
  await googleCalendarManager.startAccount(id);
1256965
- logger11.info("Enabled Google Calendar for account", { accountId: id });
1257036
+ logger12.info("Enabled Google Calendar for account", { accountId: id });
1256966
1257037
  }
1256967
1257038
  async function disableGoogleCalendar(id) {
1256968
1257039
  const now = new Date().toISOString();
1256969
1257040
  db2.update(googleAccounts).set({ calendarEnabled: false, updatedAt: now }).where(eq(googleAccounts.id, id)).run();
1256970
1257041
  googleCalendarManager.stopAccount(id);
1256971
- logger11.info("Disabled Google Calendar for account", { accountId: id });
1257042
+ logger12.info("Disabled Google Calendar for account", { accountId: id });
1256972
1257043
  }
1256973
1257044
  async function enableGmail(id) {
1256974
1257045
  const now = new Date().toISOString();
@@ -1256978,7 +1257049,7 @@ async function enableGmail(id) {
1256978
1257049
  updateSettingByPath("channels.email.googleAccountId", id);
1256979
1257050
  await stopEmailChannel();
1256980
1257051
  await startEmailChannel();
1256981
- logger11.info("Enabled Gmail for account", { accountId: id });
1257052
+ logger12.info("Enabled Gmail for account", { accountId: id });
1256982
1257053
  }
1256983
1257054
  async function disableGmail(id) {
1256984
1257055
  const now = new Date().toISOString();
@@ -1256986,7 +1257057,7 @@ async function disableGmail(id) {
1256986
1257057
  await stopEmailChannel();
1256987
1257058
  updateSettingByPath("channels.email.enabled", false);
1256988
1257059
  updateSettingByPath("channels.email.googleAccountId", null);
1256989
- logger11.info("Disabled Gmail for account", { accountId: id });
1257060
+ logger12.info("Disabled Gmail for account", { accountId: id });
1256990
1257061
  }
1256991
1257062
  async function syncGoogleCalendar(id) {
1256992
1257063
  await googleCalendarManager.syncAccount(id);
@@ -1257240,6 +1257311,10 @@ app28.get("/", async (c) => {
1257240
1257311
  const conversationRole = c.req.query("conversationRole") || undefined;
1257241
1257312
  const conversationProvider = c.req.query("conversationProvider") || undefined;
1257242
1257313
  const conversationProjectId = c.req.query("conversationProjectId") || undefined;
1257314
+ const gmailFrom = c.req.query("gmailFrom") || undefined;
1257315
+ const gmailTo = c.req.query("gmailTo") || undefined;
1257316
+ const gmailAfter = c.req.query("gmailAfter") || undefined;
1257317
+ const gmailBefore = c.req.query("gmailBefore") || undefined;
1257243
1257318
  try {
1257244
1257319
  const results = await search({
1257245
1257320
  query: query.trim(),
@@ -1257254,7 +1257329,11 @@ app28.get("/", async (c) => {
1257254
1257329
  memoryTags,
1257255
1257330
  conversationRole,
1257256
1257331
  conversationProvider,
1257257
- conversationProjectId
1257332
+ conversationProjectId,
1257333
+ gmailFrom,
1257334
+ gmailTo,
1257335
+ gmailAfter,
1257336
+ gmailBefore
1257258
1257337
  });
1257259
1257338
  return c.json(results);
1257260
1257339
  } catch (err) {