机器人命令和界面

如何从 Telegram 控制 aipager。有四种输入方式:斜杠命令、键盘按钮、Mini App 控制面板,以及自由消息(文字、文件、语音消息)。

GitHub 上的源文件 ↗本页是英文页面的译文;两者不一致时,以英文页面为准。 英文

机器人只接受来自配置过的聊天的输入;关于信任边界,见安全模型。

斜杠命令#

这些命令注册在 aipager/bot/lifecycle.py 里。Telegram 的命令菜单会显示它们,并在 aipager 启动时以及每次会话变化时刷新。

命令参数作用
/start-主页:这个聊天的会话、普通消息会发到哪里(✍️ 消息会发到 x1。),以及 🆕 新建会话、⚙️ 设置,当这个聊天有可以恢复的已结束会话时还有 ↩️ 恢复。同时显示固定键盘。
/help-一份按任务编排的简短指南(启动、对话、控制、管理、设置,对管理员还有 /update),附一条提示:点按命令会立即发送它,长按(手机)或按 Tab(电脑)可以先补充文字。对安装的所有者(在这个安装自己与机器人的私聊里的人),它还带有 🐞 报告问题 按钮:它会在那个私聊里打开问题报告的确切预览,带有发送、添加备注和取消。在你点按“发送”之前,什么都不会发送(一份报告包含什么)。
/app-打开 Mini App 控制面板(会话、diff 查看器、设置)。
/status-这个聊天的会话,显示为一个你可以操作的列表。每个会话用文字说明自己的状态(⚙️ 工作中 3:12 · Bash: run tests、⏳ 等你回复 · Bash: make deploy、💤 就绪),普通消息会发往的那个会话带有 ✍️,第二行是模型、上下文、费用、排队的内容(队列 3(1 条在队列中,2 条备注)),以及正在运行的后台代理和后台命令。每个会话有一行按钮:✍️ x1 把你之后的消息发到那里,工作中时有 ⏹ 停止,等你回复时有 回答,其余的在 ⋮ 里。下面是:🆕 新建,以及已结束会话的 ⚫ 已结束(n)(恢复、删除,或者从列表里全部清除;它们仍然留在 /resume 里)。
/stop [label]可选中断会话当前的任务。不带名称时,它会停止这个聊天里唯一正在工作的会话(即使你的消息正发往另一个会话;仍有后台代理或后台命令在运行的会话算作正在工作),有多个时用选择器询问是哪一个,一个都没有时回复 现在没有正在进行的任务。。/stop x1(或 /x1 stop)停止 x1(它没有在工作时回复 x1 现在没有在处理任何事。)。它还会丢弃排队的消息,并回复丢弃了多少条。
/now-把 Claude 在当前会话队列里保留的消息立即发送,而不是等当前这一步之后(Claude Code 自己的立即发送快捷键 Ctrl+X Ctrl+S)。任何时候都可以用,即使聊天正停止发送或处于最小模式;Claude 什么都没保留时,回复 队列里没有等待的消息。见立即发送排队的消息。
/new [name] [first message]都是可选的启动一个会话。/new x1 会立即启动它,/new x1 fix the tests 还会把“fix the tests”作为第一条消息发送。什么都不带地发送(例如从菜单点按),/new 会询问名称,你发送的下一条消息会被当作参数使用,所以先 /new 再 x1 fix the tests 结果相同。在群组里,请用名称回复这张卡片(或者提及机器人:@aipagerbot x1),因为在那里普通消息到达不了机器人。只限下一条消息:如果你先做了别的事(一张照片、另一个命令、对另一条消息的回复、键盘上的一个模板、另一个按钮),它会照常处理,卡片会关闭;/status、/settings 和键盘的菜单则会让卡片保持打开。询问名称的卡片还会显示会话将得到的模式、模型和文件夹,并带有先修改它们的按钮。无论哪种方式,最后都会到达同一张“x1 已就绪”卡片:会话得到了什么、你的下一条消息会发到它那里,以及一键切换到另一种模式或更换模型的按钮。每个人的新会话都以自动模式启动;在 /settings → 新会话 里修改 /new 使用的设置。名称前加 ! 会以完全访问模式启动会话,适用于管理员(owner 和 admin 角色),或者在自己与机器人私聊里、角色可以发送消息并回答 Claude 的人(user,不是 read_only);对其他任何人,会话会以自动模式启动,卡片会说明原因。已被使用的名称会提供 ↩️ 转到该会话(那个会话正在运行时)或 ♻️ 恢复(它已结束时)、🆕 换成新会话 和 ↩️ 取消,只给发送 /new 的人。询问名称的卡片在 10 分钟不使用后过期(⏱ 这张新会话卡片已过期。请重新发送 /new。)。角色没有 can_manage 的成员(user、read_only)只能在聊天的根文件夹里启动会话:卡片上的文件夹就是根文件夹,“文件夹”页面列出它和直接位于其中的文件夹(没有“默认文件夹”这一行),“新建文件夹”会在选中的文件夹里创建文件夹,而对在根文件夹之外工作的会话,“转到该会话”、“恢复”和“换成新会话”都会被拒绝(“这个会话在你的文件夹 ~/... 之外工作,所以你的角色不能恢复、重启它或更改它的模式。请联系管理员。”)。
/resume [label]可选按名称恢复一个已结束的会话,或者打开选择器。会话会以它原来使用的模式回来。曾处于完全访问模式的会话,只有对管理员或在自己私聊里的人才会以完全访问模式回来;对其他任何人,它会以自动模式回来,回复里会说明(♻️ x1 已以自动模式重新打开:完全访问模式仅限管理员。)。由旧版 aipager 以其旧的 Auto 模式(没有任何检查地运行)保存的会话,会以完全访问模式回来,以 Ask 保存的会话会以询问模式回来。已结束的会话会在列表里保留 GONE_SESSION_MAX_AGE_DAYS 天(默认 14),然后离开会话列表;Claude 保存的对话不受影响。角色没有 can_manage 的成员不能恢复文件夹在聊天根文件夹之外的会话(所有者和管理员可以);重启和每次切换模式也是如此,包括询问模式和自动模式之间的就地切换。
/kill [label]可选结束一个会话,总是要经过确认:⏹ 要结束 x1 吗?Claude 会停止工作,会话会关闭。,带有 [⏹ 结束] [取消]。不带名称时,如果这个聊天只有一个正在运行的会话,它会直接进入确认,否则显示这个聊天正在运行的会话的选择器。也可以在 /status → ⋮ → ⏹ 结束会话 里找到。
/restart [label]可选经过确认后重启一个会话:Claude 停止后重新运行,对话保留,使用 aipager 最近一次启动它或把它切换到的模式。在终端里用 aipager session 启动的会话会以询问模式回来,除非之后从 Telegram 修改过它的模式(aipager 不知道终端以什么模式启动了它,也绝不会猜测一个更宽的模式);从 Telegram 恢复它也一样。
/rename [label] [new]可选给会话起一个新名称(已结束的会话也可以)。/rename x1 newname 会立即改名。词更少时,它会询问:当命令只可能指一个会话时,直接问新名称,否则显示选择器(/rename 后面只有一个词时不会选择会话,因为它可能是旧名称,也可能是新名称)。在群组里,请用名称回复这个问题。
/delete [label]可选经过确认后,把一个已结束的会话从列表中删除。
/diff [label]可选显示会话工作文件夹的 git diff。不带名称时,显示你的消息正发往的那个会话。
/clearqueue-丢弃当前会话所有尚未被接收的消息(aipager 暂缓的消息和在 Claude 里排队的消息都包括),不中断正在运行的任务。回复删除了多少条。
/mode [label] [ask|auto|full]可选显示会话的权限模式,以及切换到其他每种模式的按钮:✍️ x1 当前是 🤖 自动模式。 [💬 切换到询问模式] [🔓 切换到完全访问模式]。有三种模式。💬 询问模式:Claude 在每个操作之前询问。🤖 自动模式(默认):Claude Code 的安全检查会运行安全的操作,并拦下有风险的操作让你决定。🔓 完全访问模式:一切都不经任何检查地运行(--dangerously-skip-permissions);只有管理员,或者在自己与机器人私聊里、角色可以发送消息并回答 Claude 的人(user,不是 read_only),才能选择它。自动模式和完全访问模式只改变 Claude Code 自己的权限请求:aipager 的 hook 仍然会执行成员的角色规则。/mode ask、/mode auto、/mode full 或 /mode x1 ask 会立即切换;用聊天语言写的模式词本身也可以,不需要“模式”二字(/mode x1 询问 对应 💬 询问模式,/mode x1 完全 对应 🔓 完全访问模式)。当这个聊天里正在运行的某个会话恰好叫这个名字时(比如一个叫 demander 的会话),这个词就是会话名称:/mode demander 显示那个会话的模式,而 /mode x1 demander 仍然切换 x1。ask、auto 和 full 总是模式,顺序不限(/mode x1 ask 或 /mode ask x1);只有当两个词都是模式词、并且恰好其中一个是正在运行的会话的名称时,那个词才是会话名称,所以对于一个叫 full 的会话,/mode full ask 和 /mode ask full 都会把它切换到询问模式,而单独的 /mode full 仍然是模式。在询问模式和自动模式之间切换是就地进行的,不重启,任何时候都可以(即使 Claude 正在工作):aipager 会像你一样在会话里按 Shift+Tab,卡片会显示 ⚙️ 正在将 x1 切换到 🤖 自动模式,不需要重启。,直到 Claude Code 在下一步报告新模式,然后显示结果。如果自动模式对那个会话不可用(因为它的模型或订阅),卡片会说明,会话会留在询问模式。会话正显示问题或权限请求时不能切换:请先回答。切换到完全访问模式需要管理员(或在自己私聊里的人)并经过确认,并且会重启会话(对话保留);离开完全访问模式也会立即重启它。以完全访问模式启动的会话在每次切换时都会重启,即使它之后在终端里被切换到了询问模式或自动模式;自启动以来报告过完全访问模式的会话(用 --dangerously-skip-permissions 启动的终端会话)也一样:在那里,Shift+Tab 可能会把它转回完全访问模式。对于正在工作的会话,重启会提供 🛑 停止并切换模式 / ⏳ 暂不。角色没有 can_manage 的成员不能修改文件夹在聊天根文件夹之外的会话的模式,无论是就地切换还是重启切换。点按切换按钮会修改这张卡片本身,而不是再发一条消息。/perms 是旧名称,仍然可以这样使用。
/settings-这个聊天的会话看起来和回答起来是什么样子:aipager 使用的语言、消息布局、改动预览、长任务的卡片更新、格式、回答长度、词汇水平;/new 用什么启动会话;各会话的设置;以及在所有者的私聊里,发送问题报告的提议。见设置。在群组里任何人都可以查看,修改设置则需要管理员。
/language-aipager 在这个聊天里为自己的消息和按钮使用的语言(与 /settings 第一行是同一个列表)。每种语言都用它自己的名称列出:English,然后是 Español(西班牙语)、Русский(俄语)、Português (Brasil)(巴西葡萄牙语)、简体中文、Français(法语)、Deutsch(德语)、한국어(韩语)、العربية(阿拉伯语)、Bahasa Indonesia(印度尼西亚语)、Українська(乌克兰语)、Italiano(意大利语)、Türkçe(土耳其语)、فارسی(波斯语)和 Tiếng Việt(越南语)。在这个版本之后开始的私聊会跟随你的 Telegram 应用的语言;之前就已存在的聊天会一直使用英文,直到你选择(见语言)。在西班牙语、葡萄牙语、法语、意大利语和印度尼西亚语里,回复键盘上的词仍然是 status、stop、new 和 kill,与它们的命令相同。德语和土耳其语分别使用 Status、Stop、Neu… 和 Beenden…,以及 Özet、Stop、Oluştur 和 Sonlandır;输入英文词仍然有效。在波斯语和阿拉伯语里,aipager 的文字从右往左读,使用波斯或阿拉伯数字;会话名称、命令、模型名称和代码保持原样。列表会说明它改变什么:只改变 aipager 自己的文字,在这里和在 Mini App 里;你的 Claude 会话不会改变,Claude 用你写给它的语言回答。在群组里任何人都可以查看,修改则需要管理员。language 是保留的会话名称。
/whoami-说明你在这里是谁:在团队模式下,是你的标签、你在这个聊天里的角色,以及这个角色对你施加的规则。
/update-仅限管理员(owner 和 admin 角色,或者有 can_manage 的角色)。检查 aipager 和 Claude Code 是否有更新的版本,然后用一个按钮更新有新版本的那个。见更新。

/ 菜单先显示你的会话(/x1 · 与 x1 对话),然后是常用命令:new、status、stop、now、resume、mode、settings、language、help(Mini App 开启时还有 app,只在私聊里)。kill、restart、rename、delete、diff、clearqueue、perms、whoami 和 update 不在菜单里,但输入后仍然有效;对会话的操作也可以在 /status → ⋮ 里找到。

