feat: add scoped history and artifacts
Generic Container CI/CD / test-build-publish (push) Failing after 8m25s
Agent CI/CD / build-test-publish-and-deploy (push) Failing after 8m25s

This commit is contained in:
2026-10-22 18:40:20 +08:00
parent bc4e9834e5
commit 7d47a2df86
48 changed files with 2632 additions and 78 deletions
+35 -6
View File
@@ -290,7 +290,7 @@ Shell 不能联网或访问源码、密钥、其他会话;业务查询和地
| 场景 | 工具或协议 | 说明 |
| --- | --- | --- |
| 查询供水数据 | `mcp_tjwater_*` | 启用 Server MCP 后发现25项项目查询和10项受控分析,供普通项目会话与绑定调查使用 |
| 查询供水数据 | `mcp_tjwater_*` | 启用 Server MCP 后发现42项项目查询、12项计算和13项预览式写入,供普通项目会话与绑定调查使用 |
| 地图与图表交互 | `locate_features`、`zoom_to_map`、`show_chart`、`view_history` | 通过 `frontend-action@1` 等待浏览器实际回执 |
| SCADA 地图分析 | `render_scada_analysis`、`clear_scada_analysis` | 展示和清除受控叠加层 |
| 网页搜索 | OpenCode 原生 `websearch` | 搜索服务由 OpenCode 配置管理,不经过 TJWater Server |
@@ -381,6 +381,7 @@ Agent 服务的 API 前缀是 `/api/v1`,路径不再包含 `/agent/chat`。生
| POST | `/responses` | Responses 文本请求;stream=true 返回 SSE,否则返回 Response JSON |
| GET | `/session/:session_id/inputs` | 当前会话尚未处理的用户消息 |
| PATCH | `/session/:session_id/inputs/:input_id` | 将未处理消息切换为 steer 或 queue |
| DELETE | `/session/:session_id/inputs/:input_id` | 撤回尚未消费的用户消息,返回原文和 cancelled 回执 |
| GET | `/session/:session_id/stream` | 订阅会话流 |
| POST | `/abort` | 中止运行 |
| POST | `/permission/:request_id/reply` | 回复工具审批 |
@@ -398,6 +399,8 @@ Agent 服务的 API 前缀是 `/api/v1`,路径不再包含 `/agent/chat`。生
未处理消息通过 `/session/:session_id/inputs` 查询,可 PATCH `{ "delivery": "queue" }` 或 `{ "delivery": "steer" }` 切换;已取走时返回409,前端刷新列表。每会话最多20条待处理消息,按次最多接收500条追加输入;停止会话会取消尚未处理的本类输入。接口校验用户、项目和会话归属,不能直接追加或修改后台 `task_execution` 调查输入,也不能借此更改代理协作消息。会话收尾或启动尚未就绪时返回409,不能自动重试为新一轮。
单条撤回使用 DELETE,同样校验归属与用户消息来源。原生取消对已消费 ID 也可能返回成功,因此适配器取消后还会核对原生消费记录;消费先发生则返回409,不能将已经送出的内容当作撤回草稿。成功后通过 `tjwater.user_input` 发布 `cancelled`,队列跟踪器释放等待状态。前端默认展示待处理正文,可在消息菜单中编辑或关闭排队;编辑仅在服务端确认撤回后恢复原文,保留输入框中已有草稿,等待用户手动重发。
这里是现有 HTTP/SSE 服务的 `tjwater` 扩展,映射 OpenCode 原生用户收件箱;不等同于 OpenAI WebSocket 的 `response.steer` 事件,官方协议边界见 [Mid-turn steering](https://developers.openai.com/api/docs/guides/steering)。验证命令 `bun tests/integration/response-input-smoke.ts` 使用隔离 OpenCode 和本机确定性模型,验证模式切换及队列后续输出,不调用付费推理。
流使用 `response.created`、`response.in_progress`、`response.output_item.added/done`、`response.content_part.added/done`、`response.output_text.delta/done` 和终态 `response.completed/failed/incomplete`,带单调递增的 `sequence_number`。中止返回 incomplete;未知原因不伪造标准 incomplete reason。进度、审批、问题、凭据续期、地图和富文本展示通过 `tjwater.<事件名>` 扩展携带 `response_id`。前端使用 Responses transport 接入现有 useChat 和 UIMessage 渲染;UIMessage 不再是生成接口的网络协议。标准流语义见 [OpenAI Responses streaming](https://developers.openai.com/api/docs/guides/streaming-responses)。
@@ -471,10 +474,15 @@ OPENCODE_MODE=embedded OPENCODE_PORT=0 bun run tests/integration/prompt-suggesti
相同 block_id 更新本轮内容;消息持久化与历史恢复保留结构化部分。最终答复包含
完整报告文字及 ui_block_ids 引用的已发布组件,其余过程默认折叠。已发布界面的
聊天在最终答复完成且无待完成工具时收尾,不代替后台任务的完成接口。后台调查执行
不发布聊天界面块,继续使用原任务报告和展示通道。
不发布聊天界面块,继续使用原任务报告和展示通道。`publish_task_report` 的报告可携带
`ui_blocks`,每项包含相同 `answer-ui@1` 内容块及 `evidence_refs`。最多 8 块,总 UTF-8
JSON 不超过 400000 字节,单块不超过 100000 个 JavaScript UTF-16 代码单元、48 节点和深度 12。
所有依据必须属于报告已引用的当前任务证据。固定结论、缺口和审批规则独立校验。
Server 通过原有报告 JSON 与版本保存这些内容,旧报告仍可读取。
组件参数契约由供水前端 catalog 导出到 `src/chat/answer-ui-catalog.json`,
本服务按该契约校验组件参数及树结构。前端与 Agent 需同步更新,插件变化需要重启
组件参数契约由供水前端 catalog 导出到 `src/chat/answer-ui-catalog.json` 和 Server 的
`contracts/answer-ui-catalog.json`,
本服务按该契约校验组件参数及树结构。前端、Agent 与 Server 需同步更新,插件变化需要重启
Agent,客户端模式也需更新外部 OpenCode 的可信资产。无需新增 domain key、
UI 托管服务配置或数据库迁移。详见
[前端接入与扩展说明](../next-tjwater-frontend/docs/agent-answer-ui.md)。
@@ -490,13 +498,13 @@ UI 托管服务配置或数据库迁移。详见
安装时将两个仓库的 `infra/systemd/tjwater-{server,agent}.service` 分别复制到 `~/.config/systemd/user/`,准备上述私有覆盖文件,完成后端依赖和项目迁移后执行 `systemctl --user daemon-reload` 以及 `systemctl --user enable --now tjwater-server.service tjwater-agent.service`。后端读取自身 `.env`;生产覆盖中的 issuer 必须与后端、前端登录及 worker 取令牌地址一致。本机服务状态可用 `systemctl --user status tjwater-server tjwater-agent` 检查,Agent 就绪地址为 `http://127.0.0.1:8787/health`。
项目及宿主只读查询统一由原生连接 `mcp_tjwater` 提供,工具 ID 为 `mcp_tjwater_<name>`。本地 MCP 使用官方 Server SDK 2.3.1,固定 MCP 2026-07-28,默认提供地理编码、会话搜索、调查任务列表与详情、消息读取和待办读取六项查询,启用上游后合计41项。公开网页搜索使用 OpenCode 原生 `websearch`。`memory_manager`、`skill_manager` 保留完整读写插件;消息发送、报告发布和地图展示等也保留插件形式。
项目及宿主只读查询统一由原生连接 `mcp_tjwater` 提供,工具 ID 为 `mcp_tjwater_<name>`。本地 MCP 使用官方 Server SDK 2.3.1,固定 MCP 2026-07-28,默认提供地理编码、会话搜索、调查任务列表与详情、消息读取和待办读取六项查询,启用上游后合计73项。公开网页搜索使用 OpenCode 原生 `websearch`。`memory_manager`、`skill_manager` 保留完整读写插件;消息发送、报告发布和地图展示等也保留插件形式。
`mcp_tjwater_query_nearby_elements` 查询当前授权项目的模型管网:将 `mcp_tjwater_geocode` 返回的 EPSG:4326 `center: [经度,纬度]` 原样传入,后端负责坐标转换。`element_type` 默认 `pipe`,`radius_m` 默认200、最大10000米,`limit` 默认5、最大100,支持 `offset`。结果按点到完整几何的 WGS84 椭球最短距离排序,返回模型 `element_id`、米制 `distance_m` 和 `properties`;可用 `locate_features` 定位。原始 GIS 的 `source_fid` 不在此查询范围。空结果不自动扩大半径,空间邻近不代表连通或供水归属。普通查询预算仍为30秒,用户、项目和调查证据链沿用现有 MCP 规则。
事件业务处置 Skill 由 Server 维护并经任务 MCP 分发:`get_task_context` 摘要返回 `handling_skill` 名称、版本和内容摘要,`section="skill"` 返回按已绑定任务 workflow 选择的完整流程,不保存为事件证据。Agent 本地 Skill 维护通用执行方法,不复制或覆盖 Server 业务规则;权限、人工审批与动作执行仍由 Server 控制。报告和进度的人类可读时间统一使用 Asia/Shanghai(UTC+8),API 时间保留原始时刻。更新 Agent 指令或 Server 工具 schema 后须重启 Agent 重新加载。
`TJWATER_MCP_ENABLED` 默认 false,只控制上游 Server 项目查询。启用后,宿主通过官方 Client SDK 发现35项工具(25项查询、10项分析),合并到本地 MCP 目录,由 OpenCode 原生发现调用。旧 `query_project_data`、`query_operational_task_data` 和任务上下文查询插件及其 HTTP 查询桥已移除;未启用时项目业务查询不可用,不回退。普通项目会话和调查主 Agent 可使用;直接只读 reader 仍仅可使用25项查询;身份、项目和任务由宿主确定。调查查询保存 Server 证据,普通聊天不自动保存调查证据。
`TJWATER_MCP_ENABLED` 默认 false,只控制上游 Server 项目查询。启用后,宿主通过官方 Client SDK 发现67项工具(42项查询、12项计算、13项预览式写入),合并到本地 MCP 目录,由 OpenCode 原生发现调用。旧 `query_project_data`、`query_operational_task_data` 和任务上下文查询插件及其 HTTP 查询桥已移除;未启用时项目业务查询不可用,不回退。普通项目会话和调查主 Agent 可使用;直接只读 reader 仍仅可使用42项查询;身份、项目和任务由宿主确定。调查查询保存 Server 证据,普通聊天不自动保存调查证据。
三类 Agent 均优先单对象、有过滤条件和必要短时间窗的查询。集合默认20条,按需翻页,不自动遍历全网;offset 上限10000,超过时缩小时间窗或目标。历史组合查询按序列、时间对数据点分页;读取证据优先指定 data_path 和小页。DEMO 样本超过整份摘要校验的8MiB预算时需拆分数据源,反复缩小时间窗不能解除此限制。截断或非零offset均不能证明完整范围无异常。
@@ -513,3 +521,24 @@ UI 托管服务配置或数据库迁移。详见
OpenCode 按标准 MCP `tools/call` 调用,以 `_meta["ai.opencode/sessionID"]` 传入会话关联值。该值不是凭据:本地 MCP 先校验内部密钥、loopback 地址并拒绝浏览器请求,再解析宿主会话或已验证的直接只读子会话。真实身份、项目、任务和专用 OAuth 令牌由宿主决定。精确 MCP 权限规则对直接调用和 Code Mode 均生效;reader 只可查询父任务范围,不能发布、审批、执行 Shell 或递归委派。待办读取经宿主 RPC 访问原插件存储,保留已有清单。
配套 Server 的 `docs/mcp-query-implementation.md` 提供 Keycloak、SQL migration 和配置步骤。本次没有配置真实身份平台、迁移真实数据库或部署;开启前须完成真实项目验收。
新增项目服务见 [Server MCP 项目服务](../next-tjwater-server/docs/mcp-project-services.md)。13 项写入仅对项目成员普通聊天开放,默认只预览,执行要求摘要、幂等键及原生许可;后台调查、协作和 reader 禁止写入。导出保存在当前会话 files 中,通过带身份校验的会话文件接口下载,不返回宿主路径。健康预测未接入,GIS 不跨项目回退。同步更新 Server 与 Agent 后需重载目录。
### 历史窗口接入与分页
历史专用接口契约见 [history.openapi.json](openapi/history.openapi.json);`openapi/openapi.json` 仍是产品 Server 的上游快照。
- `GET /api/v1/sessions?purpose=ordinary_chat&limit=30&q=...&cursor=...` 返回 `sessions`、`has_more`、`next_cursor`,搜索覆盖当前验证范围全部主题和未分类会话的标题与用户/助手可见正文,返回 `search_snippet`;前端以 `include_collaboration=true` 同时包含手动任务协作(继续排除后台执行会话),可选 `topic_id` 筛选主题或 `unclassified`;按更新时间及 ID 倒序,游标绑定用户、项目、用途、主题筛选和查询。
- `GET /api/v1/session/:session_id?message_limit=40` 返回最近消息及 `message_page: {has_more, before_cursor}`;`GET /api/v1/session/:session_id/messages?limit=40&before=...` 读取更早消息,每页内部按原有消息顺序排列。
- `GET /api/v1/session/:session_id/stream?message_limit=40` 的初始 `state` 同样返回最近页,后续事件协议保持兼容。停止浏览或断开订阅不停止后端生成,显式 `/abort` 才停止。
- 未指定分页参数的旧列表、详情和恢复流继续支持全量读取。游标无效、跨范围或消息边界改变时返回 400;会话读取继续核验当前用户和个人或项目归属。
- 新一轮 `/responses` 可传 `tjwater.client_message_id`(1 至 128 字符),用于保存与前端一致的用户消息 ID;同一会话已有该消息 ID 时返回 409。该字段不同于运行中追加输入的 `input_id`,不构成重复请求的成功回执。
UI 分页只影响读取窗口,服务端继续保存完整消息并从完整历史续聊;无需迁移现有文件。文件存储仍整份读取 JSON。前端内存缓存不替代这里的授权检查。
发布时先更新并重启 Agent,再发布使用分页及 `client_message_id` 的前端;旧前端可继续使用更新后的 Agent。回退前端无需回退或改写会话记录。
主题是当前用户在个人/业务项目范围内的会话组织单元,与业务 `project_id` 独立。`GET/POST /api/v1/topics` 读取/创建主题,`PATCH/DELETE /api/v1/topics/:topic_id` 重命名/移除主题,`PATCH /api/v1/session/:session_id/topic` 以 `topic_id`(UUID 或 null)调整会话归属。主题名 1–80 字符,当前主题列表存于 `SESSION_METADATA_STORAGE_DIR/topics/<scope-hash>.json`,与元数据目录一起持久化。删除主题保留会话,遗留成员 ID 在读取时视为未分类;不会改写业务项目、原生会话或授权。旧会话默认未分类,无需迁移。普通会话和任务协作可归类,后台任务执行会话不进入用户主题。
`POST /session` 或首次 `/responses` 的 `tjwater.topic_id` 可指定已授权范围内的主题;后续续聊沿用原会话,移动使用上述独立接口。主题只组织记录,本轮不自动拼接其他会话或引入主题共享指令/附件。正文搜索在通过所有权校验的记录内读取完整消息 JSON(活动会话使用当前运行消息),匹配可见文本而非工具输入或推理;分页仍按更新时间及 ID 排序,不另建搜索服务。