@gencode/plugin-sdk
v0.1.0
Published
`@gencode/plugin-sdk` 提供插件作者需要的稳定类型导出,避免直接依赖 `@gencode/agents` 的内部文件路径。
Readme
@gencode/plugin-sdk
@gencode/plugin-sdk 提供插件作者需要的稳定类型导出,避免直接依赖 @gencode/agents 的内部文件路径。
最小插件结构
my-plugin/
├── aimax.plugin.json
└── index.tsaimax.plugin.json:
{
"id": "progress-demo",
"name": "Progress Demo",
"description": "Demonstrates custom plugin progress events.",
"version": "0.0.1",
"configSchema": {}
}index.ts:
import type { PluginApi } from "@gencode/plugin-sdk";
export default function register(api: PluginApi) {
const progress = api.createProgressEmitter();
api.registerHook("session_start", async () => {
await progress.emit({
name: "progress_demo.session.started",
label: "Plugin initialized",
data: { source: "hook" },
});
});
}createProgressEmitter
插件现在可以通过 api.createProgressEmitter() 主动发出自定义进度事件。
const progress = api.createProgressEmitter();
await progress.emit({
name: "my_plugin.step.completed",
label: "Downloaded source data",
data: {
step: "download",
itemCount: 12,
},
});事件字段说明:
name: 稳定事件名,建议使用plugin_namespace.action或plugin.namespace.actionlabel: 面向用户的短文案,可直接展示在 Web/H5 时间线data: JSON-safe 结构化扩展字段,供 callback/WebSocket 消费方解析
运行时约束:
- 只能在插件运行期调用,例如 hook handler 或 plugin tool 的
execute - 不能在模块顶层或
register()执行期间直接调用 pluginId、sessionId、messageId、depth由框架注入,插件不需要也不能手工传入
事件流向
插件发出的 custom progress event 会复用现有的统一事件链路:
@gencode/agents产出AgentProgressEvent { type: "custom", ... }@gencode/cli继续通过 stdout / HTTP callback / websocket 分发@gencode/console将其映射为agent.custom
stdout 文本输出示例:
[plugin:progress-demo] progress_demo.task.completed Task completed {"step":"run","count":3}WebSocket / callback 中的事件载荷核心字段:
{
"type": "custom",
"pluginId": "progress-demo",
"name": "progress_demo.task.completed",
"label": "Task completed",
"data": {
"step": "run",
"count": 3
}
}建议
- 优先让
name稳定,便于服务端和前端做事件聚合 label保持短句,适合直接展示data放结构化上下文,不要塞大段文本- 如果一个插件需要多个阶段事件,建议统一前缀,例如:
progress_demo.fetch.startedprogress_demo.fetch.completedprogress_demo.report.generated
registerUiTool
插件可以通过 api.registerUiTool() 注册 UI 工具,让 LLM 在需要时暂停 agent 并通过前端表单收集结构化用户输入。
与 registerTool 不同,插件只需要提供 schema 和基本元数据。inputSchema 用于定义 LLM 调用这个工具时的参数结构,outputSchema 用于定义前端要渲染和回传的表单结构;session 绑定、暂停/恢复流程、结果校验都由框架自动完成。
基本用法
import type { PluginApi } from "@gencode/plugin-sdk";
export default function register(api: PluginApi) {
api.registerUiTool({
name: "collect_feedback",
label: "收集用户反馈",
description: "Collect structured feedback from the user.",
outputSchema: {
type: "object",
title: "用户反馈",
properties: {
rating: { type: "number", title: "评分" },
comment: { type: "string", title: "评论" },
},
required: ["rating"],
},
extra: {
ui: {
fields: {
comment: { widget: "textarea" },
},
},
},
});
}JSON Schema 约定
outputSchema 遵循 JSON Schema 风格的对象定义,推荐至少提供:
| 字段 | 说明 |
|------|------|
| type: "object" | UI tool 顶层 schema 必填 |
| properties | 表单字段定义 |
| required | 必填字段列表 |
| description | 表单或字段说明 |
| default | 默认值 |
| enum | 单选枚举 |
| format: "date" | 日期输入 |
| items.enum | 多选枚举数组 |
示例:
outputSchema: {
type: "object",
title: "报告参数",
properties: {
format: {
type: "string",
title: "输出格式",
enum: ["markdown", "pdf", "html"],
},
tags: {
type: "array",
title: "标签",
items: {
type: "string",
enum: ["tech", "ops", "finance"],
},
},
startDate: {
type: "string",
title: "开始日期",
format: "date",
},
},
}extra 扩展字段
有些场景不适合完全按 JSON Schema 的标准字段渲染。此时可以通过 extra 透传一份 JSON-safe 扩展配置给前端,由接入方自行解释并渲染更高自定义的 UI。
api.registerUiTool({
name: "custom_summary_form",
label: "自定义摘要表单",
description: "Collect values through a custom-rendered panel.",
outputSchema: {
type: "object",
title: "摘要确认",
properties: {
approved: { type: "boolean", title: "是否确认" },
comment: { type: "string", title: "补充说明" },
},
required: ["approved"],
},
extra: {
renderer: "summary-card",
sections: [
{ kind: "markdown", title: "摘要", content: "这里放自定义摘要内容" },
],
ui: {
fields: {
comment: { widget: "textarea", placeholder: "可选补充说明" },
},
},
},
});约束:
extra只负责扩展前端渲染,不参与内置 JSON Schema 校验- 用户提交结果仍然统一回传到
values - 建议保持 JSON-safe,避免函数、类实例或不可序列化对象
推荐把非标准控件语义放在 extra.ui.fields 中,例如:
widget: "textarea"widget: "file"placeholderenumLabels
自定义校验
内置 JSON Schema 校验之外,可以提供 validate 函数做跨字段校验:
api.registerUiTool({
name: "date_range",
label: "选择日期范围",
description: "Collect a date range with cross-field validation.",
outputSchema: {
type: "object",
title: "日期范围",
properties: {
startDate: { type: "string", title: "开始日期", format: "date" },
endDate: { type: "string", title: "结束日期", format: "date" },
},
required: ["startDate", "endDate"],
},
validate(values) {
const start = values.startDate as string;
const end = values.endDate as string;
if (start && end && start > end) {
return {
valid: false,
errors: [{ field: "endDate", message: "结束日期不能早于开始日期。" }],
};
}
return { valid: true };
},
});validate 支持同步和异步返回。内置校验失败时不会调用 validate。
与 registerTool 的区别
| 维度 | registerTool | registerUiTool |
|------|---------------|-----------------|
| 用途 | 通用工具 | 收集前端表单输入 |
| 开发者关注点 | 完整 execute 实现 | 只需提供 inputSchema? + outputSchema + 元数据 |
| sessionId | 需要自行处理 | 框架自动绑定 |
| 暂停/恢复 | 需要手动抛出信号 | 框架自动处理 |
| 结果校验 | 自行实现 | 内置 JSON Schema 校验 + 可选自定义 validator |
运行流程
- LLM 调用 UI 工具 → 自动抛出
UiToolPauseSignal暂停 agent - Runner 捕获信号,发出
ui_tool_request进度事件(包含outputSchema) - 前端根据
outputSchema渲染标准表单,或结合extra渲染自定义 UI - 用户提交后,CLI 带
uiToolResume重新调用 agent - 框架自动校验数据(内置 + 自定义 validator)
- 校验通过 → 工具返回提交值给 LLM → agent 继续执行
导出类型
@gencode/plugin-sdk 导出以下 UI 工具相关类型:
PluginUiToolDescriptor—registerUiTool的参数类型PluginUiToolOptions— 可选参数(如optional)UiToolInputSchema— LLM 调用参数 schemaUiToolOutputSchema/UiToolSchema— 表单 schemaUiToolJsonSchemaProperty— 单个字段的 JSON Schema 定义UiToolJsonSchemaType— 支持的 JSON SchematypeUiToolExtra— 前端扩展渲染负载UiToolValidationError— 校验错误UiToolValidationResult— 校验结果