作用于某个会话的命令(/stop、/mode、/kill、/restart、/rename、/delete、/diff)遵循同一条规则:

  • 带名称时(/restart x1),它们作用于这个聊天里的那个会话(/rename 为此需要旧名称和新名称,见上文)。
  • 不带名称时,它们作用于命令唯一可能指的那个会话(对 /stop 是唯一正在工作的会话,对 /kill 和 /restart 是唯一正在运行的会话,对 /delete 是唯一已结束的会话),如果有通常的确认步骤就经过它。
  • 否则,它们会显示一个只包含这个聊天里适合这个命令的会话的选择器:你的消息正发往的那个会话排在最前面,标有 ✍️,其余会话按名称排列,还有 ✖️ 取消。选择一个会话会进入和输入它的名称时相同的卡片。
  • 在群组里,不带名称时,它们只作用于你自己的当前会话,前提是命令适合那个会话;否则它们总是显示选择器,即使只有一个会话,因为那个会话可能是另一个成员的(见群组)。

/mode 和 /diff 会先看你的消息正发往的那个会话。

设置#

/settings(以及 Mini App 的设置标签页,显示同样的选项)设置这个聊天的会话看起来和回答起来是什么样子。每个聊天有自己的设置;在群组里任何人都可以查看,修改设置则需要管理员。在你自己的私聊里,无论角色是什么,你都可以自己修改它们,在 Telegram 和 Mini App 里都可以。带 ✅ 的行表示设置与默认值不同(消息布局总是显示它的选择)。

设置选项(默认在前)作用
🌐 语言自动(你的 Telegram 语言),在私聊里;Englishaipager 在这个聊天和 Mini App 里为自己的消息和按钮使用的语言,用它自己的名称显示(与 /language 是同一个列表)。它不会改变你的 Claude 会话:Claude 用你写给它的语言回答。有 English、Español(西班牙语)、Русский(俄语)、Português (Brasil)(巴西葡萄牙语)、简体中文、Français(法语)、Deutsch(德语)、한국어(韩语)、العربية(阿拉伯语)、Bahasa Indonesia(印度尼西亚语)、Українська(乌克兰语)、Italiano(意大利语)、Türkçe(土耳其语)、فارسی(波斯语)和 Tiếng Việt(越南语);“自动”会跟随你的 Telegram 应用的语言(见语言)。
🖼 消息布局工作卡片 + 单独的回答;回答放在卡片里;只留回答,删除卡片完成的任务如何显示。“工作卡片 + 单独的回答”会把卡片保留在回答上方,作为得出回答的过程记录;“回答放在卡片里”把回答放进卡片;“只留回答,删除卡片”会删掉卡片,只留下回答。默认是“工作卡片 + 单独的回答”(对从未做过选择的聊天,KEEP_FINISHED_CARD=0 会设为“只留回答,删除卡片”)。见任务结束后的回答。
📝 改动预览(diff)关;开开启时,每次 Write/Edit 都会作为一条单独的 diff 消息发在工作卡片下方。关闭时保持一张卡片和一个回答;卡片仍会列出每次编辑,Mini App 里也有 diff 查看器。
⏱ 长任务的卡片更新开;关开启时,任务进行 2 分钟后工作卡片每 10 秒刷新一次,10 分钟后每 30 秒刷新一次(无论任务多长都不会更慢),这时它的计时以 30 秒为一步跳动;关闭时,整个任务都保持最初几分钟的节奏(见故障排除)。
✏️ 简单格式关;开开启时,要求 Claude 只写简单的段落和项目符号列表:没有表格、代码块、标题和粗体。
📏 回答长度不设规则;特短;短;中等;长要求 Claude 以这个长度回答。
🎓 词汇水平不设规则;简单用词;普通;进阶要求 Claude 使用这个水平的词汇。
🎨 外观停止按钮、允许和拒绝按钮、App 按钮、设置菜单aipager 的按钮在这个聊天里的样子;一开始全部是普通(Telegram 自己的样子)。Telegram 只允许机器人把按钮涂成蓝色、绿色或红色,所以:停止按钮(工作卡片上的,以及问题或权限请求下面的)可选普通、蓝色、绿色或红色;允许和拒绝可选普通或彩色(允许和始终允许为绿色,拒绝为红色);App 按钮(置顶消息上和 /app 下面的)可选普通、蓝色、绿色或红色(消息输入框旁边的菜单按钮不能上色);设置菜单可选普通(用 ✅ 标出当前选项)或把当前选项也显示为蓝色。对整个聊天生效,不分会话;在群组里只有管理员能改。不支持颜色的 Telegram 应用会把所有按钮都显示为普通样式。Mini App 的设置标签页在“聊天设置”之后有一个单独的“外观”部分,提供同样的选项,每个选项带一个彩色圆点。
🆕 新会话权限(模式)、模型、文件夹你不做选择时 /new 用什么启动会话:询问模式、自动模式(默认)或完全访问模式,“模型”列表里的一个模型或 Claude Code 的默认模型,以及一个文件夹。只有管理员,或者在自己与机器人私聊里的人,才能在这里选择完全访问模式(私聊的界面不会对其他人显示它),而且它绝不会用在不能使用它的人身上:这样的人的会话会以自动模式启动。
👤 各会话的设置一个会话,然后是上面除“新会话”之外的任何设置为一个会话单独设置的值(也可以在 /status → ⋮ → 👤 会话设置 里找到);⭐ 标出被单独设置的项目,“使用聊天默认值”会删除单独的设置。Mini App 里的会话页面设置的是同样的单独值。
🐞 问题报告问我;关只在安装所有者的私聊里出现。“问我”允许 aipager 偶尔提议发送关于它多次遇到的内部错误的报告(最多每 3 天一次);“关”则从不提议(/help 里的“报告问题”按钮仍然可用)。连续两次拒绝提议会关闭提议,这时这一行会说明,并带有重新开启的按钮。见问题报告。

回答长度、词汇水平和简单格式会作为一段简短的指示,附加在从 Telegram 发送的每条消息上传给 Claude(由 hook 添加,见 hook),有会话单独设置的值时使用它。

无论哪种布局,每张工作卡片都以会话的状态行结尾(⏳/✅ 名称 · …),每个回答都以它的结果行开头(💬 名称,当没有已完成的卡片来显示数字时,再加上 · 完成(…));“回答放在卡片里”布局会把两行上下叠放,各在自己的部分里。工作卡片的状态行上方有一条分隔线(小字),在“回答放在卡片里”布局中,还有第二条分隔线把已完成的卡片和回答隔开。在有卡片的布局里,回答会有意比已完成的卡片晚一点到达,让你先看到卡片显示“完成”,再看到下面出现回答;用 FINISH_CARD_GRACE_SECONDS 调整或关闭这个延迟(秒,默认 0.8;0 表示两者同时发送)。在有卡片的布局里,一个没有调用任何工具的任务不会留下卡片:卡片在显示“完成”之前就被删除,回答的第一行会显示数字,和“只留回答,删除卡片”布局一样。

每个会话自己的命令#

每个正在运行的会话都有一个按它的标签注册的命令:

形式作用
/<label>让 <label> 成为你的消息将发往的会话。回复会说明这一点,并附上会话的状态和模式(✍️ 现在你在和 x1 对话 · 💤 就绪 · 🤖 自动模式,然后是“发送消息,就会发到 x1。”),会话等你回复时提供回答,还有打开它菜单的 ⋮ 更多。完整的数字在 /status 里。在群组里,它会改为说“回复 x1 的消息(或提及 @aipagerbot),就能和这个会话对话。”,因为在那里普通消息到达不了机器人(见群组)。/x1@aipagerbot,也就是你从群组菜单里选择 /x1 时 Telegram 发送的内容,效果完全相同。
/<label> <prompt>把 <prompt> 直接发给那个会话。那个会话也会成为你之后的普通消息发往的会话。
/<label> stop中断那个会话当前的任务。

/status 的结果来自 aipager status 在命令行上显示的同一份数据;会话列表本身不需要调用 Telegram。

在电脑上,aipager status 和 aipager session ls 会给每个会话一个状态:IDLE、BUSY、WAITING(一个权限请求或问题在等你,知道内容时会附上,例如 permission: Bash: make deploy)或 GONE。带 --json 时,每个会话还有 waiting_kind(permission 或 question)和 waiting_summary,除非会话在等待,否则两者都是 null。

语言#

aipager 会说 English、Español、Русский、Português (Brasil)、简体中文、Français、Deutsch、한국어、العربية、Bahasa Indonesia、Українська、Italiano、Türkçe、فارسی 和 Tiếng Việt,每种语言都用它自己的名称列出。

  • 只有 aipager 自己的文字会改变。 语言适用于 aipager 的消息、按钮、卡片和 Mini App。你的 Claude 会话不会改变,Claude 用你写给它的语言回答。会话名称、命令、模型名称和代码保持原样。
  • /language(或 /settings 的第一行)为你所在的聊天选择语言。在私聊里,它也是你自己的语言:你在群组里看到的提示和你的 Mini App 都使用它。
  • 自动(只在私聊里)跟随你的 Telegram 应用的语言,你修改后也会再次跟随。aipager 没有的语言会显示为英文。
  • 新聊天和旧聊天。 在这个版本之后开始的私聊,在你选择语言之前会跟随你的 Telegram 应用的语言,它第一条非英文消息会多一行简短的说明,告诉你这一点以及如何修改。之前就已存在的聊天会一直使用英文。如果你的 Telegram 应用使用 aipager 会说的某种语言,你会收到一张提议这种语言的卡片,附有 保留英文 按钮;无论你怎么回答,它都只出现一次。
  • 群组只有一种语言,由管理员在 /language 里设置;在那之前群组使用英文。点按之后的提示会用点按者自己的语言显示:这个人在与机器人的私聊里的选择;没有的话,对于在这个版本之后加入的人,是这个人 Telegram 应用的语言;再没有的话,就是群组的语言。见群组。
  • 从右到左。 在波斯语和阿拉伯语里,aipager 的文字从右往左读,使用波斯或阿拉伯数字。工作卡片跟随聊天的语言,不管 Claude 用什么语言写:在英语聊天里从左往右读,在波斯语或阿拉伯语聊天里从右往左读;卡片上 Claude 按另一个方向写的句子仍按它自己的方向读。回答跟随 Claude 的文字:当 Claude 在一个语言从左往右书写的聊天里用波斯语或阿拉伯语回答时,整条回答是一条从右到左的消息,其中 aipager 自己的行(回答下方的代理,以及在“回答放在卡片里”布局中回答上方已完成的卡片)和其余内容一样靠右,但仍然从左往右读。
  • 模式词。 /mode 接受你的语言里的模式词(/mode 询问),ask、auto 和 full 总是有效。如果某个正在运行的会话恰好叫这个名字,这个词就是会话名称(见上面的 /mode)。
  • 机器人的简介。 启动时,aipager 会用这些语言中的每一种写入机器人的简介(空聊天界面上的那段文字)和短简介,给 Telegram 使用那种语言的人看。Telegram 会显示这些文字,而不是你在 BotFather 里写的文字,所以当你已经有自己的文字时,aipager 什么都不设置(简介和短简介分别检查)。它绝不会覆盖你的文字,也绝不会修改英文简介或默认简介。当它当前的文字在每种语言里都已设置好时,启动时根本不会询问 Telegram。否则,它会先读取你在 BotFather 里的文字(简介读一次,短简介读一次):如果你有,它就停在那里,所以有你自己的文字时,每次启动只花这几次读取,不会更多。如果你没有,它会读取每一种还没有它当前文字的语言,所以你自己写了文字的语言会在每次启动时被再次读取。如果你在 aipager 设置了它的简介之后才在 BotFather 里写简介,使用这些语言的读者仍会看到 aipager 的简介(见故障排除)。

Mini App#

/app(以及 Telegram 的菜单按钮)会打开一个由 aipager 自己提供的控制面板。它默认开启;用 aipager miniapp enable|disable|status 管理它。每个请求都会用 Telegram 的 initData 签名校验(见安全 → Mini App 隧道)。页面不会从其他地方加载任何东西:没有来自其他网站的字体、图片或脚本。

  • 聊天。 应用一次显示一个聊天。如果你属于多个聊天(比如你的私聊和一个群组),顶部会有一行聊天名称供你切换;应用会在你的私聊打开,并在那部手机上记住上一次的选择。下面的一切都属于当前选中的聊天,使用你在其中的角色。只有一个聊天时,不显示这一行。见群组。
  • 会话。 一句聊天摘要,例如“1 个在等你回复、2 个工作中、3 个就绪”,下面是已花费的金额。每个正在运行的会话是一个方块,带有它的上下文圆环、文件夹、模型和费用;正在工作的会话会以强调色亮起。完成的会话放在可以折叠的已结束架子上。+ 按钮启动新会话。
  • 等你回复。 在等权限请求或问题的会话会放在顶部一个琥珀色的托盘里,显示它在问什么。在聊天中回答会把这个请求连同它的按钮重新发到聊天的底部,就像置顶栏上的回答按钮一样,你在那里回答。如果这是唯一在等待的会话,应用会关闭,让请求就在你眼前;否则它保持打开,并显示“已发到聊天中”。这需要向会话发送消息的权限,在请求已经被回答或被替换之后点按的副本会显示“已经回答过了”。
  • 会话页面。 页首显示上下文圆环、状态(“工作中(3:12)”、“等你回复(权限)”、“就绪,上次活动 5 分钟前”、“已结束”)以及一键的停止(工作中或等待中时)或恢复(已结束时)。下面是:等待中的请求和在聊天中回答、排成一条的最近几次工具调用(点按可看完整时间线)、最新的回答、信息、模型选择框、会话自己的设置、修改过的文件和时间线。⋮ 菜单里有所有操作,确认步骤和以前一样。
  • 新会话。 一张逐步引导的卡片:名称,然后是位置(附有你的会话用过的文件夹的最近建议,最新的在前),然后是模型(附有推荐建议),然后是用于权限模式和回答风格的更多选项。底部 Telegram 自己的按钮写着启动会话。对于角色没有 can_manage 的成员,“位置”只提供聊天的根文件夹和直接位于其中的文件夹(根文件夹是默认的,“新建文件夹”会在选中的文件夹里创建文件夹);外面的文件夹会被拒绝并说明原因,而对在它之外工作的会话执行恢复、重启和切换模式,都会回复“这个会话在你的文件夹 ~/... 之外工作,所以你的角色不能恢复、重启它或更改它的模式。请联系管理员。”
  • 设置。 应用语言在最前面:你自己用于应用里和你私聊里 aipager 文字的语言(英语、西班牙语、俄语、葡萄牙语(巴西)、中文(简体)、法语、德语、韩语、阿拉伯语、印度尼西亚语、乌克兰语、意大利语、土耳其语、波斯语或越南语;“自动”跟随你的 Telegram 应用的语言,见语言)。每个人都可以修改自己的,它绝不会发到聊天里,也不会改变你的 Claude 会话。之后是聊天的设置(在群组里只有管理员能修改;在你自己的私聊里,无论角色是什么你都能修改,和那里的 /settings 一样),对管理员还有更新,它对应 /update。对在自己私聊里的所有者,报告问题会打开一个单独的页面:写备注、查看报告、发送它并查看参考编号,都无需离开应用(见问题报告)。

