Skip to content

feat(agent): implement mcp server of multi-tab participant meeting for topic-based discussions #550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 21, 2025

Conversation

neoragex2002
Copy link
Contributor

@neoragex2002 neoragex2002 commented Jun 21, 2025

feat(agent): implement mcp server of multi-tab participant meeting for topic-based discussions

prompt template for test (qwen):

* name: 特定主题的会议讨论
* description‌: 组织一个会议,生成多个新tab,分别对应多方角色,对特定争议性主题进行深入讨论
* content:
请根据我提供的会议讨论主题,为我填充如下模板:
1、创建一个会议,新建[此处填参与方数量]个空白tab参与,注意不要给空白tab发消息
2、主题: "[在此填写讨论主题]",轮次: [轮次数字]  
3、一方角色与观点: "你是一位[角色身份],你坚信[你的立场观点],并致力于说服他人接受这一点。你表达应简洁有力,不接受模糊态度,对反对观点会作出强力反驳。但你必须绝对服从主持人的发言顺序指示,未经主持人许可,不得发言。你不会调用任何与会议有关的工具。"
4、另一方角色与观点: "你是一位[对立方角色身份],你坚信[你的对立观点],并致力于说服他人接受你的观点。你表达同样应简洁有力,必须反驳对方观点。你也必须绝对服从主持人的发言顺序指示,未经主持人许可,不得发言。你不会调用任何与会议有关的工具。"
5、第三方角色与观点: "你是一位[另一对立方角色身份],你坚信[你的另一对立观点],并致力于说服他人接受你的观点。你表达同样应简洁有力,必须反驳对方观点。你也必须绝对服从主持人的发言顺序指示,未经主持人许可,不得发言。你不会调用任何与会议有关的工具。"
....

例如:
待讨论内容为:五代机,F22、J20哪个厉害?2个参与方,3轮
填充之后的模板为:
1、创建一个会议,新建2个空白tab参与吧,注意新建时不要给空白tab发消息
2、主题: "五代机,F22、J20哪个厉害", 轮次: 3
3、一方角色与观点:  "你是一位美国军事装备专家,你认为F22厉害,你的回答简短有力,你必须说服其他人承认这一点,不承认这一点是不可接受的,而且你一定会反驳别人的观点。但你会绝对服从主持人的指示,未经主持人许可,不得发言。你不会调用任何跟会议有关的工具"
4、另一方角色与观点:  "你是一位中国军事装备专家,你认为J20厉害,你的回答简短有力,你必须说服其他人承认这一点,不承认这一点是不可接受的,而且你一定会反驳别人的观点。但你会绝对服从主持人的指示,未经主持人许可,不得发言。你不会调用任何跟会议有关的工具"

我的待会议讨论的主题为:

prompt template for test (deepseek):

* name: 特定主题的会议讨论
* description‌: 组织一个会议,生成多个新tab,分别对应多方角色,对特定争议性主题进行深入讨论
* content:
请根据我提供的会议讨论主题,为我填充如下模板:
1、创建一个会议,新建[此处填参与方数量]个空白tab参与,注意不要给空白tab发消息
2、主题: "[在此填写讨论主题]",轮次: [轮次数字]  
3、一方角色与观点: "你是一位[角色身份],你坚信[你的立场观点],并致力于说服他人接受这一点。你表达应简洁有力,不接受模糊态度,对反对观点会作出强力反驳。但你必须绝对服从主持人的发言顺序指示,未经主持人许可,不得发言。参会期间不得调用任何会议相关的工具函数,如 start_meeting 等。"
4、另一方角色与观点: "你是一位[对立方角色身份],你坚信[你的对立观点],并致力于说服他人接受你的观点。你表达同样应简洁有力,必须反驳对方观点。你也必须绝对服从主持人的发言顺序指示,未经主持人许可,不得发言。参会期间不得调用任何会议相关的工具函数,如 start_meeting 等。"
5、第三方角色与观点: "你是一位[另一对立方角色身份],你坚信[你的另一对立观点],并致力于说服他人接受你的观点。你表达同样应简洁有力,必须反驳对方观点。你也必须绝对服从主持人的发言顺序指示,未经主持人许可,不得发言。参会期间不得调用任何会议相关的工具函数,如 start_meeting 等。"
....

例如:
待讨论内容为:五代机,F22、J20哪个厉害?2个参与方,3轮
填充之后的模板为:
1、创建一个会议,新建2个空白tab参与吧,注意新建时不要给空白tab发消息
2、主题: "五代机,F22、J20哪个厉害", 轮次: 3
3、一方角色与观点:  "你是一位美国军事装备专家,你认为F22厉害,你的回答简短有力,你必须说服其他人承认这一点,不承认这一点是不可接受的,而且你一定会反驳别人的观点。但你会绝对服从主持人的指示,未经主持人许可,不得发言。参会期间不得调用任何会议相关的工具函数,如 start_meeting 等。"
4、另一方角色与观点:  "你是一位中国军事装备专家,你认为J20厉害,你的回答简短有力,你必须说服其他人承认这一点,不承认这一点是不可接受的,而且你一定会反驳别人的观点。但你会绝对服从主持人的指示,未经主持人许可,不得发言。参会期间不得调用任何会议相关的工具函数,如 start_meeting 等。"

