Feat/UI optimize 0925 - #333
Merged
Merged
Conversation
- mailbox: task_status_update 实体锁去掉 requirement 维度,同需求多任务可并行(5 并发生效,不再连坐串行) - task-service: runTask 补 cancelled 分支,外部取消后视同可恢复中断延后重放,不再永久卡死在 in_progress - agent-conservator: 新增统一存活仲裁(Conservator),有界触发 + 升级人工,杜绝心跳风暴回路 - agent/heartbeat: 状态机 setStatus 收敛为 transitionStatus(error/idle 终态统一裁定),心跳路径收口 - docs: agent-liveness-redesign 设计文档;test: conservator 回归测试
- agent.ts: recordHeartbeatSkip() 唯一 skip 出口(human-chat defer / idle / deep-sleep 三分支统一) - hearthebeat.ts: heartbeatStateFingerprint() 巡检指纹升级(队列内容签名 + 活跃任务集合,替代旧队列深度) - agent.ts: shouldEnterDeepSleep 硬编码 hasActiveTasks/hasPendingReviews 改为实时状态 - test: heartbeat-liveness.test.ts 9 用例(指纹纯函数 5 + skip 纯时间戳 + stuck-working 宽限 + 邮件独立消费 + triggerHeartbeat 收口) - docs: agent-liveness-redesign.md §五.2 标记重构 2 完成
…orce/reset) 重构 3(状态机收敛)回归测试:27 处 setStatus 调用点收敛为意图化 transitionStatus 调用(残留 0)后,验证竞态覆盖防护语义: - error 粘性:error 后并行 idle 意图不覆盖错误状态 - idle 聚合守卫:有活跃任务/并发 worker 忙碌时 idle 不落地 - force/reset:保守仲裁兜底可强制恢复 - 生命周期与 Normal 转换:start→reset / stop→offline / error→working 清错 - reconcileToIdle 走 reset 派生函数 - handleMessage 成功→idle、抛错→error(错误可见性) 15/15 通过;core 全量 3030 passed + org-manager 1210 + storage/shared 386 全绿;pnpm build:ts exit 0。
- 删除旧独立模块:agent-dirty.ts(evaluateDirtyState)/ agent-stall.ts(evaluateStall)/ agent-dirty-reconciler.ts(AgentDirtyReconciler)+ 3 个旧单测 - 判定原语内联进 agent-conservator.ts(单一组件彻底自包含,对外仅暴露 Conservator 系列符号) - 保留 test/agent-stall-api.test.ts(API 展示契约:runtime.stall 形状经 evaluateConservator 透出) - api-server.ts 旧日志字符串清理(Failed to init Conservator) - agent-liveness-redesign.md 标记重构 1-4 完成(审计记录),设计并入 ARCHITECTURE §3.10 - 验证:org-manager 55 文件 1182/1182 绿 + core 相关 24 绿 + 全包 tsc -b exit 0
- attention: 超时孤儿/取消决策/缺完成标记/重试耗尽 → mailbox.drop(异常可见 + 补偿重投),不再 complete 假装成功 - mailbox: watchdog 卡死自愈不再猜 completed,保留 dropped 语义 - storage/cli: 移除废弃 markStaleProcessingAsCompleted 死路径(watchdog 已不再调用) - 测试: mailbox-core/mailbox-redelivery/storage 断言同步至 dropped 新语义 resolved: mailbox item 终态与 resolveReinjectDecision 语义对齐 — completed=已收口(抑制重投),dropped=异常(允许补偿)
P0-1 工具注册可用性:capability-packs 新增 SCHEMA_INJECTED_TOOLS 同源清单, handleDiscoverTools 对 schema 注入工具走激活路径(不再 unknown),tool-selector 预算驱逐时已激活的 schema-injected 工具豁免 → schedule_wakeup 等不再死锁。 P0-2 自我闹钟:后端链路本就完整,P0-1 修好后工具可达即通。 P1-1 记忆整理工具化:新增 memory_stats(预算/观测/curated section 健康度) 与 memory_organize(观测按 query/ids 合并进目标 section 并归档)。 P1-2 分身共享工作记忆:context-engine 并发段注入 agent 级 NOTEBOOK 快照 (限量 5 条、截断 140 字符),getConcurrentContext 附带工作记忆。 方案文档 agent-self-management-redesign.md 含调查证据链与分阶段实施状态。 测试:schema-injected 5 + memory-organize 6 + context-concurrency 3 全绿, 既有 tool-selector/memory-tools/context-engine 78+ 无回归,tsc 0 错误。
记忆/认知重构(P-01..P-15):单一写入者(记忆文件禁止 file_write/file_edit/apply_patch/shell 直写,改走记忆工具);拆除 CPP 双目的地并删除 cognitive.ts;超预算无损归档(移入 knowledge-archive.md,写入永不拒绝/不静默截断);预算单位统一为字符;损坏文件先备份再重建;摘要追加式多代锚点;观察/碎片分池;dream 周期上报 lastConsolidatedAt;记忆健康横幅 + memory_stats 单一数据源;knowledge_* 去歧为 kb_*;admin 写入归口 agent-memory 锁;notebook 工具归一(notebook_read/upsert/clear);新增迁移读取层(读旧格式、只写新格式)。 会话压缩修复(刘利 2026-09-30 P0 报告):compactSession 增加磁盘回退(内存仅热存 20 个、磁盘 4418 个,被逐出会话此前静默空转返回假 ok);返回 found/remaining;未找到→error、无需压缩→skipped(within_keep_last)。 前端:AgentProfile 记忆健康可视化;kb_* 工具标签与 knowledge 分类;三语 locale 对齐。文档:9 篇机制文档对齐 + 2 篇设计文档(记忆认知重构、自我管理)。 测试:新增 write-guard-memory / memory-health / memory-migration / memory-observations-order / schema-injected-tools;改写受影响旧断言。core 213 文件通过、org-manager 1182 通过、全包 tsc 通过。
① 重复气泡:流式气泡此前用客户端 agentMsgId,忽略服务端 done 事件的 持久化 messageId,导致列表重载/WS 推送把 DB 消息取回后同一回复 以两个 id 各渲染一个气泡。现在流结束后把气泡 id 对齐为服务端 messageId;若 DB 版本已在列表中则丢弃本地流式副本。 配套:api.ts 透出 done 事件的 messageId(此前取到却无人消费)。 ② 无「输出中」反馈:活动态判据仅在有工具运行或以思考收尾时点亮, LLM 首 token 未返回、segments 为空时两者皆 false,气泡看起来像卡死。 改为「只要仍在流式且是最后一块」即持续显示活动反馈, isStreaming=false 后自动收束。
根因(三层叠加缺陷):
1. 超时定时器只 resolve Promise,从不终止进程 → 命令永远活着;
2. 进程组杀不中子孙:isIsolatedProcessGroup() 在 spawn 后立即用 ps 探测,
子进程尚未 setsid(),竞态返回 false → 退化为只杀 wrapper;
3. bash -i 开启 job control(set -m),每条命令自成进程组,
会话组不再包含真实工作进程。
修法:
- 超时即 terminate('timed out'):对整个进程组 SIGTERM→SIGKILL,
并重置会话(下次调用在同 cwd 惰性重建干净会话,不再继承卡死 shell);
- 会话启动写 set +m 关闭 monitor mode,令所有后代留在会话组内可被杀;
- spawn 后不再用 ps 竞态探测,直接信任 detached 契约;
- signalTree 增加 pid !== process.pid 保护,避免误杀自身进程组;
- 顺带修复:会话 shell 解析优先 POSIX shell(/bin/bash|zsh|sh),
不再盲从 $SHELL(fish/csh 等非 POSIX shell 会导致每条命令失败)。
验证:新增 test/shell-timeout-terminates-children.test.ts(后台+前台 sleep,
断言超时 <3s 触发、kill 后 pgrep 零残留);4 文件 28 测试全绿。
实测(2026-10-01):单进程/后台子进程/忽略 SIGTERM 三种场景超时后均零残留。
isolatedGroup 原先由 isIsolatedProcessGroup(child.pid) 事后探测得出,而该探测
读取的是子进程的「当前状态」而非 spawn 契约。对于自我守护型命令
(sh -c 'cmd & exit 0',wrapper 先退出、子孙留存),探测时 wrapper 已消失 →
返回 false → hasLeakedDescendants() 为 false → kill 直接答「进程已退出」,
子孙永久泄漏。
实测(2026-10-01,本机):
- wrapper 存活期间用 ps 探测 pgid:25/25 全部正确 —— 旧的「setsid 与 ps 竞态」
说法不成立;
- wrapper 退出后探测:100% 返回 false(即使进程组内仍有存活成员)。
改法:信任 spawn 契约(detached: !isWin ⇒ POSIX setsid ⇒ pgid === pid),
不再用 ps 探测反推;process-group.ts 的 isIsolatedProcessGroup() 降级为
仅诊断用途,并校正其文档注释;shell-session.ts 中不准确的注释一并修正。
回归测试:background-exec-descendant-reap.test.ts —— 起一条自我守护命令,
断言 wrapper 退出后子孙仍存活(前置条件),kill 后 pgrep 零残留。
相关 5 个文件共 35 项测试全绿。
问题:长耗时工具调用期间,SSE 连接被整页/渲染器级 teardown 掐断,前端 把「传输结束」当成「回合结束」,气泡当场定型为已完成并收起活动特效;用户 刷新后又看到服务端其实一直在正常输出。 根因: 1. api.ts reattachStream 从不记录是否见到终结事件 done,socket 被切断时 仍返回 attached:true;useChatStream 据此无条件 finalize。 2. pickStreamReattachTarget 把「有内容但非 streaming」的 agent 消息判为上一轮 已完成回复而不复用——刷新后从 DB 读回的本回合半截回复恰好长这样,导致 重连时新建 reattach_<ts> 气泡。 3. 重连传 afterSeq=status.lastSeq,服务端 useSnapshot 条件不满足 → 跳过权威 快照只发未来事件,新气泡只有续写尾巴。 修复: - api.ts:读取循环新增 terminal 标记,仅收到真正 done 才算回合结束;socket 切断时 terminal:false。 - useChatStream.ts:断线后带退避重连(最多 8 次),重连期间维持「输出中」; 仅在 terminal 为真时 finalize;重连传 status.messageId 与 allowCurrentTurnPartial,afterSeq 置 0 以拉取权威快照(完整文本 + 工具卡)。 - ChatHelpers.ts:pickStreamReattachTarget 改为回合感知——先按服务端 streamStatus.messageId 精确命中,再 isStreaming/空占位,最后(仅当服务端仍 streaming)复用最后一条 user 消息之后的含内容 agent 消息;上一轮已完成回复 仍永不复用。 - ChatComponents.tsx:ProcessRun 消费 reconnecting,断线瞬间不再收起活动态。 - 三语 locale 新增 execution.reconnecting(连接中断,正在重连…)。 验证:web-ui tsc --noEmit 通过;全量测试 370/370 通过(18 文件); 新增 ChatHelpers 回归 4 条(当前回合半截复用 / messageId 精确命中 / 上一轮回复仍不复用 / error 不复用)。
1) core/shell-session: GNU bash 5 (Linux/CI) 在 -i 交互模式下把命令回显到 stderr,而 ManagedSession 把 stderr 并入解析缓冲区,导致返回的 stdout 被 污染(echo $X 变成 'echo $X\n<值>')。修法:会话建立时追加 set +o emacs 关闭 readline emacs 模式,bash 改用普通读取不再回显。已在 bash 5.2 容器实测。 2) core/test/background-exec-descendant-reap: pgrep -f 会匹配到执行它自己的 sh -c wrapper 命令行,Linux 上报出幽灵存活进程。修法:首位数字加方括号 (sleep [7]77812),匹配真实进程但不自匹配。 3) org-manager/agent-conservator: 合并重复的 @markus/shared import。 4) web-ui/api: 删除从未赋值的冗余变量 softDisconnected,统一用 !sawTerminal。 附带移除 shell-session 中失效的 isIsolatedProcessGroup import。 验证:eslint 0 error;tsc -b + web-ui tsc 通过;core 15 / org-manager 19 / web-ui 75 测试全绿。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.