AUTHORING

能力包开发指南

能力包 = 工具 + 声明:代码只提供工具与数据,界面是数据不是代码——小诺中枢按你的贡献声明渲染全部 UI。本页是完整开发文档,左侧菜单按章节导航。

概述:能力包 = 工具 + 声明

能力包不自带 UI,只注册工具 + 可选声明视图/命令;用户只通过小诺中枢的消息框交互,UI 由中枢统一渲染。一句话:插件带数据和意图进场,不带 UI 进场——用户在标准位置发现能力,用标准组件消费产出。

执行模型(L7 进程沙箱)

  • ctx 能力跨进程:getDocuments / getConfig / storage.* / readFile / writeFile / query / llm.complete / notify.show / insertQuestion 等返回 Promise,必须 await——同步调用会拿到 Promise 静默出错。
  • 注册类保持即发即忘:registerAgentTool / registerPanel / registerViewSchema / registerCommand / timers.setInterval / log 无需 await(激活完成前宿主保证注册已落账)。
  • require 白名单:仅 path / os / fs / fs/promises / util / url / events 与插件目录内相对文件;网络、子进程、worker 一律拒绝。
  • 挂死/崩溃自愈:activate 卡死超 90 秒或进程心跳 15 秒失联,会被宿主击杀并回滚,主进程不受影响——插件无需也不应自建看门狗。
  • 不要在模块顶层读 ctx:ctx 在 activate 时才注入,顶层读取拿不到值。

快速上手

两条路线:让小诺替你写(Agent 驱动闭环),或照最小骨架手写。

1

Agent 驱动:描述需求(方案模糊时小诺先列配方目录让你报编号选关)→ create_plugin 建骨架 → write_file 写代码与黄金用例(你逐行 Diff 审阅)→ test_plugin 试玩(临时沙箱出记分板,修订零打扰,全绿再落盘)→ ask_user 确认 → apply_plugin 激活。确认卡由统一确认总线托管(多项裁决排队逐张处理,新确认静默入队),且只出现在当前对话——新建/切换对话后回原对话处理。

2

手写:按下方骨架建目录 → 放入本地市场目录(resources/plugin-market 或 userData/plugin-market)→ 插件页一键安装。

plugins/my-pack/plugin.json
{
  "id": "my-pack",
  "name": "我的能力包",
  "version": "0.1.0",
  "description": "做什么",
  "usage": "给用户看的使用说明:怎么入口、说什么指令、典型场景。强烈建议填写。",
  "entry": "index.js",
  "permissions": ["agent:tools", "db:read", "file:write"],
  "agentTools": [{ "name": "my_tool" }],
  "views": [{ "name": "my_table" }]
}
plugins/my-pack/index.js
module.exports = {
  id: 'my-pack',
  name: '我的能力包',
  version: '0.1.0',
  description: '做什么',

  async activate(context) {
    // 声明视图(可选;须在 manifest "views" 登记——manifest 是唯一事实源)
    context.registerViewSchema({ name: 'my_table', description: '我的表格', schemaType: 'table' })

    // 注册工具(能力包与中枢交互的唯一通道;须在 manifest "agentTools" 登记)
    context.registerAgentTool(
      {
        name: 'my_tool',
        description: '工具描述(LLM 据此决定何时调用)',
        parameters: { type: 'object', properties: {}, required: [] }
      },
      async (args) => {
        const data = await doSomething(args)
        return {
          output: '给 LLM 看的文字结果',
          ui: { intent: 'show_view', view: { title: '我的表格', columns: [{ key: 'name', label: '名称' }], rows: data } }
        }
      }
    )
  },

  async deactivate() { /* 工具注销由 PluginHost 兜底 */ },
}

本地调试

shell
# 改 plugins/<your-pack>/ 源码后同步到本地插件目录(只复制,不激活)
node scripts/sync-plugins.mjs
# 然后在应用「插件」页打开开关激活

分发两轨:仓库 plugins/ 下的第一方能力包与宿主同仓演进、随应用发版,不走社区市场;社区插件源码不入主仓,推自己仓库打 knomi-plugin topic 被扫描发现。两轨互不混用。

manifest 规范

字段说明
id全局唯一标识,社区市场按 id 去重
name显示名
version语义化版本;git 来源插件在线更新按它对账
description一句话描述
usage给用户看的使用说明,强烈建议填写
entry入口 JS,导出 activate(context) 与 deactivate()
permissions权限声明——声明 ≠ 授予
contributions贡献声明:扩展点 + 槽位 + 视图
configSchema用户可配置项(设置面板自动生成表单)
agentTools注册的 Agent 工具名单——代码 registerAgentTool 必须在此登记,manifest 是唯一事实源(未登记属准入漏洞,宽限期告警)
views注册的视图名单——代码 registerViewSchema 必须在此登记(独立字段,不进 contributions)
depends依赖的其他插件 id 列表(见「发布与更新」)
platforms平台适用声明(win32/darwin/linux/android/ios/harmonyos)——缺省全平台;不在声明列表的平台插件页置灰且不可启用,市场不展示

工具与意图协议

用 registerAgentTool(def, executor) 注册工具(需 agent:tools 权限),LLM 在对话中按 description 决定调用。执行器返回 { output, error?, ui? }——output 给 LLM 看,ui 声明 UI 行为:渲染是确定性的,无需 LLM 措辞。