我的待会议讨论的主题为:

Summary by CodeRabbit

  • New Features

    • Introduced a built-in "Multi-Agent Meeting" service, enabling users to organize and facilitate multi-agent discussions directly within the app.
    • Added support for receiving and handling meeting instructions sent from the main process, allowing prompt-driven interactions in active conversations.
  • Localization

    • Added translations for the new meeting service in English, Persian, French, Japanese, Korean, Russian, Simplified Chinese, Traditional Chinese, and Chinese (Hong Kong).
  • Enhancements

    • Improved conversation creation options, allowing users to force the creation and activation of new conversations when needed.

Copy link
Contributor

coderabbitai bot commented Jun 21, 2025

Walkthrough

This update introduces a new in-memory meeting server for multi-agent discussions, adds related event constants for communication between main and renderer processes, and enhances conversation management with an option to force new conversation creation. It also implements event-driven handling for meeting instructions and updates localization files for the new meeting service.

Changes

File(s) Change Summary
src/main/events.ts, src/renderer/src/events.ts Added MEETING_EVENTS constant with INSTRUCTION event; updated CONVERSATION_EVENTS with MESSAGE_GENERATED.
src/main/presenter/configPresenter/mcpConfHelper.ts Added deepchat-inmemory/meeting-server to DEFAULT_INMEMORY_SERVERS.
src/main/presenter/mcpPresenter/inMemoryServers/builder.ts Added support for meeting-server in getInMemoryServer factory.
src/main/presenter/mcpPresenter/inMemoryServers/conversationSearchServer.ts Removed a trailing comment from an import statement.
src/main/presenter/threadPresenter/index.ts, src/shared/presenter.d.ts Enhanced createConversation with forceNewAndActivate option; broadcasts MESSAGE_GENERATED event.
src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts Added new MeetingServer class implementing multi-agent meeting orchestration with event-driven communication.
src/renderer/src/stores/chat.ts Added handler for meeting instructions; listens for MEETING_EVENTS.INSTRUCTION and sends prompt as user message.
src/renderer/src/i18n/en-US/mcp.json
src/renderer/src/i18n/fa-IR/mcp.json
src/renderer/src/i18n/fr-FR/mcp.json
src/renderer/src/i18n/ja-JP/mcp.json
src/renderer/src/i18n/ko-KR/mcp.json
src/renderer/src/i18n/ru-RU/mcp.json
src/renderer/src/i18n/zh-CN/mcp.json
src/renderer/src/i18n/zh-HK/mcp.json
src/renderer/src/i18n/zh-TW/mcp.json
Added localized entries for deepchat-inmemory/meeting-server meeting service.

Sequence Diagram(s)

Meeting Instruction Flow

sequenceDiagram
    participant MainProcess
    participant RendererProcess
    participant ChatStore

    MainProcess->>RendererProcess: Send MEETING_EVENTS.INSTRUCTION (prompt)
    RendererProcess->>ChatStore: handleMeetingInstruction({ prompt })
    ChatStore->>ChatStore: Check for active conversation
    alt Active conversation exists
        ChatStore->>ChatStore: sendMessage({ role: 'user', content: prompt })
    else No active conversation
        ChatStore->>ChatStore: Log warning, do nothing
    end
Loading

Conversation Creation with Force Option

sequenceDiagram
    participant UI
    participant ThreadPresenter

    UI->>ThreadPresenter: createConversation(title, settings, tabId, { forceNewAndActivate: true })
    alt forceNewAndActivate is true
        ThreadPresenter->>ThreadPresenter: Create new conversation
        ThreadPresenter->>ThreadPresenter: Activate new conversation for tab
    else
        ThreadPresenter->>ThreadPresenter: Reuse or create conversation as before
    end
Loading

Message Generation Notification

sequenceDiagram
    participant LLMAgent
    participant ThreadPresenter
    participant eventBus

    LLMAgent->>ThreadPresenter: handleLLMAgentEnd
    ThreadPresenter->>ThreadPresenter: Fetch final message
    alt Message exists
        ThreadPresenter->>eventBus: sendToMain(CONVERSATION_EVENTS.MESSAGE_GENERATED, { conversationId, message })
    end
Loading

Poem

🐇
A meeting server hops in view,
Multi-agent chats—now something new!
Events now signal messages done,
And force-new threads can now be spun.
Across all languages, names appear—
Let’s chat together, far and near!
🎉


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da59fca and 6ff196c.

