尧图精选

qwen-code 工作区会话实时状态活动时间戳(updatedAt)协议设计解析

🕒 发布时间:2026/9/13 21:02:25 📁 来源:尧图网络
qwen-code 工作区会话实时状态活动时间戳updatedAt协议设计解析【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code本文围绕 qwen-code 工作区会话实时状态live-state协议的一次增量演进展开为GET /workspaces/:workspace/sessions/live-state返回的每个实时会话追加可选的updatedAt活动时间戳使客户端无需重新扫描持久化会话目录即可刷新已加载行的新旧程度。读完本文你将理解该字段的语义边界活动水印 vs 持久化确认、单调性与因果顺序保证、与目录版本generation revision的分工以及服务端桥接层、TypeScript SDK 与后续 Web Shell 消费方的完整契约。背景live-state 协议遗留的“最后一公里”原始的工作区会话实时状态协议见 2026-08-16-workspace-session-live-state.md把两个关注点彻底分离易变的实时状态由廉价的纯内存端点返回目录变更推进generation revision版本号。普通 turn 的完成刻意不推进目录 revision。因此第一个 Web Shell 消费方在每次 turn 结束后仍保留一个限速的全量目录对账full catalog reconciliation否则持久化的updatedAt与会话排序会一直冻结。这个回退虽然被限速但它依然把“普通 turn 生命周期事件”重新连接到 live-state 端点当初想要规避的高开销操作上。本设计文档的目标正是补齐这块缺口为 live-state 条目增加一个活动时间戳让客户端在 turn 结束后用一次纯内存请求即可完成已加载行的新旧度刷新而不必扩大目录时钟catalog clock的推进范围。值得注意的实现基础是当前代码库已经具备这条语义路径的大部分——BridgeSessionSummary与DaemonSessionSummary已经声明了可选的updatedAt?: string持久化目录摘要从 transcript JSONL 文件的修改时间推导updatedAtlive/persisted 合并辅助函数已把 live 摘要的updatedAt视为更新鲜的值所有基于活动的目录比较器读取updatedAt ?? createdAt桥接层通过publishPromptTerminal汇聚成功、失败、取消、deadline、teardown、传输终止等所有终态路径publishPromptTerminal带有每个 prompt 的terminalPublished闩锁latch并能区分“已运行过的 prompt”与“从未离开队列的 prompt”。缺失的部分仅是SessionEntry上的活动时间戳及其在摘要与 live-state 表面的投影。这可以从 acp-bridge/src/bridge.ts 与 acp-bridge/src/bridgeTypes.ts 的现有结构中直接印证。术语与权威边界水印而非持久化确认协议首先明确一个关键定性live-state 条目上的updatedAt是守护进程观测到的实时活动水印daemon-observed live activity watermark不是持久化确认。权威的推进时刻是一个其待处理条目处于running状态的 prompt 首次发布正式终态formal terminal。选择这个时刻优于一系列邻近信号设计文档给出了明确取舍候选信号被否决的原因Prompt 准入admission请求可能一直排队、永不运行过早Prompt 开始start行在 turn 落定前就移动且无法替代现有完成驱动行为单个流式更新过于频繁不构成稳定的排序边界sessionLastSeenAt它是也被客户端心跳推进的存活水印暴露它会让“空闲但已连接”的会话不断重排JSONL mtime是持久化权威但需要文件系统工作且在 daemon 终态边界上无法同步获得一个重要的工程事实ACP 子进程在 prompt 调用落定之前就把 turn 结果记录入队但记录服务使用串行化的异步写入器。因此 daemon 可以知道“某个运行中的 turn 到达了正式终态”却不能声称“最后入队的记录已落盘”。协议显式命名这一事实而不是把尽力而为的活动信号伪装成持久化保证——这正是后续诸多设计约束的根源。公开 REST 契约请求保持不变GET /workspaces/:workspace/sessions/live-state保留原有的 selected-runtime、workspace 作用域、仅信任trusted-only所有权语义且无任何查询参数。成功响应{ v: 1, catalogVersion: { generation: 7eca3164-bce1-4f50-94d8-c842c480f213, revision: 17 }, sessions: [ { sessionId: session-123, clientCount: 1, hasActivePrompt: false, isWaitingForPermission: false, isWaitingForUserQuestion: false, updatedAt: 2026-08-18T08:12:30.123Z } ] }响应必须继续携带Cache-Control: no-store。字段契约export interface DaemonSessionLiveState { sessionId: string; clientCount: number; hasActivePrompt: boolean; isWaitingForPermission: boolean; isWaitingForUserQuestion: boolean; updatedAt?: string; }当updatedAt存在时它是合法 ISO 8601 时间戳标识当前桥接层bridge对该会话观测到的最新 running prompt 终态在该实时SessionEntry生命周期内严格递增在对应正式终态事件变得可见之前写入在当前桥接层代际中首个 running turn 落定之前可能缺失在 daemon 或工作区运行时替换桥接层时可能丢失不得被解释为 transcript 刷新成功的证明。响应 schema 保持v: 1。增加一个可选对象属性是 wire-additive 的不会改变现有字段的解码方式。字段缺失不能作为全局能力探测信号——因为一个支持该特性的 daemon 在“新代际内尚无观测到终态”的 live 条目上合法地省略它。在 TypeScript SDK 中该字段已随本协议落地DaemonSessionLiveState.updatedAt带注释“Daemon-observed activity watermark ... Absent before the first such terminal and after a bridge or runtime replacement. It is not proof that the transcript was flushed”见 sdk-typescript/src/daemon/types.ts。SDK 的调用方法是WorkspaceDaemonClient.getSessionLiveState()实现为单次原生 REST 请求GET /workspaces/:workspace/sessions/live-state刻意不做每次轮询前的能力探测否则请求量翻倍见 sdk-typescript/src/daemon/DaemonClient.ts。转换矩阵什么推进、什么不推进事件updatedAt行为目录版本行为Running prompt 成功完成推进一次不变Running prompt 返回结构化或传输错误推进一次不变Running prompt 被取消推进一次不变Running prompt 到达 daemon deadline推进一次不变Running prompt 在 close/kill/crash/shutdown 时被冲刷若该 prompt 的终态闩锁尚未被赢得则推进一次成员移除另行推进队列中 prompt 在分发前被移除、取消或过期不变不变迟到结果试图在更早的 deadline 终态后发布不变重复闩锁胜出不变Prompt 准入、排队等待、开始或流式更新不变不变Attach/detach、心跳、权限等待、用户提问等待不变不变新终态前的 live 会话注册或恢复字段缺失成员 revision 推进桥接层或运行时替换字段重新缺失新 generation错误或 deadline 即使 transcript 持久化降级、子进程未正常结束时也可能推进活动时间——这是刻意为之该字段代表 daemon 观测到的运行尝试与正式终态而非持久化结果。单调性与因果顺序为什么是 epoch 毫秒SessionEntry把水印存储为 epoch 毫秒而非字符串lastTurnEndedAtMs?: number;首个被接受的 running 终态按如下逻辑推进const createdAtMs Date.parse(entry.createdAt); const floor Number.isFinite(createdAtMs) ? createdAtMs : Number.NEGATIVE_INFINITY; const next Math.max(Date.now(), (entry.lastTurnEndedAtMs ?? floor) 1); entry.lastTurnEndedAtMs next;具体实现可以为了可读性避免Number.NEGATIVE_INFINITY但必须保留相同行为。附加的1ms是逻辑平局决胜符用于墙钟时间未前进的场景不是耗时度量。createdAt下限至关重要无水印的行以createdAt为键且 live-only 游标不携带已发射身份列表。如果首个水印落后于createdAt——即创建与首个终态之间存在墙钟回拨——就会在遍历中途把已发射行的键向后移动使“严格更旧”过滤器把它重复返回。publishPromptTerminal必须依次判断待处理 prompt 是否曾运行、通过重复闩锁、推进水印、记住终态状态然后才广播turn_complete或turn_error。由此建立 happens-before 关系terminal latch wins - live activity watermark advances - formal terminal is published - client observes terminal - client starts live-state request - live-state includes the new watermark最后一步仅在该 live 条目仍存在于同一桥接层代际时成立。close 或运行时替换可能让行消失或返回新代际协议从不跨生命周期边界携带退役的桥接本地水印。settleActivePromptState的所有权门与终态闩锁继续保护下一个 prompt 免受迟到结果影响时间戳更新不得新增第二条独立的 settle 路径。目录版本契约updatedAt 不参与 generation revisionupdatedAt不参与generation revision。版本号仍然是目录成员与静态元数据的相等性令牌。若每个 turn 都推进 revision两秒一次的 live-state 轮询将观测到不匹配并重新触发本扩展想要消除的全量目录扫描。客户端像合并hasActivePrompt与等待标志一样直接从快照合并updatedAt。updatedAt变化而目录版本不变是合法且符合预期的。持久化与全量目录合并双权威与“取更新”持久化会话摘要继续从 JSONL mtime 推导updatedAt。两个时间戳权威不同持久化 mtime 描述存储中可见的最新修改liveupdatedAt描述桥接层可见的最新 running turn 终态。当 live 会话与持久化摘要合并时响应应使用两者中更晚的有效时间戳而不是盲目偏爱某一来源——这防止异步 transcript 写入在几毫秒后获得 mtime 时把终态时间戳所在的行向后移动。比较辅助函数必须在另一候选缺失或非法时保留有效值。不新增任何文件系统读取两个候选字符串在现有合并路径中都已驻留内存。所有同时持有两个候选的表面积都应用同一规则包括 Live Task 的读与等待路径它们直接读桥接摘要而非经由列表合并——否则同一任务会因查询工具不同而报告两种不同新旧度且仅以 live 值键控的等待游标在只有 transcript 推进时不会变化。活动型全量目录响应与 live-only 会话列表因此在会话存活期间使用相同的有效新旧度。live 条目消失后持久化 mtime 重新成为唯一权威且可能比原 live 水印更旧——崩溃、降级写入、正常关闭后1ms平局决胜或墙钟回拨都可能造成此现象协议不制造持久化、不保留退役水印。活动维度上的新移动模式与游标防护现有游标路由不是快照隔离的并发会话活动本就可以在调用方翻页时改变集合。填充 liveupdatedAt使这种移动在活动排序中可观测但不引入更强的分页保证。有一个新出现的移动模式值得注意此前活动键的游标分量只来自 transcript mtime只能前进而 live 水印领先 mtime 时并非持久——live 条目在遍历中途退役行的键回落到 mtime那么以更高水印编码游标的页会再次接纳该行。live-only 行在遍历中途持久化同理。因此活动游标要携带已按 live 派生键发射过的行身份after-cursor 过滤器在本轮剩余过程中排除它们保证 live 派生键移动的会话每轮至多返回一次。携带机制只覆盖机制见过的行纯持久化行在 pin 状态变化前发射的不会被携带未 pin 在两次拉取之间仍保留组织视图既有的重复模式累积分页的调用方以sessionId键行。第三个活动键游标live-only 列表无需携带首个水印推进以createdAt为下限live 键永不移回列表保持有界与自剪枝——缺失身份的保留短 TTL 持久化快照或中途分组移动下的瞬态缺失行存在且其持久化键在两种 pin 状态下都无法通过“严格更旧”过滤器的则丢弃超过 64 身份上限后持久化键最高最接近离开重接纳窗口的身份先被丢弃被丢弃身份降级为至多一次的重复而非失败。字段存在前铸造的游标保持有效字段为空时省略因此未发射 live 派生键时游标形状不变。跨轮次保证不变新轮次即新快照。桥接层与路由实现最小改动清单服务端实现应做最小改动给SessionEntry增加lastTurnEndedAtMs?: number增加一个内部辅助函数单调推进该字段首个推进以条目createdAt为下限在publishPromptTerminal中在广播前计算既有 running 状态门且仅在重复闩锁成功后推进从toSessionSummary把new Date(lastTurnEndedAtMs).toISOString()投影为已声明的BridgeSessionSummary.updatedAt从工作区 live-state 路由投影该可选摘要字段修改 live/persisted 合并选择逻辑保留更晚的有效时间戳。无需新增桥接方法——listWorkspaceSessions与getSessionSummary已返回BridgeSessionSummary其公开类型已携带可选字段。实现横跨 ACP bridge、CLI serve 与 TypeScript SDK 三个包属于小特性而非重构但仓库策略要求跨包架构变更需维护者知晓PR 的风险小节应完整列出下文的“下游消费者审计”。live-state 路由继续坚持解析选定工作区运行时无主运行时回退、按现有语义拒绝不受信任或不可用运行时、只读桥接版本与内存摘要、仅暴露新目录版本时使持久化目录缓存失效、不做任何会话存储/settings/外部命令/ACP 请求、返回Cache-Control: no-store。纯活动变化不得从 live-state 路由使持久化列表缓存失效——缓存快照包含持久化事实请求时既有 live 合并会在目录被服务时套用当前内存时间戳。下游消费者审计可选的加性影响填充既有BridgeSessionSummary.updatedAt字段的影响比投影路由本地值更广但均为刻意的加性变化消费者影响工作区 live-state为预期客户端优化返回可选活动水印全量工作区会话列表live/persisted 合并与活动排序可反映最新 running 终态Live-only 会话列表与游标live 行按填充的活动时间戳而非仅创建时间排序GET /session/:id/status直接添加已类型化的桥接本地字段不经持久化合并Live Task 线程摘要与游标running 终态推进 live task 的updatedAt/变更游标读/等待应用与列表相同的“取更新”规则Goals 路由忽略新字段行为不变会话所有者解析与准入只读身份/所有权字段行为不变创建子会话与 live 会话协调器只读调用方或交互状态行为不变GET /session/:id/status直接返回桥接摘要其updatedAt可能早于合并列表响应中选中的更晚持久化 mtime——各响应表面之间不保证相等。实现 PR 必须更新受影响公开摘要表面的 exact-object 测试同时保留证明无关所有权与状态决策不开始依赖时间戳的测试。TypeScript SDK 与能力声明TypeScript SDK 的变更仅限于DaemonSessionLiveState上的可选字段DaemonSessionSummary.updatedAt已存在。SDK 传输保持类型化原生 REST 调用不增加运行时 schema 转换也不在每次轮询时做能力请求。不新增、不修改任何能力workspace_session_live_state: { since: v1; }该能力继续只表示“端点存在”不承诺每个 live 条目都有updatedAt——因为该值依赖生命周期。不新增 Java SDK 或 ACP 方法。兼容性矩阵服务端客户端行为旧旧既有 live-state 与限速目录刷新行为新旧额外 JSON 属性被忽略既有行为不变旧新后续客户端字段缺失时保留现有限速全量目录回退新新后续客户端已知目录行从 live-state 更新新旧度未覆盖行保留回退响应 schema 保持v: 1。新 schema 版本会迫使客户端对一个原本加性的属性分支且无法解决新代际首个终态前时间戳合理缺失的问题。后续 Web Shell 消费契约服务端 PR 不改变 Web Shell 行为但协议为后续消费者设计了如下规则turnCompleted同时携带工作区 cwd 与 session id客户端仅对已知到达 running 状态的 turn 记录每会话完成序列纯排队终态无需新旧度对账live-state 请求在发起时快照待处理序列只有“在序列记录之后发起”的请求响应才能满足该序列匹配行上的有效updatedAt按服务端比较器修补新旧度并重排已加载的活动页归档页永不接受 live 活动时间戳时间戳缺失/非法或目标行不在已加载活动目录中时保留既有版本围栏version-fenced、限速的目录回退。序列是必需的一个 live-state 请求在途时第二个 turn 可能完成若只记 session id 集合旧响应会清除更新的完成事件。无法区分“仅排队终态”的消费者可保守保留现有限速目录回退但协议不要求为“未产生活动水印的事件”提供回退。客户端应保留目录与 live 时间戳中较晚者仅当有效值变化时才重排不得把未知 live 会话插入按 source/group/archive 过滤的页面——live-state 不携带证明成员资格的静态元数据。这使常见路径免扫描同时保留旧服务端、分页边界与缺页会话的正确性。失败语义路由的 HTTP 失败语义完全不变。条件结果当前 live 代际无 running 终态会话条目省略updatedAt终态之间墙钟未前进逻辑1ms保持严格单调墙钟回拨每条目单调性防止 live 时间戳回归首个推进以createdAt为下限墙钟前跳后被校正水印可能保持在未来直到时间追上或 live 条目消失记录器降级或后续冲刷失败水印仍表示终态活动无持久化声明重复或迟到终态既有闩锁抑制另一次推进排队 prompt 从未运行无推进、无必需的新旧度确认终态后会话消失live 行消失持久化目录保持权威daemon/运行时替换新目录 generationlive 水印初始缺失客户端收到无效或缺失字段忽略并保留既有回退行为高频路由不新增日志既有路由延迟与请求计数遥测已足够。实现不得增加每终态成功日志——那会给普通生命周期路径带来高基数噪音。墙钟前跳是接受的桥接本地水印行为向下校正会违反严格单调条目存活期间取更晚值的合并可能让未来水印领先正确持久化 mtime条目移除或桥接替换终结该范围。测试计划要点桥接单元测试新建或恢复的 live 条目在首个 running 终态前省略updatedAt成功 running 终态产生合法 ISO 时间戳running 成功、结构化错误、传输错误、取消、deadline 各终态路径各推进一次teardown 路径会话 close、kill、channel 退出、daemon 关闭经同一辅助函数推进但在同一操作中移除 live 条目故推进值无读取者、测试不断言排队移除/取消/deadline 不推进deadline 路径发布两次终态expiry 一次、竞态拒绝到达 settle 处理器一次仍只推进一次更晚到达的 agent 结果落在竞态 promise 落定之后既不增加终态也不推进固定Date.now()下两个 running 终态产生严格递增值墙钟前跳后校正水印永不下降桥接本地值仅随条目或桥接替换重置心跳、attach/detach、交互等待、流式更新不改变值终态订阅者被同步通知时摘要已含新时间戳turn 活动不改变目录 generation 与 revision。路由与所有权测试live-state 响应仅在桥接摘要提供时包含updatedAt既有五个必填 live-state 字段保持存在v、catalogVersion、Cache-Control、selector、信任、运行时代际行为不变路由仍不做文件系统、settings、命令或 ACP 子进程工作主/次运行时测试证明时间戳只来自被选桥接。全量列表与下游测试比持久化 mtime 更新的 live 时间戳胜出并改变活动顺序比 live 终态值更新的持久化时间戳胜出防止回归非法或缺失候选保留另一有效值live-only 列表排序与游标键使用填充时间戳会话状态暴露可选字段且不改变无关状态值Live Task 游标在 running 终态后变化、跨心跳保持稳定Goals、所有者解析、准入测试保持不变或显式忽略新字段。SDK 与兼容性测试公开 daemon 导出包含可选字段既有 live-state 方法恰好执行一次原生 REST 请求、无能力探测无updatedAt的旧形状响应仍可赋值、可用精确请求 URL、超时、客户端身份、错误行为不变。E2E 与故障注入启动真实 daemon 并创建会话prompt 终态前读 live-state允许updatedAt缺失完成一个 prompt断言后续读取返回有效时间戳且catalogVersion不变以最小延迟完成第二个 prompt断言时间戳更大发送心跳断言时间戳不变执行 running 取消或 deadline断言推进一次重启 daemon断言目录 generation 变化且 live 时间戳在下一个终态前缺失阻塞或放大持久化目录扫描确认直接 live-state 延迟与其无关。后续 Web Shell PR 增加请求计数 E2E 覆盖证明已加载行上的重复 turn 不再产生额外全量目录请求。验收标准客户端观测到的 running prompt 终态事件在同一 live 条目仍存在于同一桥接代际时因果先于后续启动的 live-state 响应中的水印水印每个 running prompt 终态恰好推进一次从不因仅排队终态或心跳推进同一墙钟毫秒内多个终态保持严格有序turn 活动不改变目录 generation/revision不使持久化列表缓存失效直接 live-state 工作与持久化会话数量、transcript 大小无关同会话 live 与持久化摘要合并时在 live 条目存在期间使用更晚的有效活动时间不把行向后移动既有状态、信任、运行时所有权、能力、错误契约保持不变旧客户端忽略该字段字段缺失对新客户端仍合法。发布与回滚服务端字段是加性的经正常 daemon 发布路径分发无需特性开关。金丝雀遥测应对比改动前后 live-state 延迟与错误率、普通 prompt 终态间的目录 revision 稳定性、live 会话列表或 Live Task 排序的意外变化。定向金丝雀冒烟测试单独验证“每个 running 终态一次独立时间戳推进”直接读端点即可无需新增每终态生产日志或指标。协议 PR 单独不降低 Web Shell 目录请求量——该指标只在后续消费者上线后变化。回滚即普通服务端二进制回滚无配置、数据或迁移状态。被否决的备选方案要点备选否决理由复用sessionLastSeenAt心跳推进它空闲但已连接的会话会不断显示为“新近”存活与对话活动是不同信号从 live-state 路由 stat transcript返回持久化权威但把文件系统工作还给定义即为“纯内存成本”的高频端点发布每个终态前冲刷记录器把 UI 新旧度变成 prompt 响应路径上的同步持久化屏障增加延迟并把桥接落定耦合到存储健康每个终态推进目录 revision所有 live-state 客户端都会观测版本不匹配并重载全量目录保留而非消除当前扫描使用浏览器Date.now()无法覆盖另一控制器完成的 turn、跨客户端不一致、无服务端排序权威在updatedAt旁新增lastTurnCompletedAt既有摘要与目录比较器已把updatedAt用作活动时间第二个公开时间戳需要另一套合并策略增加信封支持标志、新能力或v: 2属性是加性的支持 daemon 可在首个 running 终态前合法省略后续客户端可在无另一次协商表面积的情况下安全回退携带 source/organization 元数据以便插入未知行会把 live-state 演变成第二个目录协议重复过滤、失效、兼容规则目标行未加载时全量目录对账才是正确的罕见回退仅经 SSE 发布活动终态已通过会话流到达但路由级水印还能更新轮询工作区状态的其他标签页与控制器保持重连与漏事件恢复无状态、留在既有 live-state 协议内延伸阅读上游协议基座docs/design/2026-08-16-workspace-session-live-state.mdlive-state 端点、目录版本时钟、客户端一致性握手SDK 类型实现packages/sdk-typescript/src/daemon/types.tsSDK 调用实现packages/sdk-typescript/src/daemon/DaemonClient.ts桥接层核心publishPromptTerminal、BridgeSessionSummarypackages/acp-bridge/src/bridge.ts、packages/acp-bridge/src/bridgeTypes.ts相关设计上下文docs/design/2026-08-16-workspace-session-live-state.md 与 docs/design/2026-08-18-workspace-session-live-state-updated-at.md【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →