
KiloCode Plugin V2 Effect API用 Effect 为 Agent 领域注册 Transform 与 Runtime 钩子【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocodeKiloCode 的kilocode/plugin包提供了一套基于 Effect 体系的 V2 插件 API插件通过PluginContext在 OpenCode 的扩展点上安装行为Transform 钩子参与有状态领域agent、catalog、command、integration、reference、skill的重建Runtime 钩子拦截实时操作如 AI SDK 的 sdk/language 创建。读完本文你可以掌握插件定义与生命周期管理、Transform/Runtime 钩子的注册语义与重建顺序以及领域重载reload的触发方式并理解其背后的类型契约PluginContext、Hooks、Registration。定位与边界两个进程内能力V2 Effect 插件 API 明确为插件提供两个进程内in-process能力见 READMEhook在 OpenCode 的扩展点上安装行为reload针对某个有状态领域重新运行全部 transform 钩子。同时文档明确了一个边界公开的 server 客户端public server client会单独暴露目前刻意不纳入PluginContext。也就是说当前 Effect API 面向的是宿主进程内部的领域构建与运行时拦截而不是跨进程的远程调用。插件包的版本与入口可以从 package.json 确认包名kilocode/plugin当前版本 7.6.0Effect API 通过子路径导出exports: { .: ./src/index.ts, ./v2/effect: ./src/v2/effect/index.ts, ./v2/effect/integration: ./src/v2/effect/integration.ts, ./v2/effect/plugin: ./src/v2/effect/plugin.ts, ./v2/promise: ./src/v2/promise/index.ts }其中./v2/effect子路径即文档中import { define } from kilocode/plugin/v2/effect的落点其导出内容index.ts只有三项export type { PluginContext } from ./context.js export { define } from ./plugin.js export type { Plugin } from ./plugin.js定义插件define与 Effect 安装函数插件由define构造包含id与effect两个字段import { define } from kilocode/plugin/v2/effect import { Effect } from effect export const Plugin define({ id: example, effect: Effect.fn(function* (ctx) { yield* ctx.catalog.transform((catalog) { catalog.provider.update(example, (provider) { provider.name Example }) }) }), })三个关键语义均来自 README命令式注册插件的effect在运行中“命令式地”安装钩子它不返回任何钩子对象——副作用本身就是交付物配置入口为该插件提供的配置以ctx.options的形式可用PluginOptions即Recordstring, unknown定义于 options.ts作用域所有权所有注册项归插件作用域scope所有。作用域关闭时注册项被自动移除也可以通过注册项返回的dispose提前移除。从源码结构看plugin.ts 给出了精确的类型契约export interface PluginR Scope.Scope { readonly id: string readonly effect: (context: PluginContext) Effect.Effectvoid, never, R } export function defineR Scope.Scope(plugin: PluginR) { return plugin }即effect必须是一个以PluginContext为参数、返回Effectvoid, never, R的函数——无失败通道never资源需求默认是Scope.Scope。define本身是恒等函数价值在于提供类型收窄与自文档化。同一个文件还定义了宿主侧的PluginDomain接口export interface PluginDomain { readonly add: (plugin: Plugin) Effect.Effectvoid readonly remove: (id: string) Effect.Effectvoid }这与PluginContext中的plugin字段对应说明宿主可以通过ctx.plugin.add(plugin)/ctx.plugin.remove(id)在运行时动态装卸插件。PluginContext的完整字段context.ts为export interface PluginContext { readonly options: PluginOptions readonly agent: AgentHooks Reload readonly aisdk: AISDKHooks readonly catalog: CatalogHooks Reload readonly command: CommandHooks Reload readonly integration: IntegrationHooks Reload readonly plugin: PluginDomain readonly reference: ReferenceHooks Reload readonly skill: SkillHooks Reload }可以看到六个有状态领域agent/catalog/command/integration/reference/skill都附带Reload能力而aisdk是纯 runtime 钩子集合不携带重载语义。Transform 钩子参与有状态领域的重建Transform 钩子作用于“有状态领域”。注册方式是向对应领域的命名空间传入一个改造函数例如更新 agent 列表中的某一项yield * ctx.agent.transform((agent) { agent.update(reviewer, (item) { item.description Reviews code for regressions item.mode subagent }) })重建rebuild语义是这里的核心OpenCode 在任何一个 transform 被注册或被释放disposed时都会重建该领域重建从全新的领域状态出发按注册顺序依次运行所有当前活跃的 transform。这保证了最终状态是所有活跃 transform 叠加后的结果而不存在增量合并的歧义。可用领域与命名空间与 README 一致ctx.agent.transform ctx.catalog.transform ctx.command.transform ctx.integration.transform ctx.reference.transform ctx.skill.transform各领域的“草稿”Draft对象提供了 list/get/update/remove 级别的读写面。以 catalog.ts 为例export interface CatalogDraft { readonly provider: { list(): readonly CatalogProviderRecord[] get(providerID: string): CatalogProviderRecord | undefined update(providerID: string, update: (provider: ProviderV2Info) void): void remove(providerID: string): void } readonly model: { get(providerID: string, modelID: string): ModelV2Info | undefined update(providerID: string, modelID: string, update: (model: ModelV2Info) void): void remove(providerID: string, modelID: string): void readonly default: { get(): { providerID: string; modelID: string } | undefined set(providerID: string, modelID: string): void } } }即 catalog 的 transform 不仅可以按 providerID 修改/删除 provider 记录CatalogProviderRecord携带provider信息与models映射还可以修改具体模型、设置默认模型model.default.set。agent.ts 的AgentDraft额外提供default(id?: string): void用于指定默认 agentcommand.ts 的CommandDraft则以name为键提供与 agent 一致的 list/get/update/remove 面。Runtime 钩子拦截实时操作而非重建状态Runtime 钩子不改动领域状态而是拦截“正在发生”的操作。aisdk命名空间下有两个钩子事件结构见 aisdk.tsyield * ctx.aisdk.sdk( Effect.fn(function* (event) { if (event.package ! ai-sdk/xai) return const mod yield* Effect.promise(() import(ai-sdk/xai)) event.sdk mod.createXai(event.options) }), ) yield * ctx.aisdk.language((event) { if (event.model.providerID ! xai) return event.language event.sdk.responses(event.model.api.id) })两个事件契约sdk事件{ model, package, options, sdk? }——回调可替换event.sdk例如按包名动态加载ai-sdk/xai并创建对应 SDK 实例。由于加载是异步的回调本身可以是 Effect示例中用Effect.promise包装动态importlanguage事件{ model, sdk, options, language? }——在 sdk 就绪后构造LanguageModelV3实例类型来自ai-sdk/provider回调为同步函数。执行顺序上README 明确规定钩子按注册顺序顺序执行后注册的钩子可以观察到前一个钩子所做的修改例如language钩子能读到sdk钩子写入的event.sdk。重载领域数据变化后的显式刷新当 transform 捕获的外部数据发生变化时插件应主动重载受影响的领域let data yield* loadCatalog() yield* ctx.catalog.transform((catalog) { applyCatalog(data, catalog) }) data yield* loadCatalog() yield* ctx.catalog.reload()要点reload 归属于领域而不是某个单独的注册项。ctx.catalog.reload()会重跑所有活跃的 catalog transform并发布重建后的 catalog。可用的重载操作与 transform 领域一一对应ctx.agent.reload() ctx.catalog.reload() ctx.command.reload() ctx.integration.reload() ctx.reference.reload() ctx.skill.reload()从类型层面看这一能力由 registration.ts 中的Reload接口承载reload: () Effect.Effectvoid并由context.ts将其交叉进每个领域钩子类型。类型契约速览理解这套 API 只需掌握 registration.ts 中的三个基础类型export interface Registration { readonly dispose: Effect.Effectvoid } export interface Reload { readonly reload: () Effect.Effectvoid } export type HooksSpec { readonly [Name in keyof Spec]: ( callback: (input: Spec[Name]) Effect.Effectvoid | void, ) Effect.EffectRegistration, never, Scope.Scope }每个钩子的注册函数如ctx.agent.transform接收一个回调返回EffectRegistration, never, Scope.Scope即注册动作是随作用域生效的 EffectRegistration.dispose兑现了 README 中“注册项可提前移除”的承诺且与 scope 自动清理互补回调既可以是 Effect支持异步副作用如aisdk.sdk中动态 import也可以是普通同步函数如aisdk.language类型上统一为Effectvoid | void。小结KiloCodekilocode/plugin的 V2 Effect API 以PluginContext为唯一入口把插件行为划分为两类面向有状态领域的Transform 钩子注册/释放即触发“从全新状态按序重放”的重建可用ctx.domain.reload()显式刷新与面向实时操作的Runtime 钩子aisdk.sdk/aisdk.language按注册顺序串联、可相互观察修改。注册的生命周期完全由 Effect 的 Scope 机制托管——scope 关闭自动清理dispose支持提前移除。若你更习惯非 Effect 风格同一包还导出了kilocode/plugin/v2/promise子路径的 Promise 版 API见 package.json 的 exports而 Effect 版类型定义可直接在 packages/plugin/src/v2/effect/ 下逐文件阅读。【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考