意图ui payload渲染行为
start_practice{ questions, title? }内容区切做题模式,逐题作答、判分/自评回流
show_view{ view }经别名适配进 Deck 单卡宿主渲染(表格/表单/图表全卡片词汇;notify/quiz 类归位为意图)
show_dashboard{ stats }跳转学习报告插件页(统计唯一承载于能力包页)
open_document{ filePath }编辑器打开文档
notify{ type, message }轻量通知
open_settings{ group?, section? }打开设置页指定节:group=顶级分组 key,section=节地址(如 agent-hooks)——深链激活对应 tab 并高亮到节
request_permission中枢自动生成权限卡(允许/仅本次/拒绝)
show_queue{ questions, title? }产出进入标准复习队列,按调度器到期呈现
show_in_editor_panel{ view, documentId? }注入编辑器「相关」侧板标准槽位
add_graph_menu{ label, action, nodeIdMatch? }图谱节点右键菜单追加标准项
add_stage_action{ stage, slot, label, view? }阶段标准位置的行动按钮
report_outcomePluginRunOutcome执行结果回传健康分,必调,缺失计失败

声明-执行绑定:UI 意图携带的 stage/point/slot 必须匹配本插件已通过激活校验的 contributions,否则中枢丢弃该意图并计入遥测。权限被拒时中枢自动把 PermissionDenied 转为 request_permission 权限卡,授权后自动重试原消息——插件无需处理。

贡献声明(contributions)

contributions[] 声明插件向哪个学习闭环阶段贡献什么能力——能力挂在哪里由平台决定:

plugin.json
{
  "id": "my-quiz-pack",
  "permissions": ["db:write", "ai:chat"],
  "contributions": [
    {
      "stage": "practice",              // 六阶段之一
      "point": "practice.generator",    // 扩展点 ID(ingest/organize/practice/feedback/schedule/view)
      "action": "生成选择题",            // 用户可见的能力名
      "view": "progress_list",          // 产出用什么标准组件呈现
      "slot": "review.empty_state"      // 平台挂载槽位
    }
  ]
}

标准槽位(v1)

标准槽位在应用中的位置(编号对应下表;2 号 collect.toolbar 已退役,采集入口归剪藏插件页):

编辑器
1editor.header_right
4organize.editor_panel
采集
2collect.toolbar
3collect.source_list
复习
6review.empty_state7review.queue
9feedback.card
分析
8analyze.board
82%
12
图谱
5organize.graph_menu
stageslot呈现位置策略
workspace1editor.header_right编辑器工具栏右端挂载条动作按钮(appliesTo 词汇挂载必填;每词汇平铺 ≤2、超出溢出「更多」)聚合
collect2collect.toolbar已退役(2026-09-28):渲染宿主随全局工作面去插件挂载移除,采集入口归剪藏插件页聚合
collect3collect.source_list剪藏/导入对话框「来源」列表聚合
organize4organize.editor_panel编辑器「相关」侧板聚合
organize5organize.graph_menu图谱节点右键菜单聚合
practice6review.empty_state复习页空态行动按钮聚合
practice7review.queue标准复习队列数据汇入
analyze8analyze.board学习中心看板标准卡位聚合
feedback9feedback.card做题卡片反馈区数据汇入

另有独占型扩展点(按 key 互斥):file.handler(知识库文件点击入口,key=扩展名/MIME)、schedule.strategy(全局单 key)。

放置三铁律(平台强制)

  • 入口聚合:能力只出现在所属阶段的标准槽位;禁止自建顶栏入口、侧边栏项、独立窗口。
  • 来源可见:插件贡献的内容统一带来源徽标,可发现可溯源,但不抢布局。
  • 行为一致:一切产出走统一反馈组件,一切可执行项走标准确认流。

校验规则:stage/point 必须指向已注册扩展点,view 必须在视图词汇表内,slot 必须属于该 stage——不合法拒绝激活并在插件页给出原因。

挂载形态

workspace.action:入口按钮 + 交互面板

编辑器工具栏右端的入口按钮是自包含声明:启用即出现、停用即消失;点击统一打开居中 Modal 操作窗口(与碎片确认卡同款窗口语义,历史 dock 停靠呈现已退役)。标准配方三步——manifest 声明入口(按钮 / panel id / appliesTo 词汇挂载)→ activate 里 registerPanel 声明面板(表单 schema)→ 实现 submit 方法。

1剪藏
网页地址
保存到
剪藏并打开
registerPanel
async activate(context) {
  context.registerPanel({
    id: 'my-clip.form',          // 面板 id(供 manifest 的 panel 字段引用)
    label: '网页剪藏',            // 弹窗/停靠标题
    surface: 'modal',            // 统一居中 Modal 窗口(dock 停靠已退役;历史声明兼容但渲染一律 Modal)
    view: 'form',                // 声明式视图(词汇表内)
    fields: [
      { key: 'url', label: '网页地址', type: 'text', required: true },
      { key: 'dirPath', label: '保存到', type: 'target_dir' },  // 平台语义字段
    ],
    submitLabel: '剪藏并打开',
    submit: 'saveClip',          // 提交调用的插件方法名(经 plugin:invoke)
  })
}

async saveClip(params) {
  const filePath = join(params.dirPath || fallback, 'clip.md')
  await this._ctx.writeFile(filePath, md)   // 需 file:write
  return { filePath, title: '剪藏结果' }     // 返回 filePath 自动打开文档
}
  • 声明-执行绑定:panel 引用必须是本插件已注册的面板,未注册的引用在槽位解析时被剥离(fail-closed + 审计)。
  • 聊天等平台级界面使用 builtin: 保留视图,第三方插件走声明式视图,不允许注入 React 组件。
  • 无 panel 声明的 workspace.action 回退为中枢消息路径(点击发一条「请使用 X Y」);同槽位按安装时间排序,每词汇默认平铺 ≤2、超出溢出「更多」。
  • appliesTo 词汇挂载必填(2026-09-28 起):workspace.action 必须声明 "appliesTo"(如 ["markdown", "any"]),入口仅在当前文档词汇命中时渲染;键为封闭集(六类文件类别 + question-set + any),表外值或缺失拒绝激活。全局能力不设挂载条入口,走 nav.entry 插件页承载。