应用会跟随 Telegram 的浅色或深色主题变化,并在你的手机要求减少动态效果时停止轻微的动画。Mini App 里能做的一切,在聊天里也都能做:会话面板上的 ⋮ 菜单有同样的操作。

更换正在运行的会话的模型#

正在运行的会话的页面有一个模型选择框,显示会话报告的模型(来自 Claude Code 的状态行),以及与启动时的选择器和聊天的“模型”键盘相同的列表。选择一个模型会把确切的 /model <name> 输入到那个会话里,与聊天的“模型”键盘使用的输入方式相同,谁能这样做的规则也相同(任何可以向这个会话发送消息的人)。选择框会显示正在更换…,直到状态行报告了另一个模型,然后显示那个模型;如果 15 秒内什么都没变,它会显示未确认(请检查会话)。

  • 只在会话就绪时,不在 Claude 工作中或有打开的请求时。 Claude Code 会立即运行 /model,即使在任务进行到一半时,而不是等任务完成之后,所以在任务结束之前更换会被拒绝。聊天里的键盘在同样的状态下也会拒绝,措辞相同。
  • Claude Code 的“Switch model?”问题。 Claude Code 通常会在更换之前问这个问题,因为下一次回答必须重新读取整段对话。在 Claude Code 2.1.251 及更高版本上,aipager 的 PreModelSwitch hook 会批准 aipager 刚刚输入的那次更换,所以这个问题不会出现(见 hook)。其他每次更换仍然会询问。在更旧的 Claude Code 上,或者更换到 opusplan 或从它换走时,这个问题仍可能出现。这时更换会显示为未确认,问题会在终端里等待。请先在那里回答,再向会话发送任何东西,否则你的下一条消息会被输入到那个问题里。也是出于这个原因,在一次未确认的更换之后的一分钟内,或者直到会话报告了新的模型之前,aipager 会拒绝对那个会话的再次更换。

置顶状态栏#

aipager 对话的每个聊天(你的私聊,以及每个群组 scope)都有一条置顶消息:状态栏。Telegram 在聊天顶部的置顶条会从第一行开始显示这条消息,各行连在一起,所以第一行写的是最需要你关注的事:

第一行什么时候
⏳ jim 在等你回复 - Bash: make deploy某个会话在等权限请求、问题或交互式提示;还有其他会话也在等时,带 (另有 2 个)
⚙️ jim(工作中) / 💤 jim(就绪)聊天里只有一个正在运行的会话,它正在工作 / 就绪(启动期间是 🔄 jim(启动中)):除了一行关于限流的说明之外,这一行就是整个状态栏
⚙️ 2 个工作中 · 1 个就绪多个会话,没有会话在等:每种状态(工作中、就绪、启动中)各有几个会话;没有会话在工作、有会话在启动时用 🔄,全部就绪时用 💤
💤 全部就绪没有正在运行的会话

下面,只在适用期间,会有一行关于限流的说明(🐢 收到 Telegram 警告后放慢发送(429 之后的六个小时),或者 ⏸ 卡片暂时不更新(Telegram 每小时上限) / (Telegram 限制)(最小模式,见故障排除)),然后,当聊天里有不止一个正在运行的会话时,是 ✍️ 消息会发到 jim(这个聊天里普通消息会发往的会话,见下文;群组里没有这一行,因为那里每个人都有自己的目标),以及每个会话一行,附上它的状态:工作中、等你回复、就绪 或 启动中。状态栏每个会话只写一次:第一行已经写了名字的等待中的会话不会再有自己的一行,第一行也绝不会列出下面各行会重复的名字。仍有后台代理的会话会在自己那一行(或者只有一个会话时,在唯一的那一行)写出数量:💤 jim(就绪,1 个代理运行中)。Claude 的后台命令也同样计数:(就绪,1 个后台命令运行中),两者都有时是 (就绪,2 个代理、1 个后台命令运行中)。它总是一个数字,从不写它们的名字,所以状态栏只在数字变化时才变。点按状态栏可以跳到这条消息。

普通消息会发到哪里。 不是回复的消息会发往这个聊天最近对话的那个会话:你最近用 /<label> 切换到、发过消息、启动过的会话,或者刚刚在这里回复你的会话。不带名称的 /mode、/clearqueue、/diff 或 /now 也作用于这个会话(/stop 停止正在工作的那个会话)。每个聊天都有自己的目标:一个聊天里的消息绝不会发到属于另一个聊天的会话。在群组里,只有对机器人的回复、提及机器人的消息、命令或群组键盘上的点按才算数(见群组),而且每个人都有自己的目标,不会因为某个会话的回答而改变(见群组)。

置顶消息上的按钮:

  • 回复 <label>(每个等待中的会话一个,最多三个)会把那个会话的请求连同回答按钮重新发到聊天底部,让你不用滚动就能回答。如果请求在此期间已经被回答,你会收到“已经回答过了”的提示,在请求已经在别处被回答之后点按的副本(或原来的请求本身)也会同样被拒绝:它绝不会回答之后才来的请求。aipager 重启之后,等待中的请求仍然可以从工作卡片或它自己的消息上回答,但 aipager 已经不知道较早的副本显示的是哪个请求,所以点按状态栏在重启之前重新发送的副本会被拒绝,并显示“这个请求已不再打开”。能回答请求的人就能使用这个按钮;其他人不能。
  • 📱 App 会在你的私聊里打开 Mini App,在 Mini App 运行期间。

状态栏没有计时、费用、上下文百分比或模型,所以它只在会话的状态变化时才变。aipager 只在它显示的内容发生变化时才编辑它(静默编辑,没有通知),每个聊天最多每 30 秒一次(PINNED_MIN_EDIT_GAP);在这段间隔内发生的变化会在间隔结束时显示,绝不会丢失。聊天停止发送期间它绝不会被编辑,停止发送解除后会补上更新。

在群组里,机器人需要管理员权限才能置顶。如果不允许置顶,aipager 会删除刚发出的状态消息(这样它绝不会不置顶地留在群组的历史里),并且在 aipager 重启之前不在那个群组里显示状态栏;完全拒绝机器人的聊天(把机器人踢出、屏蔽机器人)也是如此。如果你删除了置顶消息,aipager 会发送并置顶一条新的,每个聊天最多每小时一次。

固定键盘#

一个固定键盘位于聊天的输入框下方。各行从上到下:

行按钮说明
会话每个正在运行的会话标签一个按钮根据会话列表自动生成
操作状态、停止、新建文字快捷方式(输入 结束 仍然有效;结束会话在 /status → ⋮ 里)
导航模板、命令Mini App 通过输入框旁的菜单按钮或 /app 打开,不在这个键盘上

模型 › 在“命令”子菜单里。点按子菜单里的一项会发送一条预先写好的消息或一个斜杠命令:

  • 模板:你发现自己一遍又一遍输入的消息,例如“编写测试”、“解释计划”、“更新记忆”。
  • 命令:Claude Code 自己处理的斜杠命令(/compact、/clear 等),会立即输入。命令或模板会写明它发到了哪个会话(🧹 已把 /clear 发送到 x1)。会话正在工作时,清空对话会被拒绝(⚠️ 会话正在工作,无法清空对话)。
  • 模型:为当前会话快速更换模型。有按系列的别名(sonnet、opus、haiku、fable、opusplan),总是指向这个系列最新的模型,还有固定的模型(claude-opus-5-5、带 1M 上下文的 claude-opus-5-5[1m]、claude-sonnet-5-5、claude-fable-5-1、claude-haiku-5-5)。启动时的选择器和 Mini App 的会话页面使用同样的列表。会话正在工作或有打开的请求时,更换会被拒绝。之后,🔄 回复会变成会话报告的模型,或者说明更换没有得到确认(见更换正在运行的会话的模型)。

要替换默认布局,写入 ~/.config/aipager/keyboard.json:

json
{
  "templates": [{"label": "Deploy",  "prompt": "Deploy to staging"}],
  "commands":  [{"label": "Compact", "send": "/compact"}],
  "models":    [{"label": "Sonnet",  "send": "/model sonnet"}]
}

每个部分相互独立:缺少的部分使用内置的默认值,所以你可以只替换一个部分,不用写其他部分。损坏的 JSON 会被忽略,并在日志里留一条警告。修改需要重启 aipager。

每条消息下的按钮#

机器人的大多数回复都带有适合当时情况的按钮:

权限请求#

当 Claude 想运行一个需要批准的工具时,工作卡片会变成权限请求:

text
🔐 Bash: List the temp directory
ls -la /tmp

  [✅ 允许]  [❌ 拒绝]
  [🟢 始终允许]  [⏹ 停止]

卡片会在 Claude 自己的说明下面,显示 Claude 要运行的真实命令(或文件路径):请批准你看得懂的内容。可能改变文字绘制顺序的字符(Unicode 的方向控制字符,例如 U+202E,能让一条命令看起来像另一条)会显示为 ⟨U+202E⟩ 这样的标记,这时卡片会加上一行 ⚠️ 这段文字里有会改变书写方向的隐藏字符(显示为 ⟨U+...⟩)。实际运行的可能和你看到的不一样。

  • 允许:批准这一次调用。
  • 拒绝:拒绝它;Claude 不会运行这个工具。
  • 始终允许:批准,并加上 Claude 建议的长期规则(“不要再问关于……”)。这个按钮只在 Claude 建议了这样的规则时才出现;对于它无法生成规则的命令(大多数组合命令),卡片会改为显示允许 / 拒绝 / 停止,对会话工作文件夹之外的只读文件访问(Read、Grep、Glob)也是如此。Claude Code 2.1.259 及更高版本会在那个位置放上:Bash 对话框里的一行“切换到 auto 模式”、外部读取对话框里的一行“从现在起阻止在工作文件夹之外读取”,以及对它无法生成单文件规则的 Write/Edit,一次权限模式的切换,例如自动接受所有文件编辑(acceptEdits);aipager 绝不会选择这些行;请用 /mode 主动切换模式。
  • 停止:中断任务,而不是回答。

回答会通过 aipager 的权限 hook 到达 Claude Code,这个 hook 最多等它 20 秒。在工作卡片之前到达的请求(这时它会作为单独的消息发送,只有允许和拒绝)也以同样的方式回答。当这段等待时间已经过去时,按钮会把回答输入到 Claude Code 自己的对话框里;这时“拒绝”会选择对话框的最后一行(“No, and tell Claude what to do differently”),这一行会像“停止”一样结束 Claude 的任务,卡片会显示 🚫 jim · 已拒绝。

每次点按都会记录到 ~/.claude/aipager-audit.jsonl,并作为一行回复重复显示在工作卡片下方:✅ jim · 已允许 · @alice · Bash: List the temp directory(调用的说明,没有说明时是它的命令;这是用 aipager config 设置的聊天;在个人模式下只有 已允许)。

当一个请求(或一个 AskUserQuestion)在等你时,会话会显示为等待中,绝不会是就绪。如果在等待期间 Claude Code 提醒输入一直没有动静,聊天会收到 ⬆️ jim · 仍在等你回复上面的内容,作为对那个请求的回复,每次等待一次。

AskUserQuestion 对话框也以同样的方式显示,每个选项一个按钮(前四个选项),对于允许多选的问题,则有 ⬜/☑ 复选框和一个 ✅ 提交 按钮。问题和每个选项的说明都会完整显示,和终端里一样。只有长到一条 Telegram 消息放不下的表单才会被缩短:工作卡片先少显示一些工具行,然后问题和说明中最长的几段会在词语边界处截断,以 … 结尾。

