feat: 持久对话 MCP 插件 v1.6.37 · Hub + Panel + MCP 三形态部署
- src/: server.js (MCP 4 工具) + hub.js (HTTP Hub) + cursor-title.js - web/: panel.html (主面板) + panel.remote.html (远端 1637) - scripts/: ensure-hub.sh (stdio→http 桥) + mongo_sync.py - mcp-config/: trae.mcp.json + cursor.mcp.json - docs/: INSTALL / DEPLOY / MCP_SETUP / ARCHITECTURE / QUICKSTART - ZIP 原始安装包备查 关键机制: - 4 MCP 工具:init / select / wait / merge - 垂直绑定:workspace::cursorTitle@hostIp 三件指纹 - 真挂起:关闭 timedReply 后 wait 真正阻塞,agent turn 不结束 - 双 Hub 部署:远端 NAS 24×7 + 本地 launchd 守护备份 - 三形态组合:远端 + 本地 + Trae 插件,可同时跑
This commit is contained in:
340
docs/ARCHITECTURE.md
Normal file
340
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# 架构(ARCHITECTURE.md)
|
||||
|
||||
> 持久对话 MCP 插件的完整技术架构
|
||||
|
||||
---
|
||||
|
||||
## 1. 整体拓扑
|
||||
|
||||
```
|
||||
┌────────────────────┐
|
||||
│ Trae / Cursor │ IDE(SOLO Agent 窗口)
|
||||
│ (MCP client) │
|
||||
└─────────┬──────────┘
|
||||
│ stdio JSON-RPC
|
||||
│ 4 tools: init / select / wait / merge
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ ensure-hub.sh │ stdio → HTTP 桥接
|
||||
│ (MCP server) │
|
||||
└─────────┬──────────┘
|
||||
│ HTTP (13458)
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ hub.js (主) │ ┌─ 远端: 192.168.110.101:13458
|
||||
│ Node 22 + HTTP │ └─ 本地: 127.0.0.1:13458
|
||||
└─────────┬──────────┘
|
||||
│ 短轮询(10s)
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ sessions.json │ 持久化层
|
||||
│ bindings.json │ - sessions: ct_xxx → 状态
|
||||
│ plan-bindings │ - bindings: workspace::title@ip → ct_
|
||||
│ thread-bindings/ │ - plan: planName → ct_
|
||||
└────────────────────┘ - thread: threadId → ct_
|
||||
|
||||
↓ 周期同步(可选)
|
||||
┌────────────────────┐
|
||||
│ MongoDB │ 远端数据库
|
||||
│ karuo_site │ (port 27017)
|
||||
└────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心数据流
|
||||
|
||||
### 2.1 初始化
|
||||
|
||||
```
|
||||
Agent → init_conversation(cursorTitle=标签, newPlan=true, planLabel=xxx)
|
||||
→ ensure-hub.sh → POST /api/init
|
||||
→ hub.js 创建 session(ct_xxx)
|
||||
→ 写入 sessions.json
|
||||
→ 写 binding:workspace::title@ip → ct_xxx
|
||||
→ 返回 { token: ct_xxx, status: holding }
|
||||
```
|
||||
|
||||
### 2.2 选择已有
|
||||
|
||||
```
|
||||
Agent → select_conversation(token=ct_xxx, cursorTitle=标签)
|
||||
→ ensure-hub.sh → POST /api/select
|
||||
→ hub.js 验证垂直绑定(workspace::title@ip 三件指纹)
|
||||
→ 不匹配 → 403 BINDING_REJECTED
|
||||
→ 匹配 → 复用 session
|
||||
```
|
||||
|
||||
### 2.3 真挂起
|
||||
|
||||
```
|
||||
Agent → wait_for_user_input(token, message, timeout)
|
||||
→ ensure-hub.sh → POST /api/wait/register
|
||||
→ hub.js 设置 session.status = "waiting"
|
||||
→ 注册到 pendingWaits[token]
|
||||
→ 长连接(短轮询 10s 续约)
|
||||
→ 收到 reply:
|
||||
a. 用户发消息 → deliverReply → 返回
|
||||
b. timedReply 触发(10s 间隔)→ deliverReply("保活续跑") → 返回
|
||||
c. POLL_TIMEOUT_MS=10s → renewal → 继续轮询
|
||||
→ Agent 端按 status 决定:
|
||||
- "replied" → 返回 text
|
||||
- "renewal" → 继续 poll
|
||||
- "gone" → 重新 register
|
||||
```
|
||||
|
||||
### 2.4 合并
|
||||
|
||||
```
|
||||
Agent → merge_conversation(fromToken, toToken, cursorTitle)
|
||||
→ ensure-hub.sh → POST /api/merge
|
||||
→ hub.js 把 fromToken 的 messages 追加到 toToken
|
||||
→ fromToken 标记 archived
|
||||
→ 增量保存 sessions.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 垂直绑定
|
||||
|
||||
### 3.1 绑定指纹
|
||||
|
||||
```
|
||||
bindingKey = `${workspace}::${cursorTitle}@${hostIp}`
|
||||
```
|
||||
|
||||
示例:
|
||||
```
|
||||
/Users/karuo/Documents/个人::持久对话永久续跑-20260626@192.168.3.1
|
||||
```
|
||||
|
||||
### 3.2 验证时机
|
||||
|
||||
| 触发 | 验证 |
|
||||
|:---|:---|
|
||||
| `select_conversation` | 严校验(必须完全匹配) |
|
||||
| `init_conversation` | 宽校验(可换 title 重新绑定) |
|
||||
| `wait_for_user_input` | 中校验(容许 title 改名) |
|
||||
| `merge_conversation` | 严校验(不匹配拒绝) |
|
||||
|
||||
### 3.3 改标签自动迁移
|
||||
|
||||
```javascript
|
||||
// 用户在 Trae 改了标签 "A" → "B"
|
||||
// wait 时传入新 title "B"
|
||||
// hub.js 检测到旧 binding 有 token,旧 bindingKey 不匹配
|
||||
// 允许迁移:把 bindingKey 改成新值,token 复用
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. session 状态机
|
||||
|
||||
```
|
||||
init
|
||||
│
|
||||
▼
|
||||
holding ──────┐
|
||||
│ │ (每 10s 续约 / 接收新消息)
|
||||
│ │
|
||||
▼ │
|
||||
waiting ───────┤
|
||||
│ │
|
||||
│ (用户发消息)│
|
||||
▼ │
|
||||
replied ───────┘
|
||||
│
|
||||
│ (autoStreak 累加)
|
||||
▼
|
||||
running
|
||||
│
|
||||
│ (merge / 结束持久对话)
|
||||
▼
|
||||
merged / archived
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. autoStreak 与 keepAlive
|
||||
|
||||
### 5.1 概念
|
||||
|
||||
- `autoStreak`:连续"用户不发消息"的轮次计数
|
||||
- `keepAlive`:Hub 在 autoStreak 超阈值时主动推"保活续跑"消息
|
||||
|
||||
### 5.2 真挂起模式(推荐)
|
||||
|
||||
```json
|
||||
POST /api/session/config
|
||||
{
|
||||
"sessionAutoContinue": false,
|
||||
"timedReplyEnabled": false,
|
||||
"timedReplyIntervalMs": 0
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 关闭 timedReply → 不会主动推"保活续跑"
|
||||
- 关闭 autoContinue → 不会因为 idle 触发续跑
|
||||
- wait 唯一返回源 = 用户真消息
|
||||
- MCP 工具调用一直阻塞,agent turn 不会结束
|
||||
|
||||
### 5.3 自动模式(不推荐,会顶回 wait)
|
||||
|
||||
```json
|
||||
{
|
||||
"sessionAutoContinue": true,
|
||||
"timedReplyEnabled": true,
|
||||
"timedReplyIntervalMs": 10000
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 每 10s 推"保活续跑"
|
||||
- wait 立即返回
|
||||
- agent turn 结束(UI 显示"任务完成")
|
||||
|
||||
---
|
||||
|
||||
## 6. 短轮询 vs 长连接
|
||||
|
||||
| 方案 | 优点 | 缺点 | 采用 |
|
||||
|:---|:---|:---|:---|
|
||||
| 短轮询(10s) | 简单、跨网段友好、稳 | 续约频繁 | ✅ |
|
||||
| WebSocket | 实时、省资源 | 需配 wss、断线重连复杂 | ❌ |
|
||||
| Server-Sent Events | 单向实时 | 不支持双向 | ❌ |
|
||||
| Long Polling | 接近实时 | 服务器维护成本 | ❌(短轮询已够) |
|
||||
|
||||
短轮询间隔:`POLL_TIMEOUT_MS = 10000`(10s),可在 `hub.js` 改。
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据持久化
|
||||
|
||||
### 7.1 文件布局
|
||||
|
||||
```
|
||||
~/.persistent-chat-local/
|
||||
├── sessions.json # 主 session 数据
|
||||
├── bindings.json # workspace::title@ip → ct_
|
||||
├── plan-bindings.json # planName → ct_
|
||||
├── thread-bindings/ # threadId → ct_ (mkdir)
|
||||
├── archive/ # 归档 session
|
||||
│ └── 2026-06/
|
||||
│ └── ct_xxx.json
|
||||
├── replies/ # 历史用户回复缓存
|
||||
├── logs/
|
||||
│ ├── hub.log # Hub 主日志
|
||||
│ ├── launchd.log # macOS launchd 日志
|
||||
│ └── launchd.err
|
||||
├── templates/ # 面板模板
|
||||
├── server.js # MCP server(41KB)
|
||||
├── hub.js # Hub 主服务(89KB)
|
||||
├── panel.html # 浏览器面板(84KB)
|
||||
├── cursor-title.js # 标签解析器(4KB)
|
||||
├── ensure-hub.sh # 启动器
|
||||
└── mongo_sync.py # → MongoDB 同步
|
||||
```
|
||||
|
||||
### 7.2 sessions.json 格式
|
||||
|
||||
```json
|
||||
{
|
||||
"ct_xxxx": {
|
||||
"token": "ct_xxxx",
|
||||
"title": "永久续跑三段式",
|
||||
"displayTitle": "",
|
||||
"status": "holding",
|
||||
"workspace": "/Users/karuo/Documents/个人",
|
||||
"createdAt": 1782448693854,
|
||||
"lastActiveAt": 1782449356228,
|
||||
"msgCount": 10,
|
||||
"autoStreak": 1,
|
||||
"bindingKey": "/Users/karuo/Documents/个人::标签@192.168.3.1",
|
||||
"hostIp": "192.168.3.1",
|
||||
"hub": "192.168.110.101:13458",
|
||||
"bindingCheckCount": 17,
|
||||
"continueMode": "optimize",
|
||||
"sessionAutoContinue": false,
|
||||
"timedReplyEnabled": false,
|
||||
"timedReplyIntervalMs": 0,
|
||||
"messages": [
|
||||
{ "role": "user", "content": "...", "at": 1782448794716, "auto": false },
|
||||
{ "role": "assistant", "content": "...", "at": 1782449116363, "auto": false }
|
||||
],
|
||||
"lastReview": {
|
||||
"hasReview": false,
|
||||
"completionPct": null,
|
||||
"nextStep": "",
|
||||
"isIdleNext": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 周期保存
|
||||
|
||||
- 每次 register / deliverReply / merge 后 `saveSessions()`
|
||||
- 写盘:原子 rename(先写 .tmp,再 rename)
|
||||
- 加载:启动时一次 loadSessions(),之后只内存操作
|
||||
|
||||
---
|
||||
|
||||
## 8. 与 MongoDB 同步(可选)
|
||||
|
||||
`mongo_sync.py` 把 sessions.json 周期同步到 MongoDB `karuo_site.sessions`:
|
||||
|
||||
```python
|
||||
# 触发
|
||||
python3 mongo_sync.py
|
||||
# 或 cron 每 5 分钟
|
||||
*/5 * * * * cd ~/.persistent-chat-local && python3 mongo_sync.py
|
||||
```
|
||||
|
||||
用途:
|
||||
- 多机同步(局域网内多 Hub)
|
||||
- 长期归档(超过 1000 个 session 不卡)
|
||||
- 跨平台(Mac / NAS / Linux)
|
||||
|
||||
---
|
||||
|
||||
## 9. 安全性
|
||||
|
||||
| 维度 | 措施 |
|
||||
|:---|:---|
|
||||
| 端口 | 13458 默认仅 LAN 监听,不暴露公网 |
|
||||
| 鉴权 | 垂直绑定(三件指纹),无 token → 拒绝 |
|
||||
| 数据 | sessions.json 落在 ~/.persistent-chat-local/,权限 600 |
|
||||
| 跨网段 | 靠 SSH 隧道 / VPN |
|
||||
| 日志 | 不记录用户隐私内容(只记 metadata) |
|
||||
| 卡密激活 | **已删除**(v1.6.37 不再有 license 校验) |
|
||||
|
||||
---
|
||||
|
||||
## 10. 性能
|
||||
|
||||
| 指标 | 值 |
|
||||
|:---|:---|
|
||||
| Hub 内存 | ~50MB(1000 个 session) |
|
||||
| Hub CPU | < 1%(空闲) |
|
||||
| 单 session 占内存 | ~5KB(仅 metadata) |
|
||||
| 单消息占内存 | ~1KB |
|
||||
| 短轮询带宽 | ~1KB/10s/session |
|
||||
| 启动时间 | < 1s(Node 22) |
|
||||
| sessions.json 加载 | 10000 个 session < 500ms |
|
||||
|
||||
---
|
||||
|
||||
## 11. 版本演进
|
||||
|
||||
| 版本 | 日期 | 关键变化 |
|
||||
|:---|:---|:---|
|
||||
| v1.1 | 2025-12 | 初版(仅 hub.js) |
|
||||
| v1.5 | 2026-03 | 加 server.js / MCP 4 工具 |
|
||||
| v1.6.30 | 2026-05 | autoStreak / timedReply |
|
||||
| v1.6.37 | 2026-06-07 | **删除卡密激活** / 加 MongoDB 同步 |
|
||||
| v1.6.68 | 2026-06-20 | 本地增强(VSIX 4.6 兼容) |
|
||||
|
||||
---
|
||||
|
||||
> 最后更新:2026-06-26 · 卡若AI
|
||||
Reference in New Issue
Block a user