Skip to content

Conversation

junaid-shirur
Copy link
Collaborator

@junaid-shirur junaid-shirur commented Oct 8, 2025

Description

  • Created per app tools
  • created google
    • searchGmail
    • searchDriveFiles
    • searchCalendar
    • searchContacts
  • for slack
    • searchSlackMessages
    • getSlackUserProfile

Testing

Additional Notes

Summary by CodeRabbit

  • New Features

    • Global unified search plus Google app searches (Gmail, Drive, Calendar, Contacts) with participant-aware email resolution, multi-app queries, time-range filtering, improved sorting, and consistent result formatting.
  • Refactor

    • Public filters and APIs renamed to use a participant-centric field (formerly intent); search parameters streamlined to query, sortBy, and timeRange for clearer multi-app flows.
  • Chores

    • Dependency bump and added public search tools/endpoints to support the new search surface.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @junaid-shirur, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request, marked as a Work In Progress (WIP), primarily focuses on two areas: refactoring the Intent type to MailParticipant for better semantic clarity in handling email participant data, and introducing a suite of new tools for integrating with various Google services. The new Google tools enable searching across Gmail (emails and attachments), Google Drive, Google Calendar, and Google Contacts. Existing tools in the agentTools mapping have been commented out, indicating a potential shift in tool management or an ongoing refactoring effort. The 'fix' aspect of the PR title is not clearly reflected in the changes, which are predominantly feature additions and type refactoring.

Highlights

  • Intent: The primary intent of this pull request, marked as "WIP", is to introduce new capabilities for interacting with Google services and to refactor existing type definitions for clarity. Specifically, it aims to integrate tools for searching across Gmail, Google Drive, Google Calendar, and Google Contacts, while also renaming the Intent type to MailParticipant to better reflect its purpose in handling email participant data. The "fix" aspect in the title is not immediately evident from the changes, which primarily involve new features and refactoring.
  • Key Changes: 1. Type Renaming (Intent to MailParticipant): The Intent type and IntentSchema have been renamed to MailParticipant and MailParticipantSchema respectively across multiple files (server/ai/types.ts, server/ai/provider/index.ts, server/api/chat/agents.ts, server/api/chat/chat.ts, server/api/chat/tools.ts). This change improves the semantic clarity of the type, indicating its use for email participants (from, to, cc, bcc, subject).
  1. Introduction of Google Service Tools: A comprehensive set of new tools for Google services has been added. These include searchGlobal (generic search), searchGmail (email search), searchGmailAttachment (attachment search), searchDriveFiles (file search), searchCalendarEvents (event search), and searchGoogleContacts (contact search). These tools are defined in server/api/chat/mapper.ts and their execution logic is implemented in server/api/chat/tools.ts.
  2. Tool Integration and Management: The agentTools mapping in server/api/chat/tools.ts has been updated to include these new Google-specific tools. Several existing tools (get_user_info, metadata_retrieval, search, and Slack-specific tools) have been commented out, suggesting a temporary state or a broader refactoring of tool management.
  3. Minor Code Adjustments: Several minor code adjustments were made, including adding a console.log for debugging provider types, improving conditional checks and formatting for readability and robustness, and updating userMetadata initialization.
  • Reviewer Activity: No specific reviewer activity has been observed yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant refactoring by renaming Intent to MailParticipant for better clarity, along with adding a suite of new Google tools. While the changes are extensive and seem to be a work in progress, I've identified several issues that should be addressed to improve code quality, correctness, and maintainability. Key areas for improvement include removing debug code, addressing hardcoded values, fixing inconsistencies in naming and logic, and correcting a bug in query construction. Please see my detailed comments below.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Renames Intent → MailParticipant across types, prompts, provider and chat surfaces; updates function signatures and variable names to use mailParticipants/participants; adds Google/global search tools, Vespa binding searchGoogleApps, and new tool wiring; minor logging, formatting, and dependency bump. (43 words)

Changes

Cohort / File(s) Summary of Changes
Type renames
server/ai/types.ts
IntentSchemaMailParticipantSchema; exported type IntentMailParticipant; FiltersSchema field intentmailParticipants.
Provider updates
server/ai/provider/index.ts
Import/type switched to MailParticipant; extractEmailsFromContext signature and internals use participants / MailParticipant; passes participants into prompt; minor spacing/logging edits.
Chat API refactor
server/api/chat/chat.ts
Replaced Intent with MailParticipant in imports, signatures, locals and logs; intentmailParticipants across metadata, parameter objects, queries and logs.
Agent filters rename
server/api/chat/agents.ts
Replaced intent field with mailParticipants in queryFilters, parsed filters and classification assembly.
Mapper/tool exports
server/api/chat/mapper.ts
Added exports searchGlobalTool and googleTools; added GoogleApps/Ollama imports; new ToolDefinition exports for Gmail/Drive/Calendar/Contacts.
Tools & search wiring
server/api/chat/tools.ts
Added searchGlobal and Google tools (searchGmail, searchDriveFiles, searchCalendarEvents, searchGoogleContacts); expanded UnifiedSearchOptions (multi-app/entity, mailParticipant, orderBy, owner, eventStatus, eventAttendees); executeVespaSearch and flows propagate mailParticipants; added formatting/response helpers.
Chat types & utils
server/api/chat/types.ts, server/api/chat/utils.ts
SearchParams: filter_queryquery, order_directionsortBy, added timeRange; utils imports MailParticipant and local extraction result typed to MailParticipant; removed subject from fieldsToCheck.
AI prompts & agent prompts
server/ai/prompts.ts, server/ai/agentPrompts.ts
Prompt text/sections renamed from "intent" to "mailParticipants" (headers, extraction rules, JSON schema examples, CRITICAL rules, examples).
Vespa binding
server/search/vespa.ts
Added export const searchGoogleApps = vespa.searchGoogleApps.bind(vespa).
Misc / formatting & deps
multiple files, server/package.json
Minor spacing/bracing/log text edits across files; dependency bump @xyne/vespa-ts 1.1.5 → 1.1.6 in server/package.json.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant ChatAPI as Chat API
  participant Resolver as resolveNamesToEmails
  participant Provider as AI Provider
  participant Vespa as Vespa Search

  User->>ChatAPI: Query (names / participants)
  ChatAPI->>Resolver: resolveNamesToEmails(mailParticipants, ...)
  Resolver->>Provider: extractEmailsFromContext(participants, userCtx, retrievedCtx, params)
  Provider-->>Resolver: { emails: MailParticipant }
  Resolver-->>ChatAPI: resolvedMailParticipants
  ChatAPI->>Vespa: executeVespaSearch(..., mailParticipants=resolvedMailParticipants, ...)
  Vespa-->>ChatAPI: results
  ChatAPI-->>User: Answer + references
  note right of Provider #ddeeff: provider/model selection & logging
Loading
sequenceDiagram
  autonumber
  actor Agent
  participant Tools as Agent Tools
  participant Vespa as Vespa Search

  rect rgb(240,248,255)
    note over Tools: New Google tool surface (Gmail/Drive/Calendar/Contacts)
    Agent->>Tools: searchGmail/searchDrive/... (params + mailParticipants)
    Tools->>Vespa: executeVespaSearch(app, entity, mailParticipants, timeRange, ...)
    Vespa-->>Tools: search results
    Tools-->>Agent: normalized results
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • zereraz
  • kalpadhwaryu
  • devesh-juspay

Poem