不止一个请求。 Claude Code 可能同时在等两个请求:一个后台代理请求权限,同时 Claude 问你一个问题,或者反过来。aipager 会把每个请求都显示出来:第一个显示在工作卡片上(或作为单独的消息),之后的每一个都作为单独的消息显示在卡片下面,各有自己的按钮。在最后一个请求得到回答之前,会话一直显示为等待中,这期间你发的消息会被暂存到那时。你可以按任意顺序回答。权限 hook 还在等待时,允许和拒绝会通过它发出,无论这个请求在哪里,都能准确到达它。需要输入按键的点按(问题的选项,或者 hook 已不再等待时的允许和拒绝)只会把按键输入到 Claude Code 最先显示的那个请求里:Claude Code 一次只显示一个对话框,按键会进入最上面那个。否则这次点按不会输入任何东西,按钮保留,并提示 Claude Code 正在先显示另一个请求,请先回答那个,再回答这个。;当 aipager 不知道顺序时(例如一个它没看到启动的代理),提示 aipager 无法确定 Claude Code 先显示哪个请求,请在终端中回答这个请求。 如果卡片上的请求先得到回答,卡片会显示正在等待,只留下停止按钮,其他请求保留各自的按钮。如果某个代理的请求还开着时 Claude 结束了任务,回答照常送达,会话继续等待那个请求,它会作为单独的消息再发一次。在两个或更多请求开着时重启,不会恢复其中任何一个:它们的按钮回复 已经回答过了,不会输入任何东西,这些请求仍然可以在终端里回答。只开着一个请求时,它会像以前一样恢复。

当上下文达到 80% 时,聊天会收到 ⚠️ name · 上下文已用 82%,即将自动压缩,附有 📦 立即压缩,它会向那个会话发送 /compact。这条警告每个对话只发一次:压缩不会让它再次出现,新的对话(例如 /clear 之后)会再收到一次。

过时的按钮#

作用于正在运行的任务的按钮(停止、结束、重启、/mode 的切换按钮及其“停止并切换模式”按钮、/new 的“换成新会话”)与显示它们时的那个任务绑定。来自之前某个任务的按钮不会改变任何东西,而不是作用于现在正在运行的任务;在会话结束又被恢复或重启之前显示的按钮也是如此。结束、重启、/mode 的切换按钮,以及 /status 或 /stop 选择器里的停止,会回复 x1 已开始新的任务…,附上需要再点按或重新发送的内容(或者 那个会话已结束。);工作卡片上的停止会回复 那个任务已经完成…,/mode 的“停止并切换模式”会把它的卡片变回 /mode 卡片,下面一行是 ⚠️ x1 已开始新的任务,所以没有任何更改。 当会话在卡片显示之后开始了新的任务时,/new 的“换成新会话”会显示 在此期间 x1 已开始工作。请重新发送 /new 来替换它。

请求的按钮只属于那个请求。它一旦得到回答(在 Telegram 或终端里),按钮就会回复 已经回答过了,不会输入任何东西,即使会话的另一个请求还开着也是如此。

⚡ 立即发送 按钮只作用于它下面的那条消息:如果 Claude 已经取走了那条消息,点按会回复 Claude 已经取走了,不发送任何东西,并去掉排队的那几行。

任务结束后的回答#

任务结束时,回答会按你的消息布局到达(见下文和设置)。如果 Claude 的回答超过一条消息能容纳的长度(约 32 KB),消息会在 📎 完整回复在下方文件中 ↓ 这一行下面显示放得下的部分,整个任务(它的步骤和完整的回答)会随后作为 <name>_full_log.md 到达,Telegram 不需要其他应用就能显示这个文件。当工作卡片不得不舍弃一些行时,也会附上同样的文件。

因为 Anthropic API 错误(额度用完、过载、请求次数限制、网络)而结束的任务,会显示 ⚠️ name · <what went wrong> 代替回答,并附有 🔄 重试,它会把最近一条消息重新发给同一个会话。在聊天停止发送期间,点按它什么都不做(消息不会被重新发送,按钮也会保留),所以请在封禁解除之后再点一次。

在有卡片的布局(“工作卡片 + 单独的回答”)里,完成的卡片放在回答上方,作为得出回答的过程记录:工具行、代理行以及 Claude 在它们之间说的话。回答会作为单独的消息到达(有通知)。一个没有调用任何工具的任务(没有工具、代理、后台命令,也没有提问或权限请求)只会留下 ✅ name · 完成 · Ns,所以它的卡片会在还显示“工作中”时被删除,永远不会变成“完成”,而由回答的第一行显示用时(💬 name · 完成(…))。给你发送文件在这里不算调用工具:文件会跟在回答后面。没有回答可显示的任务,或以 API 错误结束的任务,会保留它的卡片。如果想每个任务只有一条消息,选择“回答放在卡片里”(回答进入卡片)或“只留回答,删除卡片”(卡片被删除,只留下回答)。

每个任务恰好有一张工作卡片。在任务运行期间发送的消息绝不会给这个任务再加一张卡片,而任务已经结束的卡片不再保留“停止”按钮:如果没有任何东西关闭它(丢失的 Stop hook、一次重启),aipager 会在 30 秒后按你的布局关闭它。aipager 重启之后,任务仍在运行的卡片会被保留,并立即继续运行,时间从任务开始时接着计算,会话会显示为工作中,直到任务结束;重启之前遗留的其他所有卡片都会被关闭(“aipager 已重启”,去掉停止按钮)。在等权限请求或 Claude 的单个问题的会话,会以等待中的状态回来,而不是工作中:它的卡片和置顶栏会说明它在等你,请求上的按钮仍然可以回答,除非保存的对话显示它在此期间已经在终端里被回答了。有多个部分或多个选项的问题,以及由子代理提出的请求,会留给终端处理。一旦确定一个请求已经结束(通过点按或在终端里被回答、它的任务已经结束,或者因为在此期间已被回答、无法保存和重新读取而没有在重启后带回来),它的旧回答按钮会回复“已经回答过了”,不会输入任何东西,即使在重启之后也是如此。没人回答的请求只要 Claude 还在等,就一直开着。只有当 Claude 5 分钟没有任何消息、并且保存的对话显示这个请求已经过去(回答的消息丢了)时,aipager 才不再等它;这样的请求不算已经结束,所以它的按钮仍然会回答对话框。这里只算 Claude 自己和提出请求的代理的消息:独自工作的后台代理不算,而保存的对话对代理的请求一无所知,这样的请求要等那个代理自己的消息。

Claude 自己开始的任务(没有进行中的工作时,后台代理用 <task-notification> 回报结果)只有在真正做事时才会有工作卡片:在第一次工具调用时,或者 15 秒后,以先到者为准。大多数这样的自我唤醒只是几秒钟的“没有新消息”;现在它们只显示回答。运行期间“正在输入…”提示仍然会显示,回答本身也总是会发送:没有任何东西因为被认为不重要而被过滤掉。你开始的任务,无论来自 Telegram 还是终端,仍然会立即得到卡片。

会话在工作时,Claude 启动的每个后台代理(通过 Claude Code 的 Agent 工具,旧版本里是 Task)在工作卡片上都有自己的一行:🤖 <type> · <activity> · <elapsed>,显示代理的类型和它正在做的事,随着它自己的工具调用到来而刷新。当这个代理调用工具达到三次或更多时,这些调用会被收进它那一行正下方的一个单独的 ▸ 用了 N 次工具 按钮里;它们绝不会出现在主任务的时间线或它的 Bash ×N 计数里。代理完成后,它那一行会定格为 ✅ 🤖 <type> · 用了 N 次工具 · <elapsed>,并保留那个按钮。由另一个代理启动的代理(帮手),如果 aipager 在它启动时就知道是谁启动了它,会在自己的类型前写出它所在链条顶端的代理,🤖 pipeline-runner › ship-tester;各行仍按代理启动的先后排列。上面提到的完整 .md 附件会多一个“代理”部分,列出这个任务里运行过的每个代理、运行了多久、工具次数以及它调用了哪些工具。

当后台代理在它们的任务结束后仍在运行时,卡片会留在那里,作为整项工作的状态。当 Claude 在这期间收到新消息时,当前的卡片就是那条消息下方的卡片,它仍然显示正在运行的代理。你发给就绪会话的消息会开始一个新任务:之前那个任务的卡片会在原位定格,步骤保留,新任务有自己的卡片。排队之后被取走的消息会接续这项工作的任务:它的卡片会被重新发到这条消息下方,旧的那张会被删除。代理内部的工具调用绝不会显示为主任务自己的行,也绝不会让 aipager 以为开始了一个新任务。

Claude 的后台命令(run_in_background、Ctrl+B,或者因为运行太久而被 Claude Code 转到后台的命令)在运行期间有自己的一行:⏳ 后台命令:<description>(3:12),以调用的说明命名,没有说明时用命令的第一行。结束时,这一行会定格为 ✅ 后台命令:<description>,已完成(6:12)、❌ ...,出错结束(退出码 1) 或 ⏹ ...,已停止(6:12)(Claude 用 TaskStop 停止了它,或者 Claude Code 因为内存不足停止了它);失败和被停止的行会在完成的卡片上保留这个标记。在这样的命令仍在运行时结束的任务还没有结束:卡片会作为整项工作的状态等待(🔄 name · 1 个后台命令仍在工作 · 4:10,有代理时是 1 个代理(ship-reviewer)、1 个后台命令仍在工作),命令结束时,Claude 的后续内容会在同一张卡片上继续。在代理内部启动的命令属于那个代理,不会显示。aipager 从未看到结束的命令(一次重启、一个丢失的事件)会在两小时后不再计数(AIPAGER_BG_SHELL_MAX_TRACK,以秒为单位):它那一行会写 ⏹ ...,未看到结束,而只剩下在等它的工作会以 ⚠️ name · 已结束(2:00:04,后台命令一直没有消息) 结束。

当任务的时间线变长时,每一组较早的工具调用(连续三次或更多,并且不是正在运行的那一组)都会被收进它所在位置的一个单独的 ▸ 用了 N 次工具 按钮里,而不是全部堆在那里或者被 Telegram 的消息长度限制截掉;点按任意一个按钮就能就地阅读。说明文字绝不会被收起;最新的活动和状态行总是不用点按就能看到。仍在运行(或刚刚定格)的后台代理自己的那一行绝不会被收进按钮,只有它的工具调用会被收起,而且当它是时间线和长度限制之间唯一的东西时也不会。只有当时间线大到全部收起之后仍然放不下时,才会真正从卡片里舍弃内容;这时上面提到的 .md 附件里有完整的记录。

回答发出时代理仍在运行#

Claude Code 可以在后台模式下运行代理,启动它们的任务可能在它们仍在工作时就结束了。这时回答会以一条分隔线和这部分后台工作的简短状态结尾,样式类似 Claude Code 自己的代理面板,在运行期间持续更新。分隔线下面的内容都是小字(Telegram 的页脚),让回答本身更醒目:

text
⏳ 5 个代理工作中 · 🖥 1 个后台命令(shell)运行中

○ 🤖 pipeline-runner · Writing final.md for the pipeline

- 🤖 ship-tester · Write and run criteria tests
- 🤖 ship-developer · Editing state.py
- 🤖 ship-developer › ship-reviewer · Collect line numbers

‖ 🤖 r2-runner · 正在等自己的代理

代理完成后,结果会发到这里。
  • 第一行是计数。 正在工作(或已完成)的代理,以及这项工作正在运行、已结束或已停止的后台命令(shell)。后台命令永远只是一个数字,从不显示它的命令。
  • 每个代理一行, 一个 🤖 加上粗体的类型,然后是它正在做的事:它自己最新的一行文字(Claude 的原话,按原样显示,绝不翻译),没有的话,是它最近使用的工具,用一个说明它在处理什么的简短短语表示(Reading state.py、Writing final.md、Searching for …,或某条命令自己的说明),再没有的话,是 Claude 启动代理时交给它的任务,再没有的话,只显示它最近使用的工具(Running a command、Using TodoWrite),都没有时只显示类型。所以两个同类型、都在运行没有说明的命令的代理,仍然会各自显示自己的任务。一个停下来等待自己启动的代理的代理,会改为显示 ‖ … 正在等自己的代理。
  • 代理以树状显示。 会话启动的代理是第一层。由其中某个代理启动的代理,不管多深,都列在它正下方,每个都紧跟在启动它的代理后面;由第二层代理启动的代理会先写出那个代理(ship-developer › ship-reviewer)。工作卡片则把同一个代理写在顶端代理之后(pipeline-runner › ship-reviewer)。层级永远不超过两层。第二层上正在工作的代理没有自己的 ○:那里由 Telegram 画一个项目符号。aipager 还不知道是谁启动的代理会显示在第一层,绝不会放在一个猜测的代理下面,一旦知道了启动者,就会移到它下面。
  • 最多六行, 先是仍在工作的代理,然后用 还有 N 个代理 表示没有占一行的代理。一个代理绝不会脱离它上面的代理单独出现。
  • 承诺“代理完成后,结果会发到这里。”只在仍有代理在工作时保留。后台命令从来没有这句承诺:一个开发服务器可能永远不会结束。

每个代理的那一行会在它停止时自己定格,变成 ✓ 🤖 Explore · 已完成(0:42)(这个代理运行了多久),第一行的数字会递减。列表中的代理在回答发出后又启动的代理,会在下次更新时加入它下面的状态;会话自己后来启动的代理则会列在之后的回答下面。当所有代理都完成后,这些行会让位给单独的一行,附上从回答到最后一个代理结束的时间:

