metame-cli 1.5.3 → 1.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -18
- package/index.js +352 -79
- package/package.json +2 -2
- package/scripts/agent-layer.js +4 -2
- package/scripts/bin/dispatch_to +178 -90
- package/scripts/daemon-admin-commands.js +353 -105
- package/scripts/daemon-agent-commands.js +434 -66
- package/scripts/daemon-bridges.js +477 -68
- package/scripts/daemon-claude-engine.js +1267 -674
- package/scripts/daemon-command-router.js +205 -27
- package/scripts/daemon-command-session-route.js +118 -0
- package/scripts/daemon-default.yaml +7 -0
- package/scripts/daemon-engine-runtime.js +96 -20
- package/scripts/daemon-exec-commands.js +108 -49
- package/scripts/daemon-file-browser.js +64 -7
- package/scripts/daemon-notify.js +18 -4
- package/scripts/daemon-ops-commands.js +16 -2
- package/scripts/daemon-remote-dispatch.js +55 -1
- package/scripts/daemon-runtime-lifecycle.js +87 -0
- package/scripts/daemon-session-commands.js +102 -45
- package/scripts/daemon-session-store.js +497 -66
- package/scripts/daemon-siri-bridge.js +234 -0
- package/scripts/daemon-siri-imessage.js +209 -0
- package/scripts/daemon-task-scheduler.js +10 -2
- package/scripts/daemon.js +697 -179
- package/scripts/daemon.yaml +7 -0
- package/scripts/docs/agent-guide.md +36 -3
- package/scripts/docs/hook-config.md +134 -0
- package/scripts/docs/maintenance-manual.md +162 -5
- package/scripts/docs/pointer-map.md +60 -5
- package/scripts/feishu-adapter.js +7 -15
- package/scripts/hooks/doc-router.js +29 -0
- package/scripts/hooks/hook-utils.js +61 -0
- package/scripts/hooks/intent-doc-router.js +54 -0
- package/scripts/hooks/intent-engine.js +72 -0
- package/scripts/hooks/intent-file-transfer.js +51 -0
- package/scripts/hooks/intent-memory-recall.js +35 -0
- package/scripts/hooks/intent-ops-assist.js +54 -0
- package/scripts/hooks/intent-task-create.js +35 -0
- package/scripts/hooks/intent-team-dispatch.js +106 -0
- package/scripts/hooks/team-context.js +143 -0
- package/scripts/intent-registry.js +59 -0
- package/scripts/memory-extract.js +59 -0
- package/scripts/memory-nightly-reflect.js +109 -43
- package/scripts/memory.js +55 -17
- package/scripts/mentor-engine.js +6 -0
- package/scripts/schema.js +1 -0
- package/scripts/self-reflect.js +110 -12
- package/scripts/session-analytics.js +160 -0
- package/scripts/signal-capture.js +1 -1
- package/scripts/team-dispatch.js +315 -0
package/scripts/daemon.yaml
CHANGED
|
@@ -347,3 +347,10 @@ daemon:
|
|
|
347
347
|
- mcp__notionApi__*
|
|
348
348
|
models:
|
|
349
349
|
claude: sonnet
|
|
350
|
+
|
|
351
|
+
# Intent Engine — UserPromptSubmit hook modules
|
|
352
|
+
# Set any key to false to disable that intent module.
|
|
353
|
+
hooks:
|
|
354
|
+
team_dispatch: true # team member dispatch hints
|
|
355
|
+
ops_assist: true # /undo /restart /logs /gc hints
|
|
356
|
+
task_create: true # task scheduling hints
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
> 用 codex 建一个代码审查 agent,目录 ~/projects/pr-review
|
|
27
27
|
|
|
28
|
-
在桌面 Claude Code
|
|
28
|
+
在桌面 Claude Code 终端,操作步骤:
|
|
29
29
|
1. 创建项目目录和 CLAUDE.md(角色定义)
|
|
30
30
|
2. 编辑 `~/.metame/daemon.yaml`,在 `projects` 下新增:
|
|
31
31
|
```yaml
|
|
@@ -35,7 +35,12 @@
|
|
|
35
35
|
cwd: "~/projects/my-bot"
|
|
36
36
|
icon: "🤖"
|
|
37
37
|
```
|
|
38
|
-
3.
|
|
38
|
+
3. 保存文件即可 —— daemon 检测到变更后自动:
|
|
39
|
+
- 创建 `~/.metame/agents/my_bot/`(soul.md、memory-snapshot.md、agent.yaml)
|
|
40
|
+
- 在 cwd 里建 SOUL.md、MEMORY.md 软链
|
|
41
|
+
- 将 `agent_id` 写回 daemon.yaml
|
|
42
|
+
|
|
43
|
+
**无需**手动执行 `/agent soul repair` 或 `touch` 任何文件。
|
|
39
44
|
|
|
40
45
|
### Step 3: 绑定群聊
|
|
41
46
|
告知用户:
|
|
@@ -43,20 +48,48 @@
|
|
|
43
48
|
|
|
44
49
|
`/activate` 会自动将群与最近创建的 Agent 绑定(30分钟内有效)。
|
|
45
50
|
|
|
51
|
+
## 创建团队成员(Team Wizard)
|
|
52
|
+
|
|
53
|
+
用于在一个项目下创建多个并行工作的 Agent 分身(team members)。
|
|
54
|
+
|
|
55
|
+
### 触发方式
|
|
56
|
+
|
|
57
|
+
手机端发送自然语言(如"创建团队"、"建个工作组")或直接发送:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
/agent new team
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 创建分身(Clone Wizard)
|
|
64
|
+
|
|
65
|
+
为当前 Agent 创建一个克隆分身——共享同一个 `CLAUDE.md` 角色定义(symlink),运行在独立的工作目录。
|
|
66
|
+
|
|
67
|
+
### 触发方式
|
|
68
|
+
|
|
69
|
+
手机端发送自然语言(如"分身"、"克隆"、"副本"、"另一个自己")或直接发送:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
/agent new clone
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
分身的 `CLAUDE.md` 指向父 Agent 角色定义(改父角色分身同步生效),需手动绑定群:新建群 → 加 bot → `/activate`
|
|
76
|
+
|
|
46
77
|
## 常用命令速查
|
|
47
78
|
|
|
48
79
|
| 操作 | 手机端命令 |
|
|
49
80
|
|------|-----------|
|
|
50
81
|
| 新建 Agent | `/agent new` 或自然语言"创建agent" |
|
|
82
|
+
| 创建团队成员 | `/agent new team` 或自然语言"创建团队" |
|
|
83
|
+
| 创建分身(克隆) | `/agent new clone` 或自然语言"分身/克隆/副本" |
|
|
51
84
|
| 绑定群 | `/activate` 或 `/agent bind <名称> [目录]` |
|
|
52
85
|
| 查看列表 | `/agent list` |
|
|
53
86
|
| 编辑角色 | `/agent edit` |
|
|
54
87
|
| 解绑群 | `/agent unbind` |
|
|
55
88
|
| 切换 Agent | 直接@昵称(仅非专属群) |
|
|
89
|
+
| 向团队成员发消息 | `/msg <昵称> <内容>` |
|
|
56
90
|
|
|
57
91
|
## 注意事项
|
|
58
92
|
- 专属群(chat_agent_map 中的群)永远绑定同一个 Agent,不能通过昵称切换
|
|
59
93
|
- 新群必须发 `/activate` 才能使用,未授权群会提示"此群未授权"
|
|
60
94
|
- Codex 当前限制(MVP):`/sessions` 列表暂只展示 Claude 本地会话,Codex 会话暂不可见
|
|
61
95
|
- Codex 当前限制(MVP):`/compact` 暂不支持,请继续在同一会话中对话
|
|
62
|
-
- 需要定位脚本入口、升级步骤或文件落点时,先看 `~/.metame/docs/pointer-map.md`
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# MetaMe Hook / Intent Engine 配置手册
|
|
2
|
+
|
|
3
|
+
> 自动部署到 `~/.metame/docs/hook-config.md`。源文件:`scripts/docs/hook-config.md`。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 架构概览
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
UserPromptSubmit (每轮用户输入)
|
|
11
|
+
├── signal-capture.js → 捕获用户偏好信号(写文件,不注入)
|
|
12
|
+
└── intent-engine.js → 意图检测 + 按需注入 additionalSystemPrompt
|
|
13
|
+
|
|
14
|
+
Stop (每轮结束)
|
|
15
|
+
└── stop-session-capture.js → session 事件日志 + 工具失败捕获
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`scripts/intent-registry.js` 是单一维护源,负责调用各意图模块并返回提示块。
|
|
19
|
+
`intent-engine.js` 是 Claude hook adapter;daemon 里的 Codex 路径也复用同一 registry。
|
|
20
|
+
零匹配 → 零输出(不浪费 token)。
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 当前意图模块
|
|
25
|
+
|
|
26
|
+
| 模块 key | 文件 | 触发条件 | 注入内容 |
|
|
27
|
+
|---------|------|---------|---------|
|
|
28
|
+
| `team_dispatch` | `intent-team-dispatch.js` | 检测到"告诉/让/发给 + 成员名"等联络意图 | `dispatch_to` 命令提示(仅匹配成员) |
|
|
29
|
+
| `ops_assist` | `intent-ops-assist.js` | 回退/日志/重启/gc/状态 相关语境 | `/undo` `/restart` `/logs` `/gc` `/status` 命令提示 |
|
|
30
|
+
| `task_create` | `intent-task-create.js` | 定时/提醒/每天X点 等调度语境 | `/task-add` 命令用法提示 |
|
|
31
|
+
| `file_transfer` | `intent-file-transfer.js` | "发给我/发过来/导出" 等文件传输语境 | `[[FILE:...]]` 协议 + 收发规则 |
|
|
32
|
+
| `memory_recall` | `intent-memory-recall.js` | "上次/之前/还记得" 等跨会话回忆语境 | `memory-search.js` 命令用法 |
|
|
33
|
+
| `doc_router` | `intent-doc-router.js` | "创建/绑定 Agent"、"代码结构/脚本入口"、"hook/intent 配置" 等文档导向语境 | 统一 doc-router 文档指引 |
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 开关控制
|
|
37
|
+
|
|
38
|
+
在 `~/.metame/daemon.yaml` 的 `hooks:` 段控制(不需要改代码):
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
hooks:
|
|
42
|
+
team_dispatch: true # 改为 false 可禁用
|
|
43
|
+
ops_assist: true
|
|
44
|
+
task_create: false # 禁用任务调度提示
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
改完立即生效(intent-engine 每次运行时读取)。
|
|
48
|
+
**不需要重启 daemon。**
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 新增意图模块(标准步骤)
|
|
53
|
+
|
|
54
|
+
1. **创建模块文件** `scripts/hooks/intent-<name>.js`:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
'use strict';
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} prompt - 已清洗的用户输入
|
|
60
|
+
* @param {object} config - daemon.yaml 完整配置
|
|
61
|
+
* @param {string} projectKey - 当前 METAME_PROJECT
|
|
62
|
+
* @returns {string|null} - 提示文本,或 null(不注入)
|
|
63
|
+
*/
|
|
64
|
+
module.exports = function detect<Name>(prompt, config, projectKey) {
|
|
65
|
+
// 检测意图,返回 hint 字符串或 null
|
|
66
|
+
if (!/你的关键词/.test(prompt)) return null;
|
|
67
|
+
return '[提示标题]\n- 具体提示内容';
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. **注册到 intent-engine.js**:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// scripts/hooks/intent-engine.js
|
|
75
|
+
const INTENT_MODULES = {
|
|
76
|
+
team_dispatch: './intent-team-dispatch',
|
|
77
|
+
ops_assist: './intent-ops-assist',
|
|
78
|
+
task_create: './intent-task-create',
|
|
79
|
+
your_name: './intent-<name>', // ← 加这行
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const DEFAULTS = {
|
|
83
|
+
// ...
|
|
84
|
+
your_name: true, // ← 加这行(默认开启)
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
文档路由类场景优先复用 `scripts/hooks/doc-router.js`,只传 `patterns + title + docPath + summary`,不要再为每个文档问题单独建样板模块。
|
|
89
|
+
|
|
90
|
+
3. **在 daemon.yaml 加开关**(可选,默认开):
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
hooks:
|
|
94
|
+
your_name: true
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
4. **部署**:`node index.js`(同步文件到 `~/.metame/hooks/`)
|
|
98
|
+
|
|
99
|
+
5. **验证**:
|
|
100
|
+
```bash
|
|
101
|
+
echo '{"prompt":"触发词"}' | node ~/.metame/hooks/intent-engine.js
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 调试
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# 测试某个 prompt 是否触发意图
|
|
110
|
+
echo '{"prompt":"告诉工匠去做这个"}' | METAME_PROJECT=business node ~/.metame/hooks/intent-engine.js | python3 -m json.tool
|
|
111
|
+
|
|
112
|
+
# 查看当前已注册的 hooks
|
|
113
|
+
python3 -c "
|
|
114
|
+
import json
|
|
115
|
+
s = json.load(open('/Users/$(whoami)/.claude/settings.json'))
|
|
116
|
+
for k, v in s.get('hooks', {}).items():
|
|
117
|
+
print(k, [h['command'][-50:] for e in v for h in e.get('hooks',[])])
|
|
118
|
+
"
|
|
119
|
+
|
|
120
|
+
# 禁用某个意图(临时测试):daemon.yaml 改 false,立即生效
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 文件路径参考
|
|
126
|
+
|
|
127
|
+
| 文件 | 说明 |
|
|
128
|
+
|------|------|
|
|
129
|
+
| `scripts/intent-registry.js` | 共享意图注册表(Claude hook / Codex runtime 共用) |
|
|
130
|
+
| `scripts/hooks/intent-engine.js` | Claude hook adapter(源文件) |
|
|
131
|
+
| `~/.metame/hooks/intent-engine.js` | 部署副本(symlink) |
|
|
132
|
+
| `scripts/hooks/intent-*.js` | 各意图模块(源文件) |
|
|
133
|
+
| `~/.metame/daemon.yaml` | 用户配置(包含 `hooks:` 开关) |
|
|
134
|
+
| `~/.claude/settings.json` | Claude Code hook 注册表 |
|
|
@@ -59,7 +59,7 @@ feishu:
|
|
|
59
59
|
- `/stop`:引擎中性,按 `activeProcesses.killSignal` 停止
|
|
60
60
|
- `/compact`:
|
|
61
61
|
- Claude 会话:正常压缩
|
|
62
|
-
- Codex
|
|
62
|
+
- Codex 会话:返回"暂不支持,请继续同会话"
|
|
63
63
|
- `/engine`:
|
|
64
64
|
- 查询当前默认引擎:`/engine`
|
|
65
65
|
- 切换默认引擎:`/engine claude` 或 `/engine codex`
|
|
@@ -69,7 +69,7 @@ feishu:
|
|
|
69
69
|
- 也支持严格自然语言:`把蒸馏模型改成 5.1mini`
|
|
70
70
|
- `/doctor`:
|
|
71
71
|
- 同时检查 Claude/Codex CLI 可用性
|
|
72
|
-
-
|
|
72
|
+
- 仅在"当前默认引擎对应 CLI 不可用"时判为故障
|
|
73
73
|
- 自定义 provider 下允许任意合法模型名(不再强制 sonnet/opus/haiku)
|
|
74
74
|
|
|
75
75
|
## 5. Agent Soul 身份层
|
|
@@ -88,6 +88,9 @@ feishu:
|
|
|
88
88
|
- 活跃子进程:`~/.metame/active_agent_pids.json`
|
|
89
89
|
- 热重载备份:`~/.metame/.last-good/`(daemon 稳定运行 60s 后自动备份)
|
|
90
90
|
- 崩溃计数:`~/.metame/.crash-count`(连续 2 次快速崩溃触发自动恢复)
|
|
91
|
+
- Dispatch 队列:`~/.metame/dispatch/pending.jsonl`(本地 socket 降级)
|
|
92
|
+
- 远端 Dispatch 队列:`~/.metame/dispatch/remote-pending.jsonl`(跨设备中继)
|
|
93
|
+
- Dispatch 签名密钥:`~/.metame/.dispatch_secret`(自动创建)
|
|
91
94
|
|
|
92
95
|
## 7. 热重载安全机制(三层防护)
|
|
93
96
|
|
|
@@ -126,12 +129,25 @@ feishu:
|
|
|
126
129
|
2. 若仍失败,手动 `/new` 新开会话
|
|
127
130
|
3. 检查 `~/.metame/active_agent_pids.json` 是否残留异常进程
|
|
128
131
|
|
|
132
|
+
### 远端 Dispatch 失败
|
|
133
|
+
|
|
134
|
+
症状:`/dispatch to peer:project` 返回 `feishu bot not connected`
|
|
135
|
+
|
|
136
|
+
处理:
|
|
137
|
+
|
|
138
|
+
1. 确认 `daemon.yaml` 中 `feishu.remote_dispatch.enabled: true` 且 `self`/`chat_id`/`secret` 非空
|
|
139
|
+
2. 确认飞书 bot 已连接:`/doctor` 查看飞书状态
|
|
140
|
+
3. 确认 relay 群已加入 `allowed_chat_ids`(relay 群消息需被 daemon 接收)
|
|
141
|
+
4. 两端的 `secret` 必须完全一致
|
|
142
|
+
|
|
129
143
|
## 9. 双平台/双引擎维护矩阵
|
|
130
144
|
|
|
131
145
|
### 统一维护(改一处即可)
|
|
132
146
|
- agent-layer.js / daemon-agent-tools.js / daemon-agent-commands.js / daemon-user-acl.js
|
|
133
147
|
- ENGINE_MODEL_CONFIG(daemon-engine-runtime.js 集中管理)
|
|
134
148
|
- daemon-runtime-lifecycle.js 的语法检查和备份机制
|
|
149
|
+
- daemon-remote-dispatch.js(纯逻辑,无平台差异)
|
|
150
|
+
- team-dispatch.js(共享解析/hint/enrichment)
|
|
135
151
|
|
|
136
152
|
### 需分别维护(有平台/引擎特殊分支)
|
|
137
153
|
|
|
@@ -151,6 +167,25 @@ feishu:
|
|
|
151
167
|
|
|
152
168
|
一个项目可以有多个 team 成员(数字分身),共享同一个 `cwd`,通过虚拟 chatId 并行工作。
|
|
153
169
|
|
|
170
|
+
### 创建团队成员(向导)
|
|
171
|
+
|
|
172
|
+
在手机端(飞书/Telegram)发送以下任一方式触发创建向导:
|
|
173
|
+
|
|
174
|
+
- 自然语言:`创建团队`、`新建工作组`、`建个团队` 等(`_detectTeamIntent` 识别,位于 `daemon-command-router.js`)
|
|
175
|
+
- 命令:`/agent new team`
|
|
176
|
+
|
|
177
|
+
向导分三步,全部在 `daemon-agent-commands.js` 中实现:
|
|
178
|
+
1. **name**:输入团队名称
|
|
179
|
+
2. **members**:输入成员列表,格式 `名称:icon:颜色`,一行或逗号分隔多个
|
|
180
|
+
3. **cwd**:通过文件浏览器(`daemon-file-browser.js` `team-new` 模式)选择父目录
|
|
181
|
+
|
|
182
|
+
目录确认(`/agent-team-dir` 回调)后:
|
|
183
|
+
- 在 `<父目录>/team/<成员key>/` 下创建工作目录及 CLAUDE.md
|
|
184
|
+
- 自动执行 `git init`(支持 checkpoint)
|
|
185
|
+
- 若父目录对应已有项目,自动写入 `daemon.yaml` 的 `team` 数组;否则提示手动注册
|
|
186
|
+
|
|
187
|
+
中间状态保存在 `pendingTeamFlows` Map(`daemon.js` 中定义)。
|
|
188
|
+
|
|
154
189
|
### 配置
|
|
155
190
|
|
|
156
191
|
在 `~/.metame/daemon.yaml` 的项目下添加 `team` 数组和 `broadcast: true`:
|
|
@@ -169,6 +204,12 @@ feishu:
|
|
|
169
204
|
nicknames:
|
|
170
205
|
- 甲
|
|
171
206
|
auto_dispatch: true
|
|
207
|
+
- key: hunter
|
|
208
|
+
name: 猎手
|
|
209
|
+
icon: 🎯
|
|
210
|
+
peer: windows # ← 远端成员,运行在 Windows 设备上
|
|
211
|
+
nicknames:
|
|
212
|
+
- 猎手
|
|
172
213
|
```
|
|
173
214
|
|
|
174
215
|
### 路由规则(按优先级)
|
|
@@ -179,16 +220,33 @@ feishu:
|
|
|
179
220
|
4. **Sticky**:无昵称时 → 路由到上次显式指定的成员
|
|
180
221
|
5. **Auto-dispatch**:主忙时自动分配给空闲的 `auto_dispatch` 成员
|
|
181
222
|
|
|
223
|
+
**远端成员**:检测到 `member.peer` 时,bridges 自动走 `sendRemoteDispatch()` → relay 群 → 对端 daemon 接收执行,结果通过 relay 群回传。路由优先级不变,只是传输链路不同。
|
|
224
|
+
|
|
182
225
|
### /stop 精准路由
|
|
183
226
|
|
|
184
227
|
- `/stop 乙`:停止指定成员
|
|
185
228
|
- `/stop`:停止 sticky 成员
|
|
186
229
|
- 引用回复 `/stop`:停止对应成员
|
|
187
230
|
|
|
188
|
-
###
|
|
231
|
+
### /msg — 团队直接消息
|
|
232
|
+
|
|
233
|
+
格式:`/msg <agent昵称> <消息内容>`
|
|
234
|
+
|
|
235
|
+
- 例如:`/msg 乙 帮我看看这个文件`
|
|
236
|
+
- 按昵称解析目标(先查 team 成员,再查 projects)
|
|
237
|
+
- 以 `type='message', priority='normal'` 调度
|
|
238
|
+
- 实现文件:`daemon-admin-commands.js` resolveProjectKey 函数
|
|
239
|
+
|
|
240
|
+
### Team Broadcast(团队广播 = 可观察模式)
|
|
189
241
|
|
|
190
242
|
`broadcast: true` 时,team 成员之间通过 `dispatch_to` 互发消息会在群里用卡片广播。
|
|
191
243
|
|
|
244
|
+
**这就是"观察模式"**:开启 broadcast 后,你在群里可以实时看到成员之间互相传递任务的全过程(哪个成员发给了哪个成员、发了什么内容),以飞书卡片形式展示。
|
|
245
|
+
|
|
246
|
+
切换命令:`/broadcast on` / `/broadcast off`(实时生效,写入 daemon.yaml)
|
|
247
|
+
|
|
248
|
+
实现入口:`daemon.js` `_findTeamBroadcastContext()` + `handleDispatchItem()` 的广播分支。
|
|
249
|
+
|
|
192
250
|
### 虚拟 chatId
|
|
193
251
|
|
|
194
252
|
team 成员使用 `_agent_{key}` 格式的虚拟 chatId,与物理群 chatId 隔离。
|
|
@@ -197,7 +255,106 @@ team 成员使用 `_agent_{key}` 格式的虚拟 chatId,与物理群 chatId
|
|
|
197
255
|
|
|
198
256
|
由 `icon + name` 拼成,如 `🤖 Jarvis · 乙`。
|
|
199
257
|
|
|
200
|
-
## 11.
|
|
258
|
+
## 11. 跨设备 Dispatch(Remote Peer Dispatch)
|
|
259
|
+
|
|
260
|
+
### 概念
|
|
261
|
+
|
|
262
|
+
team 成员可以通过 `peer` 字段标记为"远端成员"——运行在另一台机器上的 daemon。用户在飞书群里对远端成员的操作体验与本地成员完全一致(昵称路由、sticky follow 等),底层通过飞书 relay 群实现跨设备通信。
|
|
263
|
+
|
|
264
|
+
### 配置
|
|
265
|
+
|
|
266
|
+
两台设备的 `daemon.yaml` 需要配置相同的 relay 群和共享密钥,不同的 `self` 标识;但不能共用同一个飞书 bot。每台机器都必须使用自己独立的飞书应用 / bot 凭据。
|
|
267
|
+
|
|
268
|
+
```yaml
|
|
269
|
+
# Mac 端
|
|
270
|
+
feishu:
|
|
271
|
+
remote_dispatch:
|
|
272
|
+
enabled: true
|
|
273
|
+
self: mac # 本机标识(唯一)
|
|
274
|
+
chat_id: oc_relay_xxx # 专用中继群(两端相同)
|
|
275
|
+
secret: shared-secret-key # HMAC 签名密钥(两端相同)
|
|
276
|
+
|
|
277
|
+
# Windows 端
|
|
278
|
+
feishu:
|
|
279
|
+
remote_dispatch:
|
|
280
|
+
enabled: true
|
|
281
|
+
self: windows
|
|
282
|
+
chat_id: oc_relay_xxx # 同一个 relay 群
|
|
283
|
+
secret: shared-secret-key # 同一个密钥
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
注意:
|
|
287
|
+
|
|
288
|
+
- 可以共用同一个 relay 群。
|
|
289
|
+
- 可以共用同一个 `secret`。
|
|
290
|
+
- 不能共用同一个飞书 bot / `app_id` / `app_secret`。
|
|
291
|
+
- 原因是飞书对同一 bot 的事件投递可能随机落到任一在线客户端;而当前代码在收到 `to_peer !== self` 的 relay 包时会直接忽略,错误机器会把包吞掉。
|
|
292
|
+
|
|
293
|
+
team 成员添加 `peer` 字段指向远端设备:
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
projects:
|
|
297
|
+
business:
|
|
298
|
+
team:
|
|
299
|
+
- key: writer
|
|
300
|
+
name: 编剧 # 无 peer → 本地执行
|
|
301
|
+
- key: hunter
|
|
302
|
+
name: 猎手
|
|
303
|
+
peer: windows # 远端设备
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### 数据流
|
|
307
|
+
|
|
308
|
+
#### 用户消息 → 远端 team member
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
飞书群消息 "猎手 去调研竞品"
|
|
312
|
+
→ bridges.js findTeamMember → { member: { key:'hunter', peer:'windows' } }
|
|
313
|
+
→ _dispatchToTeamMember 检测 member.peer
|
|
314
|
+
→ sendRemoteDispatch → encodePacket + HMAC 签名 → relay 群
|
|
315
|
+
→ Windows daemon bridges 拦截 → decodePacket + verifyPacket + isDuplicate
|
|
316
|
+
→ handleDispatchItem(local) → Claude 执行
|
|
317
|
+
→ _replyFn → encode result → relay 群
|
|
318
|
+
→ Mac daemon 拦截 → decode → sendMarkdown 到用户飞书群
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### Claude session 内跨设备 dispatch
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
Claude 看到 hook 注入:
|
|
325
|
+
- hunter(猎手 [远端:windows]): `dispatch_to --from writer windows:hunter "消息"`
|
|
326
|
+
→ dispatch_to 解析 peer:project 格式
|
|
327
|
+
→ 写 remote-pending.jsonl → daemon heartbeat drain → bot 发 relay 群
|
|
328
|
+
→ 对端 daemon 接收执行
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Packet 协议
|
|
332
|
+
|
|
333
|
+
- 前缀:`[METAME_REMOTE_DISPATCH]`
|
|
334
|
+
- 编码:Base64(JSON)
|
|
335
|
+
- 签名:HMAC-SHA256,payload = packet body 去掉 sig 字段
|
|
336
|
+
- 去重:5 分钟 TTL Map,按 packet.id 去重
|
|
337
|
+
|
|
338
|
+
### 关键模块
|
|
339
|
+
|
|
340
|
+
| 模块 | 职责 |
|
|
341
|
+
|------|------|
|
|
342
|
+
| `daemon-remote-dispatch.js` | 编解码、签名验签、去重、配置解析、`parseRemoteTargetRef` |
|
|
343
|
+
| `daemon.js:sendRemoteDispatch()` | 构造签名 packet → 通过飞书 bot 发 relay 群 |
|
|
344
|
+
| `daemon.js:handleRemoteDispatchMessage()` | 接收端:decode → verify → dedup → 执行或投递结果 |
|
|
345
|
+
| `daemon-bridges.js` | Feishu bridge 拦截 relay 群消息 + `_dispatchToTeamMember` 远端分流 |
|
|
346
|
+
| `daemon-admin-commands.js` | `/dispatch peers` 查看配置 + `/dispatch to peer:project` 手动派发 |
|
|
347
|
+
| `scripts/bin/dispatch_to` | 支持 `peer:project` 格式 → 写 `remote-pending.jsonl` |
|
|
348
|
+
| `team-dispatch.js` | `buildTeamRosterHint()` 为远端成员生成 `peer:key` 格式命令 |
|
|
349
|
+
| `hooks/team-context.js` | intent hook 注入远端 `peer:key` dispatch 命令 |
|
|
350
|
+
|
|
351
|
+
### 管理命令
|
|
352
|
+
|
|
353
|
+
- `/dispatch peers`:查看远端配置(self peer、relay chat、所有远端成员列表)
|
|
354
|
+
- `/dispatch to windows:hunter <任务>`:手动跨设备派发
|
|
355
|
+
- `/dispatch to 猎手 <任务>`:按昵称解析,自动检测 `member.peer` 走远端
|
|
356
|
+
|
|
357
|
+
## 12. 私人配置保护
|
|
201
358
|
|
|
202
359
|
- `daemon.yaml` 是用户私人配置,包含 API keys、chat IDs、个人项目配置
|
|
203
360
|
- **绝不上传**到代码仓库,已加入 `.gitignore`
|
|
@@ -206,7 +363,7 @@ team 成员使用 `_agent_{key}` 格式的虚拟 chatId,与物理群 chatId
|
|
|
206
363
|
- 同样不应上传的文件:`MEMORY.md`、`SOUL.md`、`.env*`
|
|
207
364
|
- Agent 在执行任务时,**绝不能** `cp scripts/daemon.yaml ~/.metame/daemon.yaml`,这会覆盖用户私人配置
|
|
208
365
|
|
|
209
|
-
##
|
|
366
|
+
## 13. 变更后维护动作
|
|
210
367
|
|
|
211
368
|
1. `npm test`
|
|
212
369
|
2. `npm run sync:plugin`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MetaMe 脚本/文档指针地图
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 目的:回答"这段能力在哪个文件""当前升级做到哪一步""先看哪个脚本"。
|
|
4
4
|
|
|
5
5
|
## 快速入口
|
|
6
6
|
|
|
@@ -41,22 +41,71 @@
|
|
|
41
41
|
- Agent 命令处理(新):
|
|
42
42
|
- `scripts/daemon-agent-commands.js`
|
|
43
43
|
- 关键点:`createAgentCommandHandler()` 处理 `/agent`、`/activate`、`/resume`;
|
|
44
|
-
`/agent soul [repair|edit]`;`pendingActivations` 无 TTL
|
|
44
|
+
`/agent soul [repair|edit]`;`pendingActivations` 无 TTL(消费即删);防止创建群自激活;
|
|
45
|
+
`/agent new team` 三步向导(name → members → cwd);
|
|
46
|
+
`/agent-team-dir` 回调处理目录选择并最终写入 daemon.yaml `team` 段;
|
|
47
|
+
`pendingTeamFlows` Map 维护向导中间状态
|
|
45
48
|
|
|
46
49
|
- 路由与 Agent 创建:
|
|
47
50
|
- `scripts/daemon-command-router.js`
|
|
48
51
|
- `scripts/daemon-agent-tools.js`
|
|
49
52
|
- 关键点:自然语言提取 `codex` 关键词;默认 `claude` 不写 `engine` 字段,仅 `codex` 持久化 `engine: codex`;
|
|
50
|
-
`bindAgentToChat()` 自动调用 `ensureAgentMetadata()` 建立 soul
|
|
53
|
+
`bindAgentToChat()` 自动调用 `ensureAgentMetadata()` 建立 soul 层;
|
|
54
|
+
`_detectTeamIntent()` 自然语言意图识别(含负样本过滤),识别"建团队"意图后自动路由到 `/agent new team` 向导
|
|
51
55
|
|
|
52
56
|
- 会话命令与兼容边界:
|
|
53
57
|
- `scripts/daemon-exec-commands.js`
|
|
54
|
-
- 关键点:`/stop` 引擎中性;`/compact` 在 codex
|
|
58
|
+
- 关键点:`/stop` 引擎中性;`/compact` 在 codex 会话返回"暂不支持"
|
|
55
59
|
|
|
56
60
|
- 运行时引擎切换与诊断:
|
|
57
61
|
- `scripts/daemon-admin-commands.js`
|
|
58
62
|
- 关键点:`/engine` 切换默认引擎;`/doctor` 按默认引擎检查 CLI 可用性(Claude/Codex)并兼容自定义 provider 模型名
|
|
59
63
|
|
|
64
|
+
## 团队 Dispatch 与跨设备通信定位
|
|
65
|
+
|
|
66
|
+
- 共享 Dispatch 工具:
|
|
67
|
+
- `scripts/team-dispatch.js`
|
|
68
|
+
- 关键点:`resolveProjectKey()` 名称/昵称解析(含 team member `parent/member` 复合键);
|
|
69
|
+
`findTeamMember()` 文本前缀匹配团队成员昵称;
|
|
70
|
+
`buildTeamRosterHint()` 生成团队上下文块(远端成员自动带 `peer:key` 前缀);
|
|
71
|
+
`buildEnrichedPrompt()` 注入共享上下文(now.md + _latest.md + inbox)
|
|
72
|
+
|
|
73
|
+
- 远端 Dispatch 协议:
|
|
74
|
+
- `scripts/daemon-remote-dispatch.js`
|
|
75
|
+
- 关键点:`normalizeRemoteDispatchConfig()` 解析 `feishu.remote_dispatch` 配置;
|
|
76
|
+
`parseRemoteTargetRef()` 解析 `peer:project` 格式;
|
|
77
|
+
`encodePacket()`/`decodePacket()` Base64 + HMAC-SHA256 编解码;
|
|
78
|
+
`verifyPacket()` 签名验证;
|
|
79
|
+
`isDuplicate()` 5 分钟 TTL 去重;
|
|
80
|
+
`isRemoteMember()` 检测 `member.peer` 字段
|
|
81
|
+
|
|
82
|
+
- Daemon 远端 Dispatch 入口:
|
|
83
|
+
- `scripts/daemon.js`
|
|
84
|
+
- 关键点:`sendRemoteDispatch()` 构造签名 packet → 飞书 bot 发 relay 群;
|
|
85
|
+
`handleRemoteDispatchMessage()` 接收端逻辑(decode → verify → dedup → 按 type 路由);
|
|
86
|
+
`remote-pending.jsonl` drain(heartbeat 中处理 dispatch_to CLI 写入的远端队列)
|
|
87
|
+
|
|
88
|
+
- Bridge 集成:
|
|
89
|
+
- `scripts/daemon-bridges.js`
|
|
90
|
+
- 关键点:Feishu bridge `startReceiving` 回调最前面拦截 relay 群消息 → `handleRemoteDispatchMessage`;
|
|
91
|
+
`_dispatchToTeamMember` 检测 `isRemoteMember(member)` → 走 `sendRemoteDispatch` 而非本地 handleCommand
|
|
92
|
+
|
|
93
|
+
- Dispatch CLI:
|
|
94
|
+
- `scripts/bin/dispatch_to`
|
|
95
|
+
- 关键点:支持 `peer:project` 格式 → `sendRemoteViaRelay()`;
|
|
96
|
+
`--team` broadcast 自动分流远端成员写 `remote-pending.jsonl`;
|
|
97
|
+
本地走 Unix socket / `pending.jsonl` 降级
|
|
98
|
+
|
|
99
|
+
- 管理命令:
|
|
100
|
+
- `scripts/daemon-admin-commands.js`
|
|
101
|
+
- 关键点:`/dispatch peers` 查看远端配置;
|
|
102
|
+
`/dispatch to peer:project` 手动远端派发;
|
|
103
|
+
按昵称解析到远端 member 时自动走 `sendRemoteDispatch`
|
|
104
|
+
|
|
105
|
+
- Intent Hook:
|
|
106
|
+
- `scripts/hooks/team-context.js`
|
|
107
|
+
- 关键点:检测通信意图 → 注入 dispatch_to 命令提示;远端成员自动带 `peer:key` 前缀
|
|
108
|
+
|
|
60
109
|
## Mentor Mode(Step 1-4)定位
|
|
61
110
|
|
|
62
111
|
- Step 1 数据基建:
|
|
@@ -90,6 +139,11 @@
|
|
|
90
139
|
- 夜间反思文档:`~/.metame/memory/decisions/`、`~/.metame/memory/lessons/`
|
|
91
140
|
- 知识胶囊:`~/.metame/memory/capsules/`
|
|
92
141
|
- 复盘文档:`~/.metame/memory/postmortems/`
|
|
142
|
+
- Dispatch 队列:`~/.metame/dispatch/pending.jsonl`(本地 socket 降级)
|
|
143
|
+
- 远端 Dispatch 队列:`~/.metame/dispatch/remote-pending.jsonl`(跨设备中继)
|
|
144
|
+
- 共享进度白板:`~/.metame/memory/now/shared.md`
|
|
145
|
+
- Agent 最新产出:`~/.metame/memory/agents/{key}_latest.md`
|
|
146
|
+
- Agent 收件箱:`~/.metame/memory/inbox/{key}/`(未读),`read/`(已归档)
|
|
93
147
|
- **Agent Soul 层**:`~/.metame/agents/<agent_id>/`
|
|
94
148
|
- `agent.yaml` — id / name / engine / aliases
|
|
95
149
|
- `soul.md` — 身份定义(主文件,项目目录的 SOUL.md 是其链接)
|
|
@@ -102,7 +156,8 @@
|
|
|
102
156
|
1. 先看配置:`~/.metame/daemon.yaml` 与 `scripts/daemon-default.yaml`
|
|
103
157
|
2. 再看命令入口:`scripts/daemon-admin-commands.js`、`scripts/daemon-command-router.js`、`scripts/daemon-exec-commands.js`
|
|
104
158
|
3. 再看执行链路:`scripts/daemon-engine-runtime.js` → `scripts/daemon-claude-engine.js` → `scripts/mentor-engine.js`
|
|
105
|
-
4.
|
|
159
|
+
4. 团队/跨设备:`scripts/team-dispatch.js` → `scripts/daemon-remote-dispatch.js` → `scripts/daemon-bridges.js`
|
|
160
|
+
5. 最后看离线任务:`scripts/distill.js`、`scripts/memory-extract.js`、`scripts/memory-nightly-reflect.js`
|
|
106
161
|
|
|
107
162
|
## 同步提示
|
|
108
163
|
|
|
@@ -298,16 +298,8 @@ function createBot(config) {
|
|
|
298
298
|
throw new Error(`File not found: ${filePath}`);
|
|
299
299
|
}
|
|
300
300
|
const fileName = path.basename(filePath);
|
|
301
|
-
const fileSize = fs.statSync(filePath).size;
|
|
302
|
-
|
|
303
|
-
// For text files under 4KB, just send as text
|
|
304
301
|
const ext = path.extname(filePath).toLowerCase();
|
|
305
302
|
const isText = ['.md', '.txt', '.json', '.yaml', '.yml', '.csv'].includes(ext);
|
|
306
|
-
if (isText && fileSize < 4096) {
|
|
307
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
308
|
-
await this.sendMessage(chatId, `📄 ${fileName}:\n\`\`\`\n${content}\n\`\`\``);
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
303
|
|
|
312
304
|
// For larger/binary files, try file upload
|
|
313
305
|
try {
|
|
@@ -332,7 +324,7 @@ function createBot(config) {
|
|
|
332
324
|
}
|
|
333
325
|
|
|
334
326
|
// 2. Send file message
|
|
335
|
-
await client.im.message.create({
|
|
327
|
+
const sendRes = await client.im.message.create({
|
|
336
328
|
params: { receive_id_type: 'chat_id' },
|
|
337
329
|
data: {
|
|
338
330
|
receive_id: chatId,
|
|
@@ -340,6 +332,9 @@ function createBot(config) {
|
|
|
340
332
|
content: JSON.stringify({ file_key: fileKey }),
|
|
341
333
|
},
|
|
342
334
|
});
|
|
335
|
+
const msgId = sendRes?.data?.message_id;
|
|
336
|
+
if (caption) await this.sendMessage(chatId, caption);
|
|
337
|
+
return msgId ? { message_id: msgId } : null;
|
|
343
338
|
} catch (uploadErr) {
|
|
344
339
|
// Log detailed error
|
|
345
340
|
const errDetail = uploadErr.response?.data || uploadErr.message || uploadErr;
|
|
@@ -349,18 +344,15 @@ function createBot(config) {
|
|
|
349
344
|
if (isText) {
|
|
350
345
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
351
346
|
const truncated = content.length > 3000 ? content.slice(0, 3000) + '\n...(truncated)' : content;
|
|
352
|
-
await this.sendMessage(chatId, `📄 ${fileName}:\n\`\`\`\n${truncated}\n\`\`\``);
|
|
347
|
+
const textMsg = await this.sendMessage(chatId, `📄 ${fileName}:\n\`\`\`\n${truncated}\n\`\`\``);
|
|
348
|
+
if (caption) await this.sendMessage(chatId, caption);
|
|
349
|
+
return textMsg || null;
|
|
353
350
|
} else {
|
|
354
351
|
// For binary files, give more helpful error
|
|
355
352
|
const errMsg = errDetail?.msg || errDetail?.message || '上传失败';
|
|
356
353
|
throw new Error(`${errMsg} (请检查飞书应用权限: im:resource)`);
|
|
357
354
|
}
|
|
358
355
|
}
|
|
359
|
-
|
|
360
|
-
// 3. Send caption as separate message if provided
|
|
361
|
-
if (caption) {
|
|
362
|
-
await this.sendMessage(chatId, caption);
|
|
363
|
-
}
|
|
364
356
|
},
|
|
365
357
|
|
|
366
358
|
/**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build a unified doc-routing hint for intent modules.
|
|
5
|
+
*
|
|
6
|
+
* @param {object} route
|
|
7
|
+
* @param {RegExp[]} route.patterns
|
|
8
|
+
* @param {string} route.title
|
|
9
|
+
* @param {string} route.docPath
|
|
10
|
+
* @param {string} route.summary
|
|
11
|
+
* @returns {(prompt: string) => string|null}
|
|
12
|
+
*/
|
|
13
|
+
function createDocRoute(route) {
|
|
14
|
+
const patterns = Array.isArray(route.patterns) ? route.patterns : [];
|
|
15
|
+
const title = String(route.title || '').trim();
|
|
16
|
+
const docPath = String(route.docPath || '').trim();
|
|
17
|
+
const summary = String(route.summary || '').trim();
|
|
18
|
+
|
|
19
|
+
if (!patterns.length || !title || !docPath || !summary) {
|
|
20
|
+
throw new Error('doc-router requires patterns, title, docPath, and summary');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return function detectDocRoute(prompt) {
|
|
24
|
+
if (!patterns.some((re) => re.test(prompt))) return null;
|
|
25
|
+
return `[${title}]\n- ${summary} → 先 \`cat ${docPath}\``;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { createDocRoute };
|