Hop hop, I swap the names and tidy the trail,
Intent is now MailParticipant — neat and hale.
Google tools arrive, Vespa hums a tune,
I nibble logs and wires beneath the moon. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly indicates that the pull request revamps the agentic tools surface, which aligns with the main objective of adding per-app Google and Slack tooling and updating existing tool definitions without unnecessary detail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tool_revamp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
server/api/chat/chat.ts (2)

220-327: Fix resolveNamesToEmails: wrong return shape, duplicate span.end(), and weak typing

  • Returning resolutionResult.emails || [] likely breaks downstream expecting a MailParticipant-shaped object.
  • searchSpan?.end() is called twice (Line 283 and Line 321).
  • Type should be Promise<MailParticipant>; merge resolved emails onto the original participant object.

Apply:

-export async function resolveNamesToEmails(
-  intent: MailParticipant,
+export async function resolveNamesToEmails(
+  mailParticipant: MailParticipant,
   email: string,
   userCtx: string,
   userMetadata: UserMetadataType,
   span?: Span,
-): Promise<any> {
+): Promise<MailParticipant> {
@@
-    const extractedNames = extractNamesFromIntent(intent)
+    const extractedNames = extractNamesFromIntent(mailParticipant)
@@
-    if (allNames.length === 0) {
+    if (allNames.length === 0) {
       resolveSpan?.setAttribute("no_names_found", true)
       resolveSpan?.end()
-      return intent
+      return mailParticipant
     }
@@
-    if (allNamesAreEmails) {
+    if (allNamesAreEmails) {
       resolveSpan?.setAttribute("all_names_are_emails", true)
       resolveSpan?.setAttribute("skip_resolution", true)
       resolveSpan?.end()
-      return intent
+      return mailParticipant
     }
@@
-    let resolvedData: MailParticipant = {}
+    let resolvedData: MailParticipant = mailParticipant
     const resolutionResult = await extractEmailsFromContext(
       extractedNames,
       userCtx,
       searchContext,
       { modelId: config.defaultFastModel, json: false, stream: false },
     )
 
-    resolvedData = resolutionResult.emails || []
+    const resolvedEmails = (resolutionResult?.emails ?? {}) as Partial<MailParticipant>
+    resolvedData = { ...mailParticipant, ...resolvedEmails }
 
-    searchSpan?.end()
     return resolvedData
   } catch (error) {
     resolveSpan?.end()
-    return intent
+    return mailParticipant
   }
 }

3519-3526: Double span end and premature end before downstream usage

span?.end() is called twice and is also ended before passing span to processResultsForMetadata, which still uses it.

  • Remove both early ends here to avoid ending before downstream processing. End it at the outer scope if needed.
-    span?.setAttribute("context", buildContext(items, 20, userMetadata))
-    span?.end()
+    span?.setAttribute("context", buildContext(items, 20, userMetadata))
@@
-    if (!items.length) {
-      span?.end()
+    if (!items.length) {
       loggerWithChild({ email: email }).info(
         "No documents found for unspecific metadata retrieval",
       )
       yield { text: METADATA_NO_DOCUMENTS_FOUND }
       return
     }
server/api/chat/tools.ts (1)

110-136: Fix the shadowed selectedItem variable

The inner let selectedItem inside the try block shadows the outer selectedItem. After the block, the outer variable is still {}, so selectedItems in the returned object is always empty, breaking downstream logic that relies on the parsed selections. Reuse the outer variable instead of redeclaring it.

-    let selectedItem: any = {}
+    selectedItem = {}
♻️ Duplicate comments (6)
server/ai/provider/index.ts (2)

1605-1616: Respect user timezone when building prompts.

Hard-coding Asia/Kolkata here will misstate the date for anyone outside IST. Thread the real userTimeZone (already available on the request/metadata path) into getDateForAI instead of forcing a fixed zone.


434-435: Remove debug console.log before shipping.

Please drop this debug log (or route it through the structured logger) so we don’t leak internal model/provider selections into stdout in production.

server/api/chat/chat.ts (2)

221-221: Rename param for clarity and consistency with type

Rename intent to mailParticipant to match the MailParticipant type. Please update usages inside the function as well.


3474-3475: Update log label: "Intent" → "Mail Participants"

Keep terminology consistent with classification.filters.mailParticipants.

-        `[GetItems] Calling getItems - Schema: ${schema}, App: ${apps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, Intent: ${JSON.stringify(classification.filters.mailParticipants)}`,
+        `[GetItems] Calling getItems - Schema: ${schema}, App: ${apps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, Mail Participants: ${JSON.stringify(classification.filters.mailParticipants)}`,
server/api/chat/tools.ts (2)

267-270: Restore dynamic user timezone handling

Hard-coding userTimezone to "Asia/Kolkata" (and passing the same to getDateForAI) ignores the actual user context and repeats the earlier bug that was already flagged. Please derive the timezone from the user metadata/config so all users get correct local dates.


2142-2148: Correct the Gmail attachment query construction

enhancedQuery becomes syntactically invalid (...pdf,docx)) and lacks the required field qualifiers, so Vespa receives a broken filter. Build it like the Drive tool does—prepend the proper filter key and join with OR.

-      if (params.fileType && params.fileType.length > 0) {
-        enhancedQuery += `${params.fileType.join(",")})`
-      }
+      if (params.fileType && params.fileType.length > 0) {
+        enhancedQuery += ` filetype:(${params.fileType.join(" OR ")})`
+      }
🧹 Nitpick comments (2)
server/api/chat/chat.ts (2)

3427-3430: Update log label: "Resolved intent" → "Resolved mailParticipants"

Align log text with the refactor.

-      loggerWithChild({ email: email }).info(
-        `[${QueryType.SearchWithoutFilters}] Resolved intent: ${JSON.stringify(resolvedMailParticipants)}`,
-      )
+      loggerWithChild({ email: email }).info(
+        `[${QueryType.SearchWithoutFilters}] Resolved mailParticipants: ${JSON.stringify(resolvedMailParticipants)}`,
+      )

3589-3592: Update log label: "Resolved intent" → "Resolved mailParticipants"

Consistency with filters and prior logs.

-      loggerWithChild({ email: email }).info(
-        `[SearchWithFilters] Resolved intent: ${JSON.stringify(resolvedMailParticipants)}`,
-      )
+      loggerWithChild({ email: email }).info(
+        `[SearchWithFilters] Resolved mailParticipants: ${JSON.stringify(resolvedMailParticipants)}`,
+      )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 218f626 and 7d1d9bb.

📒 Files selected for processing (6)
  • server/ai/provider/index.ts (10 hunks)
  • server/ai/types.ts (2 hunks)
  • server/api/chat/agents.ts (3 hunks)
  • server/api/chat/chat.ts (18 hunks)
  • server/api/chat/mapper.ts (8 hunks)
  • server/api/chat/tools.ts (20 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
PR: xynehq/xyne#787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.

Applied to files:

  • server/api/chat/tools.ts
🧬 Code graph analysis (4)
server/ai/provider/index.ts (3)
server/ai/prompts.ts (1)
  • temporalDirectionJsonPrompt (1637-1966)
server/utils/index.ts (1)
  • getDateForAI (3-29)
server/ai/types.ts (2)
  • MailParticipant (393-393)
  • ModelParams (284-309)
server/api/chat/mapper.ts (1)
server/shared/types.ts (1)
  • DriveEntity (33-33)
server/api/chat/chat.ts (3)
server/ai/types.ts (1)
  • MailParticipant (393-393)
server/api/chat/chunk-selection.ts (1)
  • getChunkCountPerDoc (13-148)
server/api/chat/utils.ts (1)
  • collectFollowupContext (79-144)
server/api/chat/tools.ts (7)
server/ai/types.ts (1)
  • MailParticipant (393-393)
server/utils/index.ts (1)
  • getDateForAI (3-29)
server/search/vespa.ts (1)
  • SearchVespaThreads (136-136)
server/utils.ts (1)
  • getErrorMessage (103-106)
server/api/chat/types.ts (2)
  • MinimalAgentFragment (88-94)
  • AgentTool (110-134)
server/api/chat/mapper.ts (2)
  • googleTools (1021-1261)
  • convertToAgentToolParameters (848-866)
server/tracer.ts (1)
  • Span (24-38)
🔇 Additional comments (5)
server/api/chat/chat.ts (5)

2294-2304: Good fallback for selected-context path

Clear UX when no answer found from selected context. LGTM.


3046-3053: Filters destructuring update looks correct

Switch to mailParticipants in filters is consistent with the refactor.


3240-3276: Validate shape compatibility of resolvedMailParticipants

Ensure resolveNamesToEmails returns a MailParticipant-shaped object; otherwise passing it as mailParticipants to search/getItems will break filtering.

If you adopt the fix suggested in resolveNamesToEmails, this will be consistent.


4519-4544: Follow-up context carry-forward: good guardrails

  • Excluding assistant messages with empty content avoids noise.
  • Carry-forward dedup via Set is correct.

7325-7328: Safer access with optional chaining

Prevents index errors when assistant response is missing. LGTM.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
server/api/chat/utils.ts (1)

1038-1053: Fix undefined Intent type usage.

Intent no longer exists, so this function now fails to compile and yields the wrong shape. Swap the argument and return type to the new MailParticipant.

-export function extractNamesFromIntent(intent: any): Intent {
-  if (!intent || typeof intent !== "object") return {}
-
-  const result: Intent = {}
+export function extractNamesFromIntent(
+  intent: MailParticipant | null | undefined,
+): MailParticipant {
+  if (!intent || typeof intent !== "object") return {}
+
+  const result: MailParticipant = {}
server/api/chat/tools.ts (1)

185-226: Stop shadowing selectedItem.

The inner let selectedItem creates a new binding, so the outer variable that you return stays {}. Every caller (e.g. Drive search) loses the agent’s selected items and can’t scope searches correctly. Reuse the outer variable instead of redeclaring it.

-    let selectedItem: any = {}
+    selectedItem = {}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d1d9bb and 541b08a.

📒 Files selected for processing (5)
  • server/api/chat/mapper.ts (10 hunks)
  • server/api/chat/tools.ts (23 hunks)
  • server/api/chat/types.ts (1 hunks)
  • server/api/chat/utils.ts (1 hunks)
  • server/search/vespa.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
PR: xynehq/xyne#787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.

Applied to files:

  • server/api/chat/tools.ts
🧬 Code graph analysis (1)
server/api/chat/tools.ts (9)
server/api/chat/types.ts (2)
  • MinimalAgentFragment (88-94)
  • AgentTool (110-134)
server/api/chat/utils.ts (1)
  • searchToCitation (441-539)
server/ai/context.ts (1)
  • answerContextMap (741-809)
server/types.ts (1)
  • UserMetadataType (605-605)
server/utils/index.ts (1)
  • getDateForAI (3-29)
server/api/chat/mapper.ts (3)
  • searchGlobalTool (979-1020)
  • convertToAgentToolParameters (850-868)
  • googleTools (1022-1215)
server/search/vespa.ts (2)
  • SearchVespaThreads (136-136)
  • searchGoogleApps (131-131)
server/utils.ts (1)
  • getErrorMessage (103-106)
server/search/utils.ts (1)
  • extractDriveIds (74-117)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (6)
server/api/chat/mapper.ts (4)

988-989: Fix typo: "retireve" → "retrieve".

The word "retireve" is misspelled. This typo appears in multiple tool descriptions throughout the file.

Apply this diff:

-         - query should be keywords focus to retireve the most relevant content from corpus.`,
+         - query should be keywords focused to retrieve the most relevant content from corpus.`,

1032-1035: Improve description clarity and fix typo.

The description has formatting issues and a typo:

  1. Run-on sentence with awkward flow
  2. "retireve" should be "retrieve"
  3. "keywords focus" should be "keywords focused"

Apply this diff:

-        description: `Extracted search terms from the user's query to find emails if query doesn't contain important semantic terms, will fetch based on different params e.g. timeRange, participants, labels etc.
+        description: `Extracted search terms from the user's query to find emails. If the query doesn't contain important semantic terms, the tool will fetch emails based on other parameters like timeRange, participants, and labels.
            - don't apply filters in the query like "from:sahebjot" or "subject:meeting".
-           - query should be keywords focus to retireve the most relevant content from corpus.
+           - query should be keywords focused to retrieve the most relevant content from corpus.
           `,

1072-1073: Fix grammar: "search" → "searches".

Subject-verb agreement issue: "it also search" should be "it also searches".

Apply this diff:

         description:
-          "If true, it also search for the attachments in the emails.",
+          "If true, it also searches for the attachments in the emails.",

1129-1129: Add .join() to properly format the file types list.

Using .map() without .join() will render the array representation in the description string instead of a formatted list.

Apply this diff:

-        description: `Filter files by type (e.g., ${Object.values(DriveEntity).map((e) => `- ${e}\n`)}).`,
+        description: `Filter files by type (e.g., ${Object.values(DriveEntity).map((e) => `${e}`).join(', ')}).`,
server/api/chat/tools.ts (2)

254-254: Fix property name: mailParticipantmailParticipants.

There's an inconsistency in the naming of this property. Here it's mailParticipant (singular), but in server/ai/types.ts and other parts of the codebase, the corresponding property is named mailParticipants (plural). This will lead to runtime errors.

Apply this diff:

-  mailParticipant?: MailParticipant | null
+  mailParticipants?: MailParticipant | null

Note: You'll also need to update all references to this property throughout this file (lines 289, 334, 344, 444).


266-269: Do not freeze user metadata at module load.

userMetadata is now a module-level constant with a hard-coded Asia/Kolkata timezone and a dateForAI snapshot taken at process start. That makes every response use the wrong timezone for most users and the date quickly drifts out of sync. Please derive this per request (e.g., parse usrCtx) and stop hard-coding the timezone.

You should:

  1. Remove the module-level userMetadata constant
  2. Extract user timezone from request context in each tool execution
  3. Compute dateForAI per-request using the extracted timezone
  4. Provide a sensible fallback (e.g., UTC) if the user timezone is missing
🧹 Nitpick comments (1)
server/api/chat/tools.ts (1)

1012-1060: Consider making thread-root detection more explicit.

The automatic thread message fetching is a good enhancement. However, the thread-root detection at lines 1020-1022 relies on comparing String(createdAtNum) === threadIdStr, which assumes the threadId format matches the timestamp format. This could be fragile if Slack's API or the data model changes.

Consider adding a comment explaining this assumption or using a more explicit field/flag if available in the schema to identify thread roots, rather than relying on string equality of timestamps.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 541b08a and 0f087f0.

📒 Files selected for processing (3)
  • server/api/chat/mapper.ts (10 hunks)
  • server/api/chat/tools.ts (23 hunks)
  • server/api/chat/utils.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/api/chat/utils.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
PR: xynehq/xyne#787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.

Applied to files:

  • server/api/chat/tools.ts
🧬 Code graph analysis (1)
server/api/chat/tools.ts (6)
server/api/chat/types.ts (2)
  • MinimalAgentFragment (88-94)
  • AgentTool (110-134)
server/api/chat/utils.ts (3)
  • searchToCitation (441-539)
  • getChannelIdsFromAgentPrompt (152-170)
  • expandEmailThreadsInResults (249-273)
server/ai/context.ts (1)
  • answerContextMap (741-809)
server/api/chat/mapper.ts (2)
  • searchGlobalTool (979-1020)
  • convertToAgentToolParameters (850-868)
server/utils.ts (1)
  • getErrorMessage (103-106)
server/search/utils.ts (1)
  • extractDriveIds (74-117)
🔇 Additional comments (1)
server/api/chat/tools.ts (1)

1761-2087: LGTM: Google tools implementations are well-structured.

The four Google tools (searchGmail, searchDriveFiles, searchCalendarEvents, searchGoogleContacts) follow a consistent and robust pattern:

  • Proper input validation and type checking
  • Agent permission verification
  • Appropriate error handling and logging
  • Consistent response formatting via formatSearchToolResponse
  • Gmail tool includes thread expansion for better context
  • Drive tool handles folder expansion from selectedItems

Note: The tools currently use the module-level userMetadata constant which has the hardcoded timezone issue flagged separately (lines 266-269). Once that's fixed, these tools will inherit the per-request timezone handling automatically.

@junaid-shirur junaid-shirur changed the title fix(tools): WIP fix(tools): Revamp agentic tools Oct 13, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
server/search/vespa.ts (1)

134-155: Fix getItems wrapper: param typing and payload shape mismatches

  • selectedItem is read but not declared in the param type.
  • You send processedCollectionSelections, but GetItemsParams expects collectionSelections. This triggers excess property errors.

Apply this diff:

-export const getItems = async (
-  params: Omit<GetItemsParams, "processedCollectionSelections"> & {
-    collectionSelections?: Array<{
-      collectionIds?: string[]
-      collectionFolderIds?: string[]
-      collectionFileIds?: string[]
-    }>
-  },
-) => {
+export const getItems = async (
+  params: Omit<GetItemsParams, "collectionSelections"> & {
+    collectionSelections?: Array<{
+      collectionIds?: string[]
+      collectionFolderIds?: string[]
+      collectionFileIds?: string[]
+    }>
+    // used to derive driveIds
+    selectedItem?: Partial<Record<Apps, string[]>>
+  },
+) => {
   const driveIds = await extractDriveIds(
     { selectedItem: params.selectedItem },
     params.email,
   )
   const processedCollectionSelections = await extractCollectionVespaIds({
     collectionSelections: params.collectionSelections,
   })
-  return await vespa.getItems.bind(vespa)({
-    processedCollectionSelections,
-    driveIds,
-    ...params,
-  })
+  return await vespa.getItems.bind(vespa)({
+    ...params,
+    driveIds,
+    // map processed -> expected input
+    collectionSelections: processedCollectionSelections,
+  })
 }
server/api/chat/chat.ts (2)

3403-3419: getItems param 'mailParticipants' is not part of the wrapper type

The wrapper currently accepts intent (from GetItemsParams), not mailParticipants. Passing mailParticipants breaks excess-property checks.

Apply this diff to align with current types (follow-up PR can add mailParticipants to the contract):

-          mailParticipants: resolvedMailParticipants || {},
+          intent: resolvedMailParticipants || {},

224-231: Tighten types in resolveNamesToEmails and fix default value

  • Return type should be MailParticipant, not any.
  • resolvedData should default to {} (object), not [] (array), to match MailParticipant.

Apply this diff:

-export async function resolveNamesToEmails(
+export async function resolveNamesToEmails(
   intent: MailParticipant,
   email: string,
   userCtx: string,
   userMetadata: UserMetadataType,
   span?: Span,
-): Promise<any> {
+): Promise<MailParticipant> {
@@
-    let resolvedData: MailParticipant = {}
+    let resolvedData: MailParticipant = {}
@@
-    resolvedData = resolutionResult.emails || []
+    resolvedData = resolutionResult.emails || {}

Also applies to: 315-324

server/ai/types.ts (1)

384-405: Fix leftover intent references
Replace the obsolete intent: parsed?.filters?.intent || {} at server/api/chat/chat.ts line 6491 with mailParticipants: parsed?.filters?.mailParticipants || {}.

♻️ Duplicate comments (4)
server/api/chat/chat.ts (2)

1981-1991: Undefined SearchModes.AttachmentRank and limit mismatch

  • SearchModes.AttachmentRank is not in the enum → compile error.
  • Limit should use nonCollectionFileIds.length per earlier note to match the searched set.

Apply this diff:

-        results = await searchVespaInFiles(
+        results = await searchVespaInFiles(
           builtUserQuery,
           email,
           nonCollectionFileIds,
           {
-            limit: fileIds?.length,
-            alpha: userAlpha,
-            rankProfile: SearchModes.AttachmentRank,
+            limit: nonCollectionFileIds.length,
+            alpha: userAlpha,
+            // Use a valid rank profile; fall back to NativeRank
+            rankProfile: SearchModes.NativeRank,
           },
         )

3427-3427: Log label: use 'Mail Participants' instead of 'Intent'

The message logs mailParticipants but labels it as Intent. Update for clarity/consistency.

-        `[GetItems] Calling getItems - Schema: ${schema}, App: ${apps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, Intent: ${JSON.stringify(classification.filters.mailParticipants)}`,
+        `[GetItems] Calling getItems - Schema: ${schema}, App: ${apps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, Mail Participants: ${JSON.stringify(classification.filters.mailParticipants)}`,
server/api/chat/tools.ts (2)

268-271: Critical: Remove module-level userMetadata with hardcoded timezone.

This module-level constant causes every request to use the hardcoded Asia/Kolkata timezone instead of the actual user's timezone, and the dateForAI becomes stale as it's computed only once at module load.

Per previous reviews, this needs to be computed per-request. Extract the user's timezone from the request context (e.g., usrCtx parameter) and compute userMetadata within each tool's execute function, with a fallback to UTC if unavailable.


256-256: Fix naming inconsistency: mailParticipant vs mailParticipants.

The property is named mailParticipant (singular) here, but throughout the rest of the file it's referenced as mailParticipants (plural) - see lines 346, 446. This inconsistency combined with the pipeline failure indicating mailParticipants doesn't exist in VespaQueryConfig suggests a type mismatch.

Please verify the correct property name from the VespaQueryConfig type definition and use it consistently throughout this file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f087f0 and 46a14c2.

📒 Files selected for processing (6)
  • server/ai/types.ts (2 hunks)
  • server/api/chat/agents.ts (3 hunks)
  • server/api/chat/chat.ts (14 hunks)
  • server/api/chat/tools.ts (25 hunks)
  • server/api/chat/utils.ts (2 hunks)
  • server/search/vespa.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/api/chat/agents.ts
  • server/api/chat/utils.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
PR: xynehq/xyne#787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.

Applied to files:

  • server/api/chat/tools.ts
🧬 Code graph analysis (2)
server/api/chat/chat.ts (1)
server/ai/types.ts (1)
  • MailParticipant (393-393)
server/api/chat/tools.ts (7)
server/api/chat/types.ts (2)
  • MinimalAgentFragment (88-94)
  • AgentTool (110-134)
server/api/chat/utils.ts (2)
  • searchToCitation (442-540)
  • expandEmailThreadsInResults (250-274)
server/ai/context.ts (1)
  • answerContextMap (887-985)
server/api/chat/mapper.ts (3)
  • searchGlobalTool (979-1020)
  • convertToAgentToolParameters (850-868)
  • googleTools (1022-1216)
server/utils.ts (1)
  • getErrorMessage (103-106)
server/search/vespa.ts (2)
  • SearchVespaThreads (159-159)
  • searchGoogleApps (132-132)
server/search/utils.ts (1)
  • extractDriveIds (95-138)
🪛 GitHub Actions: TypeScript Build Check
server/search/vespa.ts

[error] 132-132: Property 'searchGoogleApps' does not exist on type 'VespaService'.


[error] 144-144: Property 'selectedItem' does not exist on type 'Omit<GetItemsParams, "processedCollectionSelections"> & { collectionSelections?: { collectionIds?: string[] | undefined; collectionFolderIds?: string[] | undefined; collectionFileIds?: string[] | undefined; }[] | undefined; }'.


[error] 151-151: Object literal may only specify known properties, and 'processedCollectionSelections' does not exist in type 'GetItemsParams'.

server/api/chat/chat.ts

[error] 1989-1989: Property 'AttachmentRank' does not exist on type 'typeof SearchModes'.


[error] 3415-3415: Object literal may only specify known properties, and 'mailParticipants' does not exist in type 'Omit<GetItemsParams, "processedCollectionSelections"> & { collectionSelections?: { collectionIds?: string[] | undefined; collectionFolderIds?: string[] | undefined; collectionFileIds?: string[] | undefined; }[] | undefined; }'.

server/api/chat/tools.ts

[error] 19-19: Module '@xyne/vespa-ts/types' has no exported member 'GoogleApps'.


[error] 77-77: Module '@xyne/vespa-ts' has no exported member 'EventStatusType'.


[error] 79-79: Module '@xyne/vespa-ts' has no exported member 'MailParticipant'.


[error] 346-346: Object literal may only specify known properties, and 'mailParticipants' does not exist in type 'Partial'.


[error] 446-446: Object literal may only specify known properties, and 'mailParticipants' does not exist in type 'Omit<GetItemsParams, "processedCollectionSelections"> & { collectionSelections?: { collectionIds?: string[] | undefined; collectionFolderIds?: string[] | undefined; collectionFileIds?: string[] | undefined; }[] | undefined; }'.

🪛 GitHub Check: test
server/search/vespa.ts

[failure] 132-132: TypeError: undefined is not an object (evaluating 'vespa.searchGoogleApps.bind')

  at /home/runner/work/xyne/xyne/server/search/vespa.ts:132:39
  at loadAndEvaluateModule (2:1)
🔇 Additional comments (5)
server/api/chat/tools.ts (5)

88-153: LGTM with caveat on userMetadata.

The formatSearchToolResponse function provides good abstraction for formatting search results into a consistent response structure. The logic for building summary text and fragments is clear and handles various search contexts well.

Note: This function uses the module-level userMetadata constant at line 117, which has timezone issues (addressed in a separate comment).


363-369: Good validation logic for multi-app support.

The validation logic properly handles both single apps and arrays of apps, with clear error messages that indicate which apps are not allowed. The string formatting for singular/plural cases is well done.

Also applies to: 423-430


527-586: LGTM: searchGlobal refactored to use mapper definitions.

The tool has been properly refactored to use searchGlobalTool definition and convertToAgentToolParameters from the mapper, providing better consistency. The fallback chain params.query || userMessage || "" is reasonable.


1021-1097: Excellent: Automatic thread message fetching improves UX.

The automatic fetching of thread messages when thread roots are found is a great user experience improvement. The implementation correctly identifies thread roots by comparing createdAt with threadId, fetches the thread messages, and merges them with the original results. Error handling for failed thread fetches is also well done.


1777-2101: LGTM: Consistent Google tools implementation.

The four new Google tools (searchGmail, searchDriveFiles, searchCalendarEvents, searchGoogleContacts) follow a consistent pattern with:

  • Proper agent permission checks
  • Correct parameter handling and time range conversions
  • Use of the formatSearchToolResponse utility
  • Good error handling

The implementation is clean and maintainable. Note that these tools depend on the GoogleApps import which is currently failing (addressed in a separate comment).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
server/ai/agentPrompts.ts (1)

203-221: Fix inconsistent JSON keys and typo (“goo”) in instructions.

  • Use a single convention: rewrittenQueries (not rewritten_queries) and dateRange (not date_range).
  • Typo: “goo for better query rewrites” → “go for better query rewrites”.

These inconsistencies can cause parse failures and non‑compliant outputs.

Apply this minimal edit inside the template:

-   - Do not answer if you do not have valid context and goo for better query rewrites
+   - Do not answer if you do not have valid context and go for better query rewrites
-3. **Mutual Exclusivity:** Only one of "answer" or "rewritten_queries" should be present.
+3. **Mutual Exclusivity:** Only one of "answer" or "rewrittenQueries" should be present.
-   - If an answer is not provided, set "answer" to null and provide "rewritten_queries" along with the "date_range".
+   - If an answer is not provided, set "answer" to null and provide "rewrittenQueries" along with the "dateRange".
server/ai/prompts.ts (1)

1950-1966: Remove stray merge separator (‘=======’) from prompt.

A leftover merge divider is present inside the temporalDirectionJsonPrompt template. This will be rendered verbatim to the model and can confuse output formatting.

Apply this edit:

-=======
 If NO relevant items are found in Retrieved Context or context doesn't match query:
 {
   "answer": null
 }
server/api/chat/chat.ts (2)

1987-1989: Fix missing SearchModes.AttachmentRank enum member.

The TypeScript build is failing because SearchModes has no AttachmentRank member. Either add this enum entry (and corresponding server support) or use one of the existing rank profiles. Until this is resolved the file won’t compile. Refer to the CI error at Line 1989.


3404-3441: getItems does not accept mailParticipants.

GetItemsParams currently rejects a mailParticipants field, so these new arguments make TypeScript fail (see CI error around Line 3415). Please extend the type definition (and handler) to accept this filter or drop the property here; otherwise the build will keep breaking.

♻️ Duplicate comments (5)
server/ai/provider/index.ts (2)

857-859: Use nullish coalescing assignment for brevity.

Replace the explicit undefined check with ??= for clarity.

-    if (assistantResponse === undefined) {
-      assistantResponse = ""
-    }
+    assistantResponse ??= ""

1603-1603: Avoid hardcoded timezone in getDateForAI.

Use the user’s timezone (from user metadata or a parameter) instead of "Asia/Kolkata".

Apply this change and add an optional function parameter for timezone:

-  const dateForAI = getDateForAI({ userTimeZone: "Asia/Kolkata" })
+  const dateForAI = getDateForAI({
+    userTimeZone:
+      (params as any)?.userTimezone ||
+      Intl.DateTimeFormat().resolvedOptions().timeZone,
+  })

And (outside this line) consider adding an optional userTimezone?: string to ModelParams or thread it explicitly into this function to avoid any.

server/api/chat/tools.ts (3)

740-748: Replace console.log statements with structured logging.

Multiple console.log statements remain in production code. These should use the Logger instance for proper structured logging.

Apply this pattern throughout:

-      console.log(
-        "[Retrieve Slack Thread] Common Vespa searchOptions:",
-        JSON.stringify(searchOptionsVespa, null, 2),
-      )
-
-      const searchQuery = params.filter_query
-      console.log(
-        `[get_slack_messages_from_user] Using searchVespa with filter_query: '${searchQuery}'`,
-      )
+      Logger.debug({ searchOptionsVespa }, "[Retrieve Slack Thread] Common Vespa searchOptions")
+      Logger.debug({ filterQuery: params.filter_query }, "[get_slack_messages_from_user] Using searchVespa with filter_query")

Also applies to:

  • Line 1379-1381: [get_slack_messages_from_channel] Using filter_query
  • Line 1588-1590: [getSlackMessagesFromTimeRange] Using filter_query

Also applies to: 1379-1381, 1588-1590


266-269: Critical: Do not freeze user metadata at module load.

This is a duplicate of a previous critical issue. userMetadata is a module-level constant with a hard-coded Asia/Kolkata timezone and a dateForAI snapshot taken at process start. This causes every response to use the wrong timezone for most users and the date quickly becomes stale.

Remove the module-level constant and derive user metadata per request:

-const userMetadata: UserMetadataType = {
-  userTimezone: "Asia/Kolkata",
-  dateForAI: getDateForAI({ userTimeZone: "Asia/Kolkata" }),
-}

Then update all usages to compute userMetadata dynamically. For example, in formatSearchToolResponse:

async function formatSearchToolResponse(
  searchResults: VespaSearchResponse | null,
  searchContext: {
    query?: string
    app?: string
    labels?: string[]
    timeRange?: { startTime: number; endTime: number }
    offset?: number
    limit?: number
    searchType?: string
  },
  userTimezone: string = "UTC", // Add parameter with fallback
): Promise<{ result: string; contexts: MinimalAgentFragment[] }> {
  const userMetadata: UserMetadataType = {
    userTimezone,
    dateForAI: getDateForAI({ userTimeZone: userTimezone }),
  }
  // ... rest of function
}

Repeat this pattern for all functions that use userMetadata (lines 117, 481, 495, 785, 1077, 1420, 1629).


254-254: Critical: Fix property name causing pipeline failures.

Pipeline failures indicate that mailParticipants (plural) does not exist in VespaQueryConfig (line 344) or GetItemsParams (line 444). However, line 254 defines the parameter as mailParticipant (singular). This inconsistency is causing TypeScript build failures.

Based on the pipeline errors and the parameter definition, use the singular form consistently:

 interface UnifiedSearchOptions {
   // ...
-  mailParticipant?: MailParticipant | null
+  mailParticipants?: MailParticipant | null
   // ...
 }

Then update the usages:

   const commonSearchOptions: Partial<VespaQueryConfig> = {
     // ...
-    mailParticipants: mailParticipant || null,
+    mailParticipant: options.mailParticipant || null,
     // ...
   }
     searchResults = await getItems({
       // ...
-      mailParticipants: options.mailParticipant || null,
+      mailParticipant: options.mailParticipant || null,
     })

Note: Verify the correct property name in the type definitions. If the interface uses mailParticipants (plural), then update line 254 to match. If it uses mailParticipant (singular), then update lines 344 and 444 to match.

Also applies to: 344-344, 444-444

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 46a14c2 and 94874d3.

📒 Files selected for processing (5)
  • server/ai/agentPrompts.ts (3 hunks)
  • server/ai/prompts.ts (5 hunks)
  • server/ai/provider/index.ts (9 hunks)
  • server/api/chat/chat.ts (25 hunks)
  • server/api/chat/tools.ts (25 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
PR: xynehq/xyne#787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.

Applied to files:

  • server/api/chat/tools.ts
🧬 Code graph analysis (3)
server/ai/provider/index.ts (3)
server/ai/prompts.ts (1)
  • temporalDirectionJsonPrompt (1637-1966)
server/utils/index.ts (1)
  • getDateForAI (3-29)
server/ai/types.ts (2)
  • MailParticipant (393-393)
  • ModelParams (284-309)
server/api/chat/tools.ts (8)
server/api/chat/types.ts (2)
  • MinimalAgentFragment (88-94)
  • AgentTool (110-134)
server/api/chat/utils.ts (1)
  • searchToCitation (442-540)
server/ai/context.ts (1)
  • answerContextMap (887-985)
server/utils/index.ts (1)
  • getDateForAI (3-29)
server/api/chat/mapper.ts (3)
  • searchGlobalTool (979-1020)
  • convertToAgentToolParameters (850-868)
  • googleTools (1022-1216)
server/utils.ts (1)
  • getErrorMessage (103-106)
server/search/vespa.ts (2)
  • SearchVespaThreads (159-159)
  • searchGoogleApps (132-132)
server/search/utils.ts (1)
  • extractDriveIds (95-138)
server/api/chat/chat.ts (2)
server/ai/types.ts (1)
  • MailParticipant (393-393)
server/api/chat/utils.ts (1)
  • extractNamesFromIntent (1039-1055)
🪛 GitHub Actions: TypeScript Build Check
server/api/chat/tools.ts

[error] 19-19: Module '"@xyne/vespa-ts/types"' has no exported member 'GoogleApps'.


[error] 77-77: Module '"@xyne/vespa-ts"' has no exported member 'EventStatusType'.


[error] 79-79: Module '"@xyne/vespa-ts"' has no exported member 'MailParticipant'.


[error] 344-344: Object literal may only specify known properties, and 'mailParticipants' does not exist in type 'Partial'.


[error] 444-444: Object literal may only specify known properties, and 'mailParticipants' does not exist in type 'Omit<GetItemsParams, "processedCollectionSelections"> & { collectionSelections?: { collectionIds?: string[] | undefined; collectionFolderIds?: string[] | undefined; collectionFileIds?: string[] | undefined; }[] | undefined; }'.

server/api/chat/chat.ts

[error] 1989-1989: Property 'AttachmentRank' does not exist on type 'typeof SearchModes'.


[error] 3415-3415: Object literal may only specify known properties, and 'mailParticipants' does not exist in type 'Omit<GetItemsParams, "processedCollectionSelections"> & { collectionSelections?: { collectionIds?: string[] | undefined; collectionFolderIds?: string[] | undefined; collectionFileIds?: string[] | undefined; }[] | undefined; }'.

🔇 Additional comments (11)
server/ai/agentPrompts.ts (2)

1211-1248: mailParticipants extraction rules look good; confirm downstream alignment.

The switch from intent → mailParticipants is clear and covers names/emails/orgs with sensible defaults.

Please confirm:

  • Query parser/types accept mailParticipants in the output.
  • Name-to-email resolution pipeline consumes these fields correctly. Based on learnings.

1421-1430: New filters.mailParticipants field in output schema.

Schema addition is consistent with the extraction rules; ensure the router and validators (zod/types) include this field to avoid drops at runtime.

server/ai/prompts.ts (2)

1364-1414: mailParticipants extraction block is clear; keep naming consistent with other prompts.

Looks consistent with agentPrompts.ts. Ensure all references uniformly use mailParticipants across prompts and routers.


1432-1442: filters.mailParticipants included in schema.

Good addition. Verify that any schema validators (zod) and consumers accept this field to prevent silent drops.

server/ai/provider/index.ts (1)

1879-1901: Name-to-email resolution wiring looks correct.

The participants string and passing names into nameToEmailResolutionPrompt (as intent) match the updated prompts signature; extraction result parsing retains field defaults.

Ensure the prompt returns the expected JSON shape: { "emails": { from:[], to:[], cc:[], bcc:[] } }.

server/api/chat/tools.ts (6)

1871-1960: LGTM! Drive files search implementation is well-structured.

The searchDriveFiles tool correctly:

  • Validates email and agent permissions
  • Extracts Drive IDs from selected items for scoped searches
  • Handles file type filtering
  • Converts time ranges properly
  • Uses the standardized formatSearchToolResponse helper

1962-2046: LGTM! Calendar events search implementation is correct.

The searchCalendarEvents tool properly:

  • Validates email and agent permissions
  • Handles attendees filtering
  • Converts event status parameters
  • Processes time ranges correctly
  • Uses consistent formatting

1019-1067: LGTM! Automatic thread fetching is a good UX improvement.

The enhancement to automatically fetch thread messages when parent messages are found is well-implemented:

  • Correctly identifies thread roots by comparing createdAt with threadId
  • Handles errors gracefully without failing the entire request
  • Logs useful debugging information
  • Clearly indicates to users that thread messages are included

One minor suggestion: Consider adding a maximum limit on thread fetching to prevent performance issues if many thread roots are found.

// Fetch thread messages if any thread roots were found (limit to reasonable number)
if (threadIdsToFetch.length > 0) {
  const maxThreadsToFetch = 10 // Prevent fetching too many threads
  const threadsToFetch = threadIdsToFetch.slice(0, maxThreadsToFetch)
  
  if (threadIdsToFetch.length > maxThreadsToFetch) {
    Logger.debug(
      `[get_slack_messages] Limiting thread fetch to ${maxThreadsToFetch} of ${threadIdsToFetch.length} thread roots`,
    )
  }
  
  Logger.debug(
    `[get_slack_messages] Fetching threads for ${threadsToFetch.length} thread roots`,
  )
  // ... rest of fetching logic
}

193-209: LGTM! Improved collection ID parsing logic.

The updated logic for parsing collection IDs, folder IDs, and file IDs with proper prefix handling is clearer and more maintainable than before. The regex pattern replacement approach is robust.


525-584: LGTM! searchGlobal tool properly refactored.

The renamed searchGlobal tool correctly:

  • Uses searchGlobalTool definition from mapper
  • Converts parameters using convertToAgentToolParameters
  • Parses agent app integrations
  • Delegates to executeVespaSearch helper
  • Handles errors appropriately

1774-1869: No thread expansion gap or duplication detected The unified helper (tools.ts 456–463) and the searchGmail tool (tools.ts 1841–1850) both expand email threads appropriately, with searchGmail bypassing the helper so no duplicate expansion occurs.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
server/api/chat/agents.ts (1)

4335-4373: Refactor to eliminate variable shadowing and ensure consistent classification structure.

The code declares classification twice with the same name in nested scopes:

  1. Line 4335: Outer let classification with isFollowUp field
  2. Line 4362: Inner const classification without isFollowUp field

This shadowing creates several issues:

  • Confusing control flow: It's unclear which classification is used in different code paths
  • Inconsistent structure: The inner declaration omits isFollowUp, which could cause issues if downstream code expects this field
  • Maintenance risk: Future developers may not realize the inner declaration shadows the outer one

Recommended approach:

-let classification: TemporalClassifier & QueryRouterResponse = {
-  direction: parsed.temporalDirection,
-  type: parsed.type as QueryType,
-  filterQuery: parsed.filter_query,
-  isFollowUp: parsed.isFollowUp,
-  filters: {
-    ...(parsed?.filters ?? {}),
-    apps: parsed.filters?.apps || [],
-    entities: parsed.filters?.entities as any,
-    mailParticipants: parsed.mailParticipants || {},
-  },
-}

if (parsed.answer === null || parsed.answer === "") {
  const ragSpan = streamSpan.startSpan("rag_processing")
  // ... query rewrite logic ...
  
-  const classification: TemporalClassifier & QueryRouterResponse = {
+  // Reuse outer classification with consistent structure
+  classification = {
    direction: parsed.temporalDirection,
    type: parsed.type as QueryType,
    filterQuery: parsed.filter_query,
+    isFollowUp: parsed.isFollowUp,  // Add missing field for consistency
    filters: {
      ...(parsed?.filters ?? {}),
      apps: parsed.filters?.apps || [],
      entities: parsed.filters?.entities as any,
      mailParticipants: parsed.mailParticipants || {},
    },
  }

Alternatively, use distinct variable names like conversationClassification and ragClassification if they serve different purposes.

server/api/chat/chat.ts (1)

315-327: Fix double span end and incorrect fallback type in resolveNamesToEmails

  • searchSpan is ended earlier (Line 287). Ending it again is incorrect.
  • Fallback should be an empty object, not an array, to match MailParticipant.
-    let resolvedData: MailParticipant = {}
+    let resolvedData: MailParticipant = {}
     const resolutionResult = await extractEmailsFromContext(
       extractedNames,
       userCtx,
       searchContext,
       { modelId: config.defaultFastModel, json: false, stream: false },
     )

-    resolvedData = resolutionResult.emails || []
+    resolvedData = resolutionResult.emails || {}
 
-    searchSpan?.end()
     return resolvedData
♻️ Duplicate comments (2)
server/api/chat/chat.ts (2)

1975-2003: Correct fileId grouping and fix limit sizing for file searches

  • Attachments with prefix att_ are incorrectly grouped under collectionFileIds; they should be searched via searchVespaInFiles with AttachmentRank.
  • Limit should match the actual array being searched to avoid under/over-fetching. This was also flagged previously.
-      const collectionFileIds = fileIds.filter((fid) => fid.startsWith("clf-") || fid.startsWith("att_"))
-      const nonCollectionFileIds = fileIds.filter((fid) => !fid.startsWith("clf-") && !fid.startsWith("att"))
-      const attachmentFileIds = fileIds.filter((fid) => fid.startsWith("attf_"))
+      const collectionFileIds = fileIds.filter((fid) => fid.startsWith("clf-"))
+      const attachmentFileIds = fileIds.filter(
+        (fid) => fid.startsWith("att_") || fid.startsWith("attf_"),
+      )
+      const nonCollectionFileIds = fileIds.filter(
+        (fid) =>
+          !fid.startsWith("clf-") &&
+          !fid.startsWith("att_") &&
+          !fid.startsWith("attf_"),
+      )
       if(nonCollectionFileIds && nonCollectionFileIds.length > 0) {
         results = await searchVespaInFiles(builtUserQuery, email, nonCollectionFileIds, {
-            limit: fileIds?.length,
+            limit: nonCollectionFileIds.length,
             alpha: userAlpha,
           })
         if (results.root.children) {
           combinedSearchResponse.push(...results.root.children)
         }
       }
@@
-      if(attachmentFileIds && attachmentFileIds.length > 0) {
+      if (attachmentFileIds && attachmentFileIds.length > 0) {
         results = await searchVespaInFiles(builtUserQuery, email, attachmentFileIds, {
-          limit: fileIds?.length,
+          limit: attachmentFileIds.length,
           alpha: userAlpha,
           rankProfile: SearchModes.AttachmentRank,
         })
         if (results.root.children) {
           combinedSearchResponse.push(...results.root.children)
         }
       }

3404-3441: Unify log key and log the resolved mailParticipants

To match the refactor and improve clarity, prefer “Mail Participants” label and log the resolved value. This was requested earlier.

-        `[GetItems] Calling getItems with agent prompt - Schema: ${schema}, App: ${agentApps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, mailParticipants: ${JSON.stringify(classification.filters.mailParticipants)}`,
+        `[GetItems] Calling getItems with agent prompt - Schema: ${schema}, App: ${agentApps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, Mail Participants: ${JSON.stringify(resolvedMailParticipants)}`,
@@
-        mailParticipants: resolvedMailParticipants || {},
+        mailParticipants: resolvedMailParticipants || {},

And for the non-agent path:

-        `[GetItems] Calling getItems - Schema: ${schema}, App: ${apps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, mailParticipants: ${JSON.stringify(classification.filters.mailParticipants)}`,
+        `[GetItems] Calling getItems - Schema: ${schema}, App: ${apps?.map((a) => a).join(", ")}, Entity: ${entities?.map((e) => e).join(", ")}, Mail Participants: ${JSON.stringify(resolvedMailParticipants)}`,
@@
-        mailParticipants: resolvedMailParticipants || {},
+        mailParticipants: resolvedMailParticipants || {},
🧹 Nitpick comments (1)
server/api/chat/chat.ts (1)

224-231: Return a precise type for resolveNamesToEmails

The function always returns a MailParticipant-shaped object. Tighten the return type for safety and clarity.

-export async function resolveNamesToEmails(
+export async function resolveNamesToEmails(
   mailParticipants: MailParticipant,
   email: string,
   userCtx: string,
   userMetadata: UserMetadataType,
   span?: Span,
-): Promise<any> {
+): Promise<MailParticipant> {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05defb7 and 5e7a979.

📒 Files selected for processing (4)
  • server/api/chat/agents.ts (11 hunks)
  • server/api/chat/chat.ts (25 hunks)
  • server/api/chat/utils.ts (2 hunks)
  • server/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/api/chat/utils.ts
  • server/package.json
🧰 Additional context used
🧬 Code graph analysis (2)
server/api/chat/agents.ts (4)
server/shared/types.ts (1)
  • AttachmentEntity (39-39)
server/ai/types.ts (2)
  • TemporalClassifier (279-282)
  • QueryRouterResponse (461-461)
server/api/chat/utils.ts (1)
  • collectFollowupContext (81-153)
server/api/chat/chat.ts (1)
  • UnderstandMessageAndAnswer (3788-3923)
server/api/chat/chat.ts (2)
server/ai/types.ts (1)
  • MailParticipant (393-393)
server/api/chat/utils.ts (1)
  • extractNamesFromIntent (1046-1062)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (4)
server/api/chat/agents.ts (3)

395-401: LGTM: Attachment entity filtering.

The logic correctly filters out attachment entities from citations by checking against the AttachmentEntity enum values.


1629-1665: LGTM: Async transformations of Vespa results.

The code correctly transforms VespaSearchResult arrays into MinimalAgentFragment arrays using Promise.all with async map operations. The same pattern is properly applied for chatContexts, threadContexts, and other result sets throughout the file.


4176-4186: LGTM: Consistent mailParticipants initialization.

The rename from intent to mailParticipants is consistently applied in both queryFilters and parsed object initializations, with appropriate empty object defaults.

server/api/chat/chat.ts (1)

2976-2983: mailParticipants destructure looks good

New filter field is plumbed correctly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/api/chat/chat.ts (1)

224-231: Fix return type and assignment in resolveNamesToEmails

Return type should be MailParticipant, and fallback must not be an array. Current resolvedData = resolutionResult.emails || [] risks type/shape breakage.

Apply this diff:

-export async function resolveNamesToEmails(
+export async function resolveNamesToEmails(
   mailParticipants: MailParticipant,
   email: string,
   userCtx: string,
   userMetadata: UserMetadataType,
   span?: Span,
-): Promise<any> {
+): Promise<MailParticipant> {
@@
-    let resolvedData: MailParticipant = {}
+    let resolvedData: MailParticipant = {}
     const resolutionResult = await extractEmailsFromContext(
       extractedNames,
       userCtx,
       searchContext,
       { modelId: config.defaultFastModel, json: false, stream: false },
     )
-
-    resolvedData = resolutionResult.emails || []
+    resolvedData =
+      (resolutionResult?.emails as MailParticipant) || extractedNames

Also applies to: 314-325

♻️ Duplicate comments (1)
server/api/chat/chat.ts (1)

1974-1991: Use correct limits for per‑set file searches (duplicate of prior feedback)

The limit should match the array being searched, not the total fileIds. Use nonCollectionFileIds.length and attachmentFileIds.length respectively.

Apply this diff:

-      if (nonCollectionFileIds && nonCollectionFileIds.length > 0) {
+      if (nonCollectionFileIds && nonCollectionFileIds.length > 0) {
         results = await searchVespaInFiles(
           builtUserQuery,
           email,
           nonCollectionFileIds,
           {
-            limit: fileIds?.length,
+            limit: nonCollectionFileIds.length,
             alpha: userAlpha,
           },
         )
       }
@@
-      if (attachmentFileIds && attachmentFileIds.length > 0) {
+      if (attachmentFileIds && attachmentFileIds.length > 0) {
         results = await searchVespaInFiles(
           builtUserQuery,
           email,
           attachmentFileIds,
           {
-            limit: fileIds?.length,
+            limit: attachmentFileIds.length,
             alpha: userAlpha,
             rankProfile: SearchModes.AttachmentRank,
           },
         )
       }

Also applies to: 2006-2015

🧹 Nitpick comments (3)
server/api/chat/utils.ts (2)

40-41: Unify MailParticipant type source across files

This file imports MailParticipant from @xyne/vespa-ts/types while chat.ts imports it from "@/ai/types". Pick one canonical source (prefer "@/ai/types" if that’s the project’s schema owner) to avoid subtle structural drift.


1050-1066: Minor: rename parameter to reflect new semantics; keep type-safe initialization

Rename intent to mailParticipants (or obj) for clarity post‑refactor. If MailParticipant has all-optional keys this is fine; otherwise initialize as Partial and cast on return.

Example:

-export function extractNamesFromIntent(intent: any): MailParticipant {
-  if (!intent || typeof intent !== "object") return {}
+export function extractNamesFromIntent(mailParticipants: any): MailParticipant {
+  if (!mailParticipants || typeof mailParticipants !== "object") return {}
-  const result: MailParticipant = {}
+  const result: Partial<MailParticipant> = {}
   const fieldsToCheck = ["from", "to", "cc", "bcc"] as const
   for (const field of fieldsToCheck) {
-    if (Array.isArray(intent[field]) && intent[field].length > 0) {
-      const uniqueValues = [...new Set(intent[field])].filter((v) => v)
+    if (Array.isArray(mailParticipants[field]) && mailParticipants[field].length > 0) {
+      const uniqueValues = [...new Set(mailParticipants[field])].filter((v) => v)
       if (uniqueValues.length > 0) {
         result[field] = uniqueValues
       }
     }
   }
-  return result
+  return result as MailParticipant
server/api/chat/chat.ts (1)

17-27: Type source consistency for MailParticipant

This file imports MailParticipant from "@/ai/types" while utils.ts uses "@xyne/vespa-ts/types". Use a single canonical source to prevent drift and confusing structural typing.

Would you standardize on "@/ai/types" (MailParticipantSchema) across server/api?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e7a979 and 4f2a8d9.

📒 Files selected for processing (2)
  • server/api/chat/chat.ts (28 hunks)
  • server/api/chat/utils.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
server/api/chat/utils.ts (1)
server/ai/types.ts (1)
  • MailParticipant (393-393)
server/api/chat/chat.ts (3)
server/ai/types.ts (1)
  • MailParticipant (393-393)
server/api/chat/utils.ts (2)
  • extractNamesFromIntent (1050-1066)
  • collectFollowupContext (81-157)
server/search/vespa.ts (1)
  • searchVespaInFiles (118-118)

@shivamashtikar shivamashtikar merged commit 3ac0b9e into main Oct 14, 2025
4 checks passed
@shivamashtikar shivamashtikar deleted the tool_revamp branch October 14, 2025 08:44
kalpadhwaryu pushed a commit that referenced this pull request Oct 14, 2025
## [3.14.2](v3.14.1...v3.14.2) (2025-10-14)

### Bug Fixes

* **tools:** Revamp agentic tools ([#1067](#1067)) ([3ac0b9e](3ac0b9e))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants