dsh-scope/packages/core/scope官方

175.3kStar19.0kFork0Issue752Watching

为 DeepSeek Harness 提供带标签的作用域上下文注册机制,支持作用域过滤事件分发和父子作用域链,用于为 agent 或 preset 创建隔离的注册空间

机审证据安装命令仓库已核验dsh-plugin Topic许可证READMEAI 百科

此插件是大仓库 deepseek-ai/deepseek-harness 的子包,星数与活跃度统计的是整个仓库。

语言
TypeScript
License
MIT
分支
master
ai-agentscordisdshdsh-plugin

安装

$ dsh plugin --profile web add npm:@deepseek-ai/dsh-scope

在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程

对话式安装

帮我安装 DeepSeek Harness 插件 deepseek-ai/deepseek-harness/packages/core/scope:先查看仓库 https://github.com/deepseek-ai/deepseek-harness 确认安全性,然后执行安装命令并验证插件加载成功。

把这段指令粘贴给 DSH Web GUI 里的助手,由它代你完成安装与验证。

English | 中文

Scoped registration primitive. createScope(ctx, key) creates a tagged Cordis context whose backing fiber owns every registration made through it. scopeOf(ctx) reads the tag, and scopeTarget(base, key) routes scoped events to listeners with the same key while leaving unscoped listeners global. Keys form an optional parent chain (bindScopeParent): registration views inherit DOWN it — a child scope sees its ancestors' layers, nearest shadowing farthest — and event admission extends UP it — a listener tagged with an ancestor receives a descendant key's events, never the reverse. The agent loop creates one scope per live agent and an agent preset's standing mount is a parent scope over its agents, but the mechanism is key-agnostic so lower-level packages can use it without depending on either.

Public API

  • createScope(ctx: Context, key: ScopeKey, options?): Scope Mint a scope under ctx's fiber. Usable synchronously (effect collection is uid-gated; service resolution falls through to the minting plugin's dependency surface). The typed, same-process key is trusted; an inactive minting context still fails through Cordis (INACTIVE_EFFECT). options.parent binds the enclosing scope via bindScopeParent before the scope is usable; the binding stays internal.
  • bindScopeParent(key, parent): ScopeParentBinding / scopeParentOf(key) / scopeChainOf(key) The parent relation behind both chain directions. Binding is once: a key that already has a parent throws, and only the returned binding's rebind(parent) may re-link it — the blank-session recompose operation, valid only while nothing produced under the old parent is retained (the holder's contract — this relation cannot see what a session logged). Both the bind and every rebind reject a link closing a cycle. scopeChainOf returns [key, parent, …] nearest-first.
  • Scope.ctx The tagged context: registrations through it are scope-visible AND scope-lifetime. Derived contexts (an extend, a fiber mounted under it) inherit the tag; nested scopes shadow (nearest tag wins).
  • Scope.rawDispose The EXACT Cordis disposer for the backing fiber — a composite (generator) effect yields THIS function to nest the scope's teardown at that yield position (Cordis dedupes nested effects by function identity; yielding a wrapper leaves the scope disposing as a concurrent sibling).
  • Scope.dispose(): Promise<void> Idempotent, shared quiescence boundary for every registration made through the scope. Racing/repeat calls await the same teardown, including when rawDispose invoked the underlying single-shot Cordis disposer first.
  • scopeOf(ctx: Context): ScopeKey | undefined The tag a context (or any context derived from it) carries; undefined = context-global.
  • scopeTarget(base: T, key: ScopeKey | undefined): Scoped<T> Build the opaque dispatch thisArg for a scope-filtered event. It composes base's existing Context.filter with the scope predicate (untagged listener ⇒ admitted; tagged ⇒ admitted iff its tag is the key or an ancestor of it; key === undefined ⇒ untagged only). The carrier contains routing state only; the real subject is carried by the event arguments. { global: true } listeners bypass filtering (Cordis semantics).
  • Scoped<T> The compile-time opaque carrier brand: scope-filtered events demand it as their this type, so dispatching with a bare subject is a compile error. The type parameter records the subject type but does not expose its properties.
  • isScopeCarrier(value) / carrierKeyOf(value) Runtime carrier marks, used by the dev invariants to assert every scope-filtered dispatch carries a carrier keyed to the subject its arguments name.
  • ScopeLayer Aggregate contract for one registry's complete global or exact-scope contribution; isEmpty() controls scoped-layer reclamation.
  • ScopedLayers<L> Own one eager global layer and lazy exact-scope layers. peek() never creates and stays chain-blind (a scope's OWN contributions — restrictions, guards — must not silently pick up an ancestor's), chainLayers() returns existing overlays farthest-ancestor-first, merge() materializes insertion-ordered named shadows along the chain, and effect() derives visibility and ownership from the same context while returning the exact Cordis disposer.
  • NamedEntries<V> Insertion-ordered named storage with caller-owned duplicate diagnostics, lookup, and live iteration within one nonempty table generation; draining the table detaches existing iterators from later insertions, and insert() returns an idempotent exact-entry undo.
  • AnonymousEntries<V> Insertion-ordered anonymous storage whose unique internal keys keep equal values as independent registrations; it uses the same drained-generation iterator boundary, and append() returns an idempotent exact-entry undo.

The optional @deepseek-ai/dsh-scope/invariant companion owns that runtime assertion. It uses the generated scoped-events.generated.ts resolver map to require a carrier for every declared scoped event and, when the payload exposes its routing subject, require identity with the carrier key. The Program-backed generator derives the map from event declarations and real scopeTarget(base, key) calls.

Design contract

The registration context determines both visibility and ownership, preventing a registration from being visible in one scope but disposed with another. Scopes route trusted same-process plugins; they are not sandboxes or authority boundaries. See the agent-scope Agent Note for rationale and security non-goals.

Scope-aware services define a concrete ScopeLayer that aggregates their heterogeneous tables and domain helpers. ScopedLayers.effect() accepts one synchronous action returning one synchronous undo, installs that undo before optional notification, and reclaims an exact-scope layer only when the complete aggregate is empty. notify defaults to true; the supplied callback owns whether observer failures throw or are contained. EntryValues remains internal, the storage classes are imported from the package root rather than a /store subpath, and the shared storage does not define registry-specific filtering or iteration policy. See the shared scoped-layer storage Agent Note.

Handing out a scoped context hands out the minting plugin's service-resolution API (resolution walks the minting fiber's dependency chain, not the holder's) — mint it from the plugin whose dependencies the scoped registrations need to resolve.

Known Limitations and Deferred Work

  • Only scope-aware APIs isolate state — registries must file by scopeOf() and events must dispatch through scopeTarget(); an arbitrary Cordis service remains context-global merely because it is called through a scoped context.
  • A context carries one nearest scope key — the hierarchy lives in the key-level parent relation, not in context tags; nested scope CONTEXTS still shadow to a single tag, and multi-membership policy sets remain unsupported.
  • Service reachability comes from the scope minter — handing out Scope.ctx also hands out the minting plugin's injected services, so a broader minter cannot later be narrowed by the holder.

收录徽章

Listed on deepseek-plugin.org
[![Listed on deepseek-plugin.org](https://img.shields.io/badge/listed_on-deepseek--plugin.org-007EC6)](https://deepseek-plugin.org/plugins/deepseek-ai/deepseek-harness/packages/core/scope)

把这段 markdown 粘贴到你的 GitHub README,链接回本插件详情页。徽章只声明已被本站收录,不代表安全认证。

返回插件目录