- 闭环修复:审批 task_id 显式化(N2)、通知回退路径门闸 finally 释放(N1)、 personalization 原子写(S10,公共 atomic_write_json) - server.tasks 拆解:__init__ 轻量化、Blueprint/路由移至 blueprint.py/web.py, app_legacy 装配点惰性挂载;models.py 循环依赖死导入移除 - 执行链 Web 解耦:emit_event/run_background 安全包装(socketio 未绑定静默), 清除 4 处裸 socketio.emit(含 task_stopped 先写事件流再推送的顺序修复) - RuntimeService 补建对话下沉:chat 任务无 conversation_id 时服务层装配, 客户端无需复制 Web 两步流程 - 事件协议:get_task_events 返回 meta.window_start 缺口检测水位 - 验收:test_runtime_standalone_lifecycle 子进程隔离(config import 固化问题), 独立启动 + 协议全链路 + 审批语义全绿;全量 74 测试失败恰为 4 项存量
47 KiB
Gateway 既有研究文档汇总(research_summary)
生成日期:2026-09-07 任务性质:全程只读,未修改任何项目文件(仅在交付目录生成本汇总)。 汇总者:子智能体 #6。 汇总原则:不掺入自身推测,所有结论注明来源文件;文档间有冲突时并列呈现并注明。
来源文件清单(实际读取的文件)
| 优先级 | 任务指定 | 实际路径(已确认) | 状态 |
|---|---|---|---|
| 1 | cache_research/gateway/gateway_work_plan.md |
cache_research/gateway/gateway_work_plan.md(2026-09-07 修订版) |
✅ 已读 |
| 2 | cache_research/gateway/phase12_implementation_plan.md |
cache_research/gateway/phase12_implementation_plan.md |
✅ 已读 |
| 3 | cache_research/gateway/eval_summary.md |
cache_research/gateway/eval_summary.md(含 R1-R6 审阅注释) |
✅ 已读 |
| 4 | 四份评估子报告 | cache_research/gateway/eval_task_entry_points/task_entry_points.md |
✅ 已读 |
cache_research/gateway/eval_flask_context_deps/flask_context_deps.md |
✅ 已读 | ||
cache_research/gateway/eval_endpoint_classification/endpoint_classification.md |
✅ 已读 | ||
cache_research/gateway/eval_event_approval_coupling/support_chains_coupling.md |
✅ 已读 | ||
| 5 | astrion_audit/astrion_gateway_gap.md |
cache_research/gateway/astrion_audit/astrion_gateway_gap.md(实际位于此路径,工作区根无 astrion_audit/ 目录,见下方说明) |
✅ 已读 |
| 6 | 外部参考 | cache_research/gateway/opencode_study/opencode_architecture.md |
✅ 已读 |
cache_research/gateway/openclaw_study/openclaw_gateway.md |
✅ 已读 |
路径说明:任务指定 #5 为 astrion_audit/astrion_gateway_gap.md,但工作区根目录不存在 astrion_audit/;实测唯一一份 astrion_gateway_gap.md 位于 cache_research/gateway/astrion_audit/astrion_gateway_gap.md(已被 gateway 相关文档同置于 cache_research/gateway/ 下),本汇总即基于该文件。已按要求未读取 audit_entry_points_v2/ 等其他目录(属他人产出)。外部参考只需提炼被本项目借鉴的设计点,未做全文复述。
1. 三阶段路线全文要点(来源:gateway_work_plan.md,2026-09-07 修订版)
总体目标:让 Astrion 的内部职责、状态修改规则和任务入口更清晰,使后续功能沿稳定边界扩展。近期明确新功能场景 = 定时任务。长期方向是 Gateway(Runtime/Gateway),近期交付是在现有进程内整理运行时服务边界,让 Web、CLI 和定时触发器复用同一套任务受理/执行/控制逻辑。验收依据 = 新增入口所需理解和修改的范围 + 现有行为是否保留。
近期主线(三件事):
- 固定已有状态边界和正确性保障。
- 抽出接收显式运行上下文的公共任务入口。
- 通过定时任务验证该入口,并补齐调度所需的持久记录和生命周期。
非本次前置条件:事件持久化、传输替换、公开 SDK、设备配对和远程执行,分别评估。
参考资料边界(work plan 原文):
- 原始愿景
.astrion/user_upload/Astrion_Architecture_Product_Roadmap_Review_1.md(重点 §10–14、§35、§37–38)。 - 原始盘点
astrion_audit/astrion_gateway_gap.md的"无唯一 owner""补丁不是投影""恢复机制脆弱"等结论须结合新核查理解,不能直接当作未修复故障。 - 外部研究只参考职责分离/契约/幂等/恢复设计,不复制其部署/存储/认证方案。
现状关键结论(work plan §1,静态代码分析)
- 已有保障:
- 对话级资源隔离:
server/context.py:58按 username/workspace/conversation 生成终端 key。 - 主任务门闸:
server/chat_flow.py:139获取、:258finally 释放,实现于server/main_task_gate.py。 - 对话保存保护:
utils/conversation_manager/crud_mixin.py:335按 message_id 合并防缩减;:194I/O 锁;index_mixin.py:104原子替换文件。 - 客户端恢复:
static/src/stores/task.ts:156偏移轮询;probe.ts:73对账恢复;lifecycle.ts:137按 task_id/idx 去重。 - 审批单次决定:
modules/tool_approval_manager.py:65锁内裁决 pending,已决定时返回现状。
- 对话级资源隔离:
- 耦合:
- 执行依赖 Web 环境:
server/tasks/models.py:785后台任务用 test_request_context + 回填 session 获取资源执行。 - 写入口分散:任务 manager、terminal、conversation manager 各有职责和直接调用方。
- 过程记录为内存态:
models.py:75有界事件 deque、:108清理旧任务、:762分配任务级 idx;审批 manager 为进程内实例。
- 执行依赖 Web 环境:
- 优化候选:
server/context.py:75用户级广播(当前也是投影方案)。 - 新能力(定时任务):本轮搜索只发现任务清理、idle reaper 等维护定时器,未发现用户 Schedule 实体与到期派发链路 → 需单独设计调度状态与记录,复用现有任务执行。
- 两个有界核验项(勿将静态推断升级为故障):
models.py:157"检查运行中任务→创建记录"与最终执行门闸分属两处 → 核验并发是否产生重复任务记录。- 原报告提到
server/chat/terminal.py::issue_socket_token发放竞争,本轮未复现;若仍存在作为独立小缺陷处理。
阶段一:固定契约与已有不变量(目标/范围/验收)
目标:明确状态属于谁、新入口应该调用哪里。 范围清单:
- 产出
docs/runtime_contract.md(先描述内部服务契约;暂不强制公开网络协议/握手/实体全面改名)。 - 建立状态责任表:每项列明权威来源、允许的修改方、持久化入口、缓存刷新、并发裁决、失效条件。内存/文件/客户端缓存可共存,权威关系必须明确。
- 对齐概念:Session=现有 conversation,Run=一轮主任务,Schedule=计划,Occurrence=某次到期触发,Event=变化通知。子 agent、后台命令与主任务的关系单独说明,不把现有 task 全部等同主 Run。
- 审批与用户提问保留不同语义;可共享 ID/关联/等待/回答基础设施,提问答案不能被当作工具执行授权。
- 明确公共入口所需 principal、workspace、conversation、模型/运行配置与事件输出接口;默认值由明确解析步骤产生。
- 建立调用方迁移表:Web/CLI 对应 API、Workflow 激活、通知派发、定时触发;每项标注上下文来源、门闸获取/释放、取消传播。
- 围绕修改范围保留/补充回归用例:同对话并发、不同对话隔离、保存不丢消息、取消、审批重复回答、偏移恢复、过期响应过滤。 完成标准:目标入口的状态修改路径和执行裁决可定位;已有保障成为明确约束,未验证风险有独立记录。
阶段二:抽出显式上下文与公共任务入口(目标/范围/验收)
目标:无浏览器也能通过受控入口启动、观察和停止一轮任务。 架构示意:
Web / CLI 适配层 定时触发器 Workflow / 通知派发
\ | /
公共任务受理与控制入口
|
现有执行门闸与 Agent 执行
|
现有保存、审批、事件和取消链路
范围清单:
- 以
TaskManager.create_chat_task及执行链路为基础抽出服务接口(RuntimeService为名称候选;文件位置按真实职责确定,避免把所有 manager/状态塞入新类)。 - 服务接口至少覆盖提交、查询、取消;审批回答复用现有 manager 通过明确关联接入;会话创建按需复用,第一条验证链路可用已有会话。
- HTTP 参数解析、Cookie/CSRF/Bearer 验证放适配层,向服务层传可信 principal 和经校验资源范围;不能接受客户端或 Schedule payload 自报 role 作为授权依据。
- 消除目标执行链路对隐式 Flask session 的读取;迁移期可保留兼容适配层但要列出剩余依赖;只把 test_request_context 包进新方法不算完成解耦。
- 复用门闸/取消/审批/保存规则;分别定义任务受理去重与实际执行互斥,防重复启动或门闸泄漏。
- Web 任务入口先接入服务,再逐条迁移 Workflow/通知等调用方;每次明确哪些旧写路径已封闭。不能把"门面转发成功"写成"全部状态唯一 Owner 已完成"。
- 内部错误采用稳定状态/错误码;HTTP 状态码由适配层映射;事件先适配现有 idx/offset 和 sender,不同时重写客户端。 完成标准:不创建浏览器会话、不伪造 HTTP 请求,测试能通过显式身份和资源上下文启动一次受控任务、读取结果并取消;同对话重入仍受门闸保护;现有 Web/CLI 行为兼容。执行可用可控模型/工具替身验证,无需真实外部副作用。
阶段三:定时任务纵向落地(目标/详细设计要求——本节最重要)
目标:时钟成为公共任务入口的另一个调用方。本节为待实施设计(不代表当前已有调度器),下列保守默认作为讨论起点,UI 与默认行为在实现前确认。
4.1 计划与触发记录
- Schedule 至少保存:ID、所属 principal、目标 workspace、会话策略、提示词/任务配置、时间规则、时区、启用状态、配置版本。明确夏令时重复/不存在时刻的处理。
- 支持创建、暂停、恢复、删除计划。建议暂停/删除只影响未来触发,已受理的 Run 另行取消;最终行为须明确。
- 明确使用已有会话还是每次新建会话;首版可只实现一种,但须说明上下文累积、目标删除和工作区失效时的行为。
- 模型配置确定"创建时固定"还是"触发时解析";权限在触发时按当前有效授权重新校验,不保存可绕过权限变更的长期授权快照。
- 每个 Occurrence 有稳定触发标识(例如 schedule_id + 计划时间点);保存所用配置版本、受理状态、run_id 和终态。计划编辑后的未来触发身份规则须明确。
4.2 重复、重叠与停机
- 为 Occurrence 登记和 Run 受理定义持久幂等规则:同一触发重试返回同一受理结果,相同键不同参数拒绝;记录保留期覆盖允许的重试窗口。
- 明确"登记后未启动""已启动但关联未写完"等崩溃窗口。登记/受理应原子提交或具备可验证的恢复对账;不能只用内存 TTL 承诺跨重启不重复执行。
- 选定单个活动调度器的启动与所有权规则,防重载器或多个服务进程重复派发;不要求因此引入分布式基础设施。
- 同会话已有任务或上一轮仍运行时,定义 skip/queue/parallel 策略。建议首版跳过并记录原因,沿用会话门闸,不默默增加无限队列。
- 定义服务关闭期间错过触发的策略。建议首版记录错过并等待下个未来时点,不集中补发;服务需运行才会触发,本阶段不包含 OS 唤醒/开机自启。
- 重启后恢复 Schedule 和触发记录;上一进程未确认完成的 Run 标为中断或结果待核验,不显示仍在正常运行,不自动重做可能已产生副作用的动作。
- 触发去重仅保证任务受理规则,不承诺任意外部工具副作用 exactly-once。无法确认的执行结果进入显式待核验状态。
4.3 审批、存储与验收
- 无人在线时仍保持原有权限限制:遇到审批/提问按明确超时等待,超时结束或中断本轮并记录原因,不自动扩大权限;等待中的任务也遵守重叠策略。
- 审批关联当前 Run/会话,复用现有 pending/answer 链路。重启后旧请求不能被当作仍有执行现场的有效审批;持久审批记录与继续执行分别设计。
- 为 Schedule/Occurrence/必要的运行摘要选持久化方案,先列出原子更新、唯一性、查询和恢复要求,再决定文件或 SQLite。数据走运行态路径解析,不写源码树;不强制迁移全部对话历史。
- 沿用当前任务事件读取;补充触发记录查询,使旧内存任务被清理或重启后仍能解释触发结果。日志记录 schedule_id/occurrence_id/run_id,复用既有设施。 完成标准:可控时钟与执行替身验证一次触发、重复触发、任务重叠、计划暂停/恢复、目标失效、无人审批超时、停机错过触发及重启对账;真实入口验证不依赖浏览器。明确只恢复计划与记录,不承诺从任意执行位置续跑。
后续独立决策(有明确需求再启动)
| 决策 | 启动条件 | 决策前必须补充内容 |
|---|---|---|
| SSE / WS / 保持轮询 | 延迟/连接数/带宽不满足,或新增双向交互 | worker 模型、代理缓冲、重连、慢消费者;传输更换≠状态正确性提升 |
| 对话级订阅 | 需减少无关广播、精确受众 | 订阅与资源授权分别校验,区分任务/会话事件与用户级通知 |
| durable 事件与快照恢复 | 需超出内存窗口的过程追溯/跨重启生命周期查询 | 提交时机、序号作用域、快照边界、日志裁剪、缺口处理、schema 版本 |
| 审批持久化与可恢复执行 | 需重启后继续等待并执行原动作 | 重建上下文与待执行动作、权限重校验、过期请求处理、结果不确定性;恢复 pending 记录不能实现续跑 |
| TS 类型 / SDK 生成 | 对外契约或多客户端类型维护成实际成本 | 单一 schema 权威源、兼容策略、生成检查;不强制新网络握手 |
| 身份体系整理 | 跨凭证访问同一资源或统一授权成需求 | 统一 principal/resource/authorization;各适配层可保留不同凭证,不能直接合并 Web/API 用户数据空间 |
| 设备配对 / Remote Worker | 明确需多设备接入或远程执行 | 执行契约、连接身份、所有权转移、故障语义,独立设计验收 |
事件恢复改造五条硬边界(若启动):
- 快照注明覆盖的事件水位 N 并与同一状态版本一致;续传从 N 之后开始;快照生成/订阅/历史读取之间不得漏事件窗口;重复投递仍需幂等应用。
- durable 事件只承诺已提交记录可恢复;live delta 是否恢复单独定义;运行中未完成 Item 需当前内容快照/覆盖式更新/不完整标记,不能只依赖最终 completed 事件。
- 状态与事件写入需一致提交/恢复规则;对话 JSON、事件 JSONL、审批 JSONL 不天然构成一致快照与日志。
- 连接序号、任务偏移、持久会话序号不能混用;连接重建/历史裁剪/会话重置时给出明确重新同步规则。
- JSONL 与 SQLite 都需定义恢复、保留、迁移方案;选择依据是事务与查询边界,不承诺以后可低成本平移。
改造完成判断(work plan §6)
- 新增任务来源只需构造显式上下文、校验目标并调用公共入口,无需复制 Web 聊天启动流程。
- 已迁移路径有明确状态权威与写入规则,现有门闸、保存、客户端恢复保护保留。
- 定时任务有可查询的计划、触发记录和终态;重复、重叠、权限变化与停机行为可解释。
- 各阶段可独立验证;只为当前边界迁移做必要接口调整,不同时重写 Agent loop、前端状态管理或部署拓扑。
- 每阶段说明实际迁移入口、剩余兼容依赖、验证结果。完成门面、生成架构图或更换传输本身不算完成改造。
2. 范围评估核心结论(来源:eval_summary.md + 四份子报告,含 R1-R6 审阅修订)
2.1 196 个端点分类结果(来源:eval_endpoint_classification/endpoint_classification.md;经 R1/R2 修订)
统计口径:以 Flask 路由注册(Methods 合并计数)为准,共 196 个,分 26 个文件,与给定分布一致。
| 类别 | 数量 | 占比 | 是否迁移 |
|---|---|---|---|
| T1 任务受理 | 3 | 1.5% | ✅ 必须(POST /api/tasks、/api/v1/.../messages、/api/workflow/activate) |
| T2 任务控制 | 13 | 6.6% | ✅ 必须(cancel×2、runtime_guidance/queue×4、workflow deactivate、sub_agents/background 停止×3、审批回答×3) |
| T3 任务观察 | 14 | 7.1% | ⚠️ 可不动(只读 REST,轮询协议天然可复用) |
| C CRUD | 131 | 66.8% | ❌ 不动 |
| S 状态查询 | 20 | 10.2% | ❌ 不动 |
| A 认证管理 | 15 | 7.7% | ❌ 不动 |
| 合计 | 196 | 100% | — |
核心答案:必须迁移的最低集合 = T1 + T2 = 16 个端点(8.2%)。16 个端点全部收敛到同一批 manager 单例方法(task_manager / sub_agent_manager / background_command_manager / 三个 approval manager)。Gateway 化实质 = 把这几个 manager 方法提升为 RuntimeService 公共入口,HTTP 端点从"直接调 manager"改为"调 RuntimeService",而非改写端点本身。
迁移复杂度分布(16 个):12 小 / 3 中 / 1 大(唯一大项 = workflow activate,因门闸 token 移交 + 状态机编排)。
审阅修订要点:
- R1(范围):16 个是当前分类下的受理/控制端点集合,不是架构改造完成的充分条件。
server/chat/permission.py的权限/执行环境/网络权限变更在任务运行期会排队生效,必须进入阶段一状态责任表;其余端点应描述为"多数可保持 HTTP 兼容",不能概括为"与运行时零耦合"。查询端点可保留原 URL,但后台调用方(CLI/定时任务)应能通过内部接口(如get_task_events)查询,不必为观察任务发 HTTP。新增门面不等于全部写入口已收敛。对应子报告 §4.4 明确:chat/permission.py:166-185 / :265-284 / :352-368分别排队修改运行中的权限/执行环境/网络权限,work-mode在:420-445拒绝运行中切换。 - R2(计数与编排):11+3+1=15 应为 12 小 / 3 中 / 1 大 = 16,仍为定性估算。Workflow 编排(会话补建、激活、门闸移交、失败回滚)留在 HTTP 层只适合作为过渡,长期应按复用需求下沉到工作流服务供非 HTTP 入口调用;无需全部塞入 RuntimeService。
- R3(依赖与异常路径):core/modules/utils 无直接 Flask 导入已确认,但不能外推为所有间接调用都不依赖请求上下文,"无旁路"须限定为正常受理路径:
chat_flow_task_main.py:645-675在任务创建失败后直接执行handle_task_with_sender,绕过 TaskRecord 登记(无事件 deque、不可按 task_id 轮询/取消);外围轮询器有预占门闸,不能仅凭回退认定并发写入 bug,但迁移验收必须覆盖回退路径的记录、事件、取消、门闸生命周期。socketsend_message在socket_handlers.py:259已短路返回 DEPRECATED,属死代码,应与活跃回退分开。"5 个调用点"实为 5 类来源、6 处调用位置。 - R4(上下文设计与工作量):9 字段是旧 session 依赖的搬迁清单,不是最终领域模型;应区分"可信身份与资源范围""本次任务参数""内部执行信息"(门闸 token/通知回滚不得成为普通客户端可提交字段);明确默认值/对话配置/本次覆盖的解析优先级。6-8 文件属估算;
get_user_resources有约 170+ 调用处(含 83 处 @with_terminal 装饰器路径),影响面需 host/Web/API 身份 × 会话 × 默认值来源回归覆盖,不宜承诺每处都小。 - R5(审批产品边界):拒绝当前工具后继续运行不自动意味着不安全(后续动作仍受权限约束)。等待人工/到期终止/拒绝当前动作后继续是不同产品策略,不能把"定时任务禁止人工审批"当作必选技术条件。工具审批、计划审批、用户提问应分别定义超时含义。超时策略可在阶段三明确,不阻塞保持原有语义的阶段二上下文重构。
- R6(取消与生命周期):标准停止按钮走 REST 硬取消,可打断审批等待;缺口应限定为仅设置软停止标志的路径(socket stop_task 软 stop),不能描述成所有停止按钮失效。需跟进超时/取消后 pending 终态更新、审批与 Run 关联;条清理不能只按 TTL 删仍有合法等待者的请求。task_id 恒 None 目前仍是静态疑点,须验证运行时载荷后定性。
2.2 任务链路 Flask 依赖性质(来源:eval_flask_context_deps/flask_context_deps.md;经 R3 修订)
总体判定:依赖属于「浅层入口型」,集中两个枢纽函数,执行体干净。
- 出口即枢纽:
- 入口
server/tasks/models.py::create_chat_task(直读 session 快照)+_run_chat_task(test_request_context 包裹后台线程再调 get_user_resources)。 - 资源获取
server/context.py::get_user_resources(及内部_apply_workspace_personalization_preferences、admin policy)。
- 入口
- 执行体干净:
core/、modules/、utils/零 Flask 隐式上下文(session同名变量全是终端会话/容器句柄或 dict 参数,非 Flask);server/chat_flow*.py9 个文件函数体内零 session/request/has_request_context 使用(只有 import 行)。 - 命中统计:测试 request_context 全仓仅 1 处(models.py:794);has_request_context ~17 处全在 context.py;B 类(必须消除)集中在 2 文件:tasks/models.py(session 15 行)+ context.py(session 31 行)。
- B 类依赖清单(必须消除/显式化):
- B-1
models.py:785-812_run_chat_task的 test_request_context 包装(核心);难点不在删 wrapper 而在 get_user_resources 参数化。 - B-2
models.py:180-203create_chat_task直接读 Flask session(含 else 分支全量直读 http 上下文、setdefault 兜底,见下方"冲突/补充")。 - B-3
context.py::get_user_resources(L221-535,核心)内部 8-10 处 has_request_context 守卫读写 host_mode/workspace_id/host_workspace_id/run_mode/thinking_mode/model_key/is_api_user + 无守卫 record/role 读。 - B-4
context.py::_apply_workspace_personalization_preferences(L116-178,读 model_key/回写)。 - B-5
context.py::ensure_conversation_loaded(L750-810,写 session 回写,任务线程内已天然跳过但属隐式耦合)。 - B-6
server/auth_helpers.py:35-49认证辅助被任务链路经 get_user_resources 无守卫调用。
- B-1
- C 类均已核查归 A(可保留):
server/conversation.py的 host_mode/input_draft/terminal 相关函数、with_terminal装饰器(83 处全部路由)、socket_handlers(flask-socketio 适配层)。 - 依赖深度结论:浅层。B 类无条件耦合仅 2 处(创建入口 + 线程包装),守卫型耦合集中在 context.py 一个文件。
- R3 修订:直接搜索不能证明所有间接调用都无上下文依赖;"浅层"描述依赖位置,不等于改动风险低;get_user_resources 的身份/资源分支需行为验证,不能以删除 import 或零关键词命中代替验收。
2.3 任务入口五点评估(来源:eval_task_entry_points/task_entry_points.md;经 R3 修订)
- 执行链(正常受理路径)已单一收敛:5 类来源、6 处调用位置共用
create_chat_task → 线程 → _run_chat_task → run_chat_task_sync → process_message_task → asyncio loop.create_task(handle_task_with_sender)。 - 六处调用点概览:
| # | 文件:行 | 入口 | 触发来源 | 显式化程度 | 迁移难度 |
|---|---|---|---|---|---|
| ① | tasks/api.py:200 | create_task_api | Web HTTP POST | 低(无 session_data) | 小~中 |
| ② | api_v1.py:320 | send_message_api | Web HTTP POST(Bearer) | 低(无 session_data) | 中(is_api_user/role 必须显式) |
| ③a | workflow_runtime_api.py:184 | api_activate_workflow | Web HTTP POST | 高(session_data 显式) | 小~中 |
| ③b | workflow_runtime_api.py:264 | api_deactivate_workflow | Web HTTP POST | 高(同上,+通知池/门闸回滚) | 小~中 |
| ④ | chat_flow_task_main.py:613 | _dispatch_completion_user_notice | 内部后台轮询线程 | 极高(全显式) | 小 |
| ⑤ | chat_flow_task_main.py:1416 | _dispatch_multi_agent_idle_messages | 内部后台轮询线程 | 极高(全显式 + task_type=notice) | 小 |
- 迁移真实难点(从大到小):
get_user_resources的 session 隐式读取参数化(host_mode/is_api_user 分支选错即静默串工作区,最高风险)。- API 身份字段(is_api_user/role)正确传递(调用点②,唯一无 session_data 的 API 来源)。
- main_task_gate 门闸移交语义(预占→token 随 session_data 移交→线程认领→finally 释放/失败回滚)。
task_type="notice"互斥豁免与单对话互斥语义保持(调用点⑤)。- 三套身份取数来源统一(web session / token session / web_terminal 属性)。
- 另项(不在本 5 点但相关):
socket_handlers.py:345 → start_chat_task(socket 直连执行链,不可达死代码);chat_flow_task_main.py:664通知回退直接执行(活跃,见 R3)。
2.4 四支撑链路评估(来源:eval_event_approval_coupling/support_chains_coupling.md;经 R5/R6 修订)
总体判定:事件/取消/保存三条可直接复用(需适配层);审批链路需改造。
- 事件链路:直接复用。载体 TaskRecord(deque maxlen=20000 + 每任务 idx),调用方只需 create_chat_task 拿 task_id 即可轮询/取消;socket 推送按
user_{username}房间(离线无影响,但用户在线会收到定时任务事件 → 前台干扰,需加 source 字段或确认产品预期)。适配点:消除 test_request_context、定义定时事件来源标识。 - 取消链路:直接复用。寻址=username+task_id(stop_flags 任务级 key=task_id=client_sid),不依赖 terminal(conversation 仅副作用);保留"独立事件循环 + entry 持 loop/task"模式以支持硬取消;公共入口须把 task_id 持久化到 Occurrence。
- 保存链路:直接复用,前提明确。写入口收敛于
context_manager.add_conversation(每消息 auto_save);merge-on-save +_io_lock(manager 实例级,跨实例不互斥)+ 主任务门闸三道防线。前提:新调用方必须(1)走对话级 terminal、(2)遵守主任务门闸、(3)会话策略决定 conversation_id。存在执行链之外第二批写者(设置/压缩/CLI),共用同一保护。 - 审批链路:需改造。核心问题(§2.4):
- 等待循环默认超时 3600s 且调用点(:435/:574/:883/:1153)未传 timeout 参数(均有参数但调用点没传)。
- 超时语义现状=拒绝该工具、任务继续(非结束任务)——产品决策项,两种候选:(a)拒绝该工具继续(现状,风险无监督继续);(b)结束整个任务。
- 等待期间软停止无效(下一工具调用行首 :600 才生效)。
- manager 条目无 TTL/清理。
- 存储 manager 可复用(与 Web 前端共用 pending/answer 数据);
auto_approval分支(ApprovalAgent)是唯一现成无人工决策路径,但仅覆盖 tool 审批。
- 候选改造(最小改动排序):注入可配置超时(session_data 透传)→ 定义超时语义 → 等待循环加 stop 检查 → manager 条目 TTL/清理 → 会话策略决定审批是否可达。
- R5 修订:§2.4 的"禁止人工审批 / 60-300s 超时"均为待讨论选项,不是定时任务的技术前提;拒绝当前工具后继续仍受权限限制,不自动等于不安全;可等用户上线回答。工具审批、计划审批、用户提问应分别确定超时处理。客户端离线但服务运行时可保留现有 pending;跨服务重启保留记录与恢复等待执行是另外两层需求。
- R6 修订:停止按钮无效应限定为只置软停止标志的路径(标准按钮走 REST 硬取消可打断);条目生命周期应先定义超时/取消终态与迟到回答处理,再设保留期清理,不能只按 TTL 删仍被等待的 pending;task_id 恒 None 保持静态疑点,待运行时关联验证。
2.5 审阅修订要点汇总(R1-R6 已嵌入各节)
见上述各节 R1/R2/R3/R4/R5/R6 处。
3. 阶段一/二实施与验收记录(来源:phase12_implementation_plan.md;另见项目记忆 gateway_runtime_work_plan)
状态:阶段一、阶段二代码工作全部完成;改造相关测试 24/24 全绿;工作区改动尚未 commit;阶段三(定时任务)未做(功能未讨论)。
3.1 设计决策(按 R4 审阅意见):RuntimeContext 三层分离
TrustedPrincipal 可信身份与资源范围:username / workspace_id / host_mode /
host_workspace_id / is_api_user / role
——只能由适配层认证后构造,客户端不可自报
TaskParams 本次任务参数:message / images / videos / files / model_key /
run_mode / thinking_mode / max_iterations / conversation_id /
goal_mode / skill_context_messages / message_source
InternalDirectives 内部执行信息:main_task_gate_token / auto_user_message_event /
auto_user_message_payload / preceding_user_notices /
approval_timeout_seconds(仅透传机制,默认语义不变)
——普通客户端不可提交,仅内部调用方(通知链/工作流/派发器)使用
默认值解析优先级:本次显式传参 > 对话元数据绑定 > 会话/用户偏好快照 > 系统默认。会话配置恢复仍在 get_user_resources + 会话加载链路内完成。
RuntimeService 最小接口集:create_task(principal, params, directives) -> task_id;cancel_task(username, task_id);enqueue_runtime_guidance / enqueue_runtime_pending_message / remove_runtime_pending_message / promote_runtime_pending_to_guidance;get_task / get_task_events(username, task_id, offset)(内部查询接口,CLI/定时任务不必走 HTTP)。审批回答复用现有三个 manager,通过明确关联接入,不进 RuntimeService 首版。
两步走:步骤①(兼容期)RuntimeContext + RuntimeService 骨架落地,create_chat_task 接受显式上下文,6 处调用点迁移,test_request_context 桥保留作兜底;步骤②(拆桥)get_user_resources 增加显式参数变体,_run_chat_task 改走显式路径后删 test_request_context 桥,ensure_conversation_loaded 的 session 回写移出任务路径。
3.2 关键风险与对策
get_user_resources参数化(最高风险):host_mode/is_api_user 分支选错 → 静默串工作区。对策:显式变体与 web 路径并存,先任务线程单点切换,回归验证后再推广。- 门闸 token 移交语义:InternalDirectives 原样承载;通知链"预占→移交→认领→失败回滚"不变。
task_type="notice"互斥豁免保留。- 异常回退路径(chat_flow_task_main.py:645-675 直接执行 handle_task_with_sender)保留语义,验收覆盖其门闸/事件/取消生命周期。
- 事件前台干扰:阶段二不加 source 字段(阶段三产品决策),现有行为不变。
3.3 实施内容清单(阶段一/二)
阶段一交付:
docs/runtime_contract.md:概念对齐(Session/Run/Schedule/Occurrence/Event)、10 项状态责任表、已有保障约束清单、RuntimeService 契约(含 RuntimeContext 三层模型定义)、调用方迁移表、12 项回归用例 T01-T12。
阶段二交付:
- 新建
server/runtime/包:context.py(RuntimeContext 三层模型,from_terminal()、principal_from_session_snapshot()、to_session_data())+service.py(RuntimeService + 进程级单例runtime_service)。 TaskManager.create_chat_task强制显式 session_data(缺失即 ValueError,i18n keytasks.missing_session_data)。- 6 处调用点全部迁移到
runtime_service.create_task():server/tasks/api.py、server/api_v1.py、server/workflow_runtime_api.py×2、server/chat_flow_task_main.py×2(完成通知派发 :613、多智能体 idle 派发 :1416)。门闸 token 移交、task_type="notice"互斥豁免语义原样保留。 server/context.py(989 行)拆分为server/context/子包:identity / broadcast / personalization / usage / upload / conversation / resources / decorators / reaper 共 9 模块 +__init__.py兼容 re-export(外部 import 路径不变)。get_user_resources参数化:新增RuntimeIdentity显式身份快照(定义于server/context/identity.py,runtime 包引用,依赖方向自下而上无循环)。- test_request_context 桥已拆除:
server/tasks/models.py::_run_chat_task不再建立 Flask 请求上下文,任务线程全程 RuntimeIdentity 驱动。 - 审批超时透传管道:terminal 属性
_approval_timeout_seconds(默认语义 3600s 不变),_run_chat_tasksetattr →server/chat_flow_tool_loop.py::_approval_timeout_for()helper → 4 个_wait_*调用点。 - 附带修复:
config/_load_dotenv对禁读.env的沙箱环境加 try/except(不再 PermissionError 崩溃)。
3.4 测试构成(24/24 绿的明细)
- 改造相关 24/24 全绿:
test_server_refactor_smoke(6)test_runtime_service(10,新增)test_conversation_model_persistence(4,patch 目标随迁)test_runtime_identity_resources(4,新增,覆盖 get_user_resources 的 web/host/api 身份路由)
- 验收还含:
python3 -m py_compile触及文件全过;测试显式上下文(无 HTTP 请求)受理任务 → 拒绝同对话并发 chat 任务 → 取消;不触达真实模型调用。 - 存量失败 4 项(conversation_workspace_storage / host_workspace_manager / skills_manager / token_usage_extractor)经甄别与本次改动零相关,未修。
3.5 遗留待办 / 未验证项
- 真实运行环境验证(Web 聊天/停止/审批/workflow 激活/多智能体派发)需用户重启服务后人工完成——get_user_resources 分支选错会静默串工作区,是最高风险点。
- 审批条目 task_id 恒 None(静态疑点,待运行时验证)。
- socket 软 stop 不打断审批等待(REST 硬取消可以),留待阶段三或独立决策。
- 工作区改动未 commit。
4. astrion_gateway_gap.md 的早期结论及其被新核查修正情况
来源:
cache_research/gateway/astrion_audit/astrion_gateway_gap.md(2026-09-07 早期盘点,静态代码分析,未做运行复现)。其结论须与 2026-09-07 的 gateway_work_plan / eval_summary 等新核查对照。
4.1 早期主要论断(原文)
- API 面:12 个蓝图 REST(tasks_bp 轮询主线)+ 1 个 Socket.IO 辅助通道;消息发送与进度事件已收敛到 POST/GET /api/tasks 轮询模型;socket
send_message已废弃。 - 事件通道:单一 sender 抽象(写入事件流 + socket 推送合并);per-task idx、无全局序号;事件流是任务作用域 + 内存 + 1h TTL(deque 20000 上限,cleanup 1h)。
- 断线追数据三层:事件流偏移继续 → 对账接口 resumeTask(resetOffset) 全量重放 → 前端 task_id:idx 去重;socket 重连取一次性 token。
- 状态归属四层:①进程内存对象(terminal/task_manager/三个 approval manager/GLOBAL_MULTI_AGENT_STATES/usage/socket-token/stop flags)②运行态文件(conversation/*.json、sub_agents.json、personalization.json、settings.json)③Flask session ④前端本地。无系统级唯一 owner。
- 无统一事件序号/事件总线(idx 是 per-task,socket 事件不带 idx,通道间无法全局对齐)。
- 审批/提问/计划 = 进程内存 + 绑定 username+conversation_id(无端认领),重启即失;无"哪个端在展示/谁决定"记录。
- 终端缓存 key 已按对话隔离(好),但实例是共享可变对象;每请求 attach_user_broadcast 重绑回调。
- 广播粒度是 user 房间,无 conversation 级订阅/投影;"补丁不是投影"。
- socket-token 发放互踩(issue_socket_token 清空同用户旧 token)。
- "停止/活动"仍假设任务绑定连接(handle_disconnect 的 has_other_connection / REST running 判断)。
- REST-only 客户端存在事件盲区(标题更新无 running task 只走 socket;token_update/todo_updated/edited_files_updated 全局广播不一定进任务事件流)。
- 认证两套并存:session+CSRF(Web/CLI/host-login)与 Bearer token(/api/v1)不互通。
- socket-token 发放互踩、断线即停任务假设等多客户端缺陷尤甚。
4.2 哪些已被 2026-09-07 新核查推翻或修正(对照 work plan / eval_summary / 子报告)
新核查在 gateway_work_plan.md §0 参考资料的使用边界 明确要求:早期盘点的"无唯一 owner / 补丁不是投影 / 恢复机制脆弱"等结论不能直接当作未修复故障,须结合新核查理解。逐条对照:
| 早期论断 | 新核查状态 |
|---|---|
| "无统一事件序号 / 无事件总线"作为差距 | ✅ 部分成立:新核查确认事件 idx 是 per-task(models.py:762-782)、无全局序号;但 work plan 已把它列入后续独立决策(durable 事件),不构成阶段一/二/三的前置条件。子报告确认事件链路"可直接复用(需适配层)"。 |
| "状态真源分四层、无唯一 owner"、"补丁不是投影" | ⚠️ 需谨慎:work plan §0 明确不能直接当未修复故障;阶段一目标正是"固定状态责任表、让权威关系明确",阶段二哲理性反对"把门面转发当 owner 完成"。这是改造目标陈述而非已证故障。 |
| "恢复机制脆弱"(断线靠前端从 0 重放 + 去重) | ⚠️ 修正:work plan 现状确认客户端恢复保护(task.ts 偏移轮询 + probe.ts 对账 + lifecycle.ts 去重)是要保留的已有保障;事件链路 sub 报告判定可直接复用。脆弱性被纳入阶段三"持久化选型"与"只恢复计划/记录"的边界。 |
| "socket-token 发放互踩"(issue_socket_token 清空旧 token) | ⚠️ 未复现:work plan §1 保留为待核验项("本轮未重新复现;若仍存在作为独立小范围缺陷处理,不捆绑整个运行时改造")。早期报告自标"很大概率(依赖时序竞争)"。 |
| "审批状态进程内存、重启丢失、无端认领" | ⚠️ 部分成立并纳入阶段三:子报告确认三个 manager 纯内存、无 TTL/清理;work plan 阶段三列为"重启后旧请求不能被当作有效审批"、审批持久化列为后续独立决策。 |
| "停止假设任务绑定连接"(handle_disconnect 判断) | ✅ 方向正确:work plan 阶段三要求"任务生命周期独立于任何连接";但子报告(R6)补充标准停止按钮已是 REST 硬取消、可打断审批等待,软 stop 缺口才需处理。 |
| "REST-only 盲区 / 广播按 user 房间" | ⚠️ 部分成立:work plan 把对话级订阅列入后续独立决策;事件链路判定可直接复用但需处理"前台干扰"。 |
| "认证两套并存不通" | ✅ 成立但列为后续独立决策(work plan 身份体系整理,明确不能直接合并 Web/API 用户数据空间)。 |
结论说明:早期盘点提供素材与差距方向,但多数"差距"已被新核查重新定位为"后续独立决策"或"保留的已有保障";没有一条被新核查完全证实为当场需要修复的 bug,仅 socket-token 互踩与审批 task_id 恒 None 保留为待核验疑点。更详细的早期事实(蓝图清单、事件类型、状态归属表)见 astrion_gateway_gap.md 原文,此处不重复全文。
5. 外部参考借鉴点(来源:opencode_architecture.md / openclaw_gateway.md)
按 work plan / eval 定位:仅提炼被本项目认可的职责分离 / 契约 / 幂等 / 恢复设计,不复制其部署、存储或认证方案;外部实现未在本轮重新核验。
5.1 opencode 被本项目认可的设计原则(3-5 条)
- 事件 durable 化 + after=N 重放续传:SQLite 事件表(aggregate_id, seq, type, data)+
durable(after)先重放历史再续传实时;"live-only delta 不入库、ended 终值入库可重放"降本。→ 对应 work plan 后续"durable 事件与快照恢复"及五条硬边界。 - "单进程多 workspace 懒加载实例 + 请求头路由实例"(
x-opencode-directory)而非一项目一进程 → 对应 work plan "单活动调度器/单进程"思路。 - 协议包与实现分离 + handler 注入(protocol/server/core 分层)→ 对应 RuntimeService 职责单一、避免把 manager 塞进新类。
- OpenAPI/协议单一事实源 + 代码生成 SDK(文档与代码同源,避免过期)→ 对应后续"TS 类型/SDK 生成"决策(单一 schema 权威源)。
- 审批 = 异步请求对象 + 阻塞等待 + 事件广播 + REST 回复,多客户端共享同一请求 ID、先到先得(无需连接路由,只需请求对象全局唯一、回复幂等)→ 对应后续"审批持久化与可恢复执行"决策的幂等/认领思路。
5.2 openclaw 被本项目认可的设计原则(3-5 条)
- "Gateway 单所有者 + 客户端只读投影":真状态归 gateway 进程,前端只是 snapshot+增量事件镜像,写操作一律走 RPC 并做冲突检测 → 对应 work plan 状态责任表 / "不能把门面转发当 owner 完成"。
- 乐观锁(lifecycleRevision + expectedRevision,patch/send 带 expected_revision):轻量冲突检测 → 对应持久化"原子更新/唯一性"选型要求。
- 幂等键(idempotencyKey):协议 schema 层强制必填;"内存 Map + TTL + 容量上限 + inflight 共享 Promise + 同 key 同参数校验"以及消息类"幂等键进 transcript + 落盘扫描"持久化去重 → 直接对应阶段三「持久幂等」要求(同触发重试返回同受理结果,同键不同参数拒绝)。
- 事件不重放 + seq 间隙检测 + 断线重连全量 snapshot(客户端无状态投影,服务端不缓存事件/只做 per-connection seq 计数)→ 对比 Astrion 现有"从 0 重放 + 去重"前端机制,为将来 durable 事件/重连语义提供参考。
- 事件广播受众过滤(按 sessionKey + 订阅者集合,只发订阅了该 session 的连接)+ 方法/事件清单白名单下发(feature discovery) → 对应对话级订阅决策的"订阅与资源授权分别校验"与内部查询接口思路。
说明:上述均为"被本项目认可/借鉴"的提炼,不构成对 opencode/openclaw 自身的全面评价。
6. 遗留问题全集(「待办/待验证/已知缺口」去重汇总,每条注明来源)
按来源文档分组;同名跨文档者已去重合并,标注多来源。
来自阶段一/二实施记录与项目记忆(phase12 / gateway_runtime_work_plan)
- G1. 真实运行环境验证未完成(最高风险):Web 聊天/停止/审批/workflow 激活/多智能体派发需用户重启服务后人工完成;get_user_resources 的 host/Web/API 分支选错会静默串工作区。(phase12 §遗留待办 1)
- G2. 审批条目 task_id 恒 None(静态疑点,待运行时验证):
getattr(web_terminal,"task_id",None)全仓无赋值点;影响审批无法按 task_id 检索关联。(phase12 §遗留待办 2;eval_summary §4;support_chains §2.3 A3;R6 重申) - G3. 工作区改动未 commit。(phase12 状态;项目记忆)
- G4. 存量测试失败 4 项(conversation_workspace_storage / host_workspace_manager / skills_manager / token_usage_extractor),甄别与本次改动零相关、未修。(phase12 §测试验收)
- G5. socket 软 stop 不打断审批等待(REST 硬取消可以),留待阶段三或独立决策。(phase12 §遗留待办 3;support_chains §2.4.3;eval_summary §4)
- G6. 阶段二未加事件 source 字段(前台干扰为阶段三产品决策)。(phase12 §风险对策 5)
来自 work plan(gateway_work_plan / 项目记忆)
- G7. 两个待核验项:①
models.py:157"检查运行中→创建记录"与最终门闸分属两处,并发是否产生重复任务记录未验证;②server/chat/terminal.py::issue_socket_token发放竞争未复现,若存在作为独立小缺陷。(work plan §1) - G8. 阶段三整体未实施(Schedule/Occurrence 无代码;幂等/重叠/停机/审批超时/持久化选型全部待实现与讨论)。(work plan §4;项目记忆)
- G9. 后续独立决策均"有明确需求再启动":SSE/WS/轮询、对话级订阅、durable 事件与快照恢复、审批持久化、TS/SDK 生成、身份体系整理、设备配对/Remote Worker。(work plan §5)
来自范围评估与子报告(eval_summary + 四子报告)
- G10. 并发重复任务记录未验证(同 G7①;工作区/交互层静态推断,不能据此断言并发修改同一对话)。(eval_summary / task_entry_points R3)
- G11. 审批等待循环超时 3600s 且调用点未传参;超时语义(拒绝工具继续 vs 结束任务)为产品决策未定;工具/计划/提问三类超时含义分别定义。R5 明确它不是定时任务技术前提。(support_chains §2.4;eval_summary R5)
- G12. 审批 manager 条目无 TTL/清理,未决条目永久留在内存;需先定义超时/取消终态与迟到回答处理再设保留期。(support_chains §2.4.4;R6)
- G13. 事件前台干扰:定时任务事件会推给在线用户 socket 房间,需加 source 或确认产品预期。(support_chains §1.5;eval_summary §3.5)
- G14. 执行链异常回退路径(chat_flow_task_main.py:645-675)绕过 TaskRecord 登记,迁移验收必须覆盖其记录/事件/取消/门闸生命周期。(R3;task_entry_points)
- G15. task_id 必须回传并持久化给发起方(Occurrence 记录 run_id=task_id,取消才可寻址)。(support_chains §3.4)
- G16. 保存链路前提未全验证:新调用方须走对话级 terminal、遵守门闸、会话策略决定 conversation_id;跨实例锁不互斥风险依赖多道防线。(support_chains §4.4)
- G17.
get_user_resources约 170+ 调用处(含 83 处 @with_terminal)影响面需 host/Web/API × 会话 × 默认值来源回归验证,不宜承诺每处改动都小。(eval_summary R4;flask_context_deps B-3)
来自早期盘点(astrion_gateway_gap.md,多数已被新核查降级/转为决策,见 §4.2)
- G18. 无统一事件序号/事件总线、状态无唯一 owner、审批重启丢失、REST-only 盲区、广播按 user 房间、token 互踩、停止绑定连接、认证两套并存等早期差距——均已按 §4.2 修正为后续独立决策或保留保障,勿当作当场故障。(astrion_gateway_gap)
附:汇总口径与一致性说明
- 本汇总未掺入子智能体自身推测,所有小节结论均标注来源文件;文档间冲突(如 R2 计数 15 vs 16、R5 审批超时作为技术前提与否)已在对应处并列呈现并注明修订来源。
- 路径/行号均为各源文档读取时标注,可能因后续代码变动而失效,应以符号/函数名为主。
- 交付物仅
research_summary.md一份,存放于cache_research/gateway/audit_research_summary_v2/。