feat: complete chat routing and openai onboarding

This commit is contained in:
kris
2026-03-31 03:31:22 +08:00
parent 5b590f7cc1
commit 9c02ebb574
25 changed files with 2241 additions and 133 deletions

View File

@@ -330,9 +330,11 @@
- `body`
- `kind`: `text | voice_intent | image_intent | video_intent`
- 当前行为:
- 普通项目直接写入消息账本
- 普通单线程项目当前会在写入用户消息后,继续创建 `taskType=conversation_reply` 的主 Agent 任务
- 返回体会附带 `task.taskId / taskType / status`,给 Web 和原生 Android 保持等待真实回写使用
- `projectId=master-agent``kind=text` 时,会继续触发主 Agent 真实回复链路
- 当前主链路优先走 `Master Codex Node``task queue -> local-agent -> codex exec -> complete`
- 如果当前主控是 `Master Codex Node`,但节点离线或执行立即失败,主 Agent 当前会优先尝试已配置的 `OpenAI API` 账号,避免聊天直接只剩失败日志
- 如本机节点未接通,可切到 `OpenAI API` 容灾账号
- 群聊项目当前会带上 `collaborationGate`,用于标明当前是否需要先经主 Agent / 用户审批
- 群聊文本消息当前还会返回 `dispatchPlan / dispatchRecommendation`,用于展示主 Agent 推荐的线程下发方案
@@ -400,6 +402,38 @@
- 用途:返回 AI 账号列表、当前主控身份和切换历史
#### `POST /api/v1/accounts/onboard/openai-api`
- 用途:通过 `OpenAI API Key` 在手机端登录 `OpenAI 平台账号`
- 输入:
- `label`
- `displayName`
- `accountIdentifier`
- `model`
- `apiKey`
- 当前行为:
- 先对候选 `API Key` 做真实 OpenAI 探针校验
- 校验成功后创建或更新 `openai_api` 主账号
- 立即设为当前主控
- 返回 `activeIdentity`
- 若服务器当前无法访问 `api.openai.com`,会直接返回明确中文网络错误,而不是只返回 `fetch failed`
#### `POST /api/v1/accounts/onboard/master-node`
- 用途:显式绑定一台电脑上的 `Master Codex Node`
- 输入:
- `label`
- `displayName`
- `accountIdentifier`
- `nodeId`
- `nodeLabel`
- `model`
- 当前行为:
- 创建或更新 `master_codex_node` 主账号
- 可直接切为当前主控
- 不假装“手机里直接登录 GPT”
- 返回登录指引与当前校验结果
#### `POST /api/v1/accounts`
- 用途:新增 AI 账号
@@ -726,8 +760,10 @@
- `rawThreadReply`
- 当前行为:
- `completed` 时把真实主 Agent 回复写回 `master-agent` 项目消息账本
- `taskType=conversation_reply` 时,会把目标 Codex 线程的原始回复写回普通单线程会话
- `taskType=dispatch_execution` 时,会把线程原始结果镜像回群聊,再追加一条主 Agent 汇总,并更新对应执行单状态
- `failed` 时写入 relay 失败消息,并更新 AI 账号健康状态
- 对群聊分发推荐失败的情况,消息入口当前会额外写入一条 `system_notice`,把“没有真实线程”或“成员引用失效”明确回显给用户
- 当前保护:要求 `x-boss-device-token` 或匹配登录会话
## 4. local-agent 接口
@@ -762,8 +798,10 @@
- local-agent 会周期性请求 `POST /api/v1/master-agent/tasks/claim`
- 认领到任务后会执行本机 `codex exec`
- `conversation_reply / dispatch_execution` 当前会优先走 `codex exec resume <targetCodexThreadRef>`,把任务恢复到真实 Codex 线程;只有缺失真实线程引用时才退回 `--ephemeral`
- 执行完成后会调用 `POST /api/v1/master-agent/tasks/[taskId]/complete`
- 对群聊下发链路,认领到的 `dispatch_execution` 任务会带 `dispatchExecutionId / targetProjectId / targetThreadId`
- 对普通单线程聊天,认领到的 `conversation_reply` 任务会带 `targetProjectId / targetThreadId / targetCodexThreadRef`
- local-agent 回写完成时会同时带上 `rawThreadReply`,服务端据此把线程原始结果和主 Agent 汇总回写到群聊
## 5. 当前状态存储