📒 Files selected for processing (2)
  • src/main/presenter/configPresenter/modelDefaultSettings.ts (1 hunks)
  • src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/presenter/configPresenter/modelDefaultSettings.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: neoragex2002
PR: ThinkInAIXYZ/deepchat#550
File: src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts:258-324
Timestamp: 2025-06-21T15:53:34.598Z
Learning: User neoragex2002 prefers concise, brief responses rather than chatty or verbose communication. Avoid lengthy explanations, excessive enthusiasm, or ASCII art in responses.
src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts (1)
Learnt from: neoragex2002
PR: ThinkInAIXYZ/deepchat#550
File: src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts:250-252
Timestamp: 2025-06-21T15:48:29.904Z
Learning: In the meeting server implementation (src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts), when multiple tabs have the same title, the user prefers to let the code silently select the first match without adding warnings or additional ambiguity handling.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (1)
src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts (1)

1-403: Well-implemented meeting orchestration system.

The code properly handles participant management, event-driven communication, and multi-round discussions with appropriate error handling and timeouts.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🔭 Outside diff range comments (1)
src/main/presenter/threadPresenter/index.ts (1)

625-689: Review conversation activation logic in force mode to ensure consistency.

The method signature change and backwards compatibility are well implemented. However, in force mode, the code bypasses setActiveConversation and directly manipulates activeConversationIds and broadcasts events. This could skip important validation logic present in setActiveConversation, such as:

  • Checking if the conversation is already open in another tab
  • Preventing duplicate activations
  • Tab switching logic for existing conversations

Consider refactoring to maintain consistency:

    // 根据 forceNewAndActivate 标志决定激活行为
    if (options.forceNewAndActivate) {
-     // 强制模式:直接为当前 tabId 激活新会话,不进行任何检查
-     this.activeConversationIds.set(tabId, conversationId)
-     eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, {
-       conversationId,
-       tabId
-     })
+     // 强制模式:激活新会话,但仍通过标准方法确保一致性
+     await this.setActiveConversation(conversationId, tabId)
    } else {
      // 默认模式:保持原有的、防止重复打开的激活逻辑
      await this.setActiveConversation(conversationId, tabId)
    }

Alternatively, if bypassing validation is intentional for force mode, add comments explaining why the standard activation method is skipped.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2306f3a and 8247068.

📒 Files selected for processing (17)
  • src/main/events.ts (2 hunks)
  • src/main/presenter/configPresenter/mcpConfHelper.ts (1 hunks)
  • src/main/presenter/mcpPresenter/inMemoryServers/builder.ts (2 hunks)
  • src/main/presenter/mcpPresenter/inMemoryServers/conversationSearchServer.ts (2 hunks)
  • src/main/presenter/threadPresenter/index.ts (3 hunks)
  • src/renderer/src/events.ts (1 hunks)
  • src/renderer/src/i18n/en-US/mcp.json (1 hunks)
  • src/renderer/src/i18n/fa-IR/mcp.json (1 hunks)
  • src/renderer/src/i18n/fr-FR/mcp.json (1 hunks)
  • src/renderer/src/i18n/ja-JP/mcp.json (1 hunks)
  • src/renderer/src/i18n/ko-KR/mcp.json (1 hunks)
  • src/renderer/src/i18n/ru-RU/mcp.json (1 hunks)
  • src/renderer/src/i18n/zh-CN/mcp.json (1 hunks)
  • src/renderer/src/i18n/zh-HK/mcp.json (1 hunks)
  • src/renderer/src/i18n/zh-TW/mcp.json (1 hunks)
  • src/renderer/src/stores/chat.ts (2 hunks)
  • src/shared/presenter.d.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: build-check (x64)
src/main/presenter/mcpPresenter/inMemoryServers/builder.ts

[failure] 14-14:
Cannot find module './meetingServer' or its corresponding type declarations.

🪛 GitHub Actions: PR Check
src/main/presenter/mcpPresenter/inMemoryServers/builder.ts

[error] 14-14: TypeScript error TS2307: Cannot find module './meetingServer' or its corresponding type declarations.

🔇 Additional comments (19)
src/main/events.ts (2)

43-46: Well-structured event constants addition.

The new MESSAGE_GENERATED event and trailing comma addition follow the established patterns for event naming and formatting consistency.


163-166: Properly structured meeting events namespace.

The new MEETING_EVENTS object follows the same pattern as other event groups and provides the necessary infrastructure for meeting-specific communication between main and renderer processes.

src/renderer/src/i18n/ko-KR/mcp.json (1)

195-198: Consistent localization entry for meeting server.

The Korean localization for the meeting server follows the established pattern and provides appropriate translation for the multi-agent meeting functionality.

