跳转到内容

将客户端接入 app-server

需要管理会话、接收回合流或控制执行时,使用 app-server。脚本如果只需提交任务并取得 结果,wuu exec 已经封装了这套生命周期。

Terminal window
wuu app-server --workdir /path/to/project

让客户端持续连接进程的 stdin 和 stdout。每条协议消息是一行 JSON 对象;stdout 是 协议流,进程诊断应单独处理。未提供显式 --config 文件时,服务使用本地配置。 排查插件故障时,--safe-mode 可在不激活插件的情况下启动。

先发送 initialize:

{"id":"1","method":"initialize","params":{"protocol_version":"wuu-app-server/v0.1","client":{"name":"example-client"}}}

响应使用相同的 id,并包含 result 或 error。通知只有 method 和 params,没有 id。检查初始化结果的 status 和 issues:协议响应成功,仍可能返回 needs_setup。

初始化同时返回 catalog_providers,即本地可连接服务目录,客户端可直接展示服务选项。 config/model-catalog/refresh 成功后在同名字段中返回更新后的目录;刷新期间应保留现有 选项。旧版宿主可能省略该字段,此时可调用 config/model-catalog/providers 获取目录。

创建会话,再使用返回的 result.thread.id:

{"id":"2","method":"thread/start","params":{}}
{"id":"3","method":"run/start","params":{"thread_id":"THREAD_ID","prompt":"总结这个仓库","request":{"mode":"start"}}}

这是有先后关系的两个请求,不能原样作为批次提交。收到第一个响应后,将 THREAD_ID 替换为实际 ID。持续读取通知,直到 run/updated 为返回的运行 ID 报告终态。 run/start 响应表示执行已被接纳,不表示任务完成。

交互式客户端也可以使用 turn/start,接收回合和条目更新。自动化执行可能包含多个续接或 Schema 修正回合,因此单个 turn/completed 不代表整次运行结束。用 run/interrupt 停止运行;关闭由客户端启动的服务前,先请求 shutdown。

thread/archive 接收 thread_id 和 archived。归档运行中的会话需要用户明确确认, 然后发送 archived: true, force: true。请求会中断它捕获的执行、保留排队输入,并保持 待处理状态,直到部分历史、终态和执行器清理完成。清理超过两秒不会要求用户再次确认。 stdio 中同一会话的后续请求仍按顺序等待;其他会话和服务器关闭请求不受阻塞。

桌面归档请求没有响应超时。切换页面只使旧界面不再处理结果,不撤销已确认的请求。 服务器关闭或嵌入方取消请求上下文,会在归档元数据提交前停止归档;执行结束后会重新 检查授权。如果连接或提交确认丢失,结果可能未知,应先读取持久会话状态再决定是否重试。 错误若明确说明归档元数据已提交、但刷新视图不可用,不代表归档写入失败。已经提交的 变更不会因后续取消而回滚。

通过 stdio 调用 thread/list、thread/listAll 和 thread/listArchived 时,目录读取在 后台执行,避免大型目录阻塞会话读取和停止请求。响应可能乱序返回,请通过请求 ID 关联。 每次目录读取都保留请求受理时选中的状态提供方。目录是可刷新的视图,不保证它完成后才 执行后续会话修改。

需要紧凑会话视图时,在 thread/resume 中传入 conversation_history: true。 主机会先将已结束的工具活动和过程说明折叠为 process_summary 条目,再计算历史页大小。 回复、错误和已发布附件仍然可见;正在执行的轮次保留实时条目。 将返回的 history_cursor 原样传给 thread/history/read 读取更早的会话页。 会话页当前最多包含 100 个轮次、1 MiB 的序列化条目数据;分页可以拆分轮次, 但不会拆分处理过程摘要。iOS 在打开会话后自动逐页加载更早的历史,直到读取完成。 每次只请求一页;关闭视图、切换会话或断线时停止。浏览旧消息时保持阅读位置, 加载失败可在时间线顶部重试。工具详情和超长内容的完整版仍按需读取。