tray.menu:托盘右键菜单

打开 Knownest 快速提问
今日晨报 退出
plugin.json
{ "contributions": [
  { "point": "tray.menu", "action": "今日晨报", "method": "generateFromTray" }
] }

method 必填(缺省拒绝激活)。返回契约 { ok?, docPath?, focus?, message?, error? }——主进程统一以系统通知反馈,docPath 存在时点击通知直达文档。命名建议用名词条目(如「今日晨报」),多态行为写进 usage 与通知文案。

shortcut.bind:全局快捷键

Ctrl+Alt+R立即检查复习提醒
plugin.json
{ "contributions": [
  { "point": "shortcut.bind", "key": "Ctrl+Alt+R", "action": "立即检查复习提醒", "method": "checkNowFromTray" }
] }

key 必填(Electron accelerator);被其他应用占用时该键跳过并审计——建议同插件声明多个备用键位。返回契约与 tray.menu 相同。壳层自带 Ctrl+Shift+K / Ctrl+Shift+V,避免声明这两个。

nav.entry:侧边栏导航页

学习报告插件
plugin.json
{ "contributions": [
  { "point": "nav.entry", "slot": "nav.sidebar", "key": "learning-report",
    "action": "学习报告", "method": "pageLearningReport" }
] }
  • slot: "nav.sidebar" 必填——漏写条目静默不聚合(已两次踩坑,激活期显式校验拒绝)。
  • key 必填:路由 slug(/pack/:pluginId/:key,全包唯一);method 为页数据入口,确定性、无 LLM。
  • v1 单表格页:columns/rows 之外可声明 openKey(行点击打开文档)、rowClick(行点击调方法)、filter(头部筛选下拉)、action(页级动作按钮,ui 意图就地执行)、rowAction(行级操作列,可 when 条件渲染)。
  • v2 复合页:cards 栅格组合 stats 指标卡 / chart 折线环图雷达(radar 为 v2.4 新增:多维雷达,highlight 可选标注主色轴)/ markdown 富文本 / table 表格卡(detail 行点击下钻、rowActions 行级动作、width+ellipsis 固定布局列宽、tones 状态色标)/ form 表单卡(markdown 结果卡,route/filePath 产出型打开)/ tabs 布局卡(页内多 Tab 分区组合各型卡片;placement 声明导航位置 top 横向/left 左侧竖排,窄档自动降级 top)。
  • 内容区分档自适应(v2.4,FR-159):渲染器按内容区容器实测宽分三档封闭词汇(narrow <680 / regular 680–1240 / wide >1240)——columns[].tier 声明 essential/extended,narrow 档只渲染 essential 列(零标注自动取前 2 列),regular 档至多 4 列;>1 个行级动作窄档收进「更多」。全部分档统一走 core/layout/content-region 唯一实现,禁止各视图自写阈值。
  • 注册视图卡 view(v2.4,FR-161):{ type: 'view', view: '<注册视图 id>', props?, height? }——导航树/图谱/文件树等密集画布以注册视图进场:词汇通道管结构与动作,画布本体仍是组件;视图注册表由宿主(app 层组合根)传入,未知视图忽略+warn。载荷侧只携带数据不携带函数,行为回调在注册表包装层绑定。首消费=知识体系三 Tab(knowledge.navigator / knowledge.graph)。
  • 页级动作确认闸 action.confirm(v2.4):action 声明 confirm?/confirmOkText? 后按钮套 Popconfirm——破坏性/高成本动作的页级闸(与 secondaryActions.confirm 同语义上移);首消费=知识体系「重建」。
  • 表格排序(v2.9,2026-10-01):columns[].sortable 声明可排序列(time/number/text/enum 类型化比较,引擎 core/utils/table-sort),enum 配 columns[].sortValues 声明显式值序(SQL ORDER BY FIELD 语义);orderBy(v1 顶层与 table 卡均可)以 SQL ORDER BY 多键语义声明默认序(用户点表头临时接管,取消后回到默认序);声明排序的表建议 rowKeyField 稳定行键。缺失/非法值恒排最后;排序只作用于已拉取数据窗口;不做表达式排序。
  • 表格勾选与平台意图动作(v2.10,2026-10-02):v1 载荷顶层 selection 声明渲染行复选框(勾选集=rowKeyField 业务键);action/secondaryActions 声明 intent 后为平台意图动作——点击不走插件,渲染器把勾选集以 { intent, questionIds } 分发宿主(空勾选告警不放行)。当前封闭词表=share.export:打开平台分享导出的题目模式(按题 id 优先导出、来源文档随包)。分享属平台能力(ADR-024),插件不经手导出文件通道;intent 与 confirm 互斥。
  • 页面型结果换页(v2.11,2026-10-03):次动作方法返回 DeckPayload 形态(顶层 columns 或 cards 数组,且无 message/ui/download/clipboard 等任何处置字段)时,渲染器交宿主 onSubPayload 就地换页为下钻管理页——宿主 setPayload 即得「下钻页 + 页头刷新=返回主页面」语义,无需独立返回栈;下钻页自身声明 secondaryActions(如「返回题库」)即回程动作。处置字段优先:带 message 的混合结果不换页(向后兼容);换页路径不走结果处置与刷新。首消费=quiz-maker「错题归因」归因表。
  • 数据资产契约(v2.12,2026-10-04):plugin.json 顶层声明 provides.data(数据资产:id/title/schema 摘要/method 确定性查询入口/events 变更信号/版本位)进入全局数据资产目录——能力地图「数据资产」Tab(默认视图)与 Agent 的 list_data_assets 工具都可发现;声明 consumes:[{asset:"<providerId>/<assetId>",optional}] 即数据粒度消费(optional=false 提供方未激活拒激活,true 优雅降级)。物模型三问:你有什么数据/你怎么被发现/你消费什么(替代旧的阶段归属必答)。契约详见 docs/PLUGIN-AUTHORING.md §19。
  • 资产事件与容忍契约(v2.12):context.emitAssetEvent(assetId) 在数据变更点发信号(只能 emit 自己 provides.data 声明的资产);context.onAssetEvent(asset, cb) 订阅(须已在 consumes 声明,未声明 fail-closed 拒绝/滤除)——事件只作刷新信号不载数据,收到后回拉目录标注的 method 取权威数据。资产间无外键无级联(provider-authoritative):源条目删除后下游存量保留,展示容忍悬空引用。
  • 跨能力包动作 capability(v2.3):action/rowAction 声明 capability="目标能力包 id" 后,渲染端经能力探测门控——目标插件未启用时点击出引导弹窗(不触达插件),就绪则调用目标插件方法;插件 ID 来自页面载荷数据(非代码字面量)。参考:学习报告页「开始今日练习 / 去练」。
  • 单渲染器统一(ADR-022,v2.5):工具 show_view 与插件页/右栏面板共用同一套 cards 词汇与同一渲染器(Deck)——schemaType 10 值转为别名兼容表自动映射(table→table 卡/form→form 卡/chart→chart 卡/stat_card→stats 卡/notify·quiz_card→意图归位),旧载荷零改动,新开发直接产出 cards。