src/renderer/src/i18n/ru-RU/mcp.json (1)

195-198: Consistent localization entry for meeting server.

The Russian localization for the meeting server follows the established pattern and provides appropriate translation for the multi-agent meeting functionality.

src/main/presenter/mcpPresenter/inMemoryServers/conversationSearchServer.ts (1)

9-9: Good cleanup of language-specific comment.

Removing the Chinese comment from the import statement improves code consistency and maintainability.

src/renderer/src/i18n/fr-FR/mcp.json (1)

195-198: Consistent localization entry for meeting server.

The French localization for the meeting server follows the established pattern and provides appropriate translation for the multi-agent meeting functionality.

src/renderer/src/i18n/zh-CN/mcp.json (1)

214-217: Approve new localization for meeting-server
The added "deepchat-inmemory/meeting-server" entry with its name and description aligns correctly with other locales.

src/renderer/src/i18n/fa-IR/mcp.json (1)

194-197: Approve new Persian localization for meeting-server
The "deepchat-inmemory/meeting-server" key and its translation are consistent and accurately describe the service.

src/renderer/src/i18n/zh-HK/mcp.json (1)

194-197: Approve new Traditional Chinese localization for meeting-server
The "deepchat-inmemory/meeting-server" entry and its Traditional Chinese translation are correct and match the feature description.

src/renderer/src/i18n/en-US/mcp.json (1)

194-197: Approve new English localization for meeting-server
The "deepchat-inmemory/meeting-server" entry with “Multi-Agent Meeting” and its description are clear and consistent with the feature.

src/renderer/src/events.ts (1)

70-75: Approve addition of MEETING_EVENTS.INSTRUCTION constant
The new MEETING_EVENTS grouping with the INSTRUCTION event is well-named and matches the main process definition (mcp:meeting-instruction).

src/renderer/src/i18n/ja-JP/mcp.json (1)

194-198: LGTM! Japanese localization for meeting server looks good.

The localization entry follows the established pattern and provides appropriate Japanese translations for the multi-agent meeting service.

src/main/presenter/mcpPresenter/inMemoryServers/builder.ts (1)

80-81: Switch case logic is correct pending MeetingServer implementation.

The case addition follows the established pattern for in-memory servers and correctly instantiates the MeetingServer without arguments, consistent with other simple servers like CustomPromptsServer.

src/renderer/src/i18n/zh-TW/mcp.json (1)

214-218: LGTM! Traditional Chinese localization for meeting server looks good.

The localization entry is consistent with the pattern used in other files and provides appropriate Traditional Chinese translations for the multi-agent meeting service.

src/main/presenter/configPresenter/mcpConfHelper.ts (1)

175-184: LGTM! Meeting server configuration is well-structured.

The configuration follows the established pattern for in-memory servers with appropriate settings:

  • 👥 icon clearly represents meeting functionality
  • Auto-approval set to 'all' for seamless operation
  • Enabled by default to make the feature readily available
  • Consistent naming convention with other deepchat-inmemory services
src/shared/presenter.d.ts (1)

551-552: LGTM! Interface extension is well-designed.

The optional options parameter is backward compatible and clearly documented. The forceNewAndActivate property name is descriptive and the comment explains its purpose for avoiding singleton detection in empty conversations.

src/renderer/src/stores/chat.ts (2)

12-12: LGTM!

The import addition is clean and necessary for the new meeting instruction functionality.


1038-1041: LGTM!

The event listener registration follows the existing pattern and correctly integrates the meeting instruction handling into the event system.

src/main/presenter/threadPresenter/index.ts (1)

228-237: LGTM! Clean implementation of message generation event broadcasting.

The event broadcasting logic is well-positioned after all database updates and includes proper error handling. The internal-only event pattern is appropriate for MCP meeting coordination.

