Metadata-Version: 2.4
Name: herdr-wecom-bot-bridge
Version: 0.1.5
Summary: 企业微信与 Herdr 持久 Agent Thread 之间的桥接服务
Author: herdr-wecom-bot-bridge contributors
License: MIT
Keywords: wecom,herdr,agent,thread,bridge
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tomli>=2; python_version < "3.11"
Requires-Dist: websocket-client<2,>=1.8
Dynamic: license-file

# herdr-wecom-bot-bridge

一个可通过 `pip` 安装的企业微信 ↔ Herdr Bridge：单个企业微信 WebSocket 负责消息和心跳；每个用户或群聊映射为一个 Herdr Workspace；每个 Thread 映射为一个 Herdr Tab，并在活跃期间运行一个独立、可恢复的 Pi RPC Worker。

**先看这里**：开发过程的公开 Artifacts（架构图、进度面板、实测报告）托管在 GitHub Pages：

> https://liush2yuxjtu.github.io/lan-artifacts/

安装前建议先浏览上方的 Artifacts 存档，了解 Bridge 的设计与已实测的能力。

## 架构

```text
WeCom single WebSocket
        |
        v
WeCom inbound adapter
        |
        v
BridgeApplication (hexagonal core)
        |
        +--> FilesystemStore  ~/.herdr-wecom-bot-bridge
        +--> HerdrRuntime     Workspace -> Tab -> Worker -> Pi RPC
        `--> WeCom publisher  reply / proactive push
```

依赖只向内：领域与应用层不知道企业微信 SDK、Herdr socket 或文件系统格式。

## 映射

```text
企业微信单聊用户 -> Herdr Workspace
企业微信群 chatid -> Herdr Workspace
Thread           -> Herdr Tab
活跃 Thread      -> Tab 内的 pipe Worker + Pi RPC 子进程
```

30 分钟无活动且 Agent 不忙时，Tab/Worker 休眠；Thread 元数据、`transcript.jsonl` 和 Pi session 文件仍保留。下一条消息会创建新 Tab 并用原 session 文件恢复。

## 安装

```bash
python3 -m pip install herdr-wecom-bot-bridge
herdr-wecom-bot-bridge init
herdr-wecom-bot-bridge herdr install
```

凭据放在环境变量或 `$HOME/.env`，不要写进 Thread 文件：

```text
WECOM_BOT_ID=<redacted>
WECOM_BOT_SECRET=<redacted>
```

启动：

```bash
herdr-wecom-bot-bridge serve
```

Bridge 会持有唯一的企业微信连接。SDK 协议心跳默认每 30 秒发送一次；连续两次无回执时重连。同一 BotID 的第二个后台会替换旧连接，因此 Bridge 使用文件锁拒绝本机重复启动。

## 企业微信命令

```text
/new <主题>   新建并启动 Thread
/threads      列出当前 Workspace 的 Threads
/use <ID>     切换当前 Thread（支持唯一前缀）
/close [ID]   归档指定或当前 Thread
/status       查看 Workspace / 当前 Thread
/help         查看帮助
```

普通文本或语音转写消息会发送到当前 Thread。没有当前 Thread 时，会自动以消息开头创建一个。

## 多用户群聊

群 Workspace（按 chatid 映射）天然支持**多人 + 1 bot** 的对话，规则：

- **每次 `@bot` 都是全新 Thread**（Slack Thread 式）：每次艾特 = 独立会话上下文，
  延续对话走 bot 回复里附带的 Web 链接（`/t/<thread_id>`）。
- 每个群成员拥有**独立游标**（`workspaces/<ws>/member-cursors/<sha256(userid)>.json`），
  记录"自己最近一次新建/`/use` 的 Thread"（`/threads` 的 `>` 标记与 `/close` 按此解析）；
  `/use` 只移动自己的游标，但普通消息不读游标续接（群内永远开新 Thread）。
- 消息按 `sender_id` 归因写入 transcript，agent 与 overseer 都能区分"谁说的"。
- 单聊（USER scope）行为不同：游标续接，连续消息进同一 Thread。

## 主动介入（Overseer）

默认关闭，本地配置开启：

```toml
[overseer]
enabled = true
judge_command = "codex"
judge_model = "gpt-5.3-codex-spark"
cooldown_minutes = 10
```

- kqueue 事件驱动监听群 transcript，由 Codex Spark（本地）判断是否值得主动介入
  （典型：多人争论 A/B 方案、可并行产出的设计任务）。
- 介入采用**授权制**：bot 先发"我可以做 X，回复'同意'授权"→ 群成员回"同意/可以/ok"
  → 任务自动进入介入线程开始执行（待授权状态持久化在 `overseer_approvals.json`，重启不丢）。
- 主动发送不依赖 `@`（`aibot_send_msg`），但**接收端受企微平台限制**：群聊中未 `@` 机器人的
  消息企微不会回调给 bot，因此群内触发介入的前提是至少有人 `@` 过一次。

## 已知限制

- 群聊未 `@` 机器人的消息不可见（企微平台限制，无法绕过；单聊无此限制）。
- 图片/文件/语音消息仅在单聊投递；群聊只有 text / mixed（图文混排，`mixed.msg_item`）。
- Web UI 无鉴权（LAN-only），暴露到不可信网络前需自行加 token。
- `/tmp/herdr-wecom-test` 等测试 home 重启会清空；生产用 `~/.herdr-wecom-bot-bridge`。

## CLI

```text
herdr-wecom-bot-bridge init
herdr-wecom-bot-bridge herdr install
herdr-wecom-bot-bridge serve
herdr-wecom-bot-bridge status
herdr-wecom-bot-bridge reconcile
herdr-wecom-bot-bridge sweep
```

## 文件系统

参考 Anthropic Claude Agent View，将运行时 roster、可恢复 job/session 和 append-only transcript 分离：

```text
~/.herdr-wecom-bot-bridge/
├── config.toml
├── daemon/
│   ├── bridge.lock
│   ├── roster.json
│   └── sockets/
├── inbox/                  # WeCom msgid 去重标记
├── locks/
└── workspaces/
    └── <workspace-id>/
        ├── workspace.json
        ├── runtime.json
        ├── files/          # Agent cwd
        └── threads/
            └── <thread-id>/
                ├── thread.json
                ├── runtime.json
                ├── transcript.jsonl
                ├── agent-sessions/
                └── artifacts/
```

所有状态文件默认仅当前用户可读。Herdr Workspace/Tab/Pane ID 是可丢弃的 runtime binding；内部 `workspace_id`、`thread_id`、Pi session 文件和 transcript 才是持久事实。

## Herdr plugin

随 Python 包发布的 plugin 提供：

- Bridge 状态 Action
- 手动休眠空闲 Threads Action
- Pane 关闭/退出后的 reconcile

Bridge daemon 负责企业微信 WebSocket、路由和定时休眠；plugin 不保存业务状态。

## 当前 MVP 边界

- 支持文本与企业微信语音转写；图片、文件和视频暂不转发。
- Agent runtime 当前使用 `pi --mode rpc --no-extensions`。禁用 Pi extensions 是为了避免旧 `pi-wecombot` 在每个 Thread 内重复连接同一 BotID。
- 每个 Workspace 使用独立 cwd；这提供进程和上下文隔离，但不是操作系统沙箱。
- 本地单机文件存储，不提供多节点分布式锁。

## 开发验证

```bash
PYTHONPATH=src python3 -m pytest
PYTHONPATH=src python3 -m herdr_wecom_bot_bridge --home /tmp/herdr-wecom-test init
PYTHONPATH=src python3 -m herdr_wecom_bot_bridge --home /tmp/herdr-wecom-test status
```

## License

MIT