agent.preset:贡献一个「人格」

小诺内置 出题官插件
plugin.json
{ "contributions": [
  { "point": "agent.preset", "key": "出题官", "action": "出题官",
    "routeHint": "出题/练习/测验类请求委派给我:…",
    "systemPrompt": "你是 Knownest 的出题官……(角色定义全文,必填)" }
] }

systemPrompt 必填非空(激活期拒绝空预设)。插件预设是只读资产:设置页不管理,仅会话下拉带「插件」标记;与用户/内置预设同名让位跳过。角色定位是「叠加细则」,不要复述平台纪律(如出题接地纪律的权威源在内置 persona)。

视图词汇(ADR-022 统一):一套卡片词汇

插件页/右栏面板/工具 show_view 全部走同一套 cards 卡片词汇(由公共渲染器 Deck 统一渲染);下表 10 个 schemaType 自 v2.5 起转为别名兼容表(注册校验保留、自动映射到对应卡片),新开发请直接产出 cards。组件视觉由中枢统一实现,插件不写 CSS。

每个视图组件独立一页:点左侧菜单,就能看到该组件的全部形态、必需字段与用法代码。

Table 表格

结构化列表 · 必需字段 columns, rows

文档标签
attention.md论文
transformer.md论文
rlhf.md笔记
用法
// cards 形态(推荐)
ui: { intent: 'show_view', view: { title: '文档列表', cards: [{ type: 'table', columns, rows }] } }
// 旧 schemaType 'table'(columns/rows 直映)自动兼容,零改动

Stat Card 指标卡

指标卡 · 必需字段 label, value

82%掌握度
12连续学习
7待复习
用法
// cards 形态(推荐)
ui: { intent: 'show_view', view: { cards: [{ type: 'stats', items: [{ label: '掌握度', value: '82%' }, …] }] } }
// 旧 schemaType 'stat_card' 自动映射;show_dashboard 意图是跳学习报告页({stats}),不是渲染指标卡

Progress List 进度条目(别名→table 卡)

进度/队列条目 · 必需字段 items[{label, state}]

已掌握32
复习中11
未学习9
用法
// cards 形态(推荐):进度条目即表格卡,state 列渲染 tones 色标
view: { cards: [{ type: 'table', columns: [{ key: 'label', label: '项目', tier: 'essential' }, { key: 'state', label: '状态' }], rows: [{ label: '已掌握', state: 'done' }, …] }] }
// 旧 schemaType 'progress_list'(items[{label,state}])自动映射

Quiz Card 做题卡(别名→start_practice 意图)

做题卡片内容 · 必需字段 question, type

单选

Attention 机制的本质是什么?

A加权求和
B循环记忆
C位置编码
填空

记忆的衰减遵循 曲线。

输入答案后提交,由做题界面统一判分
用法
ui: { intent: 'start_practice', questions: [{ id, type: 'single_choice' | 'cloze', question, options, answer, sourceSnippet }] }
// 做题卡无 cards 形态——旧 schemaType 'quiz_card' 自动归位为此意图

Source Quote 原文引用(别名→markdown 卡)

原文引用块(题目溯源) · 必需字段 snippet, documentId

注意力是一种对输入的加权求和机制……notes/attention.md · ¶12
用法
// cards 形态(推荐):原文引用 = markdown 卡引用块
view: { cards: [{ type: 'markdown', content: '> 真实原文片段\n> —— notes/attention.md · ¶12' }] }
// 旧 schemaType 'source_quote'(snippet, documentId)自动映射为引用块+来源行

Diff View 变更对比(别名→markdown 卡)

变更对比确认 · 必需字段 changes[]

"providers": {- "model": "gpt-3.5"+ "model": "deepseek-v4-pro" }
用法
// cards 形态(推荐):变更对比 = markdown 卡 diff 代码块(「确认」属平台写确认流)
view: { cards: [{ type: 'markdown', content: '```diff\n# config.json\n- before\n+ after\n```' }] }
// 旧 schemaType 'diff_view'(changes[{path,before,after}])自动转换

Action List 可执行项(别名→行动作声明)

可执行项列表 · 必需字段 items[{label, action}]