View File

@@ -13,6 +13,8 @@
- 主 Agent 项目详情:`http://127.0.0.1:3000/api/v1/projects/master-agent`
- AI 账号摘要接口:`http://127.0.0.1:3000/api/v1/accounts`
- AI 账号校验接口:`POST http://127.0.0.1:3000/api/v1/accounts/[accountId]/validate`
- AI 账号 OpenAI 登录接口:`POST http://127.0.0.1:3000/api/v1/accounts/onboard/openai-api`
- AI 账号 Master Node 绑定接口:`POST http://127.0.0.1:3000/api/v1/accounts/onboard/master-node`
- 设备 Skill 同步接口:`http://127.0.0.1:3000/api/v1/devices/mac-studio/skills`
- 登录接口:`POST http://127.0.0.1:3000/api/auth/login`
- 登录态接口:`GET http://127.0.0.1:3000/api/auth/session`
@@ -100,16 +102,25 @@ cd /Users/kris/code/boss
- 原生转发目标页当前统一由 `ForwardTargetActivity` 承接;一次只允许选择一个目标会话,目标可为单线程会话、群聊、`主 Agent``审计对话`
- 当前单条消息转发会在目标会话中显示为普通转发消息,并保留 `forwardSource`;多条消息会落成 `forward_bundle` 聊天记录卡片,并保留来源会话、时间范围和摘要条目
- 当前群聊编排主链已补上第一轮闭环:群聊文本消息会先进入主 Agent 生成推荐下发方案;用户确认后会创建真正的线程执行单,并写入系统通知;执行完成后会把线程原始结果镜像回群聊,再追加一条主 Agent 汇总
- 当前普通单线程聊天也已补上真实执行链:`POST /api/v1/projects/[projectId]/messages` 不再只写用户消息,而是会追加 `conversation_reply` 任务;绑定设备上的 `local-agent` 认领后会继续恢复到真实 Codex 线程,再把线程原始回复回写到该聊天窗口
- 当前 Web 群聊详情页也已补上待确认推荐的刷新恢复:服务端会在页面渲染时读取最近一条 `pending_user_confirmation` 的 dispatch plan聊天输入区会继续显示“等待你确认主 Agent 推荐”,不再因刷新丢失确认入口
- 当前 `AI 账号` 页面已分成两条显式接入链:`登录 OpenAI 平台账号API Key``绑定 Master Codex Node`OpenAI API 登录成功后会立即切成当前主控
- 当前如果主控身份还是 `Master Codex Node`,但该节点离线或执行立即失败,主 Agent 会优先尝试已配置的 `OpenAI API` 备用账号,不再把失败日志直接原样回给用户
- 当前设备导入主链也已补上第一轮后端闭环:`heartbeat` 可上报真实项目候选,服务端会生成 `deviceImportDraft`;用户可提交勾选结果、生成导入决议,再把选中的线程真正落成聊天窗口
- Web 与原生 Android 当前都已补上“新设备导入草稿 -> 勾选 -> 决议预览 -> 应用导入”的前台流程;已绑定生产设备继续保留 heartbeat 自动导入主链
- 当前当 heartbeat 同时携带旧 `projects` 和新 `projectCandidates` 时,服务端会优先走 `deviceImportDraft`,不再绕过勾选/审核阶段直接自动导入聊天窗口
- 当前 `dispatch_execution` 完成回写已补幂等,重复完成同一个线程执行单不会再重复向群聊追加线程原始回复和主 Agent 汇总
- 当前原生 Android 已把三条聊天主链统一成等待真实回写:`主 Agent 单聊 / 普通线程单聊 / 群聊确认下发` 都会保持等待,直到收到实际回复或明确超时提示
- 当前设备导入 `review` 已补 owner/admin 鉴权,并会留下 `device_import_resolution` master task 轨迹;导入草稿在 `apply` 后再次 heartbeat 也不会从 `applied` 回退成 `resolved`
- 原生会话页当前的刷新失败策略已改成按当前 tab 独立判错:`会话` 不会再因为 `设备 / OTA / 设置` 的旁路请求失败而整体提示“刷新失败”
- 会话页、设备页、技能页和项目详情页当前都通过 `/api/v1/events` 的 SSE 自动刷新
- 我的页当前保留 `账号与安全 / 设置 / 运维与修复 / AI 账号 / 技能 / 关于` 六个一级入口;`AI 账号` 支持查看 `主 GPT / 备用 GPT / API 容灾`,并明确主链路优先走已经在绑定电脑上登录 `ChatGPT Plus / Codex``Master Codex Node`
- `AI 账号` 页当前已补上显式 `登录指引`:手机端不会直接弹出 ChatGPT OAuth`主 GPT` 需要先在绑定电脑上的 Codex / ChatGPT Plus 会话里登录,再回手机端点“测试连接 / 校验连接”
- `AI 账号` 页当前已升级成双入口:首页会显式展示 `登录 OpenAI 平台账号``绑定电脑上的 Codex 节点`
- `登录 OpenAI 平台账号` 当前通过填写 `OpenAI API Key` 完成;校验成功后会立即设为当前主控
- `绑定电脑上的 Codex 节点` 当前会保存 `master_codex_node` 主账号,并可立即切为当前主控;同时返回“登录发生在绑定设备上”的中文指引,不会再让用户误以为手机里能直接弹 ChatGPT OAuth
- 当前公网服务器对 `api.openai.com` 的直接出网仍未打通;远端 `curl https://api.openai.com/v1/models` 超时Python `urllib` 返回 `Network is unreachable`
- 因此 `POST /api/v1/accounts/onboard/openai-api` 在公网环境下已经能返回明确中文网络错误,但在服务器出网恢复前,还不能完成真实 OpenAI 平台账号探针与调用
- `POST /api/v1/accounts/[accountId]/validate` 当前对 `master_codex_node` 不再只看 `nodeId`,还会同时校验绑定设备是否在线;设备离线时返回 `degraded` 和清晰的人类可读提示
- 主 Agent 当前真实对话链路已验证通过:`Boss Web -> /api/v1/projects/master-agent/messages -> master-agent task queue -> local-agent -> codex exec -> /complete -> 项目消息账本`
- 主 Agent 同步等待窗口当前为 55 秒;若本机 Codex 节点回复更慢,项目页仍会通过 SSE 在任务完成后自动刷新出真实回复
@@ -149,6 +160,8 @@ cd /Users/kris/code/boss
- 当前附件分析任务已带受控 `task token` 下载链接和文本摘录:本地开发环境会跟随请求 origin 生成链接,生产环境默认走 `https://boss.hyzq.net`
- `2.5.x` 当前已补上会话首页独立建群入口:可以不从单线程聊天内部出发,直接在会话首页右上角 `+` 建立新群聊;同时已把多个原生自定义 top bar 页面统一纳入状态栏安全区处理
- 当前 `local-agent` 已能回写带 `dispatchExecutionId / targetProjectId / targetThreadId / rawThreadReply` 的任务完成载荷,群聊分发执行结果不再只停留在主 Agent 队列
- 当前 `local-agent``conversation_reply / dispatch_execution` 任务会优先使用 `codex exec resume <targetCodexThreadRef>`,只有缺失真实线程引用时才退回 `--ephemeral`
- 当前历史脏群如果不再包含真实线程成员,群聊消息不会再表现成“无响应”;服务端会在群内追加明确 `system_notice`,提示先重新添加线程成员
- 当前设备导入决议已经会先落 `device_import_resolution` master task 再写回结果,但决议内容仍是服务端 heuristic 版;下一阶段可再升级成真正通过 `local-agent -> codex exec` 参与理解的主 Agent 决议
- 原生 Android 当前对 `master-agent` 聊天消息已单独放宽读超时到 `65s`;之前默认 `12s` 会把等待 `Master Codex Node / local-agent` 回写的长请求误判成“主 Agent 无响应”

