~/.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-<序号>
cliIdCLI 适配器,默认 claude-code,见 adapters
model启动 CLI 用的模型名(如 claude --model opus);留空走 CLI 默认。同一 cliId 的多个 bot 可跑不同模型;modelChoices 是 setup 给出的候选
cliRuntimeCodex 兼容发行版的结构化运行时:{ id, displayName?, executable, update? },复用 codex 适配器,见下文
cliPathOverride旧版 CLI 入口覆盖,兼容 wrapper / router 与存量自定义二进制。写入端会同时保存与 cliRuntime.executable 完全相同的兼容影子,勿手工配置不一致的两者
disableCliBypasstrue 时不自动追加免审批 / 沙盒绕过参数(--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 控制
p2pOpentrue 允许应用可用范围内任何用户私聊(仅 canTalk);群聊不受影响。建议始终配至少一个 owner
oncallChatsoncall 绑定 [{ "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 授权
restrictGrantCommandstrue 时仅靠 per-user 授权放行的人禁用所有斜杠命令;owner / oncall / 整群成员不受影响。默认 false
autoGrantRequestCards默认开启;显式 false 时未授权者 @ 本 bot 被挡时不再给 owner 发 /grant 申请卡,静默丢弃

文件沙盒

字段说明
sandboxtrue 时新会话在 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 同时隐藏两项。仅控制展示,不停止统计
disableStreamingCardtrue 时不发实时流式卡片(web 终端仍跑、最终答复仍经 botmux send 到达)
silentTurnReactionstrue 时无卡片会话不再给触发消息加 GoGoGo / DONE reaction;默认 false
receivedReactionEmoji / doneReactionEmoji无卡片会话「已收到」/「已完成」reaction 的飞书 emoji_type;默认 GoGoGo / DONE。设成相同值可避免 idle 提前判定时的误导性 ✅(如 Pi)
writableTerminalLinkInCardtrue 时卡片正文直接内嵌可写终端链接(带 token);默认藏在「获取写权限」按钮后私发
privateCardtrue 时 /card 走 ephemeral 私有卡片,仅 allowedUsers 可见;仅普通群有效、不能 live 更新

主动开工

字段说明
autoStartOnGroupJointrue 时被拉入含至少一名 allowedUsers 的新群即自动开工(不必 @)。需订阅飞书 im.chat.member.bot.added_v1 事件
autoStartOnGroupJoinPrompt上面的首轮 prompt;留空则空消息开场让 bot 自读群上下文
autoStartOnNewTopictrue 时话题群每个新话题首条消息无需 @ 也自动开工(普通群无效);默认被动

总结命令

字段说明
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 总结范围 / 开启记忆 / 记忆文件路径」分别保存这三个字段。

其它字段

字段说明
contentTriggersLegacy / 不再生效。旧版关键词 / 正则免 @ 触发,当前路由不再据此唤醒;保留解析仅为兼容(旧 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。

运行时状态(自动维护,勿手改)

字段说明
defaultOncallAutoboundChatsdefaultOncall 已自动绑过的 chat_id(append-only),解绑后也不会再自动绑
quotaStatescope 级消息额度计数 { "chat:<cid>:<oid>" | "global:<oid>": { limit, used } };用满自动收回授权
noCardChats/card off|on 写入的「该群不发流式卡片」名单