生成题目→
开始复习→
用法
// cards 形态(推荐):可执行项 = 表格卡 + 行动作
view: { cards: [{ type: 'table', columns: [{ key: 'label', label: '可执行项' }], rows: [{ label: '生成题目', action: 'generate' }], rowAction: { label: '执行', method: 'runAction', paramKey: 'action' } }] }
// 旧 schemaType 'action_list'(items[{label, action}])自动映射为表格卡+行动作

Chart 图表

图表 · 必需字段 data, chartType

折线 line
饼图 pie
雷达 radar(v2.4)
用法
// cards 形态(推荐)——注意字段名是 data,不是 series
view: { cards: [{ type: 'chart', chartType: 'line' | 'pie' | 'radar', data: [{ label, value }] }] }
// 旧 schemaType 'chart'(series)自动改名映射;radar 可加 highlight 标注主色轴

Form 表单

参数表单 · 必需字段 fields[]

网页地址
模式
快速▾
提交
用法
// cards 形态(推荐)
view: { cards: [{ type: 'form', title: '参数', fields: [{ key, label, type: 'text' | 'select' | 'target_dir' }], submitLabel: '提交' }] }
// 旧 schemaType 'form'(fields+submit)自动映射;交互面板场景用 registerPanel(见「挂载形态」)

Notify 轻通知(别名→notify 意图)

轻量通知 · 必需字段 type, message

已生成 5 道题并入题库
已开始后台索引文档
未配置 Provider,AI 功能不可用
Git 同步失败:检测到冲突
用法
ui: { intent: 'notify', type: 'success' | 'info' | 'warning' | 'error', message }
// 轻通知无 cards 形态——旧 schemaType 'notify' 自动归位为此意图

Tabs 布局卡

页内多 Tab 分区组合各型卡片(v2.2) · 必需字段 tabs[{key, title, cards}]

Tab A Tab B
42Tab A 内指标
用法
{ type: 'tabs', tabs: [
  { key: 'tabA', title: 'Tab A', cards: [...同 cards 词汇] },
  { key: 'tabB', title: 'Tab B', cards: [...] },
] }

Markdown 富文本

Markdown 渲染(与编辑器预览同管线) · 必需字段 content

Markdown 富文本卡渲染 加粗、斜体、代码 等全要素,与编辑器预览共用同一渲染管线。
用法
{ type: 'markdown', title: '标题', content: '**Markdown** 文本' }

View 注册视图(v2.4)

密集画布(导航树/图谱/文件树)以注册视图进场:词汇通道管结构与动作,画布本体仍是组件 · 必需字段 view

画布组件由宿主注册表提供(如 KnowledgeNavigator / GraphCanvas)
用法
{ type: 'view', view: 'knowledge.navigator', props: { focusNode: '...' }, height: 400 }

配置声明(configSchema)

在 plugin.json 声明配置数据结构,设置页由应用统一渲染表单——插件不写 UI。配置值持久化于 plugin_kv 的 config 命名空间(plugin_id 强制隔离,随快照迁移)。

plugin.json
"configSchema": [
  { "key": "endpoint", "label": "服务地址", "type": "string", "default": "https://api.example.com" },
  { "key": "mode", "label": "模式", "type": "select",
    "options": [{ "value": "fast", "label": "快速" }, { "value": "deep", "label": "深度" }] },
  { "key": "presets", "label": "预设", "type": "list",
    "item": [
      { "key": "id", "label": "ID", "type": "string", "required": true },
      { "key": "prompt", "label": "提示词", "type": "text", "required": true }
    ] }
]
  • 字段类型封闭集:string / text / number / boolean / select / list;select 必带非空 options,list 必带非空 item(条目仅标量,不可嵌套 list)。
  • 激活期校验,不合法拒绝激活(与 contributions 同门,fail-closed)。
  • 整存语义:设置页每次保存提交全量表单值;声明缺省在读取时合并。写入仅限已声明键(未声明键拒绝并审计);setConfig 需 db:write。
  • 读取 context.getConfig() 返回合并后全量值;onConfigChange(namespace, values) 回调让配置即时生效(失败仅审计)。

十种权限,声明 ≠ 授予

file:readfile:writedb:readdb:write agent:toolsai:chatnotifynet:http net:web plugin:manage 仅内置

未声明默认只读(file:read + db:read + agent:tools);激活时用户逐项确认:允许 / 仅本次 / 拒绝;被拒时中枢自动展示权限卡,授权后重试原消息。做题入库(insertQuestion)需 db:write,且 sourceSnippet 必填真实原文片段;作答回流由渲染进程 IPC 处理,能力包无需参与。

沙箱与限额

独立进程沙箱

require 白名单仅 path / os / fs / util / url / events,网络、子进程、worker 一律拒绝。

网络代理

唯一通道是 net:http 宿主代理:仅 http(s),20 秒超时、8MB 上限、全量审计。

资源限额

单插件最多 20 个工具;storage 每值 ≤ 1MB、每插件 ≤ 50MB,超限抛错并入审计。

看门狗

activate 卡死超 90 秒或心跳 15 秒失联,宿主击杀并自动回滚。

数据与同步契约

插件持久化唯二合法位置,两者都自动随全量快照与云端同步——不需要也不允许自建导出/导入,更不要写 userData 旁路文件。

位置用途快照迁移
context.storage结构化状态:配置值、会话、草稿、缓存随全量快照
context.writeFile文档类产出(进索引)随全量快照
  • 换机恢复快照后配置原样复现(重装插件后声明缺省自动合并),插件业务数据零同步代码。
  • 生命周期:停用/升级保留数据;卸载即清 plugin_kv 私有数据(含配置值),共享学习资产(你入库的题目/作答)按契约归用户保留——文档里提醒用户先导快照再卸载。
  • 大块内容请落知识库仓库文档,不要存单条大 blob。