text
✅ 2 个代理已完成(6:12) · 🖥 1 个后台命令(shell)运行中

当那个后台命令结束时,会变成 ✅ 2 个代理已完成(6:12) · 1 个后台命令(shell)已结束(2:13:05)(命令运行了多久;被结束或被 Claude 停止时是 已停止,自己结束时是 已结束,无论有没有出错;卡片上它的那一行会说明是哪种情况)。工作里只有一个后台命令的回答会显示 🖥 1 个后台命令(shell)运行中,然后定格为 1 个后台命令(shell)已结束(…) 或 1 个后台命令(shell)已停止(…)。

只有属于回答自己这项工作的后台命令才会被计数:之前某个任务留下运行的开发服务器不会在每个回答下面重复出现(置顶栏仍然会计数所有后台命令)。/stop 会结束这项工作,但 Escape 不会结束后台命令:仍在运行的命令会留在置顶栏上直到它结束,并且不再在之后的回答下面计数;它之后的后续内容会开始一个单独的任务。

这个回答在任务结束时立即发送,作为回复你的消息的普通消息(有通知):正是终端显示的那段文字。工作卡片会留在它上方,在每种布局下都作为这项工作的实时状态:🔄 name · 1 个代理(general-purpose)仍在工作 · 1:18,附有它的停止按钮。当代理回报结果时,Claude 对这部分的回答会作为一条单独的新消息到达;之前的回答绝不会被重新发送,即使 aipager 重启也是如此。代理多次回报的工作,每次回报都会有一个回答,每个回答一写完就会发送。

在它自己的后台工作仍在运行时停下的代理算作仍在工作,因为它之后会继续。一个代理启动了自己的代理、然后停下来等它,aipager 会从那次启动(它自己发起另一个代理的调用)认出这种情况:它在状态里仍显示为正在工作,并让这项工作保持打开,直到它在自己的代理都已不在运行时停下。如果在其中最后一个代理结束两分钟后它仍未继续,它就不再算作正在运行,状态保持最后一次显示的样子。Claude 为某个代理发出的 <task-notification> 也会在一条备注里说明这一点;aipager 在那条通知开始一个任务时读取它,也会在工作卡片还在时、Claude 在任务进行到一半收到它时读取。一行在显示为已完成之前会等几秒钟,让那条备注有时间先把它取回来。在每种布局下都会加上这个状态,使用聊天的语言。

它如何更新。 aipager 只在状态显示的内容发生变化时才静默地编辑回答,每个回答最多每 30 秒一次(回答本身算作第一次)。每个带状态的回答都会这样更新(每个会话最多五个等待中的回答)。编辑的优先级很低:被发送额度拒绝的编辑,或者正赶上聊天停止发送的编辑,不会进行,状态会等下一次变化。最后一次编辑,也就是一切都结束时的那一次,会在停止发送解除之后或额度拒绝时再重试一次,然后放弃。不会编辑已被删除的回答、已结束或已被关闭的会话的回答,也不会进行会让回答对 Telegram 来说太长的编辑(长回答被截断时,会给状态留出位置)。

当 aipager 无法知道工作已经结束时,状态会保持最后一次显示的样子,绝不会变成已完成:

  • aipager 30 分钟没有收到任何消息的代理(AIPAGER_SUBAGENT_SILENCE)不再算作正在运行,但沉默并不意味着已完成;
  • 运行超过两小时(AIPAGER_BG_SHELL_MAX_TRACK)而没有看到结束的后台命令不再计数;
  • aipager 重启会忘记哪些回答在等待。

aipager 重启会保留仍在运行的代理,包括正在等待自己的代理的那些:重启后发送的回答会列出它们,重启接手的工作卡片会再次显示它们,它们之后的结束也会照常对应上。在重启前超过 30 分钟保存的代理不会被恢复。

因为停止发送而被暂缓的回答,或者在带格式发送失败后以纯文本发送的回答,会不带状态地发送,没有回答文字(只有第一行)就结束的任务也是如此。

结束确认#

/kill、它的选择器和 ⋮ → ⏹ 结束会话 总是会要求确认:

text
⏹ 要结束 jim 吗?Claude 会停止工作,会话会关闭。

  [⏹ 结束]  [取消]

点了 ⏹ 结束 之后,消息会在这个聊天其余的会话上方显示 ⏹ 已结束 jim。在会话开始了新任务之后点按的确认会被拒绝(jim 已开始新的任务…),对之后才启动的同名会话的确认也会被拒绝。

安装语音消息功能(还没有安装附加组件时)#

当你发送语音消息而 aipager[voice] 还没有安装时:

text
⚠️ 语音消息需要一个可选组件
   (安装约 200 MB · 首次使用时模型约 74 MB)。

  [📦 安装语音消息功能]  [取消]

点按“安装”会为你的安装程序运行正确的安装命令(uv tool install --reinstall aipager[voice]、pipx install --force,或者后备的 pip install faster-whisper),每 5 秒编辑一次消息说明仍在进行,成功后提供一个 [🔄 重启 aipager] 按钮。

重启#

🔄 重启 aipager:

  • 作为 systemd 用户服务运行的 aipager 会在 5 秒后安排一个独立的 systemctl --user restart aipager.service(精确到 1 秒的定时器),放在 aipager 自己进程组之外的一个临时服务里,这样它在 aipager 退出后仍然存在。当服务文件会关闭你的会话时(KillMode 不是 process 或 none),它会拒绝,并让你先运行 aipager service install。
  • macOS 上,当 launchd 代理已经安装时(~/Library/LaunchAgents/com.aipager.daemon.plist):launchctl kickstart -k gui/<uid>/com.aipager.daemon,即使是你自己启动的 aipager 也是如此。
  • 否则,对你自己启动的 aipager(aipager start),在 Linux 上即使服务也已经安装:它会创建一个独立的替身,这个替身等父进程退出后,运行当初启动 aipager 的同一条命令(它的输出写入自重启日志)。当前的 aipager 会在替身启动后立即干净地停止。

不需要 SSH。要完成 aipager 的更新,更新卡片上的 🔄 立即重启 会改为在同一个终端里原地重启 aipager(见更新)。

更新#

/update(仅限管理员;在个人模式下只有机器人主人)会回复一个按钮:🔄 检查更新。在你点按它之前,什么都不会查询。点按会把这条消息改成每个产品一行:

  • 有更新的版本时(PyPI 上的最新版本),aipager 1.0.1 → 1.1.0;
  • 没有时,aipager 1.0.1(最新);
  • 查询失败时(网络故障、5 秒超时,或者找不到 claude),Claude Code 2.1.282(无法检查)。

Claude Code 会与它自己更新渠道(autoUpdatesChannel:latest、stable 或 rc)上的最新版本比较。在这些行下面,用小字写着 aipager 的安装方式(例如 pipx, from PyPI 或 pipx, from local path …;群组聊天里绝不显示路径)。

如果有更新的版本,会有一个按钮,只更新有更新的产品:更新 aipager、更新 Claude Code 或 全部更新,另外还有取消。当提议更新 aipager 时,消息还会说明重启会如何进行:重启:自动,在没有任务运行时进行。、重启:安装完成后点“立即重启”。(没有服务的 aipager,见下文)或 重启:手动(原因)。

如果更新的 aipager 已经安装,但正在运行的 aipager 仍是旧版本(一次没有重启就结束的更新,或者手动执行的 pip install -U),那一行会写 aipager 1.1.0 已安装,正在等待重启(当前运行 1.0.1)。,消息会提供 🔄 立即重启 而不是更新:对于只需要重启的版本,绝不会提议“更新 aipager”。已安装的版本是通过启动 aipager 重启时会使用的解释器来检查的,所以已安装但无法加载的版本会如实说明(aipager 1.1.0 已安装,但无法加载。请用 … 重新安装。),并且不提供任何操作。比已安装版本更新的发布版仍会作为更新提供。在无法从这里更新的安装上(editable、Nix、Snap、系统软件包、容器,或者另一个用户的安装),更新的 aipager 会附带原因显示,但不会被提议。用 npm 安装到你的用户无法写入的文件夹里的更新版 Claude Code 也是如此(例如 /usr/lib/node_modules 里的 sudo npm install -g),在那里 claude update 只会失败:它那一行写着 无法从这里更新:installed with npm into a folder your user can't write to; run "claude install" once to switch to Claude Code's own installer, then restart aipager。如果没有可以更新的东西,消息会写“一切都是最新的。”、说明某项检查失败了,或者在有更新版本但无法从这里安装时写“有新的更新,但无法从这里安装。”,附有再次检查按钮,没有更新按钮。

更新按钮只运行检查所提议的内容。如果检查已经超过 10 分钟、之后又有另一次更新运行过,或者版本已经和按钮对不上,它会让你再检查一次。来自较早 /update 菜单的按钮(每个产品单独的 更新 Claude Code / 更新 aipager / 两者都更新 按钮)会回复“这个菜单已过时,请重新发送 /update”,不做任何事。每次点按都会重新检查仅限管理员的规则。

更新 Claude Code 会运行 claude update(按绝对路径,最多等 5 分钟),并报告 Claude Code A → B、“已是最新版本”,或者错误以及它输出的末尾部分。正在运行的会话会继续使用旧版本,直到你重启它们(/restart);回复会列出这些会话。不会替你自动重启任何会话。新会话使用新版本。

更新 aipager(更新 aipager,或者全部更新的后半部分,先更新 Claude Code):

  1. 对于已经是最新版本的 PyPI 安装,它会说明这一点并停止。
  2. 如果 aipager 能自己重启,它会先等到没有任何东西进行到一半:没有会话在运行任务、在等问题、在运行后台代理、在显示活动的工作卡片、在运行工具或有打开的权限请求,也没有回答在等限流结束而被暂缓。消息会列出它在等什么,附有立即重启(跳过等待)和取消。10 分钟后它会再问一次:再等 10 分钟、立即重启、取消。一小时内没人回答,它就会自己取消。
  3. 它通过安装正在运行的 aipager 的那个安装程序升级(pipx upgrade aipager、uv tool upgrade aipager、brew upgrade aipager,或 <venv>/bin/python -m pip install --upgrade aipager),按绝对路径,最多等 10 分钟。安装程序运行期间没有“取消”按钮。
  4. 它会在一个新的解释器里检查新版本能否导入。失败、超时或无法导入的升级不会重启任何东西。超时的升级是在中途被停止的,所以消息会警告安装可能不完整,并给出在下次重启之前需要运行的重新安装命令。安装程序的输出只在私聊里显示;群组会收到“(输出在 aipager 日志中)”。
  5. 如果升级期间有任务开始,它会再次等待。
  6. 它会在 5 秒后安排一个独立的 systemctl --user restart aipager.service(精确到 1 秒的定时器),状态消息会显示 ⏳ aipager A → B 已安装,正在重启…。在第 5 步等待期间,它显示 ⏳ aipager A → B 已安装,当前任务结束后重启。重启之后,新的 aipager 会把同一条消息改成 ✅ aipager 已更新 A → B,重新连接了 N 个会话(没有回来的会话还会有 ⚠️ 未重新连接:…)。如果那条消息已被删除或无法再编辑,它会把结果作为一条新消息发到发起请求的聊天里。

在 Mini App 里,设置 → 更新会显示“正在重启 aipager…”和一个旋转的灯笼,直到新的 aipager 应答,然后显示“已更新到 B,重新连接了 N 个会话。”。它从不显示倒计时。大约 2 分钟没有应答之后(绝不会早于旧的 aipager 来得及报告重启没有发生),它会显示“仍在重启,请稍后重新打开应用。”。使用 aipager 的托管隧道时,更新后的重启会保留应用的地址(新的 aipager 接管隧道),所以打开着的页面能撑过重启,并显示“已更新到 B”。如果最终没能保留隧道,页面会在大约 2 分钟后显示“仍在重启,请稍后重新打开应用。”。从没有这个功能的 aipager(或设置了 MINIAPP_KEEP_TUNNEL=0)开始的更新仍会改变地址:当旧的 aipager 不再应答时,打开着的页面会显示“aipager 已重启。等聊天中提示更新完成后,请重新打开应用”;请关闭它,再从聊天的菜单按钮或 /app 重新打开。应用的登录持续 5 分钟,所以打开时间更长的页面会改为显示“请重新打开应用查看更新结果。”。

更新之后,只有当 aipager 作为 systemd 用户服务运行,并且这个服务文件有 KillMode=process(或 none)时,aipager 才会自己重启。如果 KillMode 不安全,aipager 仍然会被升级,消息会写 需要重启:…:先运行 aipager service install(它会列出重启会关闭的会话)。

没有服务时的立即重启。 没有任何服务管理器负责重启的 aipager(在终端或 tmux 里运行的 aipager start、Docker,或 macOS 上的 launchd 代理)会安装更新,然后显示 点“立即重启”即可运行(aipager 会等到没有任务在运行)。 和一个 🔄 立即重启 按钮。点它(和所有更新按钮一样,只有管理员能点):

  1. 检查已安装的 aipager 能否加载(⏳ 正在检查已安装的 aipager…);损坏的安装不会重启任何东西,版本相同时会说明没有需要重启的;
  2. 像 systemd 的重启一样,等到没有任何进行中的工作,期间有 立即重启(跳过等待)和 取消 按钮;
  3. 再检查一次安装,然后显示 ⏳ aipager A → B 已安装,正在重启…,并以干净的方式停止 aipager(就像按 Ctrl+C);
  4. 原地启动新的 aipager:同一个进程、同一条命令、同样的环境和文件夹,在同一个终端或 tmux 窗口里(在 Docker 里则是同一个容器进程)。会话继续运行。新的 aipager 会把消息改成 ✅ aipager 已更新 A → B,重新连接了 N 个会话。

