Skip to content

Latest commit

 

History

History
144 lines (100 loc) · 6.26 KB

File metadata and controls

144 lines (100 loc) · 6.26 KB

给 dsh 的需求:工具注册来源

提出方:dsh-insight(配置与插件树洞察面板) 结论:当前 dsh 无法回答「这个工具是谁注册的」,缺一个字段。

想解决的问题

用户在 Insight 面板里问的原话:

我想以工具名为切面。可能一个插件或者一个服务会提供很多个子工具,我想列出所有的工具。 可能很多个工具来自同一个插件来自同一个服务,我也想追踪是谁提供、谁开启、谁禁用的这个工具。

这是个很合理的诉求——用户要决定「哪些工具留、哪些关掉」,就得先看到工具清单, 以及每个工具背后是哪个插件(因为关工具的动作是关插件)。

实测:现在拿不到

dsh 0.1.1-rc.2 的运行实例上,从一个装载中的插件的 Context 探测:

探测 结果
ctx.get('tools').schemas() 0
tools.layers.global.tools.data Map(0)
tools.layers.scoped Map(0)
ctx.get('skills').list() []
ctx.get('commands').list() 4(这个能读)
ctx.get('agents').list() []
agentPresets.serviceFor(presetId) 抛错——要的是真实 scope 对象(Symbol(dsh.scope)),不是 id
agentPresets.mount(agentCtx, id) 签名要 agent 的 scope Context

两个结论:

  1. 工具只在 agent 活着时存在。 它们不是插件加载时注册的,是 agent 实例化、 预设被 mount 成一个 scope 之后才 tools.register() 进去的。静止的 harness 里 全局层和 scoped 层都是空的。
  2. 即使读到工具,也反查不回插件。 ToolDefinition 继承 ToolSchema,字段是 name / description / input / output / execute / timeoutMs / …——没有任何一项 记录是谁注册的

第 2 条是关键:第 1 条还能靠「面板打开时若有活跃 agent 就快照」绕过,第 2 条绕不过。

请求

在工具注册时记录注册者,并在读取面上暴露出来。

最小改动:ToolRuntime.register(definition) 内部已经能拿到调用方的 Context / fiber,把它对应的 loader entry id 记进注册表条目,并在 schemas() / get() 的返回上带出来。

interface ToolSchema {
  name: string
  description: string
  // …

  /** 注册它的 loader entry 完整 id,如 include:agent-presets:tool-bash。 */
  readonly registrant?: string
}

registrant 只读、可选(拿不到时不填),不影响任何现有调用方。

有了它之后能做什么

  • 工具清单按工具名列,而不是按插件列——一个插件注册多个工具时能看清细分
  • 每个工具能追到注册它的插件,进而追到「哪一层配置把这个插件插进来的、哪一层 把它关掉的」(这两段 Insight 已经有了,只差 tool → plugin 这一跳)
  • 「关掉某个工具」这个动作能给出确切的落点:禁用哪个 entry

次要请求(可选)

让工具在没有活跃 agent 时也可枚举。 比如 agentPresets 暴露一个纯查询接口, 返回「某个预设会挂载哪些工具插件 / 工具」,不需要真的 mount 出一个 scope。

这条不如上一条要紧——面板可以退而求其次,只在有活跃会话时展示运行时快照。 但如果有,就能回答「我换到 PTC 模式会多出哪些工具」这类问题,而不必先切过去试。

当前的替代方案(已实现)

在拿到上面的字段之前,Insight 面板里做的是插件粒度

  • 一行一个工具插件tool-* 的 loader entry),22 条
  • 同名两份归并(宿主面一份常被 bundle 层禁用、agent 预设 realm 里一份活着), 给出净状态;两份不一致时明确标出来——单看任一份都会得出错误结论
  • 每条能追到:来源包、磁盘路径、哪一层插入、哪一层禁用
  • 界面上写明这一层的边界,不假装是工具清单

够用来做「关掉哪些」的决策,但粒度是插件不是工具。


追加:设置命名空间的注册来源

同一个字段缺口,出现在 settings 上。

现象

Insight 面板列插件时,有两行没有包名,看着像坏掉的插件:

ns 实际情况
shell 设置归 include:shell-env 插件,但 ns 叫 shell、entry 短 id 叫 shell-env,对不上
ui-onboarding ctx.loader.entries() 里根本没有这个 entry——注册它的是客户端插件

实测:现在拿不到

dsh 0.1.1-rc.2FileSettingsProvider(基类 SettingsProvider):

探测 结果
settings.describe({ redactSecrets: true }) 每行的字段 ns / schema / value / revision / base / user / applies / secrets
settings.registrations 里每条注册的字段 ns / schema / base / applies / resolved / revision / watchers
applies 全是 'live',是生效时机不是归属
装观察器包 SettingsProvider.register 来不及——设置在插件加载时注册,dsh-insight 排在后面,绝大多数已经注册完了
从 fiber 反查 settings.register("shell") 那个 effect fiber._disposablesDisposableList,里面是没有标签的裸函数,取不回

工具那边至少还能靠「注册发生在 agent 跑起来之后」旁听到;设置这边连这条路都没有。

请求

register() 内部已经拿着调用方的 Contextthis.ctx.effect(...) 用的就是它), 把它对应的 loader entry id 记进 registration,并在 describe() 的返回上带出来:

interface SettingsDescription {
  ns: string
  // …
  /** 注册它的 loader entry 完整 id,如 include:shell-env;客户端注册的没有。 */
  readonly registrant?: string
}

只读、可选,不影响任何现有调用方。

有了它之后

  • 设置能挂回它的插件,不再有「没有包名的插件行」
  • 「我改过的这条设置属于哪个插件、那个插件是哪一层插进来的」能一路追下去
  • 客户端注册的(registrant 为空)可以据实标成「客户端」,而不是和前一种混在一起

当前的替代方案(已实现)

不猜。挂不上的如实标成「设置命名空间」,并说清两种成因——设置本身是真的, 改动照样生效,只是追不到注册者。