本体数据读取:ontologyScope 限定范围

五个只读原语读取知识库结构化本体(体系树/画像/关系/概念),v2.14 起并入统一数据资产目录(原 ctx.ontology* 门退役):kernel/ontology-get(点查)、kernel/ontology-neighbors(邻域图)、kernel/ontology-subtree(双树子树切片)、kernel/ontology-search(标题与标引词检索)、kernel/ontology-path(概念先修链与文档桥)。消费走统一门 ctx.invokeAsset('kernel/ontology-…', method, [input]),均需 db:read 权限。

plugin.json 顶层可选声明 ontologyScope: { categories: ["类目路径前缀"], repos: ["仓库名"] }——声明即钳制:宿主对每个响应按类目/仓库后过滤(范围外文档被剔除,ontologyGet 返回 found:false + OUT_OF_SCOPE);未声明 = 全库只读(多词条 OR、双维 AND;未来收紧为「未声明即拒绝」会随发版公告)。词汇封闭只增不破:资产 id/method/objectRef type/kinds 不删不改,新能力只追加。

声明式调度:周期任务交给宿主

插件需要周期性后台任务(定时检查/定点生成/轮询催办)时,在 plugin.json 顶层声明 schedules,不要自滚 setInterval——宿主统一托管:到点直接调用你导出的 method,任务自动进「后台任务台账」(设置页可见、可立即运行),并享受忙时让路与错过补跑。

plugin.json
"schedules": [
  {
    "id": "auto-weave",
    "every": "1h",
    "missPolicy": "skip",
    "method": "runAutoWeave",
    "description": "自动织网检查"
  }
]
  • 形态:every(≥60s,如 "30m"/"1h")或 dailyAt("HH:mm" 每日定点)二选一;everyConfig/dailyAtConfig 用配置项覆盖节奏(用户可调);missPolicy=skip(跳过等下轮)或 catchup(空闲后补跑一次,如晨报「8 点没开机,开机后生成」)。
  • 校验 fail-closed:每插件最多 3 项、every 下限 60 秒、missPolicy 封闭词汇、未知键拒绝——非法声明激活被拒并审计 schedule.reject。
  • 触发语义:系统忙(做题/对话进行中)本拍延迟、空闲后补跑;设置页「立即运行」与定时同一生产路径;停用/卸载即注销。调度只管「何时叫你」——LLM 阈值/冷却闸写在你的 method 内。
  • 台账与自省:任务自动进统一台账——设置页「后台任务」节可刷新查看(上次执行结果/耗时/下次预期)并手动立即执行;小诺经 get_background_tasks 工具只读查询;插件自身经 ctx.schedule.list() 读台账、ctx.schedule.runNow() 仅可触发自己的任务(跨插件触发宿主拒绝)。调度为尽力而为,实际执行时间以台账如实呈现。

数据资产契约:你的数据如何被发现与消费

插件不只有 UI——你维护/产出的数据本身就是能力。在 plugin.json 顶层声明 provides.data(数据资产)与 consumes(数据粒度消费),即进入全局「数据资产目录」:能力地图「数据资产」Tab(默认视图)、Agent 的 list_data_assets 工具、其他插件的 consumes 都以此为唯一发现面。创建插件时回答物模型三问:你有什么数据 / 你怎么被发现 / 你消费什么。

plugin.json
"provides": {
  "data": [
    {
      "id": "error-analyses",
      "title": "错因归因记录",
      "schema": "行字段: questionId, category, reactionMs, answeredAt",
      "method": "listAnalyses",
      "events": ["mistake-analyzer.error-analyses.changed"],
      "version": 1
    }
  ]
},
"consumes": [
  { "asset": "kernel/fragments", "optional": true }
]
  • 字段语义:id(插件内唯一,目录全名=<插件id>/<id>);schema 是描述性摘要(形状契约由你的黄金用例锁定);method 为确定性查询入口(禁 LLM、禁写副作用);events 必须以本插件 id 前缀命名(伪装他插件前缀拒绝激活);derivesFrom 派生来路仅展示;version 破坏性变更 +1。
  • 双层校验:激活期结构 fail-closed(id/title/method 必填、id 唯一、事件前缀、consumes 全名形态)——不合法拒绝激活;运行时形状 fail-open——坏资产在目录标「已降级」,不炸宿主不连坐。
  • consumes.optional:缺省 false = 提供方未安装/未激活时拒绝激活;true = 运行时门控优雅降级(消费方 invoke 返回引导文案,不触达提供方)。渲染层用 useDataAsset(assetFullName) 四态门控。
  • 发现与事件:能力地图「数据资产」Tab + Agent 的 list_data_assets 工具(开发消费型插件前必调)。context.emitAssetEvent 只能发自己声明的资产;context.onAssetEvent 须已声明 consumes——事件只作刷新信号不载数据,回拉查询 method 取权威数据。
  • 跨层容忍:资产间无外键无级联(provider-authoritative)——源条目删除后下游存量保留,展示容忍悬空引用。已注册资产示例:quiz-maker/questions·review-due、mistake-analyzer/error-analyses·forget-heatmap、study-analytics/study-stats、内核四资产。

界面词典(i18n):插件文案跟随界面语言

插件不用写任何 i18n 代码——在 plugin.json 顶层声明可选 i18n 词典(源文为键),平台在渲染端按当前界面语言替换你贡献与页面载荷里的 UI 文案;未命中恒等回退原文,中文用户零词典零行为变化。