重启那几秒内发来的点按会被丢弃,和任何重启一样。重启期间按下 Ctrl+C 或执行 docker stop 会优先:aipager 停止,不再启动。如果新的 aipager 无法原地启动,旧的退出后会由一个分离的副本来启动它(它的输出写入自重启日志,即实例文件夹里的 aipager.log 或 /tmp/aipager.log,如果这个副本没有回来,aipager 的日志会写出要运行的命令);如果连这也失败了,或者解释器本身已经没了,aipager 会以代码 1 退出,aipager 的日志会写 restart.failed: 和要运行的命令。

当 aipager 完全无法自己重启时,消息会给出适合你的环境的确切步骤,而不是按钮:

  • 终端或 tmux:在运行 aipager 的终端或 tmux 窗口中按 Ctrl+C,然后执行 aipager start。
  • macOS:在 Mac 上执行 launchctl kickstart -k gui/<uid>/com.aipager.daemon。
  • Docker:在运行 Docker 的电脑上执行 docker restart <container>。(当容器的主机名就是它的短 ID 时,用这个 ID),并提醒你从镜像重新创建容器会回到该镜像里的 aipager 版本。

当更新的 aipager 已安装并在等待时,立即重启 也会出现在 检查更新 的结果里,还会出现在一条只发一次的通知里:当 Claude Code 的 hook(每个事件都会从已安装的 aipager 重新启动)运行的 aipager 已经比正在运行的更新时,所有者会针对每个更新的版本和每次 aipager 运行,收到一条带这个按钮的静默消息,⚠️ aipager 更新只完成了一半:…。它会从 hook 第一次运行更新版本时起等两分钟(安装后立刻重启的话永远不会出现),再等到所有者的私聊里没有任务在运行。重启到那个版本之后,这条通知会被改成 ✅ aipager 更新已完成:aipager 现在运行的是 B。,不再带按钮。在 Mini App 里,设置 → 更新会把同样的状态显示为卡片上方的一行,并带有 立即重启(会先确认)。

在 Mini App 里,更新是设置标签页上的第一张卡片:它的名称、正在运行的版本,以及这一行末尾一个小小的 检查更新。打开设置标签页会自动检查(最多每十分钟一次,更新运行中或 aipager 重启时绝不检查;在那里失败的检查什么也不显示),点一下则总会检查。在有事要做之前,这张卡片一直是安静的一行:有更新的版本时会显示它的几行内容和那一个更新按钮,“一切都是最新的。”是没有按钮的一行,而正在进行的更新、重启或 立即重启 都保留各自的按钮。

任何时候只有一次更新在运行,/update、Mini App 的设置 → 更新卡片(同样的检查、同样的一个更新按钮、同一个任务)和命令行里的 aipager update 合起来算。这也包括“已安装,正在重启…”和真正重启之间的那几秒:/update 会回复 aipager 即将重启,Mini App 不提供任何按钮,语音消息功能的重启 aipager 按钮在更新正在运行或等待重启期间会拒绝。如果在安排重启两分钟后 aipager 仍然活着,它会停止等待中的重启定时器,释放更新锁,并让你自己重启它。

如果 aipager 在安装程序运行期间关闭,安装程序有 3 秒时间完成,然后被停止(否则它会在下一个 aipager 启动时继续写入安装)。状态消息以及重启之后的一条新消息会说明安装可能不完整,以及如何修复。

一旦开始关闭,就不会重启任何东西,也不会开始任何新的东西:/update 的按钮会回复 aipager 正在关闭,Mini App 会回复 503,不会启动任何安装程序或版本检查。在 3 秒内完成的安装程序仍然算数。状态消息会说明新版本已经安装、没有重启任何东西,下一次启动会通知 aipager 已更新 A → B。你用 aipager service stop 停止的 aipager 会保持停止状态。关闭过程中更新部分总共最多需要 8 秒。

自由消息#

文字#

会被当作发给当前会话的下一条消息:这个聊天的目标,也就是你最近切换到、发过消息、启动过的会话,或者刚刚在这里回复你的会话(见置顶状态栏)。在群组里,每个人都有自己的目标,不会因为某个会话的回答而改变(见群组)。消息会立即到达 Claude,即使你自己的任务正在运行,就像在终端里输入一样(在群组里,别人的消息会等那个任务结束;见下文)。发送多条消息时,它们会在 Claude 内部排队,Claude 会在自然的停顿点逐条取走:

你的消息上的表情回应会跟着它变化,就像在 Claude Code 里排队的消息会从灰色变成白色一样:

  • 👀:已经交给会话(或者被暂缓,见下文),但 Claude 还没有取走。任务运行期间发送的消息,在 Claude 的队列里等待时仍然是 👀。
  • 👍:Claude 已经取走了它:开始了一个任务、把它并入正在运行的任务,或者把它交给了正在运行的后台代理。
  • 🤷:它永远不会被取走:被暂缓的消息被 /stop、/clearqueue 或 /kill 丢弃,或者放行时发送失败;仍在等待的消息或命令被 /stop、/clearqueue、/kill 或会话结束丢弃(不是 /clear 或 /resume);或者是 Claude Code 拒绝的消息(见下文)。Claude 已经排队的消息只有在 aipager 还能看到那个队列时才会被标记(对保存的对话的实时扫描正在运行,并且没有后台工作在等待);否则它们保持 👀。终端里的 Escape 会把 Claude 的队列拉回输入框,在那里它们可以被重新发送,所以以这种方式被丢弃的消息也保持 👀。
  • 👌:一个 Claude Code 命令已经运行(见下文),或者 aipager 确认了 /stop。

表情回应只会往前走,所以一条消息最多得到三个表情回应,每种一次。正赶上 Telegram 因限流而阻止的表情回应会被跳过,之后不会补发,一次清理最多只标记它丢弃的最新十条消息。

命令按钮(压缩对话、/model …):在会话就绪时点按,命令会立即得到 👌;Claude Code 在按下 Enter 时运行它,而像 /model 这样的本地命令不会发出任何之后能报告回来的 hook。在任务运行时点按(/model 除外:它在任务结束之前会被拒绝,见更换正在运行的会话的模型),它会是 👀,直到那个任务结束(正常结束、作为后台工作的一个暂停点,或者因为 API 错误),然后才是 👌;在那之前被 /stop、/clearqueue、/kill 或会话结束丢弃的话,它永远不会运行:🤷。当 Claude Code 把它作为消息排入队列时,它会走 👀 → 👍 这条路。语音消息一旦它的转写文字发出,就会得到和文字一样的表情回应。

工作卡片和最终的回答会跟着 Claude 真正用于某个任务的那条消息(会话就绪时用来开始任务的消息,或者并入正在运行的任务的消息),绝不会简单地是你发送的最后一条消息。在中途补发一条消息不会让回答“跳”到那条消息上:如果 Claude 把它并入正在形成的回答,卡片会在那一刻跳到那条消息下面;如果 Claude 先完成再取走它,第一个回答会留在第一条消息下面,补发的消息会有自己的任务,下面有自己的卡片和回答。

如果 Claude Code 直接拒绝了一条消息(一个未知的斜杠命令,或者一个只在终端里打开对话框的内置命令),不会发出任何 hook,所以不会有任何东西结束 aipager 刚刚宣布的那个任务。8 秒没有 hook 之后(PROMPT_HOOK_GRACE_SECONDS),工作卡片会变成 ⚠️ name · Claude Code 没有收到消息,附一行说明,会话重新就绪;原因在终端上,消息会得到 🤷(斜杠命令得到 👌:打开对话框的内置命令也不会发出任何 hook)。只有开始了新任务的消息才会这样判断;排在正在运行的任务后面的消息会保持 👀,直到 Claude 取走它或者它被丢弃。

有三种情况会暂缓而不是发送,结束后会自动发送:

  • 有打开的权限请求或问题,并且在它打开期间一直如此(没有时间限制):否则你的文字会被当成对那个对话框的回答。这时你发的第一条消息会在请求下面收到一条回复,告诉你 Claude 还在等你回答(之后的消息只会收到 👀)。你一回答,不管是在 Telegram 还是在终端,被暂存的消息就会按你发送的顺序发给 Claude,那条回复也会被删除。如果请求还开着会话就结束了,这些消息会被丢弃并标上 🤷。
  • (团队模式)另一个人的消息仍在等待被取走。
  • (团队模式)另一个人的任务正在运行,包括那个任务启动的后台代理。在终端里输入的任务算作所有者的,所以只有所有者的消息(或者在私聊里,那个聊天自己成员的消息)才会并入它。

后两种情况让不同人的消息不进入同一个任务,这样每条消息都以它自己发送者的权限运行。如果仍然有消息并入了(例如 aipager 不认识的发送者,这种发送者从不会被暂缓),这个任务会按其中所有人最严格的规则运行(见群组)。如果消息等待的那个任务被停止了,或者因为一个被禁止的工具而被拦下,被暂缓的消息会被丢弃,并得到一个 🤷。

每个会话最多暂缓 50 条消息,它们在 24 小时后过期;/clearqueue 会把它们和 Claude 保留的所有内容一起丢弃。

立即发送排队的消息#

在 Claude 处理你自己的任务时发送的消息会立即到达 Claude(aipager 不会暂缓它),并在 Claude 的队列里等到当前这一步结束。一旦 Claude 的队列显示出它,aipager 就会在下面回复:

text
⏳ 已排队:Claude 会在这一步之后读取
[⚡ 立即发送]

每条排队的消息都有自己的一行,一旦 Claude 取走那条消息,这一行就会连同它的 👍 一起消失。在看到它的队列记录之前就被 Claude 取走的消息没有这一行,只有 👀 然后 👍。

点按 ⚡ 立即发送(或者发送 /now)会在那个会话里按下 Claude Code 自己的立即发送快捷键(Ctrl+X,然后 Ctrl+S)。这时 Claude 会一次读取它排队的所有内容,而不只是你点按的那条消息。正在进行的工作会怎样,由 Claude Code 决定:正在运行的命令或代理会继续在后台运行;如果 Claude 正在写回答,这个回答会被打断,并和你的消息一起重新开始。其他任何类型的步骤(例如另一个工具)在短暂等待之后也可能被中断。在某些情况下(例如关闭了 Claude Code 的后台任务时),立即发送会退回成普通地中断正在运行的这一步。这些都是 Claude Code 自己的立即发送方式,不是 aipager 决定的。

点按会回复 已立即发送给 Claude,所有的行会同时消失,因为立即发送把 Claude 保留的一切都交给了它;/now 也会同样去掉它们,立即发送已经送出的消息不会有这一行(之后你发送的消息有自己的一行)。如果 Claude 取消了正在运行的这一步,而不是把它转到后台,它只会立即取走最早排队的那条消息,其余的在下一步读取;回答会在它取走的那条消息下面,那条消息不会运行第二次。如果按键要稍微多花一点时间才能到达 Claude(电脑正忙),点按会改为回复 正在发送给 Claude;这时这一行会在按键到达时消失,或者如果没能送到,这一行会留下并显示 ⏳ 已排队:还没送到 Claude;请再点一次“立即发送”,附上它的按钮,让你再点一次。只有当 Claude 保存的对话显示它仍然保留着这条消息时才会按键;否则,点按会回复 Claude 已经取走了 并去掉这一行。在以下情况下它会被拒绝,不按任何键:有打开的权限请求或问题时(请先回答打开的问题),Claude 可能刚刚打开了一个 aipager 还来不及显示的请求时(在上一个任务的回答还在发送期间,新任务的事件会被暂缓:现在还不行,请稍后再试),以及 aipager 正在往会话里输入另一条消息时(aipager 正在输入一条消息,请稍后再试)。任何可以向会话发送消息的人都可以使用这个按钮和 /now;read_only 成员不行。

当它的消息离开 Claude 的队列时,这一行会自己消失:Claude 取走了它(并入正在运行的任务、交给后台代理,或者作为下一个任务开始),终端里的 Escape 把队列拉回了输入框,或者它被 /stop、/clearqueue、/kill、/restart、一次安全停止或会话结束丢弃了。aipager 重启会去掉所有还在显示的行。

这些行绝不需要等待限流管理器:即使在繁忙或变慢的聊天里、在最小模式下,它们也会立即发送和去掉。只有在 Telegram 自己让聊天停止发送时,才不会发送任何行(这时 /now 仍然可用),如果 Telegram 刚刚要求 aipager 放慢发送,一行会正好等 Telegram 要求的那几秒。每条等待中的消息为它的那一行花一次对聊天的调用;一起出现的行会在一次调用里去掉。

文件#