用户打开摘要时,调用 thread/activity/read,传入 thread_id、turn_id、摘要的 item_id,并将 activity.last_item_id 作为 last_item_id。响应包含 turn 和可选的 next_cursor;将后者作为 cursor 读取下一页详情。详情页应与会话状态分开保存。 单个超长条目通过 remote_content_ref 和 thread/content/read 获取完整内容。 若历史编辑移除了分页边界,应重新打开会话,不再使用过期游标。

未传入 conversation_history 的客户端继续使用原来的历史格式。 旧版主机会忽略此选项,返回常规历史页。

空闲的 Wuu 引擎会话可以用服务器返回的最新轮次 ID 调用:

{"id":"continue-1","method":"turn/start","params":{"thread_id":"thread-id","resume_after_turn_id":"thread-id-turn-0001"}}

省略 prompt 和附件。核心先获得线程执行租约、刷新持久历史,再比较源轮次。源 ID 过期、正在执行、只读、没有历史、非 Wuu 引擎或至多一次工具的结果未知时,会拒绝接纳。成功返回新的空轮次,不插入用户消息;即使新轮次已经结束或服务器重启,重复源 ID 仍会被拒绝。响应结果不明确时先重新读取会话,不要自动换成新的最新轮次 ID 重发。

持久化的 meta/turn_continue 只划分显示轮次,不进入模型输入。采样前从账本恢复已完成工具结果;带 incomplete_stream_fragment cause 的显示片段被排除。接纳后进程崩溃会留下独立的中断轮次,用户可以再次明确继续。

Responses 和 Chat Completions 使用经过规范化的客户端历史,保留配对工具结果及兼容推理;失败响应身份不会复用,Responses 原生状态仍检查 provider、model 和 scope。Anthropic Messages 的历史若以 assistant 结束,会仅在本次请求加入 user 兼容引导,避免使用不受支持的 assistant prefill;工具结果结尾本来就映射为 user。兼容引导不会成为持久用户消息。参考 Anthropic prefill 限制和 Responses 会话状态。

持久对话中的 turn/queue、turn/steer 接收输入,以及编辑和取消操作,都会先提交本地存储, 再返回成功响应。重试响应不明确的请求时应复用稳定的 client_id:重复排队返回原内容, 已消费的 ID 在不同输入方法间也会被拒绝。取消会阻止同一发送模式的重试;明确切换排队与即时引导模式, 会取代前一种模式的取消记录。修改内容使用 turn/update-queued;重复 turn/queue 不会修改或重新执行输入。 hold: true 只保留输入,不派发。

核心重启后,未消费输入通过 held_user_messages 返回,不会恢复为自动执行的队列。 仍由当前进程管理的输入只出现在 pending_user_messages 中。用户可以用 turn/steer 明确发送待处理项。 输入进入历史和清理恢复记录在同一事务中完成;即时引导在下一次模型请求前提交尚未归档的完整历史后缀, 避免把已经派发的输入当作未执行消息重放。并发编辑和取消会阻止过时输入进入历史。 内置引擎在模型回复过程中收到 turn/steer 时,会在当前可见文字处结束助手条目, 将该片段作为未完结的已引导片段保存,并在它后面接收用户消息,然后继续同一轮的下一次模型请求。 工具执行期间收到的引导会在下一次模型步骤前接收,不取消已经启动的工具。

持久 Codex 对话支持向当前轮次补充明确的人类输入。Wuu 用 expected_turn_id 绑定当前轮次,适配器再用原生轮次 ID 约束发送目标;附件和稳定的 client_id 沿用普通输入入口,不改变工具或权限。RPC 成功只确认本地接收,原生确认返回的 轮次 ID 匹配且用户历史提交成功后,才显示已消费的用户消息。同一轮次可以接收多次补充。