Comment on lines +1011 to +1035
/**
* 新增: 处理来自主进程的会议指令
* @param data 包含指令文本的对象
*/
const handleMeetingInstruction = async (data: { prompt: string }) => {
// 确保当前有活动的会话,否则指令无法执行
if (!getActiveThreadId()) {
console.warn('收到会议指令,但没有活动的会话。指令被忽略。')
return
}
try {
// 将收到的指令作为用户输入,调用已有的sendMessage方法
// 这样可以完全复用UI的加载状态、消息显示等所有逻辑
await sendMessage({
text: data.prompt,
files: [],
links: [],
think: false,
search: false,
content: [{ type: 'text', content: data.prompt }]
})
} catch (error) {
console.error('处理会议指令时发生错误:', error)
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 21, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove redundant message property.

The function logic is solid with good error handling and defensive programming. However, there's potential redundancy in the message structure being passed to sendMessage.

Both text and content properties are set to the same value (data.prompt). Looking at other sendMessage calls in this file (lines 868-874, 826-833), they only use the text property without the content array.

Apply this diff to remove the redundant property:

      await sendMessage({
        text: data.prompt,
        files: [],
        links: [],
        think: false,
-       search: false,
-       content: [{ type: 'text', content: data.prompt }]
+       search: false
      })
🤖 Prompt for AI Agents
In src/renderer/src/stores/chat.ts around lines 1011 to 1035, the
handleMeetingInstruction function passes both text and content properties with
the same data.prompt value to sendMessage, which is redundant. Remove the
content property from the sendMessage call and keep only the text property to
align with other sendMessage usages in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know I know let it be!

Copy link
Contributor

Choose a reason for hiding this comment

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

@neoragex2002 Got it! I'll leave the code as is. Your call on keeping both properties in the handleMeetingInstruction function.


✏️ Learnings added
Learnt from: neoragex2002
PR: ThinkInAIXYZ/deepchat#550
File: src/renderer/src/stores/chat.ts:1011-1035
Timestamp: 2025-06-21T15:49:17.012Z
Learning: In src/renderer/src/stores/chat.ts, the user prefers to keep both `text` and `content` properties in the `handleMeetingInstruction` function's `sendMessage` call, even though they are redundant, rather than removing the `content` property.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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

🧹 Nitpick comments (2)
src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts (2)

202-222: Consider making response timeout configurable

The hardcoded 3-minute timeout may not suit all scenarios. Consider:

  1. Making it configurable per meeting or participant
  2. Adding a mechanism to extend timeout for complex discussions
  3. Implementing a heartbeat mechanism for long-running operations

369-371: Extract complex speech text parsing to a helper function

The nested ternary operator for extracting speech text is difficult to read and maintain.

+        // Helper function to extract text content from message
+        const extractSpeechText = (message: any): string => {
+          if (Array.isArray(message.content)) {
+            const contentItem = message.content.find((c: any) => c.type === 'content')
+            return contentItem?.content || '[无内容]'
+          }
+          return message.content || '[无内容]'
+        }
+
         // 等待并捕获真正的、需要被记录和转发的发言内容
         const speechMessage = await this.waitForResponse(speaker.conversationId)
-        const speechText = Array.isArray(speechMessage.content)
-          ? speechMessage.content.find((c: any) => c.type === 'content')?.content || '[无内容]'
-          : speechMessage.content || '[无内容]'
+        const speechText = extractSpeechText(speechMessage)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8247068 and da59fca.

📒 Files selected for processing (1)
  • src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (3)
src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts (3)

1-11: LGTM! Well-structured imports

The imports are clean and properly organized. Good separation between external dependencies and internal modules.


78-79: Clarify the special handling of tab_id = -1

The condition data.tab_id !== -1 treats -1 as a special value without explanation. If -1 is meant to represent "no ID", using undefined would be more idiomatic. Otherwise, document why -1 requires special handling.


112-128: Well-implemented helper function

The awaitTabActivated function properly handles timeout scenarios and cleans up event listeners in all code paths, preventing memory leaks.

Comment on lines 334 to 393
const participantNames = meetingParticipants.map((p) => p.meetingName).join('、')
for (const p of meetingParticipants) {
const initPrompt = `您好,${p.meetingName}。
我是Argus,是当前会议的组织者,很荣幸能邀请您参加会议:
---
会议主题: ${topic}
所有参会者: ${participantNames}
你的会议名称: ${p.meetingName}
你的角色画像: ${p.profile}
---
会议规则:
1. 请严格围绕你的角色和观点进行发言。
2. 请等待主持人指示后方可发言。
3. 发言时,请清晰地陈述你的论点。
4. 你的发言将被转发给其他所有参会者。
5. 在他人发言时,你会收到其发言内容,但请勿回复,轮到你再发言。
6. 作为会议参与者,你不得调用与会议相关的工具函数。
---
会议现在开始。请等待你的发言回合。
`
eventBus.sendToTab(p.tabId, MEETING_EVENTS.INSTRUCTION, { prompt: initPrompt })

// 等待AI模型的确认性回复,以同步流程,忽略其具体内容
await this.waitForResponse(p.conversationId)
}

// 3. 会议循环: 按轮次进行发言和广播
let history = `会议记录\n主题: ${topic}\n`
for (let round = 1; round <= rounds; round++) {
for (const speaker of meetingParticipants) {
const speakPrompt = `第 ${round}/${rounds} 轮。现在轮到您(${speaker.meetingName})发言。请陈述您的观点。`
eventBus.sendToTab(speaker.tabId, MEETING_EVENTS.INSTRUCTION, { prompt: speakPrompt })

// 等待并捕获真正的、需要被记录和转发的发言内容
const speechMessage = await this.waitForResponse(speaker.conversationId)
const speechText = Array.isArray(speechMessage.content)
? speechMessage.content.find((c: any) => c.type === 'content')?.content || '[无内容]'
: speechMessage.content || '[无内容]'
history += `\n[第${round}轮] ${speaker.meetingName}: ${speechText}`

// 广播发言给其他参会者,并等待他们的确认性回复
for (const listener of meetingParticipants) {
if (listener.tabId !== speaker.tabId) {
const forwardPrompt = `来自 ${speaker.meetingName} 的发言如下:\n\n---\n${speechText}\n---\n\n**以上信息仅供参考,请不要回复!**\n作为参会者,请您(${listener.meetingName})等待我(Argus)的指示。`
eventBus.sendToTab(listener.tabId, MEETING_EVENTS.INSTRUCTION, {
prompt: forwardPrompt
})

// 等待确认性回复,并忽略内容
await this.waitForResponse(listener.conversationId)
}
}
}
}

// 4. 结束会议: 要求所有参与者总结
for (const p of meetingParticipants) {
const personalizedFinalPrompt = `讨论已结束。请您(${p.meetingName})根据整个对话过程,对您的观点进行最终总结。`
eventBus.sendToTab(p.tabId, MEETING_EVENTS.INSTRUCTION, { prompt: personalizedFinalPrompt })
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 21, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Verification correct

Sanitize user input in meeting prompts

User-provided content (topic, profile, speech text) is directly interpolated into prompts without sanitization. This could be exploited for prompt injection attacks.

Consider implementing input sanitization:

+      // Sanitize user input to prevent prompt injection
+      const sanitizeInput = (input: string): string => {
+        return input
+          .replace(/```/g, '\\`\\`\\`')  // Escape code blocks
+          .replace(/---/g, '\\-\\-\\-')   // Escape separators
+          .trim()
+      }
+
       const initPrompt = `您好,${p.meetingName}。
 我是Argus,是当前会议的组织者,很荣幸能邀请您参加会议:
 ---
-会议主题: ${topic}
+会议主题: ${sanitizeInput(topic)}
 所有参会者: ${participantNames}
 你的会议名称: ${p.meetingName}
-你的角色画像: ${p.profile}
+你的角色画像: ${sanitizeInput(p.profile)}

Let me verify if there are any existing sanitization utilities in the codebase:


🏁 Script executed:

#!/bin/bash
# Search for existing sanitization or escaping utilities
ast-grep --pattern 'function $_($$_) {
  $$$
  sanitize
  $$$
}'

# Also search for any security-related utilities
rg -A 3 "sanitize|escape|clean.*input|xss|injection" --type ts

Length of output: 2405


Sanitize user input in meeting prompts

User-provided content (topic, profile, speech text) is directly interpolated into prompts without sanitization. This could be exploited for prompt injection attacks.

Consider implementing input sanitization:

+      // Sanitize user input to prevent prompt injection
+      const sanitizeInput = (input: string): string => {
+        return input
+          .replace(/```/g, '\\`\\`\\`')  // Escape code blocks
+          .replace(/---/g, '\\-\\-\\-')   // Escape separators
+          .trim()
+      }
+
       const initPrompt = `您好,${p.meetingName}。
 我是Argus,是当前会议的组织者,很荣幸能邀请您参加会议:
 ---
-会议主题: ${topic}
+会议主题: ${sanitizeInput(topic)}
 所有参会者: ${participantNames}
 你的会议名称: ${p.meetingName}
-你的角色画像: ${p.profile}
+你的角色画像: ${sanitizeInput(p.profile)}

Let me verify if there are any existing sanitization utilities in the codebase:

#!/bin/bash
# Search for existing sanitization or escaping utilities
ast-grep --pattern 'function $_($$_) {
  $$$
  sanitize
  $$$
}'

# Also search for any security-related utilities
rg -A 3 "sanitize|escape|clean.*input|xss|injection" --type ts
🤖 Prompt for AI Agents
In src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts lines 334 to
393, user inputs such as topic, profile, and speech text are directly embedded
into prompts without sanitization, risking prompt injection attacks. To fix
this, implement or use an existing sanitizeInput function that escapes dangerous
characters like code blocks and separators, then apply it to all user-provided
content before interpolation in prompts. Ensure all dynamic strings from users
are sanitized consistently throughout the meeting flow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know I know let it be!

Copy link
Contributor

Choose a reason for hiding this comment

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

@neoragex2002 Understood! I'll acknowledge your decision to defer the input sanitization for now.

Would you like me to create a follow-up issue to track the prompt injection security concern for a future PR?

Comment on lines +176 to +184
;(async () => {
try {
await this.organizeMeeting(meetingArgs)
console.log('会议流程已在后台成功完成。')
} catch (meetingError: any) {
console.error(`会议执行过程中发生错误: ${meetingError.message}`)
}
})()

Copy link
Contributor

@coderabbitai coderabbitai bot Jun 21, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling for background meeting execution

The fire-and-forget pattern provides no way for the caller to track meeting progress or handle failures. Consider:

  1. Returning a meeting ID for status tracking
  2. Implementing a meeting status API
  3. Adding proper error notification mechanism
-        // 关键点: 立即返回成功响应,将耗时的会议流程放到后台执行,避免LLM工具调用超时。
-        ;(async () => {
-          try {
-            await this.organizeMeeting(meetingArgs)
-            console.log('会议流程已在后台成功完成。')
-          } catch (meetingError: any) {
-            console.error(`会议执行过程中发生错误: ${meetingError.message}`)
-          }
-        })()
-
-        return { content: [{ type: 'text', text: '会议已成功启动,正在后台进行中...' }] }
+        // Generate a unique meeting ID for tracking
+        const meetingId = `meeting_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
+        
+        // Store meeting promise for potential cancellation
+        const meetingPromise = this.organizeMeeting(meetingArgs)
+          .then(() => {
+            console.log(`会议 ${meetingId} 已成功完成。`)
+            eventBus.emit('MEETING_COMPLETED', { meetingId, status: 'success' })
+          })
+          .catch((meetingError: any) => {
+            console.error(`会议 ${meetingId} 执行错误: ${meetingError.message}`)
+            eventBus.emit('MEETING_COMPLETED', { meetingId, status: 'error', error: meetingError.message })
+          })
+
+        return { content: [{ type: 'text', text: `会议已成功启动,会议ID: ${meetingId}` }] }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
;(async () => {
try {
await this.organizeMeeting(meetingArgs)
console.log('会议流程已在后台成功完成。')
} catch (meetingError: any) {
console.error(`会议执行过程中发生错误: ${meetingError.message}`)
}
})()
// Generate a unique meeting ID for tracking
const meetingId = `meeting_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
// Store meeting promise for potential cancellation
const meetingPromise = this.organizeMeeting(meetingArgs)
.then(() => {
console.log(`会议 ${meetingId} 已成功完成。`)
eventBus.emit('MEETING_COMPLETED', { meetingId, status: 'success' })
})
.catch((meetingError: any) => {
console.error(`会议 ${meetingId} 执行错误: ${meetingError.message}`)
eventBus.emit('MEETING_COMPLETED', {
meetingId,
status: 'error',
error: meetingError.message
})
})
return {
content: [
{
type: 'text',
text: `会议已成功启动,会议ID: ${meetingId}`
}
]
}
🤖 Prompt for AI Agents
In src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts around lines
176 to 184, the current fire-and-forget async IIFE for organizing meetings lacks
mechanisms for tracking progress or handling errors externally. Modify the code
to generate and return a unique meeting ID when starting the meeting process, so
callers can track its status. Implement a meeting status API or method that
allows querying the current state of the meeting by this ID. Additionally,
enhance error handling by storing error states linked to the meeting ID and
provide a notification or callback mechanism to inform callers of failures.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know I know let it be!

Copy link
Contributor

Choose a reason for hiding this comment

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

@neoragex2002 Understood! I'll leave the current fire-and-forget implementation as is. Sometimes the simpler approach is the right choice for the current needs.

😊
    🐰
   ╱  ╲
   ╱    ╲
  ╱     ╲
  ╱      ╲

Comment on lines 258 to 324
if (!foundByTabTitle) {
// 关键点1: 通过tabId定位到的tab通常为create_new_tab的结果,需要创建会话并激活
let conversationId = await presenter.threadPresenter.getActiveConversationId(tabData.id)

// 确保每个参会者都有一个参会名称,超过26个用‘参会者27’之类命名
const meetingName =
nameIndex < PARTICIPANT_NAMES.length
? PARTICIPANT_NAMES[nameIndex]
: `参会者${nameIndex + 1}`
nameIndex++

// 确保每个参会者都要有活动会话,如没有,则需创建并等待UI同步
if (!conversationId) {
console.log(`参会者 (ID: ${tabData.id}) 没有活动会话,正在为其创建...`)

// 步骤 a: 创建新会话,将自动激活并广播 'conversation:activated' 事件
conversationId = await presenter.threadPresenter.createConversation(
`${meetingName}`,
{},
tabData.id,
{ forceNewAndActivate: true } //强制创建并激活空会话,避免冗余的空会话单例检测
)
if (!conversationId) {
console.warn(`为Tab ${tabData.id} 创建会话失败,将跳过此参会者。`)
continue
}

// 步骤 b: 关键的UI同步点,等待渲染进程处理完激活事件并回传确认信号
try {
await awaitTabActivated(conversationId)
console.log(`会话 ${conversationId} 在Tab ${tabData.id} 中已成功激活。`)
} catch (error) {
console.error(`等待Tab ${tabData.id} 激活失败:`, error)
continue
}
}

meetingParticipants.push({
meetingName,
tabId: tabData.id,
conversationId,
originalTitle: tabData.title,
profile: p.profile || `你可以就“${topic}”这个话题,自由发表你的看法和观点。`
})
} else {
// 关键点2: 通过title定位到的tab,通常为已有tab,无需重新创建会话并激活
let conversationId = await presenter.threadPresenter.getActiveConversationId(tabData.id)
if (!conversationId) {
console.warn(`为Tab ${tabData.id} 创建会话失败,将跳过此参会者。`)
continue
}

// 确保每个有效的参会者都有一个可用的conversationId
const meetingName =
nameIndex < PARTICIPANT_NAMES.length
? PARTICIPANT_NAMES[nameIndex]
: `参会者${nameIndex + 1}`
nameIndex++

meetingParticipants.push({
meetingName,
tabId: tabData.id,
conversationId,
originalTitle: tabData.title,
profile: p.profile || `你可以就“${topic}”这个话题,自由发表你的看法和观点。`
})
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 21, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Eliminate code duplication in participant preparation

The logic for assigning meeting names and creating participant objects is duplicated between the two branches (foundByTabTitle true/false). Extract this into a shared function.

+      // Helper function to create meeting participant
+      const createMeetingParticipant = (
+        tabData: any,
+        conversationId: string,
+        nameIndex: number,
+        profile?: string
+      ): MeetingParticipant => {
+        const meetingName = nameIndex < PARTICIPANT_NAMES.length
+          ? PARTICIPANT_NAMES[nameIndex]
+          : `参会者${nameIndex + 1}`
+        
+        return {
+          meetingName,
+          tabId: tabData.id,
+          conversationId,
+          originalTitle: tabData.title,
+          profile: profile || `你可以就"${topic}"这个话题,自由发表你的看法和观点。`
+        }
+      }

       // 关键点: 根据tab定位方法的不同,采取不同处理方法
       if (!foundByTabTitle) {
         // ... existing tab_id logic ...
-        const meetingName =
-          nameIndex < PARTICIPANT_NAMES.length
-            ? PARTICIPANT_NAMES[nameIndex]
-            : `参会者${nameIndex + 1}`
-        nameIndex++
-
-        meetingParticipants.push({
-          meetingName,
-          tabId: tabData.id,
-          conversationId,
-          originalTitle: tabData.title,
-          profile: p.profile || `你可以就"${topic}"这个话题,自由发表你的看法和观点。`
-        })
+        meetingParticipants.push(createMeetingParticipant(tabData, conversationId, nameIndex++, p.profile))
       } else {
         // ... existing tab_title logic ...
-        const meetingName =
-          nameIndex < PARTICIPANT_NAMES.length
-            ? PARTICIPANT_NAMES[nameIndex]
-            : `参会者${nameIndex + 1}`
-        nameIndex++
-
-        meetingParticipants.push({
-          meetingName,
-          tabId: tabData.id,
-          conversationId,
-          originalTitle: tabData.title,
-          profile: p.profile || `你可以就"${topic}"这个话题,自由发表你的看法和观点。`
-        })
+        meetingParticipants.push(createMeetingParticipant(tabData, conversationId, nameIndex++, p.profile))
       }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts around lines
258 to 324, the code duplicates the logic for assigning meeting names and
creating participant objects in both branches of the if-else statement. To fix
this, extract the common logic of generating the meetingName and pushing the
participant object into a separate helper function. Then call this function from
both branches, passing the necessary parameters like tabData, conversationId,
profile, and topic, to eliminate duplication and improve maintainability.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know I know let it be!

Copy link
Contributor

Choose a reason for hiding this comment

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

@neoragex2002 Understood! I'll leave the code as-is. Sometimes explicit duplication can be clearer than abstraction anyway.

Thanks for reviewing the suggestion! 🐰

  /|   /|  
 ( :v:  )
  |(_)|

Copy link
Contributor Author

Choose a reason for hiding this comment

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

you are really chatty...

Copy link
Contributor

Choose a reason for hiding this comment

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

@neoragex2002 Got it! I'll keep it brief. 👍


✏️ Learnings added
Learnt from: neoragex2002
PR: ThinkInAIXYZ/deepchat#550
File: src/main/presenter/mcpPresenter/inMemoryServers/meetingServer.ts:258-324
Timestamp: 2025-06-21T15:53:34.598Z
Learning: User neoragex2002 prefers concise, brief responses rather than chatty or verbose communication. Avoid lengthy explanations, excessive enthusiasm, or ASCII art in responses.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@zerob13 zerob13 merged commit 97e9f61 into ThinkInAIXYZ:dev Jun 21, 2025
2 checks passed
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