上传的文件会被下载到它所来自的那个聊天的私有文件夹 /tmp/aipager-files/<chat>/(命名为 <time>_<file name>;<chat> 是由聊天 ID 和机器人 token 生成的名称,不会暴露聊天 ID)。这些文件夹只有运行 aipager 的那个系统用户能读(0700,文件 0600),一个聊天里成员的任务不能读取、搜索或写入另一个聊天的上传文件(见安全)。这次改动之前的文件留在原处,直接放在 /tmp/aipager-files/ 里;不会移动或删除任何东西,aipager 启动时会把这个文件夹收紧到 0700,所以只有那个用户能访问它们。换了新的机器人 token,每个聊天都会有一个新文件夹;在那之前的上传文件留在旧文件夹里,成员的任务再也读不到它们。下载文件夹如果是符号链接,或者属于另一个用户,会被拒绝,上传失败。路径会交给 claude:有说明文字时,消息是说明文字加上路径;没有说明文字时只有 check this: <path>(相册是 check these: <paths>),这样 claude 会被指向文件,而不会被告知要拿它做什么。以 /<label> 开头的说明文字会选择会话,和文字的 /<label> <prompt> 一样,标签会从消息里去掉:只有 /api 会把 check this: <path> 发给 api,而 /api compare these 会把 compare these <paths> 发到那里。只有开头的 /<label> 才会选择目标;说明文字后面的斜杠只是文字。没有会话使用的标签会被拒绝,显示 ⚠️ 没有叫这个名字的会话:…,不发送任何东西。会先检查 Telegram 机器人 20 MB 的下载上限;过大的文件会在任何下载之前得到明确的拒绝。遇到临时网络错误的下载总共最多尝试三次(等 1 秒,然后 2 秒),然后你才会看到错误,错误会写明是哪个文件。相册(作为一条消息发送的多张照片或多个文档)会在最后一项到达后作为一条消息交给 claude(说明文字,然后依次是每个文件路径);如果某一项下载失败,其余的仍会发送,并附一条写明缺少哪一项的备注。

Claude 发送的文件#

Claude 可以从它的会话里给你发文件:用你自己的话说就行("把截图发给我"、"把 ~/proj/chart.png 这张图发给我"),或者它觉得你需要时会自己发。它用的是 Claude Code 的 SendUserFile 工具(Claude Code 2.1.301 或更新版本),aipager 启动的每个会话都有这个工具:aipager 用 CLAUDE_CODE_ATTACHMENTS_BY_PATH=1 启动会话,并告诉 Claude 有这个工具。在你的 aipager 支持这个功能之前启动的会话,需要重启后才有这个工具:/restart、会重启会话的模式切换,或者恢复。

  • 文件在这次任务的回答之后到达,作为对回答的回复(任务没有回答时,回复你的消息),不响提示音,而且只发到这个会话自己的聊天。任务运行期间,工作卡片会显示 📎 正在发送 2 个文件。
  • 每个文件按内容而不是按名字发送:图片在符合 Telegram 的照片限制时作为照片发送(最大 10 MB,宽加高最多 10,000,长边最多是短边的 20 倍),否则作为文件发送;GIF 作为动图;MP4 作为视频;MP3 或 M4A 作为音频;其他都作为文件。Claude 要求普通附件时,全部作为文件发送。
  • 2 到 10 张照片和视频作为一个相册发送;文件和文件一起,音频和音频一起;动图单独发送。更多时会平均拆分(11 个分成 6 个和 5 个),每个相册不超过 50 MB。
  • 说明文字是 💬 <session>,Claude 自己写的说明放在第一条消息的下一行,最多 1,024 个字符。
  • 每个文件都会先检查,对所有人都一样,owner 也不例外(安全):aipager 的设置、Claude 的凭据、密钥和看起来像机密的文件永远不会发送;文件必须是普通文件、不能为空、最大 50 MB;成员只能发送聊天根文件夹里的文件。一个请求里只要有一个文件被拒绝,这个请求就什么都不发:Claude 会知道原因并告诉你,任务继续进行。
  • aipager 发送的是 Claude Code 读取文件时它做的一份私有副本,保存在 ~/.local/state/aipager/outbox/,发送后立即删除,或一天后删除。
  • aipager 自己不设数量上限:发送节奏按 Telegram 的频率限制来,一个相册按每个文件各算一次。聊天被禁言期间,文件会等待,并跟在回答之后发出。
  • 发不出去的文件会在聊天里有一行说明,例如 ⚠️ 无法发送 report.pdf:Telegram 拒收了这个文件。 aipager 在 Claude 自己的对话记录里找不到请求的文件,不会有任何一行说明(安全)。请求在对话记录里、但 aipager 无法把它和正在运行的任务对上的文件,会收到 ⚠️ 无法发送 report.pdf:aipager 无法确认这是 Claude 在这个任务中发送的文件。 在 Claude 工作时发送、被它接着处理的消息,以及 Stop hook 让它继续的任务(/goal),也会带来文件。 每个已发送、被拒绝或发送失败的文件在审计日志里都有自己的记录。

语音消息#

语音消息会经过 faster-whisper(附加组件 aipager[voice])。音频在本机转成文字,以 🎙️ 我听到的是:… 的形式复述,然后像你自己输入的一样被输入,包括作为 /new 的名称或等待中的重命名。超过 Telegram 机器人 20 MB 下载上限的语音消息会被拒绝。接下来会发生什么,见 hook → UserPromptSubmit。


命令行:所有命令#

运行 aipager <command> --help,可以在你的电脑上看到同样的列表。

命令作用
aipager config交互式设置向导:机器人 token、聊天、成员和角色、Claude Code 的 hook。重新运行它可以修改任何东西(见群组)。
aipager setup不提任何问题地完成同样的首次设置,供脚本和编程代理使用(见下文)。
aipager start在前台运行 aipager。有另一个 aipager 在运行时,它会拒绝启动。
aipager service install [--yes]把 aipager 安装为后台服务(Linux 上是 systemd 用户服务,macOS 上是 launchd 代理)并启动它。--yes 会在已安装的服务文件不同时跳过询问。
aipager service start|stop|status|logs|uninstall启动、停止或查看服务,跟踪它的日志,或者卸载它。
aipager status [--json]一眼看到 aipager 和所有会话:状态、模型、上下文、费用,以及 Telegram 的限流状态。
aipager logs [-f] [-n LINES]aipager 的日志(Linux 上是 journald,macOS 上是 launchd 日志):最后 100 行,或者 -n 行;-f 持续跟踪。
aipager doctor [--fix] [--safety-check] [--json]健康检查(见故障排除)。--fix 会提议修复 Claude 的登录信息和不明确的 claude 安装,先询问;--safety-check 打印当前生效的安全规则;--json 供脚本使用(见下文)。
aipager session <name> [claude args]在当前文件夹里、在 dtach 下启动一个名为 <name> 的 Claude Code 会话,如果它正在运行就连接到它。名称之后(或 -- 之后)的参数会原样传给 claude,例如 --resume。需要一个正在运行的 aipager。
aipager session ls [-a] [--json]列出会话;-a 包括已结束的会话。也可以用 aipager session list。
aipager session kill <name> [-y]结束一个会话,除非有 -y,否则会先询问。
aipager resume [name]带着对话把一个已结束的会话找回来;不带名称时显示选择器。
aipager miniapp enable [--port PORT] [--url URL]开启 Mini App,在 127.0.0.1:PORT(默认 8765)上,位于托管隧道或你自己的 https:// 网址之后。在 aipager 下次重启时生效。
aipager miniapp disable|status关闭它(在下次重启时),或者显示它的设置和地址。
aipager policy validate检查 policy.yaml(以及 policy.d);有问题时以非 0 退出码退出。
aipager update通过安装 aipager 的那个安装程序(uv、pipx、Homebrew、pip)升级它。它从不重启 aipager;请自己重启,或者使用 Telegram 里的 /update,它能做到这一点(见更新)。
aipager report [--note TEXT]显示一份问题报告,如果你同意就发送它(见下文)。
aipager uninstall [-y]停止并卸载 aipager 和它的配置,保留数据(见下文)。
aipager version、aipager --version打印版本。
aipager help [command]aipager 或某个命令的帮助。

命令行:aipager setup(供编程代理使用)#

上面的部分介绍的是 Telegram 机器人。这一部分介绍的是你在终端里运行的一个命令,或者你的脚本或编程代理替你运行的命令:aipager setup 不提任何问题地设置 aipager,结果和第一次运行 aipager config 相同。

sh
# 1. 找到用户的 Telegram ID(用户打开机器人并点 Start)
aipager setup detect-chat --token-file ~/bot-token.txt --json
# 2. 用户确认是本人之后,设置 aipager
aipager setup --token-file ~/bot-token.txt --chat-id 123456789 --service --json
# 3. 检查安装
aipager doctor --json

它按这个顺序做的事:读取机器人的 token,向 Telegram 验证 token(getMe),在不写入任何东西的情况下检查这台电脑上的一切(现有的配置、依赖项、~/.claude/settings.json、是否有 aipager 在运行),给用户发一条测试消息("aipager is set up for you. ..."),然后才写入。写入的文件,和设置向导写入的一样:

  • ~/.config/aipager/aipager.yaml(权限 0600):bot_token,以及一个唯一成员是用户的私聊 scope(label: owner,owner 或 admin 角色)。
  • ~/.claude/settings.json:aipager 的 hook 和 statusLine(现有的文件在改变时会备份为 settings.json.bak.<time>)。
  • 当有人成为所有者时,审计日志会得到一条 grant-owner 记录。
  • 带 --service 时,后台服务(和 aipager service install --yes 一样)。
  • 只在旧的 config.env 安装上(还没有 aipager.yaml),设置向导会做的转换:一份 config.env.bak.<time> 副本(有 team.yaml 时还有 team.yaml.bak.<time>),以及在还没有时的一个示例 ~/.config/aipager/policy.yaml。config.env 本身保持不动。

如果任何一项检查失败,什么都不会发送,什么都不会写入。当 aipager 已经在运行时,聊天或角色的更改会不重启地重新加载;新的机器人 token 需要重启(aipager service stop,然后 aipager service start),setup 从不自己做这件事,--service 会被忽略。如果 setup 无法判断是否有 aipager 在运行,它会当作有(警告 daemon_unknown):这时它从不安装服务,所以绝不会重启正在运行的 aipager,并且在任何更改之后都不发送重新加载的指令,而是报告 restart_needed: true。

aipager setup 的参数#

参数含义
--token-file PATH从这个文件读取机器人的 token(最多 4096 字节;token 周围有其他文字也没关系)。其他用户能读的文件仍然可以使用,但会有警告 token_file_shared:请 chmod 600 它,或者在 setup 成功后删除它。
--token-stdin从 stdin 读取机器人的 token(管道或重定向的文件,绝不是终端)。管道必须关闭:setup 最多等 30 秒让 stdin 结束,然后以退出码 2 和 token_stdin_timeout 退出。printf '%s\n' "$TOKEN" | aipager setup --token-stdin ... 和 < token.txt 都会关闭它。
--chat-id N必填。这个人的数字 Telegram 用户 ID,也就是这个人私聊的 ID。群组用 aipager config 添加。
--role owner|admin这个人的角色(默认 owner)。
--service同时安装并启动后台服务(有 aipager 在运行时会被忽略)。
--force允许替换现有安装的机器人 token、所有者的私聊或角色。群组 scope 和其他私聊保持不变。
--dry-run运行所有检查,不发送任何东西,不写入任何东西,并报告会改变什么。
--json在 stdout 上只打印一个 JSON 对象;其他一切都输出到 stderr。

--token-file 和 --token-stdin 必须恰好给出其中一个。token 绝不会作为参数的值被接受:--token 和 --bot-token 会被拒绝(token_on_command_line),值也不会被显示。缩写的参数(--token-f、--dry)不被接受。setup 从不提问,从不需要终端,也从不从环境变量读取 token。

用同样的 token、聊天和角色重新运行不会改变任何东西,并会说明这一点(unchanged,没有测试消息)。在现有安装上使用不同的 token、聊天或角色会以退出码 6 退出,直到你加上 --force。旧的 config.env 安装会先被转换,就像设置向导做的那样(它给出 admin 角色,所以在那样的安装上使用 --role owner 需要 --force)。

aipager setup detect-chat#

sh
aipager setup detect-chat (--token-file PATH | --token-stdin) [--timeout SECONDS] [--json]

等待发给机器人的最新私聊消息(点 Start 就会发送一条),并打印它的发送者:ID、名字和用户名。它不保存任何东西,所以请把这个人给用户看,只有在用户确认后才运行 aipager setup --chat-id。只有在命令开始之前不超过 10 分钟发送的消息才算数。--timeout 是 0 到 3600 秒(默认 300;0 表示只看一次)。它读取 Telegram 时不会确认任何消息,所以 /start 仍然留给设置向导和 aipager 使用。

当同一个机器人的 aipager 正在运行时,那个 aipager 会接收机器人的消息,而且它不会记录尚未设置的人发来的私聊消息。这时 detect-chat 会以退出码 9 退出(source: "daemon"):停止 aipager(aipager service stop)然后重新运行,或者向用户要数字 ID。对于另一个机器人的 aipager,它会直接读取 Telegram(source: "telegram")。

限制:因为 detect-chat 从不确认消息,Telegram 只让它看到过去 24 小时里最早的 100 个未读更新。当机器人有这么多在等待时,新的 /start 是看不到的:这时 detect-chat 会超时(退出码 8)并发出警告 updates_backlog,再点一次 Start 也没用。请改为向用户要数字 Telegram ID(或者启动一次 aipager,它会读取并清空积压)。