plugin.json
"i18n": {
  "en-US": {
    "题库": "Question Bank",
    "开始做题": "Start Practice"
  }
}
  • 平台替你翻译的三个消费点:① 侧边栏贡献标签与徽标标题(contributions.action / badge.title);② nav.entry 页 Deck 载荷白名单字段(页/卡标题、列名、动作 label/confirm/confirmOkText、统计与图表 label、表单 label/placeholder/选项/submitLabel、tabs 标题,递归);③ 插件配置面板字段 label/description/选项文案。
  • 永不翻译:表格行数据、markdown 内容、view 卡 props——那是数据不是 UI;行值恰好与某标签同文也保持原样(白名单红线)。
  • 校验 fail-closed(validatePluginI18n,与贡献/配置/调度同门):i18n 须为「语言 id → 词典」对象,语言 id 须为 BCP 47 简化形态(zh-CN/en-US),译文须为非空字符串——非法声明拒绝激活并审计。
  • 发版纪律:改了原文 = 旧词典键静默失效(该处残留中文),改文案时同批核对 i18n 键;生效语言集由平台 UI 语言注册表决定(zh-CN/en-US)。
  • 合规硬闸:manifest 必须声明 i18n 且至少一个非源语言面非空——未国际化的插件不允许安装(市场/Git/更新三通道拒绝)也不允许激活;词典键可含插件名,平台在侧边栏/插件页/配置卡同步翻译插件名。

黄金用例、遥测与健康分

每次工具执行必须经 report_outcome 回传结果遥测:

index.js
const result = await doGenerate(args)
context.reportOutcome({
  status: 'partial',                   // success | partial | failed
  produced: { itemsValid: 8, itemsRejected: 4,
              rejectReasons: ['missing_source_snippet', 'schema_mismatch'] },
  cost: { llmCalls: 2, tokens: 5300, durationMs: 8200 },
  userAction: 'pending'                // pending|accepted|rejected|modified
})
  • status 只能由落库结果驱动,禁止「调用发出了就报成功」。
  • failed/partial 由中枢渲染为统一结构化反馈(成功 N / 拒收 N / 原因 / 重试),不把原始 JSON 甩给用户。
  • insertQuestion 入口强制校验:无 sourceSnippet 或 schema 不合法 → 拒收计入 itemsRejected——拒收不是错误,是契约的一部分。
  • 黄金用例:每个工具附「调用 → 期望 OpResult/视图 JSON」用例,数据契约与渲染契约一起回归。

健康分每周按可靠性 / 采纳率 / 学习贡献 / 负反馈率计算;低分插件自动生成「插件体检」任务——Agent 拉取 rejectReasons 与差评样本定位问题 → 修复 → 黄金用例回归 → 健康分对比。连续两周期低于阈值,插件页亮黄灯并建议停用。遥测缺失本身计为一次 failed。

发布与更新

发布到社区市场

把插件推送到自己的 Git 仓库并添加 GitHub topic knomi-plugin。社区市场程序化扫描:Search API 按 star 降序拉取 → 逐仓库校验 plugin.json(id/name/version/entry 必填)→ 校验通过才展示,结果缓存 10 分钟。

  • 安装/更新自动语法预检:vm 编译入口及相对依赖链(最多 32 文件),零代码执行,语法错误直接回滚。
  • 安装成功后自动发起 AI 安全评估(LLM 静态行为审查:网络外传/凭据/破坏性操作/混淆/超权限),结果作为参考标签展示,需已配置 AI Provider。
  • 在线更新:git 来源插件记录 knomi-source.json(仓库 + commit SHA);检查更新对账远端版本,一键更新(停用 → 备份 → 替换 → 重激活,失败自动回滚);新增权限再次弹确认。

依赖声明(depends)

plugin.json
{ "id": "mistake-analyzer", "depends": ["quiz-maker"] }
  • 依赖是元数据不是互调许可:禁止 require/import 对方模块、禁止私有 IPC;只约束安装/启用的先后与可用性。
  • 激活硬闸:依赖未安装/未启用时激活被拒(插件页提供一键修复);反向保护:被依赖时无法停用/卸载;循环依赖扫描期即拒绝。
  • 不需要就不要声明:依赖面越小,用户启用路径越短。

平台机制与 API 语义

事件订阅机制(doc:changed 单源扇出)

平台对仓库文件的写/删/改统一经写漏斗广播单一事件源(doc:changed),知识树、图谱、面板与插件订阅同一事件多路消费。你的插件用 ctx.onDocChanged 订阅——依赖的文件被删除会收到 type: 'delete',重命名是 delete+write 两条事件,按下方语义处理就不会「文件没了插件还不知道」。

API 语义速查

