架构中的插件
第一方 IPlugin 通过 PluginContribution 贡献工具、聊天客户端工厂、审批分类器或斜杠命令。工具贡献总有文本 ITool 实现,并可以额外提供同一定义名称的 IMultimodalTool;后者可以返回通用图像内容,同时复用宿主审批策略。内置工具和全部六种线路协议适配器通过该表注册。PluginRegistry 不从磁盘加载程序集。
磁盘插件使用 CrystalCode.Plugins.IPlugin,由 PluginCatalog 从 plugins/ 加载。每个插件一个不可收集的加载上下文。共享契约(Crystal、Crystal.Tools、CrystalCode.Tools 和 CrystalCode.Plugins)来自宿主。CrystalCode、CrystalCode.Engine、CrystalCode.Display 和 CrystalCode.Providers 被拒绝。磁盘插件可以贡献工具,实现 IHostTool 或 IHostMultimodalTool 的工具在该调用开始时收到捕获的 ToolHostContext。它也可以贡献内置适配器不拥有的协议的协议工厂、未知工具的分类器、不复用内置动词的斜杠命令、钩子、原始钩子和提示占位符。插件、外部工具和技能加载之后,宿主用只读快照调用 IPlugin.Attach,并在这些目录重新加载时再次调用。快照列出已发现的插件目录和外部工具集(目录、来源、enabled、effective、loaded 和跳过原因)、已加载的外部工具(名称、集合、来源,以及 Plan 或 Work),以及 skill 工具可以加载的技能(名称和描述)。它包括调用方插件。它不包括程序集路径、清单路径、内置工具或技能文件正文。Contribute 在该快照存在之前运行。快照不启用、禁用、调用或重排其他插件或工具。宿主随后用会话模型和审查模型的活动视图调用 IPlugin.AttachSession。每次读取返回当前提供商、协议、模型名称、上下文窗口、最大 token、temperature、top-p、图像输入、声明的音频输入、声明的视频输入、思考档位,以及已设置时的所选服务层级和速度。插件模型的 ContextWindow 描述配置的最大模型能力,不是所选的本地会话预算。/context 不改变该能力值;界面和用量事件携带压缩和 /status 使用的所选预算。音频和视频标志复制模型条目。它们不使回合、工具结果或插件多模态客户端接受音频或视频。Review.Independent 只在审查使用自己的模型时为 true。它为 false 时,审查使用会话模型,已存储的选择被省略。该视图没有 API 密钥、端点、组织或项目。实现 IPluginModelClient 的插件还收到 AttachClients。宿主在加载时点名该插件。这些客户端是同一提供商和模型上的新实例,与回合或审查内部的实例分开。实现 IPluginDataDirectory 的插件通过 AttachDataDirectories 收到其全局和项目运行时数据目录。至少接纳了一个状态字段的插件随后收到 AttachStatus。汇只接受该插件的名称。之后的目录重新加载会退役先前的汇。工具重新加载不会再次附加。失败的附加丢弃该汇已经发布的任何文本。PluginRegistry、外部工具集和 crystal run 不收到一个。IndependentReview 匹配审查标志。该标志为 false 时 Review 不存在。标志为 true 且审查客户端无法创建时,读取失败,会话客户端留在原地。宿主在重建会话客户端或审查客户端时,以及审查停止使用自己的模型时,丢弃旁路客户端。Contribute 在任一调用之前运行。PluginRegistry 不使用它们。占位符解析收到同一模型视图。钩子追加提示和压缩文本,在用户消息存储前改写它,并在不写入存档的情况下修订一次出站模型请求的文本,使模型能看到存档未存储的文本。钩子也读取一次返回的模型响应,而不改变宿主使用的候选,在审批再次运行前改写工具调用,替换工具结果,并提高审批风险或要求另一次提示。它们不降低风险,不跳过审批,也不替换 Work、Plan 或 Review。会话开始和结束跟随编码会话。/clear、/resume 和 /fork 结束正在离开的会话,并开始替换它的会话。工作区更改在正在离开的目录中结束,重新加载插件,并在新目录中开始。同时更改工作区的恢复是单次结束和开始。
权限按方法决定,带额外权限的方法位于自己的接口和自己的注册列表上。IPluginRawHook 就是该接口。插件把它注册在 PluginContribution.RawHooks 上,与 Hooks 分开,因此宿主可以分辨哪些插件持有特权方法,而无需检查类型。宿主用一条说明宣布每个这样的插件,且不向操作者询问任何内容。RebuildModelAsync 在普通模型钩子之前、按插件加载顺序运行。它以宿主能够表示的任何方式重建一次出站请求:丢弃、重排、添加或改写任何种类的项,包括活动系统提示。宿主只拒绝自己无法表示的内容:重复的项 id、空推理文本,或会话不持有的图像,这包括无法携带图像的请求上任何添加的图像(PluginModelRequest.AcceptsImages)。它不检查提供商有效性。work 或 plan 调用在原始钩子更改其请求后失败时,引擎写一条点名该钩子的谨慎说明。该重建不写入存档。
RewritePrompt 和 RewriteCompaction 在该文本的普通追加之后运行。每一个都可以用任意文本(包括空字符串)替换完整字符串。RewritePrompt 替换为 work、plan、review 和 compaction 组合的系统文本。Work 和 plan 文本成为活动系统消息并被存档;不写入 ~/.crystal/prompts 下的文件。RewriteCompaction 替换压缩用户提示,或被存储的摘要正文。它不选择折叠哪段历史。RewriteApproval 在普通审批钩子之后运行,可以设置任意风险、任意权限、任意摘要和任一提示要求。审批策略随后使用该分类。普通钩子仍然不能降低风险或替换那些提示。原始钩子不受这些规则约束。抛出的原始钩子被跳过。新的原始方法加入此接口,从不加入普通钩子。之后会在这些接口上添加更多钩子方法。它们不会预先保留为空类型。插件是否彼此声明依赖,以及相关的加载问题,仍在考虑中,尚未决定。
以后的模块系统可能位于插件和原始钩子之下,并通过反射或其他机制触及宿主。它不在此构建中。其行为和所有权未定义,此构建不为它预留类型。目录顺序是内置工具、磁盘插件工具、外部工具,然后是 skill。
/plugins enable|disable|show 和 /tools enable|disable|show 更改一个目录。home 或 project 选择树;否则项目目录优先。crystal plugins 和 crystal tools 在会话外做同样的清单编辑。默认输出是 Spectre.Console 表格。--format text 打印对齐的纯文本。它们在该清单上写入 enabled,不写入 config.json,也不启动会话。全局发现开关保持分开。
操作者工具集不是插件。它们从 tools/ 发现,并由 ExternalCatalog 包裹。exec 子进程从工作区根启动,并只在该进程上收到 CRYSTAL_WORKSPACE、CRYSTAL_SESSION、CRYSTAL_APPROVAL、CRYSTAL_TOOL_DATA 和 CRYSTAL_PROJECT_DATA。"output": "content" 从 stdout 读取一个 JSON 对象(text 和可选的围栏图像)。dotnet 集合只为该目录使用一个不可收集的 AssemblyLoadContext。共享契约类型(Crystal、Crystal.Tools、CrystalCode.Tools,以及已加载的 System.* / Microsoft.*)来自已经加载了 Crystal.Tools 的宿主上下文。公共 ITool 和 IMultimodalTool 实现被直接加载。一个类型可以实现任一契约或两者;实现两者时,两个定义必须匹配。一个类型也可以实现 IHostTool 或 IHostMultimodalTool。包装器随后传入该调用开始时捕获的 ToolHostContext(工作区根、会话 id、审批模式,以及该集合的全局和项目数据目录)。其他工具保留原来的 InvokeAsync。原生多模态工具只为支持图像的模型和提供商加入活动目录。外部工具加载器不实现 CrystalCode.Plugins.IPlugin,也不扫描 plugins/。
环境变量:
CRYSTAL_HOME覆盖数据目录。DEEPSEEK_API_KEY、OPENAI_API_KEY、GEMINI_API_KEY和CRYSTAL_API_KEY为该进程覆盖credentials.json。提供商特定的变量优先于CRYSTAL_API_KEY。持久存储是credentials.json。