引导结果的可选 delivery 字段表示计划发送路径 active_turn 或 queued,不代表原生已消费。 不支持当前轮次引导的 引擎和临时原生对话继续使用下一轮排队;原生返回不支持该方法时也退回普通队列。 原生明确拒绝的输入则恢复为待处理,不会静默转入后续轮次。

如果发送后的确认丢失、发送后被取消,或确认记录无法提交,待处理项会包含 delivery_unconfirmed: true。即使重启,也不能编辑、撤回、重新排队或重发这个 ID。 桌面端显示原因,并保留原始内容供查看和复制。 应先检查原生对话,再决定是否提交替代输入。这个边界防止自动重复执行, 不承诺外部工具效果恰好发生一次。原生轮次结束时尚未接收的补充会保留为待处理。

执行时重新读取当前权限选择,不会恢复旧权限。这些保证针对本地进程崩溃,不会自动重试效果不明的工具调用。 临时会话仍只保存在内存中,也不提供持久待处理输入。

内嵌浏览器客户端的基础能力包含六个反向请求方法:browser/cdp、browser/screenshot、 browser/open_tab、browser/close_tab、browser/set_visibility 和 browser/list_tabs。 这些请求由核心发给客户端;客户端应持续读取协议流,并以原请求 ID 返回结果或错误。

每个浏览器请求都带核心指定的 thread_id 和 workdir。标签页创建、列举、输入、截图和 清理按对话隔离;页面打开的标签页继承来源页的归属。现有标签页属于其他对话时,应拒绝 请求,不得导航或接管该标签页。

browser/list_tabs 还可返回 workspace_tabs:用户已打开的网站或插件页面候选项,包含 tab_id、url、title、workspace_id、source 和 active。候选项与当前对话拥有的 tabs 分开;列举只提供元数据,不读取正文或取得控制权。工具显式 attach 时,在原有活动和执行租约内发送 browser/open_tab,令 tab_id 与 workspace_tab_id 相同且不传 initial_url。宿主借用原生页面,拒绝已占用、已关闭或其他窗口的页面;不得按 URL 重建 workspace: 标签。browser/close_tab(工具 detach)、finalize、轮次结束、接管、停止和所属宿主销毁都释放绑定,不关闭用户页面。被释放的绑定 ID 出现在 closed 中,原生页面仍存活。借用期间沿用 Agent 权限限制,不扩大截图或媒体权限。

request_id 标识进行中的操作。核心停止等待时,browser/request_cancelled 通知携带 该 ID 及相同的 thread_id、workdir。只取消匹配的操作,在异步工作后和输入派发前 再次检查。用户接管或停止活动会撤销该对话所有标签页的输入权限;之后归还控制权可 授权新操作,但不能恢复已经撤销的请求。

桌面客户端还协商 browser/finalize 和 browser/turn_ended,支持按执行实例清理。每个工作区的首次浏览器请求前,核心发送 browser/turn_started,携带 workdir、thread_id、turn_id 和不可变的 execution_id;同一次执行中的所有浏览器请求都带这组标识。逻辑轮次恢复时可以复用 turn_id,但必须分配新的执行 ID;旧执行的请求和结束通知不得影响新执行。

browser/finalize 原子应用 keep: [{tab_id, status}],其中状态为 persistent、handoff 或 deliverable,并关闭未保留的临时页面,但不结束执行。手动打开或接管的页面保持持久。保留弹窗时,先保护它仍存活的同任务来源页,再关闭临时页面。宿主重启后,持久记录中的标签 ID 可以尚未创建;若对应的存活标签属于其他任务,则返回错误。

轮次完成或中断后,browser/turn_ended 带执行标识及保留记录。客户端先使未完成操作失效,再关闭临时页,并释放保留页面的调试器和输入、指针状态,不替换页面内容或改变当前预览布局。清理不得删除历史消息引用的截图文件。持久保留记录读取失败时,preserve_all: true 要求保留该任务所有页面,避免丢失上下文。两个方法都返回 closed、kept,以及可选的 tabs(含 tab_id、url、title、status);过期结束请求返回 stale: true,不修改标签。未协商新增方法的宿主继续使用基础请求契约。

thread/resume 接受 session_id,省略时选择工作区最近的可见会话。thread/fork 接受 thread_id,创建独立对话。thread/start 的 ephemeral: true 创建内存会话,服务退出后 无法恢复。

会话快照中的可选字段 title_revision 是随标题修改一起保存、单调递增的版本号,省略时 视为零。合并列表、恢复响应或 thread/updated 通知时,保留版本较大的标题; updated_at 表示会话活动时间,不能用来判断标题的新旧。自动生成只填补空标题, 可在后续回合成功后重试,不会覆盖已保存的手动标题。

每个对话保留自己的模型和权限选择。应在对话空闲时,用带 thread_id 的 config/model/update 修改;这不会改变工作区默认值。对话忙时返回 thread_busy,客户端可 等待后重试。不带 thread_id 的请求修改未来 对话的默认设置;加上 keep_selection: true 则只保存服务的连接,不把它设为默认。不要通过 turn/start 临时覆盖单个回合的权限模式。

thread/listArchived 返回当前会话存储中的全部已归档对话,包括其他工作区的对话。 删除这份列表快照时,为每个 ID 单独调用 thread/delete,并设置 only_if_archived: true。每次请求独立执行永久删除,客户端应报告各项失败。 如果对话在删除事务开始前已恢复,服务会拒绝删除,保留聊天记录和侧聊内容。 正在运行的对话、活跃子 Agent 或正在运行的侧聊也会阻止删除。 省略 only_if_archived 时,仍可删除普通空闲对话。

turn/start、turn/queue、turn/update-queued 和 turn/steer 接受可选的 content_parts,用于在 prompt 之外保存有序的展示元数据。二进制附件仍使用 images / files。

type字段
texttext: string
pasted_texttext: string,可选 title: string
file_selectiontext: string、source: FileSelectionSource、intent: "comment" | "edit" | "quote"、id: string,可选 comment: string
response_selectiontext: string、selection: ResponseSelection,引用捕获时已显示的助手回复,支持仍在输出的回复

FileSelectionSource 包含字符串字段 workspace、path、quote、revision, 以及整数字段 start_line、start_column、end_line、end_column。行号和 UTF-16 列号均从 1 开始,结束位置不包含在选区内。revision 标识捕获时的文件 内容,不一定是 Git 版本号。服务端检查必需元数据、意图值、正数坐标及范围顺序, 不读取文件或核实其版本。

PDF 选区增加可选的 source.pdf 元数据,其中 start_page 和 end_page 从 1 开始, 包含首尾页。四个行列坐标均为 0,不把页码写成文本行号。已交付 PDF 还会在 pdf 中保存 artifact_uri、artifact_sha256、artifact_thread_id,并把 revision 设置为 sha256:<artifact_sha256>。Wuu 检查页码顺序和交付标识的一致性。这些字段标识 引用时的快照,不授予文件访问权限,也不证明摘录确实存在于文件中。桌面的“添加到对话” 和“评论”只把摘录加入当前草稿,不会发送。工作区 PDF 的版本标识对应捕获摘录时观察到的文件。

文件选区的 text 必须包含模型可见的完整序列化文本块,包括来源上下文、意图和 可选评论。客户端按顺序拼接各段文本形成 prompt。只有拼接结果与提交的提示词 在去除首尾空白后相同,服务端才保留有效元数据;服务端不重建或解析客户端的 序列化格式。未知或无效分段会被丢弃,文本不匹配时丢弃展示元数据,但不修改 提示词。Provider 和引擎使用规范消息文本;Codex 接收普通文本输入,无需特殊的 text_elements。

已接受的元数据会随历史和暂存队列回放保留。客户端必须容忍未知字段和分段类型; 无法展示元数据时,回退到完整的消息 text 或队列 prompt。模型所需的上下文 不能只保存在元数据中。