API语义坑
ctx.readFile(path)仅返回已入库文档内容;未入库路径返回空串不抛错不要用空串判断文件不存在——存在性判断改用 ctx.query 查 documents 表
ctx.query(...)返回行字段为驼峰命名(file_path → filePath)按 SQL 原始列名取值会得到 undefined
ctx.timers.setInterval宿主托管:停用/卸载自动清理,interval 下限 1 秒勿自滚 setInterval 兜底——会绕过停用清理
ctx.onDocChanged(cb)订阅文档变更推送(v2.6):文件写/删/改即时回调 {filePath, type: 'write'|'delete'}(rename=旧路径 delete+新路径 write),返回取消订阅函数;file:read 权限门控,未授权激活期订阅即抛(fail-closed)事件只作刷新信号——用 filePath 回拉权威数据,勿缓存副本;写文件仍走 ctx.writeFile 正常漏斗(防自激循环须自行判重);回调在插件子进程本地执行——事件经宿主总线按需转发,未订阅的沙箱不被唤醒
ctx.getDocuments()返回 camelCase 行(filePath,非 file_path)按 SQL 原始蛇形列名取值会得到 undefined
ctx.invokePluginMethod / ctx.invoke仅内置插件可用(plugin:manage 门控);沙箱 RPC 清单不含 invokePluginMethod沙箱插件做同插件内动作直调内部函数即可——自重入经互调通道必被拒
ctx.invokeAsset统一数据资产门(v2.14,详见上方「本体数据读取」与「数据资产契约」节):kernel/* 直调内核资产(本体族按插件 ontologyScope 声明钳制);其他 provider/asset 等价互调视图=查询的投影,不要绕过资产自拼 SQL 重建本体形状(表结构非契约会漂移);范围外文档被宿主静默剔除(ontology-get 返回 found:false + OUT_OF_SCOPE),不是缺陷

确认与裁决边界(统一确认总线)

需要用户拍板的事项由平台统一确认总线托管——待裁决事项排队、同一时刻只弹队头一张卡、新确认静默入队不打扰、可最小化稍后处理(当前首消费方=碎片落点确认卡)。能力包暂无确认卡贡献点:需要用户澄清用 ask_user,写文件的把关走平台 Diff 确认流。两类确认卡都有会话隔离保证——只在发起它的对话弹出,新建/切换对话后回原对话处理,不会窜到别的对话。

事件目录:产品会产生哪些事件

Knownest 是多面板应用,文件与业务数据在面板间相互关联——平台用事件解耦这种联动。能力包可订阅下方四族事件,据此做「收件箱收到新碎片自动分类」「仓库变更重建索引」这类有意思的插件。事件只作刷新信号:收到后用 ctx.readFile / ctx.query 回拉权威数据,不要缓存副本,也不要基于事件直接改文件(写操作仍走 ctx.writeFile 正常漏斗,防自激循环须自行判重)。

事件(订阅 API)载荷触发时机
doc-changed
ctx.onDocChanged(cb)
{filePath, type: 'write'|'delete'}仓库目录内文件写入/新建/删除;重命名是 delete+write 两条事件;编辑器保存、插件写文件、碎片落盘共用同一写漏斗
inbox-changed
ctx.onInboxChanged(cb)
{domain:'inbox', type, filePath, status?, title?}arrived=碎片落盘(手机投递/桌面新增);renamed=标题改名;status=加工状态流转(pending / processing / needs-confirm / processed,含产物被删后的回退)
repo-changed
ctx.onRepoChanged(cb)
{domain:'repo', type, repoId, name}added=仓库登记成功;removed=仓库移除成功(sys-hub 系统仓不可删,不会产生 removed)
ontology-changed
ctx.onOntologyChanged(cb)
{domain:'ontology', type:'changed', reason, conceptId?}tree=体系树投影变化(聚合后一次);relation=文档关系边增删;concept=概念/关系状态机流转。收到后用 ctx.ontology* 原语回拉权威数据

四个事件族订阅语义一致:首次订阅向宿主报备(按需转发,未订阅的沙箱不被事件唤醒);回调在插件子进程本地执行;插件停用/卸载自动清理;file:read 权限未授权时激活期订阅即抛(fail-closed)。

平台内部还有渲染层与主进程自用的事件通道(doc:changed 微批广播、碎片加工进度/报告/确认卡、插件启停、系统仓同步、应用更新、体系树版本信号)以及调度域产品事件(domain:'schedule',task.started/finished/failed——声明式任务的执行生命周期,FR-169 目录成员;台账数字以 schedule:list 为权威)——它们由平台内置面消费,插件不可直接使用,列此仅供理解产品全貌。

module.exports = {
  async activate(ctx) {
    // 收件箱哨兵:新碎片到达即触发(自动分类 / 提醒 / 统计)
    ctx.onInboxChanged((e) => {
      if (e.type !== 'arrived') return
      ctx.readFile(e.filePath).then((content) => { /* 回拉权威数据后处理 */ })
    })
  },
}

参考实现

插件示范点
text-clipperworkspace.action 全链路:modal + form + target_dir + submit 返回 filePath
morning-reporttray.menu 托盘挂载;nav.entry v2 复合页 + detail 下钻 + tabs 双分区
study-remindershortcut.bind 快捷键与备用键位
study-analyticsnav.entry v3 全声明式学习报告页(summary+今日焦点+三 Tab,capability 去练/detail 错题回看)
quiz-makerstart_practice 就地做题;agent.preset「出题官」
knomi-agentMCP 一等工具桥(mcp__server__tool 直调)+ agent.preset 内置人设 + configSchema(agent_presets)

知识分享与订阅

Knownest 支持将知识库中的文档与题目打包为 .knomi-share 文件(ADR-024 附录 A),接收方导入后即可直接练习。你的作答记录与复习进度不随包分享。

导出:知识体系 Tab → 「导出分享包」→ 勾选目录 → 选择 license 与署名 → 生成文件。导入:知识体系 Tab → 「导入分享包」→ 选择 .knomi-share 文件 → 预览确认 → 题目立即可练。

发布与订阅:将 .knomi-share 上传到你自己的 GitHub 公开仓库(根目录含 manifest.json),通过分享中枢插件(share-hub)订阅他人的知识包仓库,一键检查更新。

所有授权与导出操作均可在设置页「数据与授权」查看审计记录。你的作答与复习进度永远只在本地。

商业边界:哪些永远免费

Knownest 的商业边界由 ADR-024(商业边界宪法)约束:本地数据的一切能力——知识库、出题复习、Agent(自带 Key 的 BYOK 模式)、插件生态、知识包导出导入、Anki 通道——永久免费,不做任何本地功能解锁付费。

未来的订阅服务只有一类:在本地能力之上的增值服务(内容供给、零知识加密同步、分享托管),且必须通过「零知识或显式发布」测试——服务器只见到密文或你主动公开的内容。付费插件=服务配额包装,不卖本地功能。默认零遥测:任何统计上报都需要你在设置中显式开启。