守护进程(Daemon)
本机常驻的 TypeScript 进程,自己跑 tool-call 循环。关窗不停;窗口崩了轮次还在。插电时,有轮次在跑或有渲染在等,它就挡住空闲睡眠(屏幕照样会灭);合盖、菜单栏睡眠、用电池、关机仍会睡,唤醒后续上。只有窗口进程还活着(含藏到托盘)时,被杀的守护进程会被拉起来;窗也没了之后再崩就停,直到你再开 App 或下次登录。设置里有可选独立运行时,默认关,生产在原生封闭门通过前保持禁用;未开启时监督仍是窗口进程。它还持有你开的终端会话,所以关窗之后那些进程照跑。退出或守护进程自己停掉时,进程一起停,但开在哪、屏幕上有什么会留下来;下次启动在原来的位置重新开一个,分屏布局按这个接回去。结束会话才忘掉它。
回避:云电脑, 24/7 服务器, 把 UI 进程当运行时, 合盖仍继续, 杀一次就停, 独立 KeepAlive 登录项, 关窗杀掉终端
窗口(App window)
Tauri 桌面窗,里面是 Svelte 写的界面(代码和开发说明里叫信使,messenger)。关窗是隐藏到托盘,不是退出;隐藏时仍留在 Dock。登录时只到 Dock 和托盘,窗不弹。点 Dock / Cmd-Tab / Spotlight / 托盘左键 / 再开 App 把窗叫回来。同时只跑一个窗口进程;第二次启动只唤醒已有的那个并连上已有守护进程。窗口崩了不会自己回来。连上守护进程之前:退出可用,Stop 不可用,界面显示「连不上运行时」。拖过的宽高和是否最大化,退出后再开还是关前那样;不记位置,免得换显示器落到屏外。拖边框的过程中不去问窗口是不是最大化:那一问在 macOS 上会把窗口挪开,松手时位置就和拖着的时候不一样。
回避:只用浏览器打开, 无托盘, 隐藏即从 Dock 消失, 窗口 KeepAlive, 登录抢屏, 多窗口, 窗崩当退出
退出(Quit)
结束这次会话。窗口监督(默认)时窗口进程和守护进程都停,托盘和 Dock 都消失。独立运行时若已开启,退出只关 UI,作业继续;要停作业须显式 stop(写 runtime.stop)。进行中的轮次标成中断,你开的终端会话里的进程一起停;下次打开时,每个还开着的终端在原来的位置重新开一个,分屏布局按这个接回去。不是关窗。结束会话才忘掉它。不动登录启动偏好。关机和注销在轮次上也按退出处理。开发态同一条规则。生产独立运行时仍 gated,默认仍是窗口监督。
回避:关窗当退出, 默认只关窗留无头守护进程, 退出当关掉开机启动, 开发态 Cmd+Q 只关窗, 把 gated 独立模式当成已开启
登录启动(Launch at login)
可选,默认开。登录时只拉窗口进程到 Dock 和托盘,窗不弹;窗口进程再确保守护进程在跑。关掉只影响下一次登录,不杀当前这次;下次登录窗和守护进程都不来。退出也不清这个开关。守护进程不是自己的登录项。
回避:登录弹窗, 关掉立刻杀掉当前这次, 守护进程自己当登录项, 退出清掉开机启动, 关掉开机启动却留下会自己起来的作业
托盘(Tray)
窗口进程上的菜单栏图标。左键把窗叫回来。右键:显示窗口、Stop(没开窗则最近活动轮)、退出。没有隐藏、没有暂停守护进程。窗崩了托盘也没了。
回避:托盘角标, 第二套界面, 守护进程自己做托盘
本机接口(Local API)
守护进程在 127.0.0.1 上给窗口用的 HTTP + WebSocket,带本机 token。变更走 HTTP;转录流走一条只收的 WebSocket。窗口经它读写名册、发消息、订阅转录流。
回避:把 UI 进程当运行时, 云 API, 双向命令通道, 工作区文件当 HTTP 资源, 把本机 token 放进 URL
本机 token(Local token)
守护进程每次进程启动新铸的随机秘密,用来认证对本机接口的请求。HTTP 用 Authorization: Bearer;WebSocket 在连上后第一条消息带上,不进 URL。窗崩不换;守护进程退出或崩溃后下一进程再铸。不是端点 API key,不进 SQLite,不进仓库。
回避:API key, 安装一次的长活密钥, 写进 .env, 放进 WebSocket query
本机记录(Records)
这台机器上应用自己存下的东西:库里的消息、轮次、工具调用、工作记录、质量事件、规划与任务、交付与审查、需求台账、整理跳的运行记录、判断、花费、复盘、记忆与技能、内置提示词的修改,以及守护进程的日志。Bot 用 describe_data 看有哪些表,用 query_data 跑一条只读的 SQL,用 read_data_log 读日志的末尾,拿来回答「哪类活常被退回」「哪条调用常读不懂」,再据此提改进。只读,在守护进程另起的子进程里跑,10 秒就停;不出批准卡,叫停中也能查。查不到的就是不给 Bot 看的:远程访问的密钥和回执、推送配置、你的终端都在拒绝名单上,数据目录里只读得到两份日志,日志里像密钥的抹掉。只在你要它分析、改进时才查。细节见行为说明·本机记录和 ADR 0065。
行为细节
Bot 能只读地查这台机器上应用自己存下的东西,拿依据分析、提改进(ADR 0065):
- 三个工具:
describe_data列出表和行数,主要的表附一句说明,后面附几条现成的查询(recipes),给table看那一张的列和类型;query_data跑一条只读的 SQLite 查询,或用recipe按名字跑现成的那几条——单条 SELECT 或 WITH … SELECT,?按params顺序绑定,默认 100 行、上限 1000,长格子截断,二进制只给字节数,10 秒没答完就停,列名写错时错误里带上点到的表的列;read_data_log读daemon.log(或开发版的daemon-dev.stderr.log)的末尾,可以 grep。都没有副作用:不出批准卡,叫停中、只读段里也能用。Claude Agent 跑的 Bot 也有。 - 怎么跑:查询在守护进程另起的子进程里跑(
--query-data),只读连接加query_only,同时最多两个;超时直接杀掉,不卡守护进程。 - 查不到的:
remote_*、pending_keys、request_receipts(远程访问的密钥和回执)、notification_push_config、notification_devices(推送)、terminals(你的终端)、sqlite_*。名单上的表回「no such table」;经视图、索引碰到它们,或想打开别的库、写库的,按查询计划拦下。数据目录里只读两份日志,不跟软链,日志里像密钥的抹掉。 - 读得到的也包括别的 Bot 的私聊和记忆(只读,不进你的上下文)、你在聊天里贴过的任何东西——密钥该贴在批准卡上,别贴进聊天。
- 什么时候用:只在你要 Bot 分析、改进时;它拿到的依据用来改自己的技能和记忆,或者经批准卡改内置提示词。
回避:Bot 写库, 让 Bot 读终端或远程访问的密钥, 读数据目录里日志以外的文件, 后台定时去查, 把别的 Bot 的记忆读进自己的上下文
界面方案(Interface profile)
你对应用样子和用法的改动,叠在应用自己的上面:令牌(颜色、字体、圆角、阴影这些设计令牌,浅色深色分开设)、一份固定的用法清单(专注模式、每种窗口从哪打开、回车还是 ⌘回车 发送、时间格式、动效、代码配色、内容区玻璃)、你自己的 CSS,和界面上改过的字。所有窗口和配对的手机共用一份。在任何一个和 Bot 的会话里说你想要什么,Bot 用 describe_interface 看、edit_interface 改:这是一次「改」,按它的权限模式,自动和全部允许下立刻生效,改动先问时出卡、能先在窗口里试看,只读下拒绝;它回复下面挂着撤销。你在「设置 › 界面」里也能看、改、撤销,每次修改都记着谁改的、为什么。停止按钮、等你的卡片和设置入口不能被盖住:哪个窗口里被盖住了,那个窗口就停用令牌和 CSS;?ui=default 和 ⌥⇧⌘U 让一个窗口暂时回到默认样子。拖出来的宽度和布局不在方案里,照旧只存本机。细节见行为说明·界面方案和 ADR 0106。
行为细节
应用的样子和用法你能改,也能让 Bot 照你说的改(ADR 0106):
- 改什么:令牌(
tokens.css里的任何--x:颜色、字体、圆角、阴影、聊天最大宽度,浅色、深色分开或一起设);用法(专注模式、每种窗口在桌面上从哪打开、回车还是 ⌘/Ctrl+回车 发送、24 或 12 小时制、动效跟随系统或一直减弱、代码配色、内容区玻璃);自定义 CSS(最多 64 KB);界面上的字(按位置的键,原文的占位符一个不能少);还有主题和语言。拖出来的宽度和布局不在里面,照旧只存本机。 - 你的消息气泡跟着强调色:改了
--accent而没单独改--you时,你发的消息的气泡跟着强调色变(深色下深一档,白字看得清);要气泡和强调色不一样,就单独设--you。 - 怎么让 Bot 改:在和它的会话里说,比如「强调色换成紫色」「侧栏深一点」「改成 ⌘回车 发送」。它先
describe_interface看现在的样子、可用的钩子和令牌,再edit_interface改,然后过几秒再读一次,看各个窗口的回报。按权限模式算「改」:自动和全部允许下立刻在所有窗口生效,它的回复下面挂着「界面已改 · 撤销」;改动先问时出一张ui-edit卡,能点「在这个窗口试看」,放行才生效;只读时拒绝。 - 稳定的钩子:自定义 CSS 用
[data-ui="…"]当选择器(侧栏、会话行、消息、你的话、Bot 的回复、输入框、批准卡、窗格、标签、设置、流程图……一共二十多个),它们跨版本不变;类名可能会变,应用更新后对不上的选择器会出现在回报里。根上有data-surface="desktop|tablet|phone"可以分设备写,data-theme分浅色深色。 - 底线:停止按钮、等你的卡片上的按钮、提问卡、去设置的入口、暂停自定义界面和撤销按钮不能被盖住。每个窗口套用之后都查:被方案藏起来、变得几乎透明、缩到很小、点不到或被别的盖住,就在这个窗口停用令牌和 CSS,提示一次,并回报给 Bot。应用自己收起来的按钮不算。
- 回到默认:地址里加
?ui=default,或按 ⌥⇧⌘U(Windows、Linux 上 Ctrl+Alt+Shift+U),这个窗口暂时回到应用自己的样子,再按一次恢复;「设置 › 界面」的开关让所有窗口都回去,「全部恢复默认」把四部分一起清掉。 - 记录:每次修改记着谁改的、为什么、前后的整份方案和当时的应用版本。只能撤销最新的一次,而且只在界面还是它改成的样子时;任何一版都能恢复。设置里连续的保存合成一条,改回原样另记一条。
- 设置 › 界面:最上面是总开关(这个窗口暂时用默认界面时,原因也写在这里),下面分五页:用法、令牌、自定义 CSS(停手一秒自动保存)和可用的钩子、界面上的字、修改记录(连同各种窗口的回报、全部恢复默认、回到默认的快捷键)。宽窗口里是开关下的一排标签;手机上是一张列表,每行写着这一页改了什么,点开是单独一页,返回回到列表。
- 代码配色:浅色、深色各从一份固定名单里选一个 Shiki 主题(GitHub、Vitesse、One、Solarized、Catppuccin、Rosé Pine、Gruvbox、Dracula、Nord、Tokyo Night、Monokai……),聊天代码块、命令输出和文件编辑器都用它,连同它的底色和正文色;没选的那种外观用应用自己的(聊天 GitHub、编辑器 Vitesse)。换了之后屏幕上的代码就地重画。暂停自定义界面时它照旧,它算用法。
- 内容区玻璃:实色、磨砂、通透三档。打开后内容卡片透出窗口的毛玻璃(只在有窗口材质的桌面上:macOS、Windows 11),卡片上的对话(Bot 的和项目会话都算,连同顶上的会话头)、文件预览、流程图和文件树不再各画一层底;菜单、弹窗、浮窗、编辑器和终端保持实色。令牌
--content-glass(卡片留多少,比如60%)能按外观微调。别在钩子上写backdrop-filter:模糊是窗口材质做的,它只会把弹出的图片和菜单困在窗格里。 - 够不着的:PDF 和 Office 预览只认浅色深色;少数组件自己重定义了令牌,要用 CSS 改。字号和间距不是令牌,整体缩放还没有。
回避:Bot 不问就改界面, 用 CSS 藏掉停止或批准按钮, 写在类名上指望它永远对得上, 每个设备一份界面, 把布局和宽度推给手机, 改应用的源码来换样子
中断(Interrupted)
守护进程挂了、被杀后拉起、你退出、关机或注销时,进行中的轮次被标成中断。那些轮里还在跑的命令(连同它开出来的进程)被要求退出;守护进程挂了、或退出时它们还没退完的,下次启动时停掉,停之前先核对这个进程号还是当初那个进程,已经换成别的程序的不碰。转录里那一行和其他 Bot 消息一样带名字和头像(你和它单聊时两样都不带),「继续」贴在「中断」气泡后面:新开一轮、触发条是这条中断行,不重试断掉的那一下;该 Bot 的下一轮(哪个会话都行)能看见「上次断了」,看一次即止。重启打断的活,3 级及以下不会自己接着做,4 级起由监督器按重启原因决定是否、何时自己接着做;会自己接着做的(或被你的叫停扣着的)不发什么,各轮自己的「中断」行就够了。不会自己接着做的(3 级及以下、结果不明的外部调用、开发版接连重启、没挂在事上的轮),重启后在这件事所在、有你的会话里(你最近说它的地方优先)每件各发一句:为什么重启(有意停下、意外退出、开发版重启)、每一轮停在哪一步,带「继续」「先放着」和一条通知;本来要自己接着做、还没来得及守护进程又启动的,由后一次启动补发这一句。细节见行为说明·中断。合盖 / 系统睡眠不是中断:冻住,醒来接着跑。睡着的时间不算卡住,也不算命令超时;被睡眠打断的请求醒来后等网络回来(约一分钟)再整个重发。窗口崩了不是中断:守护进程继续。补全超时、流挂、端点 4xx/5xx 不是中断。健康检查暂时失败而守护进程进程还在,也不是中断:窗不拉第二份,第二份抢不到端口就不改库。
行为细节
守护进程重启时,进行中的轮次都被标成中断,那一行写在这一轮所在的会话里。长活的轮多半在你不在的 Bot↔Bot 私聊里:2026-09-26 一件视频活被重启打断,中断只写在那条私聊里,停了 7.6 小时,直到你自己发现去点了继续。所以重启之后,引擎一起来、调度器走第一拍之前(停机期间到期的回看和日程不会抢在它前面叫醒 Bot),被打断、又不会自己接着做的活每件各发一句。4 级起监督器会自己接着做的(有意停下立即续;意外退出稳定 1 分钟后续一次,开发版重启连续 5 分钟没有再重启后续一次),和被你的叫停扣着的,这一句不发、通知也不发:各轮自己的「中断」行就够,它们各自的中断通知作废、标已读(2026-10-04 起;之前每次开发版重启都发一句、一条通知,2026-10-04 早上《一拳超人》的两句上你都按了「不续」,第二次把一件 1 分钟后本来会自己接着做的活放下了)。要发的是:3 级及以下的、最后一步结果不明的、开发版 5 分钟内接连重启的、没挂在事上的轮(监督器不接);本来要自己接着做、还没到 1 分钟守护进程又启动了的(之后就不会自己做了),由后一次启动补发,写明「它是之前开发版重启时断的,还没来得及自动接着做,守护进程又启动了一次」。这一句发在这件事所在、有你的会话:规划的轮发在你最近说这件事的会话(和监督器自己的提醒一样,你还在那里时),否则规划所在的会话;你不在那里时,依次找这一轮所在的会话、这条 Bot↔Bot 私聊往上开出它的那个会话、你和这个 Bot 的私聊。同一件事、同一个地方的几轮(比如一件规划在两条 Bot↔Bot 私聊里各断了一轮)合成一句。原来先发在规划所在的会话:2026-10-04 一件在群里开、上午在你和视频导演的私聊里接着做的事,两次重启的这一句都发在群里,「中断」行却在私聊里、挨着你最后说的话。这一句是应用写的系统行,不进 Bot 的上下文,显示成应用自己的话(见叫停的按钮):
- 为什么重启:这一次是开发版(
bun --watch/--hot跑的,或带REAL_BOT_DEV=1),说「开发版守护进程重新启动了」,不管上一次怎么结束的:--watch每次保存代码都先发 SIGTERM、不等进程退出就原地重启,收到 SIGTERM 的停机又先写下了「有意停下」,在那里这个标记分不清改代码和退出;不是开发版、上次是有意停下的(退出、更新、你让它重启,写了last_shutdown = clean),说「守护进程停下后重新启动了」;其余是「守护进程意外退出后重新启动了」。 - 断在哪:每一轮的 Bot、所在的任务(没有任务就是规划)、不在这个会话时它在哪、最后一步(执行记录里最后一条命令或 MCP 调用)。
- 接下来:3 级及以下不会自己接着做;4 级起这一句写明监督器的续跑安排(有意停下立即续;意外退出稳定 1 分钟后续一次;开发版连续 5 分钟没有再重启后续一次,不管之前重启过几次;最后一步是结果不明的外部调用、或被叫停覆盖的不自己续),每一轮安排不同时逐个写明,自己续跑走的就是下面这个「继续」。点「继续」,这件事被打断的每一轮都照它自己那条中断行的「继续」接着做:新开一轮,触发条是那条中断行,同样受叫停管——叫停把这几轮全扣住时这次按下不算,解除后再按;只扣住其中几轮时,其余的照常接着做,按钮旁说接着做了几轮、还有几轮被叫停扣着,这一句不算按过,解除后再按接着做剩下的;已经接着做了的、Bot 已经在那里干活的、会话或 Bot 没了的跳过,都已经接着做了时按下就算按过,什么也不多做。点「先放着」就先不动它,中断行自己的「继续」回来,之后还能从那里接着做(这个按钮 2026-10-04 前叫「不续」,读起来像是关掉提醒)。每句只按一次。
- 通知:发了这一句的,一件事一条(
interrupted),这几轮各自中断行的通知作废;同一个会话里被这一句覆盖的中断行,在你按下按钮之前不再各带一个「继续」。没发这一句的,各轮的中断通知作废并标已读(goes_on_by_itself),不弹系统通知、不算 Dock 角标。
哪些轮算被重启打断:守护进程开始一次停掉所有轮时(退出、应用更新或远端重启时的强制排空、守护进程自己停掉),以及启动时收拾上一次留下的活轮时,把那一刻的活轮记下来;下次启动只取仍是中断、中断行还没被接着做的。只有紧接着的那次启动取:中间另有一次运行开过库却没取(没起来就挂了),或者中间跑过一个不认识这条记录的旧版应用、又开过轮,这条记录就过时了,只清掉,不拿这一次重启的原因去说那次早先的停机,那些轮的中断行和通知照旧。发这几句时出错只写一行 daemon.log,守护进程照常起来。卡住被清扫中断的轮不算;强制排空之后又取消的,取消时忘掉。每次启动在工作记录里记一条 daemon.restart(原因和被打断的轮),有被打断的再写一行 daemon.log。通知本身不看引擎级别;按原因自己续跑在 4 级(监督器),先查副作用记录,最后一步结果不明的不续,否则最后一步是送审或渲染时会付两遍钱。每一行都被接着做之后(不论是监督器还是你),这一句记为已继续、通知解决。「继续」「先放着」走 POST /v1/messages/:id/control({action: "resume" | "leave"},手机上同样可用),这一句的 control 是 {kind: "restart", cause, notes, offer, acted?},notes 是这几轮的中断行;只接着做了一部分时回答带 partial: {continued, held}。
回避:从断点接着调工具, 中断只写在你不在的 Bot↔Bot 私聊里, 会自己接着做的也发卡片让你点「继续」「不续」, 重启后不问你就把结果不明的最后那次送审或渲染再交一遍, 不核对就按进程号杀上次留下的进程, 睡眠当中断, 关机当睡眠, 窗崩当中断, 把「上次断了」永远挂在人设上, 把补全超时当中断, 把端点错误当中断, 把健康检查超时当中断
补跑(Catch-up)
守护进程回来之后(唤醒、崩溃拉起、退出后再开、开机),每个错过的日程只再执行最近该跑的那一次,不把错过的周期连开。睡眠醒来后先等一分钟再补跑:合盖时系统会自己醒几秒,那时网络常常还没回来。
回避:错过就跳过, 错过几次跑几次, 只补睡眠不补退出
终端(Terminal)
你自己在这台机器上开的一条 shell 会话,守护进程持有;和 Terminal.app 同权,cwd 默认落在工作区,但不关在工作区里,也不走批准。Bot 看不见、用不了,也不知道它存在;桌面上一个终端标签就是一条会话,手机和窄屏上所有会话收在终端那一页里当它的标签。要停必须显式「结束会话」并确认。细节见行为说明·终端。
行为细节
你自己在这台机器上开的一条 shell 会话,守护进程持有。和 Terminal.app 同权:cwd 默认落在工作区,但不关在工作区里,也不走批准——批准管的是 Bot 的危险动作,你不需要向自己申请。项目的右键菜单(目录行或项目会话)里的「在项目目录打开终端」开在那个项目目录,没设工作区也能开。用来跑你自己的活,包括启动项目这类一直开着的进程。关窗不停。它在哪个目录,以 shell 自己报告的为准(zsh 开箱即用,每个提示符前报一次):cd 之后标签名跟着变;从没报告过的,就是开的那个目录。退出或守护进程自己停掉时,里面的进程一起停,但它在哪个目录、屏幕上有什么会留下来,下次启动在那个目录重新开一个。结束会话才忘掉它。屏幕也在守护进程那里:接回时拿到的是它现在的样子,全屏程序也原样;程序问终端的问题只由它回答,窗格不答,所以几个窗格同时看一条会话也只答一次。shell 拿到的环境按 Terminal.app 那样配,但只放行一份白名单,守护进程自己的其他环境变量(包括密钥)不带进去。桌面和手机同时看一条会话时,它的行列按你最近在看、在敲的那一端来。配对过的设备同权,手机上看得见也敲得动。Bot 看不见、用不了,也不知道它存在。桌面上一个终端标签就是一条会话:新开一个终端标签就是新起一个 shell,标签里不再有一排会话可切。关掉标签只是从窗里拿走,进程照跑;没被任何标签摆出来的会话在「+」和空窗格里可以「接回」。手机和窄屏上没有窗格,远程连过来的设备不管多宽也没有(平板也一样),所有会话收在终端那一页里当它的标签。要停必须显式「结束会话」并确认。守护进程那份会话列表才是真的;同一个文件夹里开的几条按开的先后编号,桌面标签和手机上叫法一样。
回避:把它当 Bot 的壳, 给 Bot 开终端, 让终端命令走批准卡, 关窗即杀, 关标签即杀进程, 把窗格标签当会话列表, 一个终端标签里再套一排会话, 两个标签指着同一个 shell, 跨守护进程重启续命, 云电脑
远程屏幕(Remote screen)
已配对的设备看到并操作这台 Mac 的屏幕,在手机的「Mac 屏幕」一页里。画面和输入来自 macOS 自带的「屏幕共享」,Deskfolk 自己不抓屏、不注入输入。能直连就直连,连不上就经中继。默认关,只有 Mac 窗里的设置能打开;屏幕共享要这台 Mac 的账户密码,在手机上输一次,连上后记在手机上,之后自动登录。锁屏时能用,合盖、睡眠、重启后还没人登录时不能用。Windows 上画面和输入来自用户装的 VNC 服务,这一页叫「电脑屏幕」,要的是 VNC 密码。细节见行为说明·远程屏幕。
行为细节
已配对的设备在「工具 → Mac 屏幕」里看到这台 Mac 的屏幕并操作它。画面和输入都来自 macOS 自带的「屏幕共享」(本机 5900 端口):Deskfolk 不抓屏、不注入输入,所以不要录屏或辅助功能权限,每次更新也不会掉权限。守护进程只连这一个本机端口,不是能指向别处的隧道。
开关:默认关,只在 Mac 窗的 设置 → 远程控制 里开,配对的设备改不了;关掉时正在进行的连接当场断开。「屏幕共享」本身要在 系统设置 → 通用 → 共享 里打开,卡片上写着它此刻开没开,没开时点一下就到那一页。没开远程屏幕时,手机页面写明去哪里开。
登录:屏幕共享要这台 Mac 的账户名和登录密码。在手机上输一次,连上之后它存在这台手机上(和配对密钥放在同一处),下次自动登录;登录表单里取消「记住」就不存。屏幕共享拒绝它时,手机忘掉它、让你再输;配对换了或换了一台 Mac,也不会拿来用。代价写在这里:能读到这台手机配对密钥的人本来就能用你的终端,记住的密码让他还能登进 Mac 的屏幕、用 sudo,这是换来不用每次输入的。锁屏时也能看能操作,可以在锁屏上输密码解锁:锁屏界面闲置半分钟就会关掉显示器,而关着的显示器被屏幕共享拍成一片黑,所以有设备连上时 Mac 会点亮显示器,连着期间一直亮着,最后一个会话结束才放开。合盖又没接外接显示器、睡眠、重启后还没人登录时用不了:守护进程是按用户跑的。
两条路:手机先试直连——WebRTC data channel,非 trickle 的 offer / answer 走已加密的远程链路,所以直连的对端仍是这台已配对的设备,Mac 端由 real-bot-rtc 接起再连到屏幕共享。同一网络、IPv6 或 Mac 设置里填的 STUN / TURN 服务器让两边找得到彼此;默认不填,不用任何第三方服务器。手机拿不出任何候选地址时直接走中继;拿得出但 8 秒内没连上,也改走中继:守护进程自己连屏幕共享,字节装进远程链路上的 type 9 帧,两端都不超前对方确认的 48 KiB。走中继时受中继的带宽预算限制,Retina 屏全屏刷新会慢。页面顶栏标着「直连」或「经中继」。
操作:点按是左键,双指点按是右键,双指拖是滚动。底部一排补上软键盘没有的键:⌘ ⌥ ⌃ ⇧ 按一下作用于下一个键,esc、⇥、方向键,以及弹出键盘;软键盘打的字按字符发过去,退格、回车也是。双指捏合放大的是手机上的画面,Mac 那边不会缩放:捏住的那一点留在指下,最多放到 Mac 的一个像素占手机两个像素。放大后双指拖动的是画面,不再滚动 Mac;一指仍是 Mac 上的点按和拖动。顶栏「原始大小」一下放到 Mac 一个像素对手机一个像素,「适应屏幕」回到整页。
触摸板模式:顶栏「触摸板」把整块屏幕(连同画面上下的黑边)当成触摸板,画面上画出一个指针,这个选择记在这台手机上。单指滑动移动指针:走多远看手指滑多远,不看落在哪;滑得慢走得细(不到手指滑过的画面距离),滑得快能一下划过整块屏幕。点按在指针处单击,双指点按是右键,双指滑动滚动;按住不动约半秒再滑动是按着左键拖拽,指针尖上出一个圈,抬手松开。捏合照样放大手机上的画面;放大后指针总在看得见的地方,滑到边上画面跟着移,在画面本身的边上不留边距,菜单栏和屏幕四角照样够得着。顶栏「原始大小」在这个模式下以指针为中心放大。指针刚出现在屏幕中间,或者在上一次点按的地方,第一次滑动或点按时 Mac 的指针才跳过来。noVNC 没有把指针放到手指以外地方的接口,它的手势还要滑过 50 像素才算拖动,所以这个模式的触摸由页面自己读,指针事件由页面按 RFB 直接写进同一条通道(ARD 登录之后是明文 RFB)。
流畅模式:顶栏「流畅」让 Mac 在连着时临时降低分辨率,这个选择记在这台手机上。屏幕共享把帧缓冲的每个像素都发过来:16 寸 MacBook Pro 在「更多空间」下是 4112×2658,照片壁纸加一个窗口的整屏变化约 7.5 MB,手机还要用 JavaScript 解压;降到 2336×1510 约 2.8 MB,解码时间也只剩三分之一左右。挑的是主显示器上一个像素对一个点、长宽比和现在一样、按点算不小于现在的模式里最小的那个,所以窗口不会被挤小,只是 Mac 本机屏幕这期间发虚;已经是一个像素一个点的屏幕不降,页面会说明。按下开关时页面重连:会话开始时守护进程先让 real-bot-rtc display-hold 换好模式再答应手机,屏幕共享连上时报的就是新尺寸;关掉时同样先恢复再答应。几台设备共用一次降低,最后一个要流畅的会话结束后再等 3 秒(给重连留余地)才恢复;在 Mac 窗里点断开、关掉远程屏幕时立刻恢复。模式是按进程设的(kCGConfigureForAppOnly):helper 不论怎么结束(守护进程关掉它、守护进程挂了、它自己崩了),macOS 都会把原来的模式放回去,正常结束时它先恢复好再说一声。Mac 窗的远程屏幕卡片上写着现在降到了多少。
谁在看、怎么结束:有设备连着时,Mac 窗的设置卡片上写着是哪台、直连还是经中继,可以当场「断开」。撤销那台设备、关掉开关、远程链路断开、手机页面切到后台、Mac 45 秒收不到页面的保活,连接都会结束;从后台回到这一页会自动接回。
Windows:Windows 没有自带的屏幕共享,画面和输入来自用户装的 VNC 服务(推荐 TightVNC,注册成系统服务、只允许本机回环),守护进程照样只连本机 5900(ADR 0059)。手机从远程链路得知对面是 Windows:这一页叫「电脑屏幕」,登录只要 VNC 服务的密码,按键行是 Ctrl、Alt、Win、⇧,顶栏原来「流畅」的位置是 Ctrl+Alt+Del,没有流畅模式(TightVNC 发 JPEG,本来就小)。有设备连着时 real-bot-rtc stay-awake 按住显示器和睡眠,作用和 Mac 上的 caffeinate 一样。没有 VNC 服务应答时,电脑窗的卡片列出安装和配置的几步,按钮打开 TightVNC 的下载页。还没在真的 Windows 电脑上验过。
接口:手机经远程链路调 POST /remote/screen/start | offer | tunnel | status | stop,只在远程链路上存在、本机回环到不了;/remote/features 的回答里带 screen: "rfb-v1" 时手机才显示入口,host 说对面是 Mac 还是 Windows。Mac 窗经本机接口调 GET / PUT /v1/remote/screen(开关和 ICE 服务器)和 POST /v1/remote/screen/disconnect,这几条不在远程白名单里。协议细节见远控协议,取舍见 ADR 0056。Bot 用不了。
回避:远程桌面, 让 Bot 操作屏幕, 在手机上打开这个开关, 在本机窗里看本机屏幕, 自己抓屏, 把 Mac 密码存到 Mac 或中继上, 云电脑
命令流(Command stream)
Bot 执行一次 shell 时 stdout / stderr 的实时外推,让那一次调用在它跑的时候就看得见。只是可见性,不改变那次调用是什么:仍然 stdin 关闭、仍然十分钟超时、路径仍然逐次判定。不是终端,没有输入,不进主转录:进行中挂在那条气泡下面,结束折叠成一行,回看走工作目录的轮次视图。
回避:给命令流加输入, 把它当终端会话, 让它改批准粒度, 进主转录, 新增一种消息种类
窗格(Pane)
桌面窗里的一块内容区:一块窗格是一组标签页,一个标签可以是一条会话、一个终端、日程图、工作区、花费或用量。窗里可以纵横任意分割出多块窗格;窗里分成多块窗格,那仍然是同一个窗口进程,浮起来的也不是第二个系统窗口。同时只有一块是当前窗格;窄屏(≤680px)和手机上没有窗格,仍然是一次一屏、一层层盖上去。每种窗口还没开着时放在哪(新标签、替换同类、分屏、相邻窗格、浮窗……)是它的打开方式,在设置里按种类选,只记在这台机器上;已经开着的只切到前面。细节见行为说明·窗格。
行为细节
桌面窗里的一块内容区。一块窗格是一组标签页;一个标签可以是一条会话(它的产物预览、流程图都跟着它;对话上方有一条页头:和谁、他们在做什么、这里有几件等你批准或回答(点一下滚到那张卡),以及置顶、流程、设置,和标签 ⋯ 里的是同一组;它自己的设置从这个标签里的对话右侧滑出、带一层只盖这块窗格的遮罩,不另占标签),也可以是一个终端(一个标签一条 shell 会话)、日程图、工作区、花费或用量。窗里可以纵横任意分割出多块窗格,分隔条和交汇处都能拖;标签可以在自己的标签栏里拖动排序、在窗格之间拖、拖到边缘就地分割、按住 ⌥ 拖出来变成浮在上面的窗格;按住标签栏的空白处拖的是整块窗格,它的标签一起走,拖着时这些标签都留在原处变淡,跟着指针的小标签写「整个窗格 · N 个标签页」(只有一个标签时写那个标签的名字)。侧栏里的会话(列表里的每一行、置顶的头像、收起后窄栏里的头像)也能这样拖进工作台:放到窗格中间,在那块窗格里开一个新标签;放到标签栏上,插在那个位置;放到边缘,就地分出一块;按住 ⌥ 松手,浮成一块浮动窗格。这条会话已经开着时,挪过去的是它原来那个标签,不另开一个;松在工作台外面什么都不变;只点一下仍按它的打开方式打开。在窗格里任意位置右键,可以向上、下、左、右就地分出一块空窗格,也可以「浮动这个标签页」:右键的那个标签(在内容上右键就是正显示的那个)单独浮成一块浮动窗格,在原处、略小一圈,同一块窗格里的其他标签留在原处;窗格只有这一个标签时它合拢,只剩一块平铺窗格时底下留一块空窗格;浮动窗格里不止一个标签时,也能把其中一个再单独浮出来;标签的 ⋯ 里也有这一项;里面本来有右键菜单的(消息、代码编辑器)和输入框仍是它们自己的,按住 ⌥ 右键是网页视图的原生菜单。浮动窗格像当初的流程图浮窗:按住它标签栏的空白处拖着走,双击空白处停靠回布局;四条边和四个角都能拖着改大小,拉哪一边对边不动,小不过里面内容要的最小尺寸,也拖不出工作台;不就地分,右键提供停靠回布局与关闭窗格。有别的窗格时,空窗格右上角可直接关闭(只剩一块平铺窗格时不显示:关掉它也只是再留一块空的,这时标签条上什么都没有,整条收起);每块窗格的右键菜单也可关闭整块窗格及其标签,工作区和产物预览有未保存编辑时先确认。最后一块平铺窗格关闭后留下一块空窗格。窗里分成多块窗格,那仍然是同一个窗口进程,浮起来的也不是第二个系统窗口。同时只有一块是当前窗格——键盘在哪块就是哪块,在哪块里点一下或滚一下也换到哪块:Stop 打它那一轮,点通知先找已经开着那条会话的窗格。摆着不止一块时,像 Mac 上前台窗口和排在后面的窗口那样分出前后:当前那块的卡片连同窗头带一圈柔和的阴影浮在底色上,其余窗格(平铺的、浮动的都算)只在标签栏上退后——标签文字变灰变淡、正显示的标签沉进标签栏、卡片平贴在底色上,不是当前的浮动窗格阴影短一截;窗格里显示的内容一律不压暗,另一块里开着的任务地图、流程图照样看得清。只有一块时什么都不变。一样东西已经开着,再要一次只是把那一块切到前面。还没开着时放在哪,按它是哪一种窗口(会话、流程、产物、工作区、终端、日程、花费、用量;Bot ↔ Bot 私聊不开标签,在弹窗里看,见私聊),在设置 › 行为 ›「窗口打开方式」里选(「行为」页签只在桌面工作台开着时出现),只记在这台机器上,窄屏和手机上没有这一项(ADR 0074)。可选的有:
- 新标签页。
- 替换同类标签:当前窗格正显示同类就替换它,不是就替换离键盘最近那块正显示同类的;那块有没保存的编辑,或哪块都没有,就新开标签。「同类」是设置里的同一行。
- 后台新标签页:放进当前窗格但不切过去。
- 向上、下、左、右分屏:把当前窗格往那边切开;放不下或当前是浮窗,就新开标签。
- 上、下、左、右侧窗格:那一侧有窗格就放进去;当前窗格本身就在那一侧,就放进当前窗格;都不是才往那边分出一块。
- 浮窗:浮在布局上。这种窗口上次浮着时被你拖过或调过大小,就回到那个位置和大小(窗口变小了就收进窗口里,正好压在别的浮窗上就错开一点);没拖过的居中,已有浮窗就往右下错开一点,错开六次后回到正中。位置按窗口种类记在这台机器上,恢复默认也会忘掉。
默认是:
- 会话:替换同类标签。在侧栏里一条条点着看不堆标签;分屏时换掉的是正读的那条,不是旁边的产物栏。终端、工作区这些不会被顶掉。
- 流程、产物、工作区:开在右侧窗格。
- 终端:开在下侧窗格。
- 日程和花费:新标签页。
从某块窗格的「+」或空窗格里挑的,就放进那一块。会话设置同时只在一条会话上开着,在别处打开就把原来那处收起;日程图同时只有一块。每条会话只有一块产物预览和一个流程标签页(2026-10-12 起,ADR 0086:任务栏、流程图和详情栏里的要点都在这一个标签里,之前流程、看板、要点各是一个标签):在这条会话里、它的预览文件树里或它的流程图上再打开别的文件、别的那件事,是那一块转过去,不另开一块。流程标签从右键菜单直接打开:消息的右键菜单里有「这件事的流程」(停在这条消息的卡片上,选中这条消息所在的任务),侧栏里会话那一行的右键菜单和会话标签的 ⋯、右键里有「流程」(这个会话最近的那件事);已开着就切过去,没开就在当前窗格新开一个标签。以前存下的看板、要点标签页打开时,分别是展开的看板和按它的任务点亮的要点,再要一次就和流程标签合成一个。这些标签名是固定的,跟会话标题走:「<会话名>流程」「<会话名>的产物」,不跟打开的文件或这件事改。流程图标签换到后面再换回来,停在你离开时的地方:拖到哪、缩放多少、折起展开的轮、筛选和点亮的任务都照旧;在它的切换器里换一件事再换回来也一样;只有一条消息新要它去某张卡片时才会移开。会话窗格不再另画顶栏:头像(Bot 在干活、等你或出错时带状态点,空闲不带)和名字始终在标签上,顶栏的第二行成了标签的提示;鼠标移到标签上才出现 ⋯,里面是置顶、流程和设置,右键会话标签也在分割前面给出这几项。右键任何一个标签(会话标签的 ⋯ 里也有),可以关闭这个标签、同一块窗格里的其他标签、它右侧的标签或全部标签;全部关掉就和关掉最后一个标签一样,这块窗格跟着关掉,工作区和产物预览有未保存编辑时先确认。其他标签(终端、工作区、日程、花费、流程、产物)也始终在名字前带上各自的图标。窄屏(≤680px)和手机上没有窗格,仍然是一次一屏、一层层盖上去。
回避:多窗口, 第二个窗口进程, 把浮动窗格当系统窗口, 手机上分屏, 看得见就算已读, 不按打开方式自己改布局, 已经开着的按打开方式再开一个, 空窗格留着, 设置另开标签, 窄窗格里顶栏和标签各报一遍名字
布局(Layout)
窗格是怎么分的:纵横分割、各自的宽高、哪些浮着、哪块是当前的、终端窗格摆着哪条会话。只记在这台机器的 localStorage 里,不进守护进程,也不进 URL——URL 只说当前窗格正看着哪条会话。退出再开还是关前那样;引用的会话或终端没了就自己收拾掉;重置回默认的一块窗格前先问未保存的改动。
回避:布局进库, 布局进 URL, 跨设备同步布局, 换台机器跟着走, 重置直接丢掉未保存的改动
版本闸(Schema gate)
守护进程开库时的检查:数据库记的 schema_min_compatible 高于这个版本能懂的 SCHEMA_LEVEL,就拒绝打开,把原因写进 daemon.log 后退出,而不是接着跑、误读它不懂的新语义。engine_level 是配套的整数开关,代替一堆各自的功能开关,只支持按顺序往前调:守护进程每次启动都试着把它调到这一版自己会开的最高一级(ENGINE_LEVEL_BY_DEFAULT,2026-10-04 起是这一版的最高级,8 级质量事件与教训),同一步把库的地板抬到那一级要的高度(到叫停这一级是 1,到 8 级是 6);以后的版本带来比它更高的级别时,那一级先算试验,不论数据目录有没有旧安装版共用,都只在开发者明确放行后才开;但和它共用默认数据目录的安装版要是还不读这道闸(或读不出版本号;不在 Mac 上时无从知道装的是哪版,也算在内),源码跑的守护进程就不调,把原因写进 daemon.log,照当前级别运行——那样的旧版本会无视地板打开这个库。开发者在自己的机器上可以明确放行(engine_gate_optin:本机 API 的 POST /v1/capabilities/raise,或 apps/daemon/scripts/engine-level.ts --accept-older-app,手机上不行),接受那份旧应用在守护进程没跑时打开会无视叫停,级别当场调上去(守护进程没跑时下次启动调);收回放行不会把级别调回去;放行只到它接受时那一版的级别(越过旧安装版时也可以用 --level 只放行到某一级,比如只开 5 级、不开 6 级;没有旧安装版挡着时 --level 不起作用,库照样到默认级别),之后的版本带来新的级别要再接受一次;早先版本记下的、低于默认级别的放行,不会把没有旧安装版拦着的库压在默认级别以下。GET /v1/capabilities 把这两个数字和已开的功能报给调用方——包括手机,因为本机跑的可能是装好的旧守护进程,而信使源码树已经更新。
回避:把 schema_min_compatible 当成应用的版本号, 两个条件(闸已随发布、装好的应用版本已经够新)没都满足、开发者也没明确放行就调高 engine_level, 当成用户能看到的设置项