scope 是地基:system-prompt 的 ScopedLayers、tools 的 restrict、agent 的 agent.ctx 全部建立在它上面。
一个 agent 的注册既 scope-可见又 scope-生命周期 —— 一个事实驱动两者。
非插件(无 name/inject/apply):纯函数库 + 可选 invariant 伴随插件(@deepseek-ai/dsh-scope/invariant)。
导出(index.ts:11-12):createScope / bindScopeParent / scopeParentOf / scopeChainOf /
scopeOf / scopeTarget / isScopeCarrier / carrierKeyOf + { AnonymousEntries, NamedEntries, ScopedLayers } 与 ScopeLayer。
ScopeKey = object,按对象同一性比较(引用相等)。惯例:活体 agent 就是它自己 scope 的 key。
scope 是 trusted same-process 插件路由机制,不是 sandbox / 权威边界。
createScope(ctx, key, options?): Scope // 铸带 tag 的 Cordis context;backing fiber 拥有经它做出的每次注册 bindScopeParent(key, parent): void // 一次性;已绑定再 bind 抛错 scopeChainOf(key): ScopeKey[] // [key, parent, …] 最近优先;环检测抛错 scopeOf(ctx): ScopeKey | undefined // 读最近 tag;派生 context 继承 tag scopeTarget(base, key): Scoped<T> // scope 过滤的只路由事件载体 isScopeCarrier(x) / carrierKeyOf(x) // 供 invariant 用(WeakMap 打标) // 存储层: ScopedLayers<L>.effect(ctx, action, opts) // 可见性与所有权的同一来源,返回精确 disposer ScopedLayers<L>.peek(scope) // chain-blind:只取该 scope 自己层的贡献 ScopedLayers<L>.chainLayers(scope) // 最远祖先在前,精确 scope 最后 ScopedLayers<L>.merge(scope, pick) // 全局打底 + 链上各层按名覆盖(shadowing 在 store 层)
createScope(ctx, key, options?) { if (options?.parent !== undefined) bindScopeParent(key, options.parent) const fiber = ctx.plugin(scope) // 共享 no-op 插件作 backing fiber const scoped: Context = fiber.ctx.extend({ [kScope]: key }) ... return { ctx: scoped, rawDispose: fiber.dispose, dispose: () => (disposing ??= quiesceFiber(fiber)) } }
rawDispose 是精确 Cordis disposer(合成 effect 可 yield 它以嵌套在指定 yield 位置);dispose 幂等共享静默边界,
即使 rawDispose 已被单次调用也等 fiber 惯性结束。嵌套 scope 的 context shadow 为单一最近 tag(层级在 key 的父链,不在 context tag)。
载体只含路由状态;真实 subject 通过事件参数传递(Scoped<T> 不暴露属性)。Scoped<T> 是编译期不透明品牌 —— 裸 subject 作 thisArg 是编译错误。
effect(ctx, action, options) { const scope = scopeOf(ctx) // 调用方 ctx 决定可见性与生命周期 const dispose = ctx.effect(function* () { // 无 scope → global 层;有 scope → 惰性创建/复用 try { undo = action(layer) } catch (error) { // 失败回滚:若该层是本次新建且完全空,立即从 scoped Map 删除(不留空壳层) throw error } yield () => { undo(); // 层全空则回收;notify onChange } if (notify) onChange() }) return dispose // 精确 disposer }
挂 internal/dispatch(global),对每个声明为 scope-filtered 的事件(scoped-events.generated.ts,由 pnpm run gen-scoped-events 生成):
① 必须带载体分派(thisArg 必须是 scopeTarget 产物,否则 fail);
② 载体 key 必须等于 payload 命名的主体(如 agent/* 的 agent 参数、system-prompt/assemble 的 args[1].scope)。
session/*、subagent/* 解析器为 null(只查载体存在)。
agent.ctx:loop 挂载于 agent 属性;在 agent/created 或 preset mount 等时机经 agent 参数获得agent.ctx.systemPrompt.section/tools/variable(...)、agent.ctx.tools.restrict(...)` 等 —— ScopedLayers.effect 从 scopeOf(ctx) 提取 scope,注册既 scope-可见又 scope-生命周期agent.ctx.on('system-prompt/assemble', ...) 只收到该 agent 的组装;agent.ctx.on('tools/execute', ...) 只收到该 agent 的工具执行scope.dispose() 统一清场(幂等、共享静默)ScopeLayer(聚合各表 + isEmpty());用 ScopedLayers 存层;effect() 挂注册 APIscopeTarget(base, scope) 分派自己的 scope-filtered 事件,事件声明用 Scoped<...> this 类型gen-scoped-events 生成源,让 invariant 强检生效bindScopeParent 对已绑定 key 抛错;重链只能经返回的 rebind(blank-session recompose 契约,旧父下产物不得再被保留)