退出码#

退出码error含义
0成功(installed、updated、unchanged、dry_run,或者 detect-chat 的 found)。
1config_malformed、ambiguous_install、settings_invalid、test_send_failed、write_failed、service_failed、internal_error失败。ambiguous_install:有不止一个私聊可能是所有者的;请在 aipager config 里修复。
2usage、missing_token_source、token_source_conflict、token_file_unreadable、token_stdin_is_tty、token_stdin_timeout、token_malformed、token_on_command_line、bad_chat_id、bad_role、bad_timeout输入错误或缺失;fix 会指出是哪个参数。未知的参数或 setup 后面的错误单词会由参数解析器报告(纯文本,没有 JSON),也是退出码 2。
3deps_missing缺少 dtach、claude、aipager-hook 或 aipager-statusline;fix 里有需要运行的命令。不写入任何东西。
4token_rejectedTelegram 拒绝了 token。
5chat_not_started、bot_blocked用户还没有在机器人里点 Start("Open t.me/<bot> and press Start, then run this again."),或者屏蔽了机器人。不写入任何东西。
6existing_install已经设置了不同的 token、聊天或角色;请加上 --force。
7telegram_unreachable网络错误,或者 Telegram 回复了 429 或 5xx。
8detect_timeoutdetect-chat 在 --timeout 结束之前没有看到任何私聊消息(当 100 个旧的未读更新挡住了更新的消息时,会附带警告 updates_backlog)。
9daemon_runningdetect-chat:一个正在运行的 aipager 在接收机器人的消息。
10updates_conflictdetect-chat:另一个程序在读取这个机器人的消息。
130interruptedCtrl-C。

当多项检查同时失败时,退出码按这个顺序:config_malformed、ambiguous_install、existing_install、settings_invalid、deps_missing。--dry-run 会以真正运行时这些检查会得到的退出码退出。

JSON:aipager setup --json#

每个键总是存在;未知的值是 null。

json
{
  "command": "setup",
  "status": "installed",
  "ok": true,
  "exit_code": 0,
  "error": null,
  "message": "aipager is set up for @example_bot (chat 123456789, role owner). ...",
  "fix": null,
  "dry_run": false,
  "bot_username": "example_bot",
  "chat_id": 123456789,
  "role": "owner",
  "changed": ["bot_token", "owner_dm", "settings_json"],
  "test_message": "sent",
  "deps": {"ok": true, "items": [{"name": "dtach", "found": true, "path": "/usr/bin/dtach", "fix": null}]},
  "settings_json": {"path": "/home/you/.claude/settings.json", "status": "created", "backup": null, "repointed": 0},
  "daemon": {"running": false, "reload": "not_needed", "restart_needed": false},
  "service": {"requested": false, "result": "not_requested"},
  "warnings": [],
  "next_step": "Start aipager with `aipager service install` ..."
}
  • status:installed、updated、unchanged、dry_run 或 error。
  • changed:改变了什么(或者试运行时会改变什么),按这个顺序:migrated_v1、bot_token、owner_dm、role、settings_json、service。
  • test_message:sent、not_needed、skipped_dry_run、failed 或 not_attempted。
  • settings_json.status:created、patched、unchanged、would_change 或 not_checked。
  • daemon.reload:reloaded、refused、not_reloaded 或 not_needed。在 aipager 运行期间换了新 token 之后,以及在 aipager 无法重新加载、或 setup 无法判断是否有 aipager 在运行时的任何更改之后,restart_needed 为 true。
  • service.result:not_requested、installed、already_installed、skipped_daemon_running、would_install、failed,或者在 setup 于那一步之前停止时为 null。
  • warnings:带有 code(token_file_shared、reload_refused、daemon_unknown、audit_write_failed)和 message 的对象。audit_write_failed:所有者权限已经写入,但它的 grant-owner 记录无法追加到审计日志里;记录只是尽力而为,所以退出码仍然是 0。
  • 在 aipager 运行期间、无法重新加载的更改之后,next_step 会让你重启它(aipager service stop,然后 aipager service start);只有在 token 改变时才会提到机器人 token。
  • 有警告 daemon_unknown 时,next_step 绝不会说 aipager 正在运行。它会说 setup 无法判断,指向 aipager doctor --json,并让你在 aipager 没有运行时用 aipager service install 或 aipager start 启动它。需要重启时,它只在有 aipager 运行时要求重启,否则让你用同样的方式启动它。
  • 出错时,status 为 error,error、message 和 fix 说明哪里出了问题以及该怎么做;next_step 重复 fix。当错误发生在写入之后,changed 会列出已经写入的内容,输出通常会多一行 stderr:Already written: aipager.yaml, ... (changed: ...)。

JSON:aipager setup detect-chat --json#

json
{
  "command": "detect-chat",
  "status": "found",
  "ok": true,
  "exit_code": 0,
  "error": null,
  "message": "Found Ada L (@ada), Telegram id 123456789.",
  "fix": null,
  "bot_username": "example_bot",
  "source": "telegram",
  "candidate": {"id": 123456789, "first_name": "Ada", "last_name": "L", "username": "ada", "date": 1700000000},
  "other_candidates": 0,
  "warnings": [],
  "next_step": "Show this person to the user; once they confirm, run: aipager setup --token-file <path> --chat-id 123456789"
}

除非 status 是 found,否则 candidate 为 null。warnings 包含带有 code(token_file_shared、updates_backlog)和 message 的对象。other_candidates 统计在同样的 10 分钟里给机器人发过消息的其他人:它不是 0 时,请确认你选对了人。next_step 会重复你使用的 token 参数,绝不会重复 token 本身。

aipager doctor --json#

aipager doctor 的检查,以一个 JSON 对象输出到 stdout。和普通的 aipager doctor 一样,它只读取,不发送任何 Telegram 消息。有检查失败时以退出码 1 退出,否则为 0。它不能和 --fix 或 --safety-check 一起使用(以退出码 2 和一个错误对象退出)。

json
{
  "command": "doctor",
  "version": "1.0.1",
  "ok": true,
  "summary": {"ok": 14, "warn": 1, "fail": 0},
  "checks": [{"key": "token_valid", "status": "ok", "title": "Telegram bot token", "detail": ["@example_bot"], "fix": null}]
}

checks 按这个顺序、使用这些键:config_parses、config、token_valid、chat_reachable、team、role_shell_access、peer_messages、claude、claude_auth、dtach、hook_scripts、settings_json、daemon、service_installed、service_unit_path、miniapp。崩溃的检查仍然保留它的键,状态为 warn。文字是纯文本(没有格式),任何看起来像机器人 token 的内容都会被替换为 <redacted>。

命令行:aipager uninstall#

aipager uninstall 会停止 aipager 并卸载它的服务,删除配置文件夹(~/.config/aipager:机器人 token、聊天和设置)和会话列表(~/.claude/aipager-sessions.json),清理 aipager 在 /tmp 里的文件,并卸载 aipager 本身。它会先询问(-y 跳过询问)。

它会保留 aipager 的数据,这样重新安装后可以接着上次继续:~/.local/share/aipager(会话文件夹、下载的辅助程序、锁文件和标记文件)、~/.local/state/aipager(问题报告的记录和语言状态)、~/.claude/aipager-audit.jsonl(对权限请求的回答日志)、~/.claude/aipager-flood-state.json(Telegram 的限流历史)、~/.claude/aipager-pending-users.json(没有权限却尝试使用机器人的人)以及 ~/aipager-users(成员的根文件夹和他们的工作)。在询问之前,它会列出你电脑上有哪些这样的内容,最后会打印出把它们也删除的 rm -rf 命令。

它还会从 Claude Code 的 ~/.claude/settings.json 里删除 aipager 自己的条目:运行 aipager-hook 的 hook,以及运行 aipager-statusline 的状态行(你自己的状态行会保留)。这个文件里的其他内容都保持不变,文件之前的版本会以 settings.json.bak.<time> 保存在旁边。如果这个文件是指向另一个文件的链接、无法读取、不是有效的 JSON,或者不是 Claude Code 能读取的格式,它会原封不动,uninstall 会说明哪些条目需要手动删除。你自己的提到 aipager-hook 或 aipager-statusline 的 hook(例如包装脚本 aipager-hook-capped.sh)也会保留,uninstall 会写明它。打开着的 Claude Code 会话会继续运行它们已经加载的 hook,直到你重启它们。重新安装之后,aipager config 会把这些条目加回来。你的 Telegram 机器人和较早的 settings.json.bak.* 备份不会被动,skipDangerousModePermissionPrompt(跳过 Claude Code 在关闭权限检查运行之前的警告的设置)也一样,因为 aipager 设置过它,但你自己也可能设置过。

命令行:aipager report#

aipager report 根据 aipager 在这台电脑上记录的内容,生成一份和机器人的“报告问题”按钮同类的问题报告(关于正在运行的 aipager 的信息更少:模式、聊天、功能、运行时长、会话和 Claude Code 的细节留空或标为未知),完整地打印出来,并问 Send this report? [y/N]。只有 y 或 yes 才会发送;其他任何回答,或者没有终端时(脚本、管道、Claude Code 会话的 shell),都不会发送任何东西。--note "text" 会添加一条最多 500 个字符的备注,显示在打印出的报告里。报告已发送、按你的选择没有发送或者只是打印出来时,它以退出码 0 退出;你确认的发送没能发出时,退出码为 1。一份报告包含什么、发到哪里,见问题报告。

环境中的设置#

有几项设置是从 aipager 的环境里读取的,而不是从 aipager.yaml。对于 systemd 服务,用 systemctl --user edit aipager 把它们加在 [Service] 下(Environment=STALE_BUSY_TIMEOUT=900),然后 aipager service stop 和 aipager service start;对于 macOS 上的 launchd 服务,把它们加到 plist 文件的 EnvironmentVariables 里(~/Library/LaunchAgents/com.aipager.daemon.plist;aipager service install 会重写这个文件,所以运行它之后请重新添加);对于你自己启动的 aipager,在 aipager start 之前设置它们。正常使用时,这些设置都不需要。

设置默认值改变什么
AIPAGER_WHISPER_MODELbase语音消息的语音识别模型(tiny、base、small、medium……):越大越准确,也越慢。
MINIAPP_PORT8765Mini App 在 127.0.0.1 上的端口(aipager miniapp enable --port 会把它保存下来)。
MINIAPP_ENABLED按 aipager.yaml0 关闭 Mini App,1 开启它,无论 aipager.yaml 怎么写(aipager miniapp enable|disable 会把选择保存下来)。
MINIAPP_PUBLIC_URL无在你自己的 https:// 网址上提供 Mini App,代替托管隧道(安全)。
KEEP_FINISHED_CARD10 会为从未在 /settings 里选择过布局的聊天,把消息布局设为“只留回答,删除卡片”。
STALE_BUSY_TIMEOUT7200(2 小时)工作中的会话可以沉默多少秒,之后聊天会收到一条“仍在工作(120 分钟没有消息)”的提示。
PROMPT_HOOK_GRACE_SECONDS8Claude Code 一直没有取走的消息,在多少秒之后被标为“Claude Code 没有收到消息”(自由消息)。
FINISH_CARD_GRACE_SECONDS0.8在有卡片的布局里,完成的卡片比回答提前多久;0 表示两者同时发送。
GONE_SESSION_MAX_AGE_DAYS14已结束的会话在 /resume 里保留多少天;0 表示永远保留。
STREAM_EDIT_INTERVAL、BUSY_EDIT_INTERVAL1.2、3.0工作卡片在输出文字时和安静时的刷新节奏,以秒为单位;Telegram 对每个聊天的限制仍然优先(故障排除)。
TYPING_INDICATOR_INTERVAL4.5“正在输入…”提示之间的秒数;0 关闭它们。
TELEGRAM_MAX_RETRY_AFTER90Telegram 要求等待的秒数超过多少时,aipager 会把一次限流当作封禁(不要调低它;见故障排除)。
CLAUDE_RICH_SUMMARIES10 关闭带代码块的回答的丰富格式(从旧版本保留下来的一种后备方式)。
AIPAGER_INTERACTIVE_TIMEOUT300一个权限请求或问题在 Claude Code 没有任何消息的情况下等待多少秒后,aipager 会查看保存的对话;只有对话显示请求已经结束,会话才会重新算作工作中(故障排除)。
AIPAGER_SUBAGENT_SILENCE1800沉默多少秒之后,还没看到停止的子代理会被放弃(hook)。
AIPAGER_BG_SHELL_MAX_TRACK7200多少秒之后,还没看到结束的后台命令不再计数。
AIPAGER_REPORT_PROMPTS开0 关闭自动提议发送问题报告(问题报告)。
AIPAGER_CLAUDE_BIN无安装了多个版本时要运行的 claude 程序(aipager.yaml 里的 claude_path 优先)。
AIPAGER_SOCKET_PATH$XDG_RUNTIME_DIR/aipager.sockaipager 的控制套接字。aipager 启动的会话会把它传给自己的 hook。
AIPAGER_DEBUG关在会话的环境里设为 1,会让 aipager-hook 和 aipager-statusline 把诊断信息打印到 stderr。
AIPAGER_INSTANCE_DIR、AIPAGER_TELEGRAM_API_BASE无第二个独立的 aipager,用于测试(架构)。

另见#