thread/start 未指定 engine 时使用 engines.default_engine,空默认值选择 wuu。所选引擎被禁用、移除或不可用时,在持久化会话之前拒绝创建,不会改用 另一实现。通过 engine/update 禁用引擎会保留配置中的默认选择;重新启用它, 或显式将 default_engine 设为可用引擎(例如 wuu)后,才能继续创建对话。 已有对话保留其引擎绑定与历史,修改默认值不会迁移这些对话。

engine/update 的响应形状与 engine/list 相同,但不会启动原生模型发现; 每个条目的 models_status 均为 deferred。变更生效后如需模型目录,应单独 调用 engine/list。

此行为替代了原先自动回退 Wuu 的逻辑。客户端应处理默认引擎不可用的情况, 提供显式选择,而不是自动改写默认值。

engine/list 接受 { "cwd": "/absolute/session/directory", "refresh_models": true }。 也接受 { "include_models": false },此时只返回引擎描述与设置,不启动原生模型发现; 每个条目的 models_status 为 deferred,未返回目录不表示目录为空或不受支持。 客户端可以用这份可用性清单选择引擎并创建会话,包括使用原生默认配置或已知选择, 随后再单独请求完整目录。省略 include_models 时仍执行完整发现。

cwd 默认使用运行时根目录;会话位于其他目录或 worktree 时应传入实际路径。 显式刷新重新扫描桌面安装目录和登录 shell 路径,将保存的启用/禁用选择重新应用到 运行时,并绕过原生模型发现缓存。应用启动后新安装的引擎无需重启即可使用, 正在执行的轮次保留原有引擎租约。enabled 表示运行时可用性,binary_ok 表示 检测到了可执行文件;保存的 settings.<id>.enabled 仅表示用户选择。

engine/list 可选参数 { "include_quota": true } 使用各模型服务和支持的外部 Agent 自己的凭据查询上游额度,返回已配置服务 subscription_providers。服务或引擎可附带 quota:

  • status:available、stale、unavailable、sign_in 或 unsupported。
  • kind:订阅、套餐或余额;plan 为可选的上游套餐信息。
  • account:不透明、按来源隔离的 id,可选账号 label 和凭据 source;不返回 token 或密钥。
  • checked_at 为最近尝试时间,observed_at 为最后成功读取时间,expires_at 为新鲜度期限,目前为成功读取后五分钟。
  • windows 提供 id,以及可选的 label、used_percent、window_minutes、resets_at、model、scope、display 和上游明确报告的 unlimited。
  • balances 保留币种及十进制字符串 amount,不合并不同币种或账号;可选 reset_credits 表示上游重置次数。
  • error_code 为安全的失败类别,不包含上游原始响应。

只需要当前账号额度时,可同时设置 include_activity: false,跳过本地历史统计。省略该参数时仍按原有行为返回历史统计。

已接入原生 ChatGPT/Codex、Grok Build 订阅、Anthropic OAuth、Kimi Code 和智谱/Z.ai 套餐周期,以及 DeepSeek/OpenRouter 预付余额。外部 Codex 通过 CLI 账号请求读取;外部 Claude Code 和 Grok 使用自己的本机登录。macOS 默认 Claude 登录可读取钥匙串,但自定义凭据目录不会借用默认账号。浏览器 SuperGrok、任意兼容端点和没有适配器的 Agent 暂不支持。

缺失百分比表示未知,不是零或无限;上游可用 100% 或更大数值表示耗尽。ACP 上下文占用和本地 token 不能推导账号额度,过期快照也不能在重置时间自行补满。暂时读取失败且仍能确认账号时,保留该凭据来源的最后成功快照为 stale,不改成功读取时间或到期时间;认证被拒绝时丢弃该账号的缓存额度。快照跨服务重启保留,更换凭据不会继承另一个账号的数据。所有来源的采集共用八秒期限。额度查询不发起推理,不更改模型选择、工作区默认值或路由策略。

