Why
深度审计确认(服务端唯一确认级内存泄露):packages/opencode/src/share/share-next.ts:187-214 的 5 个 yield* watch(...)(Session.Updated / MessageV2.Updated / PartUpdated / Session.Diff / Session.Deleted)丢弃了 events.listen 返回的 Unsubscribe。listener 被 push 进 EventV2 进程级单例的数组(packages/core/src/event.ts:698-705),唯一移除途径是调用返回的 Unsubscribe——被丢弃即永不移除。
InstanceState(ScopedCache)dispose → 重挂载(worker.reload()、configUpdate、server.instance.disposed 重连)每轮 +5 listener,闭包持有 _ctx、cache(含已清空 map 与 state 骨架)永久驻留;且进程内每个事件都要跑过所有陈旧 listener 的 directory 过滤(CPU 税)。
Scope
packages/opencode/src/share/share-next.ts:171-214(watch 定义与 5 个调用点)
- finalizer 位置:同文件
:154-163(现有 finalizer 只清 cache.queue/cache.shared,不含退订)
Approach
- 收集 5 个
watch() 返回的 Unsubscribe,在 InstanceState.make 闭包的 Effect.addFinalizer 中逐一调用
- 或在
events.listen 层提供 scoped 包装(Effect.acquireRelease(listen, (unsub) => unsub)),使订阅生命周期自动绑定实例 scope(对其他调用点亦有益,但本 issue 限定 share-next)
Acceptance
- 回归测试:构造实例 → dispose → 重建,断言 EventV2 listener 计数不增长(spy 或暴露计数)
- 既有 share/event 相关测试全绿(
packages/opencode/test/ 相关文件)
Why
深度审计确认(服务端唯一确认级内存泄露):
packages/opencode/src/share/share-next.ts:187-214的 5 个yield* watch(...)(Session.Updated / MessageV2.Updated / PartUpdated / Session.Diff / Session.Deleted)丢弃了events.listen返回的 Unsubscribe。listener 被 push 进 EventV2 进程级单例的数组(packages/core/src/event.ts:698-705),唯一移除途径是调用返回的 Unsubscribe——被丢弃即永不移除。InstanceState(ScopedCache)dispose → 重挂载(
worker.reload()、configUpdate、server.instance.disposed重连)每轮 +5 listener,闭包持有_ctx、cache(含已清空 map 与 state 骨架)永久驻留;且进程内每个事件都要跑过所有陈旧 listener 的 directory 过滤(CPU 税)。Scope
packages/opencode/src/share/share-next.ts:171-214(watch定义与 5 个调用点):154-163(现有 finalizer 只清cache.queue/cache.shared,不含退订)Approach
watch()返回的 Unsubscribe,在InstanceState.make闭包的Effect.addFinalizer中逐一调用events.listen层提供 scoped 包装(Effect.acquireRelease(listen, (unsub) => unsub)),使订阅生命周期自动绑定实例 scope(对其他调用点亦有益,但本 issue 限定 share-next)Acceptance
packages/opencode/test/相关文件)