~/.botmux/bots.json 配置机器人:文件是数组,每个元素一个 bot(生产环境一个 bot 对应一个独立 daemon 进程)。用 botmux setup 交互式创建,或手动编辑 / 用 dashboard 的 Bot 配置页(改的是同一份文件)。多数字段可选:只填 larkAppId / larkAppSecret 就能跑,其余按需增配;改完 botmux restart 生效。
配置优先级:BOTS_CONFIG 环境变量 → ~/.botmux/bots.json(BOTS_CONFIG 见 env)。
必填
| 字段 | 说明 |
|---|
larkAppId | 飞书应用 App ID |
larkAppSecret | 飞书应用 App Secret |
CLI 与模型
| 字段 | 说明 |
|---|
name | 进程名后缀,如 claude-main → botmux-claude-main;留空默认 botmux-<序号> |
cliId | CLI 适配器,默认 claude-code,见 adapters |
model | 启动 CLI 用的模型名(如 claude --model opus);留空走 CLI 默认。同一 cliId 的多个 bot 可跑不同模型;modelChoices 是 setup 给出的候选 |
cliRuntime | Codex 兼容发行版的结构化运行时:{ id, displayName?, executable, update? },复用 codex 适配器,见下文 |
cliPathOverride | 旧版 CLI 入口覆盖,兼容 wrapper / router 与存量自定义二进制。写入端会同时保存与 cliRuntime.executable 完全相同的兼容影子,勿手工配置不一致的两者 |
disableCliBypass | true 时不自动追加免审批 / 沙盒绕过参数(--yolo、--dangerously-*);缺省保持原行为 |
backendType | 会话后端 pty / tmux / herdr / zellij。留空默认 tmux(PTY 已退役自动回落);tmux/herdr/zellij 本机不可用时硬拦截弹卡提示安装,不再静默降级 pty(zellij 需 ≥ 0.44)。pty 仅作显式兜底,直连进程、不跨 daemon 重启存活。见 tmux |
launchShell | 启动 CLI 的 shell,覆盖 daemon 的 $SHELL:shell 名或绝对路径。用于登录 shell 的 rc 里有 exec zsh 类跳转顶掉 CLI 的场景。PATH / nvm / pnpm 要放进所选 shell 的 rc。下个会话生效,仅 tmux / zellij 后端;也可在 dashboard 或 /config launchShell 配置 |
lang | 该 bot 界面语言 zh / en;留空回落 BOTMUX_LANG / LANG |
customPassthroughCommands | 在固定白名单与 adapter 默认放行之外,额外放行透传给 CLI 的 slash 命令(如 ["/export"];Claude Code / Codex 的 /goal 已默认放行)。自动归一化;遮蔽 daemon 命令(如 /status)的项被丢弃。用 /list-slash-command 查完整清单,见 slash-commands |
env | 该 bot 进程环境变量 { "KEY": "值" }。按会话注入(下个新会话生效),不写共享 tmux server 全局、不会串到别的 bot;BOTMUX_ / LARK_APP_ 保留键被忽略。常见用途:per-bot 接 GLM / 第三方服务商,见下文 |
codexAppCleanInput | 实验性,仅对实际运行 codex-app 的托管 session 生效。true 时可见 UserMessage 只保留用户原始输入,上下文改走 additionalContext;默认关闭,从下一次 turn 派发生效,见下文 |
Codex 兼容发行版
严格兼容 Codex 的独立发行版不需要新 cliId:保留 cliId: "codex",再声明运行时:
{
"cliId": "codex",
"cliPathOverride": "vendor-codex",
"cliRuntime": {
"id": "vendor-codex",
"displayName": "Vendor Codex",
"executable": "vendor-codex",
"update": { "provider": "npm", "packageName": "@vendor/codex" }
}
}
id 是稳定身份,仅限字母、数字、.、_、-,最长 64 字符;改名视为切换发行版。
executable 是可执行文件名或路径,不是 shell 命令(勿拼参数)。Dashboard 保存时执行只读 --version 预检,输出需含可识别的 X.Y.Z 版本号。
displayName 只影响展示,省略时用 id。
update.provider 可选 auto / self / npm / none:auto 只信任可精确追溯到该二进制的唯一 npm 包,无法确定来源时标记「未托管」,绝不拿官方 Codex 版本号比较;self 使用 CLI 自报的 doctor 信息且要求与 --version 一致;npm 必须同时给 packageName;none 关闭更新检查。
- 仅支持
cliId: "codex",不能与 wrapperCli 同用。必须同时写入与 executable 等值的 cliPathOverride 降级影子,缺失或不等会校验失败(保证可安全降级)。
- 旧
cliPathOverride 配置不失效,更新探测采用同样的安全 auto 策略;Dashboard 显示为只读兼容态。由于无法证明完整兼容契约,Codex RPC 等增强能力保持关闭。
- 会话创建时冻结 runtime 快照。只改模型仅影响新会话;切换 CLI / runtime / wrapper 时立即关闭仍用旧启动身份的活跃会话,避免 lazy resume 到错误发行版。
接入 GLM / 第三方服务商(per-bot env)
{
"cliId": "claude-code",
"env": {
"ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
"ANTHROPIC_AUTH_TOKEN": "你的 GLM Coding Plan key"
}
}
- GLM 国内站用
https://open.bigmodel.cn/api/anthropic;Codex 这类 OpenAI 协议 CLI 填 OPENAI_BASE_URL / OPENAI_API_KEY。
- env 按会话注入(全后端一致,tmux / zellij 经每个 pane 注入),bot 间不串。
- 值以明文存于 bots.json 与进程环境;
/config get 等聊天面脱敏显示(dashboard owner 鉴权后显示原值)。改完下个新会话生效。
Codex App 纯净输入(实验性)
codexAppCleanInput 清理 Codex App 显示的用户消息,同时保留调用模型所需上下文。可由 owner / allowedUsers 用 /botconfig set codexAppCleanInput on|off 热更新(无需重启),或直接写 bots.json。要点:
- 仅 Botmux 托管且实际 CLI 为
codex-app 时生效;session 已冻结的 CLI 优先于后来修改的 bot 默认 CLI;/adopt 外部桥接 session 不受影响。
- 开启后用户 turn 以原文作为
UserMessage;sender、mentions、附件、引用、whiteboard、Skills 等上下文走隐藏的 additionalContext;可读绝对路径图片作为 localImage 输入。
- 需可识别的 Codex CLI
>= 0.135(纯净文本 + additionalContext),>= 0.136 附带 clientUserMessageId;版本过旧回退 legacy 组合 prompt。
- 仅当 app-server 在
turn/started 前明确拒绝实验字段时才用 legacy prompt 重试一次并关闭纯净模式;一般错误不自动重试。切换在下一次派发时采样,不回填历史。
additionalContext 不进消息气泡但可能留在 rollout / 诊断记录;botmux 同时保留 legacy prompt 与 sidecar 以支持降级与 retry_last_task。此功能只为展示整洁,不是隐私擦除机制。
工作目录
| 字段 | 说明 |
|---|
workingDir | 默认工作目录,支持逗号分隔多个。从该目录向下递归找 git 仓库(最多 3 层),不向上扫 |
workingDirs | 数组写法(["~/a", "~/b"]);显式配置时优先于逗号分隔形式 |
defaultWorkingDir | 单仓库默认目录:无 oncall / 无同群兄弟 session 时直接进入,跳过 repo 选择卡片;/cd 仍可切换。纯运行时回落 |
权限与授权
| 字段 | 说明 |
|---|
allowedUsers | 操作权名单(完整邮箱或 ou_xxx)。配了 allowedChatGroups 时至少要有一个作为 owner |
allowedChatGroups | 可对话群(oc_xxx):群内任何成员可对话(仅 canTalk),敏感操作仍由 allowedUsers 控制 |
p2pOpen | true 允许应用可用范围内任何用户私聊(仅 canTalk);群聊不受影响。建议始终配至少一个 owner |
oncallChats | oncall 绑定 [{ "chatId": "oc_xxx", "workingDir": "~/projects/foo" }],见 oncall |
defaultOncall | 新群聊首条新话题自动绑定 oncall:{ "enabled": true, "workingDir": "~/foo", "since": <epoch ms> };since 之前的老群不受影响 |
globalGrants | 全局可对话名单(ou_xxx,人或 bot),任意群可对话,仅 canTalk |
chatGrants | 按群 per-user 授权 { "oc_xxx": ["ou_yyy"] },仅 canTalk;一般由 /grant 卡片写入 |
messageQuota | { "defaultLimit": N }:新授权卡与 Oncall 使用 N 条额度;未配时新授权卡默认 3 条、Oncall 不设额度。显式 /grant @用户 N 始终用 N。仅约束 talk 授权 |
restrictGrantCommands | true 时仅靠 per-user 授权放行的人禁用所有斜杠命令;owner / oncall / 整群成员不受影响。默认 false |
autoGrantRequestCards | 默认开启;显式 false 时未授权者 @ 本 bot 被挡时不再给 owner 发 /grant 申请卡,静默丢弃 |
文件沙盒
| 字段 | 说明 |
|---|
sandbox | true 时新会话在 Linux 文件沙盒中启动,写入被隔离,需 /land 审阅落盘,见 sandbox |
sandboxHidePaths | 沙盒内用空目录 / 空文件遮罩的路径(如 ["~/.ssh", "~/.botmux/bots.json"]) |
sandboxReadonlyPaths | 沙盒内额外只读挂载的已存在路径(共享源码快照、参考仓库等) |
sandboxNetwork | 缺省 / true 保留网络与代理;false 加 --unshare-net 阻断普通网络出口 |
ZMX 后端无法执行文件沙盒或读隔离,开启这些组合会 fail closed,见 zmx。
卡片与终端
| 字段 | 说明 |
|---|
brandLabel | 卡片底部品牌文案:undefined=默认 botmux 链接;""=隐藏;其它原样渲染(支持 markdown)。纯样式 |
showUsageInCardFooter | 缺省 / true 卡片页脚展示 Context / Token 用量;false 同时隐藏两项。仅控制展示,不停止统计 |
disableStreamingCard | true 时不发实时流式卡片(web 终端仍跑、最终答复仍经 botmux send 到达) |
silentTurnReactions | true 时无卡片会话不再给触发消息加 GoGoGo / DONE reaction;默认 false |
receivedReactionEmoji / doneReactionEmoji | 无卡片会话「已收到」/「已完成」reaction 的飞书 emoji_type;默认 GoGoGo / DONE。设成相同值可避免 idle 提前判定时的误导性 ✅(如 Pi) |
writableTerminalLinkInCard | true 时卡片正文直接内嵌可写终端链接(带 token);默认藏在「获取写权限」按钮后私发 |
privateCard | true 时 /card 走 ephemeral 私有卡片,仅 allowedUsers 可见;仅普通群有效、不能 live 更新 |
主动开工
| 字段 | 说明 |
|---|
autoStartOnGroupJoin | true 时被拉入含至少一名 allowedUsers 的新群即自动开工(不必 @)。需订阅飞书 im.chat.member.bot.added_v1 事件 |
autoStartOnGroupJoinPrompt | 上面的首轮 prompt;留空则空消息开场让 bot 自读群上下文 |
autoStartOnNewTopic | true 时话题群每个新话题首条消息无需 @ 也自动开工(普通群无效);默认被动 |
总结命令
| 字段 | 说明 |
|---|
summaryRange | @机器人 /summary 的历史读取范围:limit 普通群最近 N 条(默认 50)、sinceHours 最近 N 小时(默认 24);0 表示该维度不限制。话题群始终读当前话题历史再按窗口过滤 |
summaryMemory | 默认 false。开启后 /summary 把总结整理成中文「问题解决记录」追加写入记忆文件,并向后续会话注入 <summary_memory> 复用提示(关键条件完全一致才复用历史结论) |
summaryMemoryPath | 记忆文件路径,默认 summary.md;相对路径按当前项目根解析。仅 summaryMemory 开启时生效 |
要点:只有显式 @机器人 /summary 触发(无关键词自动唤醒);存在上一条同 bot /summary 时窗口只含其后到本次的消息;仅当 summaryMemory 开启时 /summary 后的文字是「硬边界」(定位最近一条包含该文字的消息,找不到不回退,直接报错给 agent),关闭时仅为侧重提示;显式边界按设计优先于 limit / sinceHours 上限。bot 开 sandbox 且记忆文件在工作目录之外时,需把已存在的父目录加进 sandboxPaths.readWrite(worker spawn 时过滤不存在路径,新文件本身会被丢弃)。dashboard 的「/summary 总结范围 / 开启记忆 / 记忆文件路径」分别保存这三个字段。
其它字段
| 字段 | 说明 |
|---|
contentTriggers | Legacy / 不再生效。旧版关键词 / 正则免 @ 触发,当前路由不再据此唤醒;保留解析仅为兼容(旧 dashboard 配置 dashboard-default-summary-trigger 会迁移为 summaryRange 兜底)。新配置用 summaryRange |
voice | 该 bot 语音引擎覆盖,按字段合并到 ~/.botmux/config.json 全局 voice 块之上(per-bot 优先)。有凭据时回复卡片出现「🔊 语音总结」按钮,见 voice |
会议监听角色:vcMeetingAgent.meetingConsumer.consumerProfiles 定义会议监听角色(Dashboard 提供内置模板:重要信息同步 / 纪要与行动项 / 主持 / 方案评审 / 访谈洞察,点击使用即复制为可编辑 profile)。responseMode 与 listenerDelivery.placement 是两个独立维度:
responseMode: "silent" 自动输出不可见;"listener_thread" 发到会议监听群(需 listener.output.request capability)。
placement:auto(沿用当前会话路由,省略等同)/ chat(每次群顶层消息)/ topic(首条开固定话题、后续回复同一话题)。
listener_thread 用内部 skip | publish 控制协议:Agent 判断是否值得发布,格式异常 fail closed;不做去重 / debounce。agentAppId 是执行该角色的 bot;把 profile id 加入 defaultConsumerIds 且 defaultMode: "agents" 可默认启用。详见 vc-agent。
运行时状态(自动维护,勿手改)
| 字段 | 说明 |
|---|
defaultOncallAutoboundChats | defaultOncall 已自动绑过的 chat_id(append-only),解绑后也不会再自动绑 |
quotaState | scope 级消息额度计数 { "chat:<cid>:<oid>" | "global:<oid>": { limit, used } };用满自动收回授权 |
noCardChats | /card off|on 写入的「该群不发流式卡片」名单 |