只有上述按需订阅响应读取历史统计。initialize、配置响应、普通 engine/list 和 engine/update 不附带 latest_request 与 local_usage;需要统计的客户端应独立加载订阅快照,不阻塞导航。所有请求来源共用一次历史扫描;扫描失败时保留服务清单,统计字段缺失而非零值。

该响应中引擎及订阅来源的 local_usage 汇总本地保留历史中已上报的输入、输出、缓存 token 和 reported_turns,不含未上报或 Wuu 外用量,也不是账单。可选 latest_request 提供最近请求的 status、error、at、model、usage_reported,有上报时附带 token 计数。新旧顺序按请求时间判断,来源按持久记录归属;对话编辑和供应商切换不改变历史归属,旧请求用量不得填充到新请求。

usage/overview 汇总本地保留历史中已记录的 token 用量:total_sessions(至少有一条用量记录的会话数)、metrics(与 settings/usage 相同的汇总对象,含 active_days)和 days(每个活跃日一条,按日期升序)。它只读取用量记录,不读取对话内容。可选参数 { "timezone": "America/Los_Angeles" } 按 IANA 时区划分日期;省略时使用 UTC,未知时区返回请求错误。没有记录时返回零值和空的 days。与 local_usage 一样,这些值不含未上报或 Wuu 外的用量,也不是账单。桌面「用量」页使用的完整快照 settings/usage 接受同样的可选 timezone;桌面端会带上自己的时区,让每天的柱子落在日历对应的那一天。其中的 engine_breakdowns 把同一份 Wuu 内用量按会话绑定的 Agent 引擎归桶,并为每个引擎增加 outside 一侧——从引擎自己的本地账本(~/.claude 转录、~/.codex rollout、OpenCode 数据库、Devin sessions.db)读出的 token 合计,减去 Wuu 已认领的原生会话,两列不会重复计数。从不向 Wuu 用量记录上报的引擎——ACP 家族,如 Devin——则把其已认领会话的账本行计入 in_wuu 一侧,因为 Wuu 自己的记录无法覆盖它们。没有可读本地账本或扫描失败的引擎不返回 outside。同样的账本合计也会落到每日序列——已认领行落在当天自身的 token 字段,外部行落在 days[].outside——趋势图和热力图因此能给页面统计到的每一天着色;每天的轮次和 agents 保持不变。

Terminal window
wuu debug app-server initialize --workdir /path/to/project
wuu debug app-server send --workdir /path/to/project config/read '{}'

每条调试命令都会启动服务、完成请求并关闭服务。流式执行需要持续连接的客户端,不应靠 串联独立探查命令实现。

stdio 协议是受信任的本地控制接口,不是带认证的网络服务。远程或托管部署必须在外围提供 传输安全和隔离。协议参考(英文)说明消息 格式、能力协商、选择规则和云端进程身份。

Codex 提问复用现有的 user-question/requested、user-question/resolved 通知及 user-question/list、user-question/respond、user-question/cancel、 user-question/hold 方法。请求携带所属 Wuu thread_id 和 turn_id,原生条目标识 位于 call_id。问题 ID 仅在当前宿主请求内有效,回答时应原样返回。

reply_to_tool: true 表示答案直接返回正在等待的引擎调用,不应再发送消息或追加 steer。 mode: "ask" 使用常规问答表单;mode: "offer" 使用可跳过卡片,并沿用 20 秒过期时间, user-question/hold 可暂停倒计时。取消、跳过或过期均返回无答案,不会自动选择选项。 回合完成、中断或原生进程断线会撤销问题,迟到的回复会被拒绝。原生请求省略阻塞标记时 按阻塞问题处理。秘密输入请求明确不受支持,不会显示在明文输入框中。

宿主问答桥接可用时,Wuu 为普通原生回合启用提问。原生配置显式设置 features.default_mode_request_user_input = false 时会保留禁用,Wuu 也不会覆盖 原生工具级别的禁用设置。功能配置在创建线程或冷恢复时生效;已加载的原生线程保留 首次加载的功能配置。更改原生提问开关后,需重新连接原生引擎或开启新对话才能生效。