Files
deepseek-harness/packages/api/activity-controller/README.zh.md
T
Yichen Jiang 69d8b6e7ea fix(activity): bind observation cleanup to its generation and settle before mapping the outcome
Both ds-review-bot criticals verified and fixed:

- ClientActivityFeed release closures resolved the entries map at release
  time, not the entry they were minted for. A late release of a superseded
  observation decremented (and could tear down) the successor's stream, and
  the post-dispose model clear could land after a re-observation inside the
  dispose round-trip re-anchored the view — blanking the panel until the
  popover reopened (React StrictMode's double effect hits the same window).
  Releasers now capture their entry, reconcile the map only when it still
  holds that entry, and skip the view clear whenever a successor owns the
  key. Both regressions fail on the previous code.

- The background observation tap mapped the process outcome as soon as the
  pump settled — including its failure path, where the process is usually
  still running: processOutcome requires a settled handle, so an early
  mapping froze 'exit code: 0' onto a live task and first-wins end discarded
  the real terminal state. The tap now awaits proc.done before mapping (the
  promise never rejects) and swallows mapping failures per the module's
  contract, in bash and its pwsh mirror.

Also applied from the same review: the Remote tier's cross-session
visibility is now stated in the controller README, ActivityRead.next
documents its chunk-boundary nature that client concatenation relies on,
mergeRows records the one-activity-per-job assumption, and the 08-24 Agent
Note now reflects the shipped unified task list.
2026-08-25 15:46:15 +08:00

2.4 KiB
Raw Blame History

@deepseek-ai/dsh-api-activity-controller

English | 中文

架在可选 ctx.activities 注册表之上的 Activity Remote 流,附 React-free 客户端模型。纯读面:不启动、不取消任何工作,也绝不触碰模型侧消费游标——wire 路径只读注册表的非消费 offset。

宿主 ActivityController(命名空间 activity)暴露两个 @Remote({ mode: 'stream' }) 方法:

  • control(signal)——一份完整 roster baseline,之后每次注册表变化按 owner 会话推送整桶替换帧(与 session control 流承载 jobs 的自愈形态一致)。没有注册表的组合返回空 baseline。Remote 层有意把所有会话的行提供给任何已连接的浏览器,observe 以镜像到的 owner 替调用方满足注册表的 owner 栅栏:按会话的栅栏是进程内契约,这个单用户本地 BFF 跨栅栏读取,与 session control 流对 jobsBySession 的做法完全一致。
  • observe({ activityId, from? }, signal)——一帧 opened 锚点,随后是合并的 output 帧(flushMs 窗口、maxFrameBytes 软预算;更大的单 chunk 整帧发出),activity 结算且排干后发一帧终态 status,然后本代正常关闭。重连方以上一帧的 next 作为 from 续传;落后于保留窗口的续传会带 lossy 标记。状态与输出同流,结算永远不会与仍开着的输出通道竞态。
配置 默认值 含义
flushMs 100 新输出到达后、观察读取前的合并窗口。
maxFrameBytes 65536 每个观察输出帧的软字节预算。

客户端半区(./client)安装 ctx.activityFeed:ClientActivityModel 镜像 roster(last-wins 分桶、按代整体替换)并为每个被观察 activity 累积有界渲染尾巴;ClientActivityFeed.observe(id) 打开按引用计数共享的观察流,跨 carrier 代次从模型游标续传。

模型体验

无:activity 传输是浏览器与宿主的观察状态,不注册任何 prompt、工具或会话事件。

KV Cache 影响

无;本包从不组装或改动 provider 请求。

已知限制与暂缓事项

  • 注册表存在性在构造时采样——控制器之后才挂载的注册表在控制器重载前不会被观察。
  • roster 不携带水位——观察流自带锚点;roster 行与观察 offset 的对齐是客户端策略。