View File

@@ -0,0 +1,66 @@
# 聊天主链补完 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 打通主 Agent 单聊、普通线程单聊和群聊推荐执行三条聊天链,并让 Android 端能稳定展示等待、确认、失败和结果回流。
**Architecture:** 后端继续沿用 `boss-state.json + master-agent task queue + local-agent` 路线,把普通线程单聊纳入 `conversation_reply` 任务执行链;群聊继续沿用 `dispatch_execution`。Android 端用轮询项目详情和待确认计划来收束聊天状态,而不是一次发送后只做单次刷新。
**Tech Stack:** Next.js App Router, TypeScript, file-backed state store, local-agent, Android AppCompat/Java, node:test, Gradle unit tests
---
### Task 1: 后端补普通线程单聊执行链
**Files:**
- Modify: `/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/messages/route.ts`
- Modify: `/Users/kris/code/boss/src/lib/boss-data.ts`
- Modify: `/Users/kris/code/boss/local-agent/server.mjs`
- Test: `/Users/kris/code/boss/tests/single-thread-message-execution.test.ts`
- [ ] 写失败测试,覆盖“普通线程单聊发送后会排 `conversation_reply` 任务”
- [ ] 跑测试确认当前失败
- [ ] 在消息路由和状态模型里补任务创建
- [ ]`local-agent` 完成回写时把线程原始回复写回单线程项目
- [ ] 再跑测试确认通过
### Task 2: 后端补主 Agent/普通线程统一任务状态语义
**Files:**
- Modify: `/Users/kris/code/boss/src/app/api/v1/projects/[projectId]/messages/route.ts`
- Modify: `/Users/kris/code/boss/src/lib/boss-master-agent.ts`
- Test: `/Users/kris/code/boss/tests/project-message-task-status.test.ts`
- [ ] 写失败测试,覆盖发送返回 `taskId/taskType/status`
- [ ] 跑测试确认失败
- [ ] 在消息接口里把任务状态统一返回给客户端
- [ ] 主 Agent 单聊保持现有执行逻辑,但返回值补清晰任务状态
- [ ] 再跑测试确认通过
### Task 3: Android 补发送后等待/轮询/收束链
**Files:**
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java`
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/BossApiClient.java`
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityChatFlowTest.java`
- [ ] 写失败测试,覆盖主 Agent / 普通线程 / 群聊三种发送后的状态流
- [ ] 跑测试确认失败
- [ ]`ProjectDetailActivity` 增加轮询与等待态收束
- [ ] 群聊确认后继续轮询直到看到线程结果或主 Agent 汇总
- [ ] 再跑测试确认通过
### Task 4: 文档、验证、部署
**Files:**
- Modify: `/Users/kris/code/boss/README.md`
- Modify: `/Users/kris/code/boss/docs/architecture/current_runtime_and_deploy_status_cn.md`
- Modify: `/Users/kris/code/boss/docs/architecture/api_and_service_inventory_cn.md`
- [ ] 运行 `npm run lint`
- [ ] 运行 `npm run build`
- [ ] 运行新增 node:test 与 Android 单测
- [ ] 验证 `curl -sS http://127.0.0.1:3000/api/health`
- [ ] 验证 `curl -sS http://127.0.0.1:4317/health`
- [ ] 部署到服务器并验证 `curl -sS https://boss.hyzq.net/api/health`
- [ ] 同步文档写明三条聊天链当前状态

View File

@@ -0,0 +1,162 @@
# 聊天主链补完设计
## 背景
当前 Boss 已经具备这些基础能力:
- 主 Agent 会话、普通线程会话、群聊会话
- `master-agent task queue -> local-agent -> codex exec -> complete` 的基础执行链
- 群聊消息 -> 主 Agent 推荐下发 -> 用户确认 -> `dispatch_execution` -> 原始结果回群 的第一轮闭环
- 设备导入、线程归档、原生 Android 聊天页和会话页
但“聊天通路”还没有真正打通,主要断点有三处:
1. `主 Agent 单聊`
- 后端能接单并排队任务
- Android 端没有等待态和轮询回收,用户会感知成“发了消息没反应”
2. `普通项目线程单聊`
- 当前只把用户消息写进账本
- 没有创建真正的线程执行任务,也没有线程结果回写
3. `群聊调度`
- 后端主链基本具备
- Android 端仍缺“推荐计划待确认 / 执行中 / 回写后收束”的稳定体验
## 目标
1. 主 Agent 单聊在 APP 和 Web 中都能形成真实回复,不再只有消息入账
2. 普通线程单聊也能进入 `local-agent -> codex exec -> complete` 执行链
3. 群聊保持“主 Agent 推荐 -> 用户确认 -> 线程原始结果回群 -> 主 Agent 汇总”的现有结构,并补稳前台状态
4. Android 聊天页发送后要有清晰的等待、超时、失败和完成回显
5. 不改当前产品路线,不引入数据库或额外消息中间件
## 非目标
- 本轮不重做 UI 视觉
- 本轮不改变设备导入或附件存储路线
- 本轮不实现 ChatGPT OAuth
- 本轮不做新的原生真机视觉细抠
## 设计
### 1. 统一三条聊天链的后端执行模型
服务端把聊天请求统一收敛成两类:
- `conversation_reply`
- 用于主 Agent 单聊和普通线程单聊
- `dispatch_execution`
- 用于群聊推荐下发后,针对具体线程的执行单
#### 1.1 主 Agent 单聊
- `POST /api/v1/projects/master-agent/messages`
- 继续调用 `replyToMasterAgentUserMessage()`
- 仍然允许两种执行后端:
- `master_codex_node`
- `openai_api`
- 返回值继续允许“立即有内容”或“任务已排队”
- 但会明确返回 `taskStatus`,供 Android 端轮询
#### 1.2 普通线程单聊
- `POST /api/v1/projects/[threadProjectId]/messages`
- 如果目标项目:
- 不是 `master-agent`
- 不是群聊
- 且有完整 `threadMeta`
- 则服务端在写入用户消息后,创建一个新的 `conversation_reply` 任务
- 任务必须挂上:
- `projectId`
- `targetProjectId`
- `targetThreadId`
- `deviceId`
- `codexFolderRef`
- `codexThreadRef`
- `requestMessageId`
- `local-agent` 认领后,通过当前电脑已登录的 `codex` 会话执行,并把线程原始回复回写到这个单线程项目中
### 2. 单线程会话回写规则
普通线程单聊完成后:
- 线程原始结果直接作为一条 `device``thread` 消息回到当前单线程项目
- 不再额外套一个主 Agent 汇总层
- 这样单线程会话保持“我和这个线程直接聊天”的心智
如果执行失败:
- 在当前会话插入一条失败系统消息
- 保留用户消息,不吞掉失败状态
### 3. 群聊保持现有编排结构,但补稳状态语义
群聊继续使用:
- 用户消息入账
- 主 Agent 推荐计划 `dispatchPlan`
- 用户确认
- `dispatch_execution`
- 线程原始结果回群
- 主 Agent 汇总
本轮只补强两个方面:
- 前台能稳定恢复“当前还有待确认推荐”
- 执行中、失败、完成三种状态都能回到聊天页
### 4. Android 聊天页状态机
发送消息后不再只做“一次 reload”。
新增最小状态:
- `pending_task`
- `pending_dispatch_plan`
- `awaiting_result`
- `failed`
#### 4.1 主 Agent / 普通线程单聊
如果发送接口返回:
- 已经带回真实回复
- 直接刷新并结束等待态
- 只返回 `taskId`
- Android 进入轮询
- 轮询项目详情或任务状态,直到看到新回复 / 失败 / 超时
#### 4.2 群聊
如果返回 `dispatchPlan`
- 立即显示“等待你确认主 Agent 推荐”
- 确认后显示“已下发,等待线程返回”
- 后续靠轮询项目详情把原始回复和主 Agent 汇总拉回来
### 5. API 返回语义补充
聊天发送接口要统一补足这些字段:
- `masterReply`
- `dispatchPlan`
- `task`
- `taskId`
- `taskType`
- `status`
- `collaborationGate`
即使没有立即回复,也要让客户端知道:
- 是已经排队了
- 还是需要确认
- 还是确实失败了
### 6. 测试与验收
必须覆盖:
1. 主 Agent 单聊:消息发送后产生真实回复或明确排队状态
2. 普通线程单聊:消息发送后创建线程执行任务,并把线程回复写回项目
3. 群聊:推荐计划、确认、结果回流保持可用
4. Android发送后不会只停在“消息已发送”而是能收束成真实回复 / 推荐确认 / 失败提示