Files
next-tjwater-agent/.opencode/README.md
T
jiang 6a49d2209e feat(agent): unify MCP tools and adopt Responses delivery
Move project and host queries into native MCP with host-owned OAuth/context,
while retaining memory, skills, actions and publication plugins. Add nearby-model
queries and explicit chat/task/learning model defaults.

Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped
session ownership, pending input edits and investigation communication.

Disable local-provider discovery: Server /health allowed native vLLM to probe
/v1/models on port 8000. A native before/after regression prevents recurrence.

Validation: 170 focused Bun tests; service/plugin typechecks; native discovery,
provider-discovery and input-inbox smoke checks; no paid model inference.
2026-10-19 18:46:58 +08:00

229 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 供水 Agent 的 OpenCode 资产
这里维护服务使用的 Agent 指令、插件和工具。项目查询与受控分析统一通过第一方 MCP,公开网页搜索使用 OpenCode 原生 `websearch`;回答展示、业务写入、记忆和 Skill 管理保留插件接口。管理员通过这些入口管理可复用 Skill 和外部 MCP 连接。
[agent-plugin.json](agent-plugin.json) 是本项目的描述清单,列出各项能力的用途、文件路径和配置来源,方便检索和交接。它是管理索引,不参与运行时加载;字段中的相对路径以 `.opencode/` 为基准,环境变量指定的路径仍遵循服务原有解析规则。
## 目录与加载入口
| 入口 | 作用 | 实际加载方式 |
| --- | --- | --- |
| [agents/instruction.md](agents/instruction.md) | 公共数据规则及普通聊天、协作指令 | 配置生成器去除 frontmatter 后,设为 `instruction` Agent 的 system 指令 |
| [agents/investigation.md](agents/investigation.md) | 后台调查协调规则 | 叠加公共业务指令,仅可信任务执行会话选择 |
| [agents/investigation-reader.md](agents/investigation-reader.md) | 只读核查子代理的独立指令 | 单独加载,不继承普通聊天和调查主代理指令 |
| [tjwater-plugin/package.json](tjwater-plugin/package.json) | 插件包名称、版本、描述和导出入口 | OpenCode 显式加载该目录包 |
| [tjwater-plugin/index.ts](tjwater-plugin/index.ts) | 插件包入口 | 导出 `plugins/tjwater-tools.ts` 的默认插件 |
| [plugins/tjwater-tools.ts](plugins/tjwater-tools.ts) | `tjwater.tools` 插件实现 | 注册业务与待办工具、沙箱、MCP 策略和内部 RPC |
| `skills/` | 可复用流程及引用、脚本 | 默认 Skill 根目录,可由 `OPENCODE_SKILLS_ROOT_DIR` 替换 |
| [../opencode.json](../opencode.json) | 工具权限和 MCP 连接 | 服务合并部署覆盖配置后校验并交给 OpenCode |
| [../agent-models.json](../agent-models.json) | 模型和 provider | 统一生成 OpenCode 模型定义,不在描述清单中重复配置 |
[serviceOpencodeConfig](../src/runtime/opencode2-config.ts) 将可信插件包、Agent 指令和 Skill 根目录解析为绝对路径。会话目录位于 `data/session-workspaces/` 时也能加载这些服务资产,并在保留业务数据静态拒绝规则的同时,为受管理的会话文件生成权限规则。插件包名 `tjwater-tools-plugin`、运行时插件 ID `tjwater.tools`、Agent ID `instruction` 各有用途,新增描述时应保留它们的对应关系。
原生插件通过 `Plugin.define({ id, setup })` 注册,额外插件使用 OpenCode 的 `plugins` 配置。参考 [OpenCode 插件文档](https://opencode.ai/v2/docs/build/plugins/)。本项目显式加载可信目录包,新增文件不等于已经注册了业务能力,需检查运行时工具目录。
排查插件状态时需同时核对会话的 Location。仓库根目录会话还会自动扫描 `.opencode/plugins/tjwater-tools.ts`,与显式加载的目录包产生同 ID 重复记录;本次隔离核验中,一个 `tjwater.tools` 正常激活,另一个报 `Duplicate plugin ID: tjwater.tools`。使用独立会话工作目录时仅出现一个正常激活记录,工具发现与调用验证通过。实际聊天使用受管理的会话 `workspace/`,调试时也应在对应目录检查插件和工具。
## Prompt 组织与角色权限
加载方式由 [opencode2-config.ts](../src/runtime/opencode2-config.ts) 决定,本次整理不改变加载机制:普通聊天使用 `instruction.md`,后台调查使用 `instruction.md` 加 `investigation.md`,只读子代理只使用 `investigation-reader.md`。
`instruction.md` 集中维护数据来源、限定查询、分析适用范围和消息语义,回答展示与人工补充事实标明仅适用于普通聊天、任务协作。`investigation.md` 只补充后台调查的委派、进度、Mermaid、展示、报告和收尾规则,不重复整套查询说明。reader 不继承公共文件,因此保留独立的查询与证据约束,不加载聊天展示或正式报告流程。
| 角色 | 产品 MCP | 子代理 | 发布与通信 |
| --- | --- | --- | --- |
| 项目普通聊天、任务协作 | 25 项查询、10 项受控分析 | 关闭 | 聊天展示、用户事实写入、调查消息,不直接改写正式报告 |
| 后台调查主代理 | 25 项查询、10 项受控分析 | 最多 2 个前台只读子任务,仅一层 | 统一发布任务进度、展示、报告与动作提议,只收取和回复绑定运行的消息 |
| `investigation-reader` | 仅 25 项查询,可读已有分析结果 | 关闭 | 向父代理返回发现与证据编号,无通信、发布或设备操作权限 |
表中是角色允许的工具类别,不是额外授权。实际调用还需满足产品 MCP 开关、用户项目权限、任务范围及服务端校验;reader 的直接父子关系、运行有效性由宿主核验。只读指不修改业务对象、不发起计算,查询仍可能保存任务证据快照。分析不保存正式方案,也不实际控制设备。
Prompt 负责引导行为,不替代权限检查。维护角色规则时同步核对 [task-agent-policy.ts](task-agent-policy.ts)、[项目 MCP 白名单](../src/runtime/product-mcp-tools.ts) 和服务端工具契约,不通过修改文字扩大权限。更新后需重新加载 Agent/OpenCode 运行时资产;client 模式需同步外部 OpenCode 配置,本次文档修改不代表运行中会话已加载新指令。
## 当前工具目录
`tjwater.tools` 显式注册 20 个工具;描述清单中的 `plugins[].tool_groups` 按下表列出准确 ID。插件内部 RPC `list`、`mcp`、`check_models` 和 `read_todos` 供宿主使用,不是模型工具。
| 用途 | 插件工具 |
| --- | --- |
| 回答组织与收尾 | `present_answer`、`final_answer` |
| 地图、图表与监测展示 | `locate_features`、`zoom_to_map`、`show_chart`、`view_history`、`render_scada_analysis`、`clear_scada_analysis` |
| 调查进度、展示与报告 | `publish_task_progress`、`publish_task_presentation`、`publish_task_report`、`publish_execution_plan` |
| 人工证据、动作提议与授权申请 | `add_task_human_evidence`、`propose_task_action`、`request_task_data_access` |
| 调查讨论写入 | `send_task_message`、`reply_agent_message` |
| 完整读写管理接口 | `memory_manager`、`skill_manager` |
| 当前会话待办写入 | `todowrite` |
`view_history` 是带数据读取的历史曲线展示动作,保留插件形式;纯数据查询使用 `mcp_tjwater_query_element_history`。`present_answer` 发布本地界面,不等待浏览器回执;地图/图表动作遵循各自的回执契约。后台 `publish_task_presentation` 记录展示请求,不能把记录成功解释为浏览器已显示。
OpenCode 原生工具包括文件工具 `read`、`glob`、`grep`、`edit`、`write`、`patch`,会话沙箱 `shell`,网页工具 `websearch`、`webfetch`,以及 `skill`、`question` 和受限 `subagent`。保留 `opencode_models`、`opencode_session_move`、`opencode_session_rename`、三个 `opencode_worktree_*` 管理工具及两个 MCP 资源工具;完整 ID 见描述清单的 `native_tools`。这些目录项不代表每种会话都能执行,最终由权限、审批、会话归属及沙箱决定。原生浏览器自动化工具已禁用。
原生 `websearch` 的仓库默认配置为 `websearch.provider = "exa"`;实际搜索引擎以合并后的部署配置为准。无需恢复 `web_search`、Server 搜索接口或 MCP 搜索副本。
任务专用工具 `publish_task_progress` 和 `publish_task_presentation` 也由该插件显式注册,启动工具发现必须包含两者。进度复用 Server artifacts,展示使用 Server 的有序事件流;内部桥只接受后台 task_execution 的可信任务上下文,不接收模型提供的 task/run/用户身份。它们不增加浏览器等待,普通聊天的展示回执保持原有语义。
[task-agent-policy.ts](task-agent-policy.ts) 保留原生 subagent 执行器,增加可信任务用途、
指定只读 Agent、最多两个并发前台子调用及禁止恢复旧子会话的检查。原生直接父子关系
由服务端核验,只有 MCP 项目查询可以使用已核验的父任务身份,其他工具不会继承该身份。
Mermaid 并行分支须对应实际子任务,主 Agent 统一维护整图;图与 todo 不参与调度。
## 受控调查通信
`mcp_tjwater_list_investigation_tasks`、`mcp_tjwater_get_investigation_task` 和
`mcp_tjwater_read_agent_messages` 通过本地 MCP 提供,OpenCode 原生发现和调用。
插件只注册写入类 `send_task_message`、`reply_agent_message`,使用
`/internal/agent-messages/send|reply`。读取操作没有内部 HTTP 查询路由。
MCP 和写入桥均依据宿主会话核定身份、项目、绑定运行和回复线程;模型不能提供
身份、项目、接收会话 ID、URL 或鉴权头。
通信采用原生消息能力,消息来源保留为 `originalMessage`,不升级为 `userinstruction`。
| 会话 | 工具范围 |
| --- | --- |
| 当前项目的普通聊天、任务协作 | 列表、详情、发送、读取和回复自己的有效线程 |
| 后台 `task_execution` | 仅读取和回复绑定运行的消息 |
| `investigation-reader` | 无通信工具 |
[agent-message-policy.ts](agent-message-policy.ts) 收紧模型工具目录,配置生成器为两种
主 Agent 设置明确的静态权限;最终身份、项目和运行权限由本地 MCP 与写入桥分别校验,学习、个人
或未绑定会话不能借工具目录获得项目通信权限。普通聊天和协作的子代理规则不变。
先列表再读取详情,使用返回的稳定 UUID `task_id`、`run_id`;收到消息后使用稳定
`message_id` 回复。发送与回复的 UUID `request_id` 是幂等键,重试同一操作复用。
`queued` 表示接受排队,不代表对方读取、处理或调查完成。使用 `mcp_tjwater_read_agent_messages`
明确收取消息,后续分页沿用返回的 opaque cursor。只在确有内容需要答复时回复,不做
自动确认互相触发。消息不保存 human_input、不创建调查新轮次,不自动唤醒空闲主会话。
正式报告、用户事实和任务完成仍使用原有业务流程。
聚焦验证使用 `bun test tests/opencode/agentMessages.test.ts tests/chat/agentMessageCoordinator.test.ts`。
可选原生验证使用 `bun run tests/integration/agent-messages-smoke.ts`:它创建隔离原生数据目录,
仅使用本机确定性 SSE 假模型端点,核验真实工具发现、静态权限、收件箱状态、稳定 ID 去重、
`resume:false` 不唤醒空闲会话,以及显式对话启动后消费消息,不调用付费推理。
## 管理 Skill
业务处置 Skill 与下述 Agent 本地 Skill 分开维护。爆管、水质投诉和预约跟进流程位于 Server 的 `resources/work-order-skills/`,由 `mcp_tjwater_get_task_context` 的 `handling_skill` 元数据发现,使用 `section="skill"` 读取正文、版本与内容摘要。Server 根据已绑定任务的 workflow 选择流程,沿用任务权限;流程不保存为调查证据。Agent 只保留加载与执行规则,不通过 `skill_manager` 写入或覆盖 Server 业务流程。新增和修改业务流程须发布 Server 资源;Agent 指令变化仍需重启相关运行时。
Skill 是模型按需读取的流程说明,工具负责实际执行。一个 Skill 推荐使用以下结构:
```text
skills/
└── supply-data-review/
├── SKILL.md
├── references/
│ └── field-guide.md
└── scripts/
└── summarize.py
```
`SKILL.md` 至少包含名称、用途和适用条件,正文引用的文件路径相对于该 Skill 目录:
```markdown
---
name: supply-data-review
description: 核对供水数据的时间范围、单位和缺失情况。
---
# 供水数据核对
适用于已通过受控工具取得的数据。
1. 核对查询范围、时间和单位。
2. 将真实缺失与接口错误分别记录。
3. 输出数据结论和未确认事项,保留实际证据来源。
```
目录名称决定 OpenCode 的 Skill ID,frontmatter 的 `name` 用于显示。推荐使用唯一的小写名称,避免同名 Skill 被后加载的来源覆盖;完整规则见 [OpenCode Skill 文档](https://opencode.ai/v2/docs/skills/)。
当前 Skill 根目录由 `OPENCODE_SKILLS_ROOT_DIR` 统一指定,供 OpenCode 加载和 Agent 的 SkillStore 使用。描述清单只引用该入口,不维护另一份 Skill 名单。使用绝对路径可减少外部 OpenCode 和 Agent 的启动目录差异。
`skill_manager` 支持列出、写入和删除 Skill,以及维护 pattern、reference 和 script。它通过可信内部工具桥调用 [SkillStore](../src/skills/store.ts),对内容、路径和写入历史进行处理;默认备份位于 `data/backup/skills/`。当前 Skill 存储按部署实例共享,应保存通用方法,避免写入用户凭据、具体会话数据和未经验证的业务结论。
服务当前的指令要求:流程已经验证可复用,且用户明确需要保存时,才通过 `skill_manager` 沉淀。Skill 中的执行步骤不能改变工具权限;引用脚本仍需通过批准的会话沙箱执行,业务 API 仍使用类型化工具。
`skills/` 被 Git 忽略,保存或交付 Skill 时需要单独准备目录和持久化挂载,克隆仓库不包含已有实例的 Skill。修改后重启相关运行时以重新发现;需要后续实现在线刷新时使用原生 Skill reload 接口,当前管理桥不保证即时刷新 OpenCode 目录。
## 管理 MCP
管理员添加的外部连接配置位于仓库根目录 `opencode.json` 的 `mcp.servers` 中,部署覆盖使用 `OPENCODE_CONFIG_CONTENT`。保留的第一方 `mcp_tjwater` 连接由宿主生成,再由 [query-mcp.ts](query-mcp.ts) 注入原生 MCP;不在外部连接配置中另建同名服务。`agent-plugin.json` 只指向配置和实现来源,不存地址、命令或认证头。
服务名使用 `mcp_` 前缀。添加、修改或禁用连接时,按根目录 README 的 [MCP 配置说明](../README.md#配置外部-mcp) 操作;支持远程 Streamable HTTP 和本地 stdio,详细字段参考 [OpenCode MCP 文档](https://opencode.ai/v2/docs/mcp-servers/)。
密钥通过 `{env:NAME}` 引用 `.local.env` 或部署环境。需要 OAuth 时,由管理员使用同一服务账号和 XDG 数据目录执行认证,不把 TJWater 用户令牌转交给 MCP 服务。修改连接后重启运行时;client 模式需同步外部 OpenCode 的配置与可信插件。
查看会话中实际加载的服务和工具:
```text
GET /api/v1/session/:session_id/mcp
```
该接口校验产品 Bearer、会话归属与个人或项目范围,项目会话额外验证项目头。接口只返回服务名、类型、状态、配置协议和实际工具 ID。`connected` 只表示连接状态,仍需检查工具 ID 是否已出现;响应不包含地址、进程命令或凭据。
管理员配置的外部 MCP 执行策略由 [mcp-policy.ts](mcp-policy.ts) 和 [服务配置校验](../src/runtime/mcp-config.ts) 维护。下表不包含第一方 `mcp_tjwater`,其查询和分析按后文的产品白名单与业务权限校验:
| 会话 | 调用规则 |
| --- | --- |
| 普通聊天、任务协作 | 管理员明确允许的只读工具可自动执行;其他允许审批的调用按本轮选择逐次询问或自动批准 |
| 后台任务 | 仅调用精确的只读白名单工具 |
| 学习复盘 | 禁用 MCP 调用 |
只读白名单需在 `permissions` 中使用状态接口返回的精确工具名,不能用宽泛的 `mcp_* allow` 替代。Code Mode 和通用资源访问也应用同一策略。本地 stdio MCP 运行于可信宿主进程,不受会话 Shell 沙箱约束,环境过滤不等于 OS 隔离。
聊天输入框的“始终允许”自动处理本轮原生权限询问,包括 Shell、文件修改和 MCP 请求;只回复 `once`,避免切回“请求批准”后继续沿用自动授权。明确拒绝、项目业务权限、后台只读白名单及会话沙箱均继续执行,自动批准不开放被禁止的能力。工具的业务人工审核与向用户提问也不由此模式自动作答。
## 修改后的检查
更新 Agent、插件或管理入口时,同步修改描述清单和相关说明,保持路径及 ID 对应。插件的运行时代码变更需要类型检查和相关测试:
```bash
bun run check
bun test tests/frontendAction/callIdPlugin.test.ts tests/runtime/opencode.test.ts
```
MCP 策略变更使用 MCP 相关测试,实际连接联调可选择根目录 README 列出的本地 smoke 脚本。模型配置修改使用 `bun run check:models`。这些命令从仓库根目录运行;仅修改描述或说明时检查路径、JSON 和 `git diff --check` 即可。
运行时确认以 Agent 的启动预热和 `/health` 为准。client 模式要同时更新外部 OpenCode 使用的资产。服务启动、工作区保留和容器挂载参见 [根目录 README](../README.md),代码修改约束见 [AGENTS.md](../AGENTS.md)。
`tools/present_answer.ts` 注册本地交互式回答工具。组件契约来自前端导出的
`../src/chat/answer-ui-catalog.json`,由服务与前端同时校验;注册入口仍是
`plugins/tjwater-tools.ts`。更新后需要重新加载 Agent/OpenCode 的可信插件资产。
发现验证使用 `bun run tests/integration/answer-ui-smoke.ts`,从 Agent 仓库根目录运行。
## 第一方查询与分析 MCP
配置构建器在插件列表末尾添加 `-opencode.browser` 和 `-opencode.browser.*`,禁用原生浏览器插件及其子插件,并拒绝 `browser` / `browser_*` 权限。`present_answer`、地图/图表/SCADA 前端动作、任务展示发布和记忆/技能管理插件继续保留;它们使用各自的业务协议,不依赖原生浏览器自动化工具。
项目查询、受控分析及宿主只读查询通过保留连接 `mcp_tjwater` 统一暴露;插件只配置原生 MCP 连接和权限策略,不注册这些 MCP 工具或自定义查询执行器。公开网页搜索使用 OpenCode 原生 `websearch`,不另设 Server 或 MCP 搜索入口。MCP 实现位于 [query-server.ts](../src/mcp/query-server.ts),连接配置位于 [query-mcp.ts](query-mcp.ts)。
默认提供六个宿主查询:`geocode`、`session_search`、`list_investigation_tasks`、`get_investigation_task`、`read_agent_messages`、`todoread`。Agent 合并 25 项项目查询和 10 项受控分析,目录匹配且上游启用时合计 41 项,原生工具 ID 为 `mcp_tjwater_<name>`。`memory_manager` 和 `skill_manager` 保留完整读写插件能力;发布、动作、发送、地图展示和 `todowrite` 也保留插件形式。
产品目录包含 35 项工具:25 项查询、10 项分析。名单以 [product-mcp-tools.ts](../src/runtime/product-mcp-tools.ts) 为准,`McpQueryClient.prepare()` 对两端工具名做全等检查;升级需先发布 Server,再重启 Agent 和 client 模式下的原生连接。分析权限和适用边界见配套 Server 的 [MCP 分析工具说明](../../next-tjwater-server/docs/mcp-analysis-tools.md),reader 不获得分析能力。
25 项项目查询按以下分组组织;表中省略共同前缀 `mcp_tjwater_`。工具名称以 [product-mcp-tools.ts](../src/runtime/product-mcp-tools.ts) 为准,输入和查询说明来自 Server 实际发现的工具定义,不在描述清单中复制参数契约。
| 查询范围 | 工具名称 |
| --- | --- |
| 管网与资产 | `get_network_options`、`get_network_schema`、`list_elements`、`query_nearby_elements`、`get_element_properties`、`get_element_gis_properties`、`get_source_gis_properties` |
| SCADA 与实测历史 | `list_scada_devices`、`get_scada_device`、`query_scada_readings`、`query_element_history` |
| 模型基线 | `get_model_baseline_at_time`、`query_model_baseline_field`、`query_model_baseline_series` |
| 分析成果 | `list_analysis_runs`、`get_analysis_run`、`list_analysis_results`、`query_analysis_values`、`query_analysis_series` |
| 布点成果 | `list_sensor_placement_runs`、`get_sensor_placement_run` |
| 工作台、外围数据和任务证据 | `get_workbench_runtime`、`query_peripheral_data`、`get_task_context`、`get_task_evidence` |
第一方 MCP 的会话边界由 [query-mcp-policy.ts](../src/runtime/query-mcp-policy.ts) 和宿主身份校验执行。下表表示策略上限,工具还必须实际发现、上游启用并通过用户/任务权限检查。
| 会话 | 第一方 MCP 范围 |
| --- | --- |
| 个人聊天 | `session_search`、`todoread` |
| 项目普通聊天、任务协作 | 25 项项目查询、10 项分析及 6 项宿主查询 |
| 后台 `task_execution` | 25 项项目查询、10 项分析,以及 `geocode`、`session_search`、`read_agent_messages`、`todoread` |
| 已核验的 `investigation-reader` | 仅父任务授权的 25 项项目查询,不含宿主查询 |
| 学习复盘 | 禁用 MCP 查询 |
业务凭据和查询上下文由宿主绑定,模型不能指定用户、项目或授权头。工具可发现或允许调用不会扩大用户及任务已有的业务权限;Server MCP 关闭时不回退到旧 HTTP 查询桥。
连接使用 MCP 2026-07-28 Streamable HTTP,独立监听 127.0.0.1 临时端口,启动目录发现前已就绪。宿主写入元数据目录旁的 `mcp-query/connection.json`,仅含公开端点;外部 OpenCode 须共享该路径(`TJWATER_AGENT_MCP_CONNECTION_PATH`)、同一内部密钥及同版本可信插件,Agent 重启后同步重载原生连接。业务 OAuth 密钥及产品 Bearer 仍只在 Agent 宿主。
OpenCode 以 `params._meta["ai.opencode/sessionID"]` 传入会话关联值;它不是认证凭据。服务先验证内部密钥及本机请求,再解析宿主会话与真实父子关系,拒绝浏览器请求、个人项目查询、学习复盘和越权子代理。子代理仅能读取绑定任务的 25 项项目查询。旧查询插件和 `/internal/tools/project-data`、`/internal/operational-task/data-query` 等查询桥已移除;产品 MCP 未启用时项目查询不可用,不回退。
原生聚焦验证:`bun run tests/integration/query-mcp-smoke.ts`;使用隔离目录、样例查询服务和停止推理的 hook,验证原生 `websearch` 调用选定的测试搜索服务,不调用模型或外部搜索。