| feature_ids |
|
|||
|---|---|---|---|---|
| topics |
|
|||
| doc_kind | interface-baseline | |||
| created | 2026-10-17 |
本页描述当前源码实现的公开接口。接口新增或行为改变必须与实现、类型及本页一同评审、验证;npm 可获取版本须另核发布记录。项目布局、依赖、构建与安装。下列方法写作 SDK FeatureContext 形式;不用 SDK 的模块按同一契约实现入口,模块 Host 的消息发送接收 {threadId, ...draft},SDK 封装成两个参数。
Host 在安装时掌握 pluginId、安装实例、包版本/摘要和声明,并在加载时建立该实例的 client object。context.pluginInstanceId 与 context.featureId 由 Host 注入,仅用于归属;业务参数不另提交一份身份或签名。外部平台与线程、消息、成员的业务映射由插件保存,不成为第二份 Host 成员或消息目录。
Host 调用 action 时,将 PluginActionContext 作为 handler 第二参数传入:catId/threadId/userId/invocationId/originTriggerMessageId?/source?/assertCurrent();owner 设置操作或 HTTP 请求使用第三参数 PluginHostActionContext,包含其调用类别、实例、请求事实、signal/assertCurrent()。输入正文不是身份来源。线程与消息接口遵循普通用户资源规则、调用上下文和实例存活;插件绑定不是额外的资源可见性规则。
同进程模块直接持有 Host 创建的能力对象。当前还实现了按 runtime.transport 声明启动的独立载体,其会话、身份与生命周期管理属于 carrier 内部。插件业务自行启动的 helper 子进程属于插件自己的 start/stop 资源,Host 不理解其 PID 或内部协议。
原生 MCP contribution 只声明进程与环境,不声明 action 方法白名单。RPC 的 method 路由到模块实际导出的 actions;未知方法返回普通未暴露错误,token 只证明当前活跃实例与调用上下文。webhook、direct-tool、配置按钮等 action.method 仍用于各自的路由。
调用取消由实际载体的 AbortSignal、action 的 assertCurrent()、feature 撤销共同完成。未提供 signal 参数的方法依靠实例撤销/Host 方法检查;普通写操作不会因结果丢失自动重试,拒收晚到结果不等于已经发生的写入被撤销。config/secrets.get 每次读取当前有效配置;配置保存保持启用意图,运行实例通过 configRefresh 更新实际资源。
SDK 公开方法是接口边界,不再按插件 ID 或 feature 能力数组复制一张 API 授权表。新 manifest 与 SDK 绑定不含 API 权限清单;旧 manifest 字段在原解析入口忽略,旧握手额外字段只在 hello 兼容边界读取后丢弃,不回流新模型;普通功能与方法声明、包完整性、实例 token、owner/线程可见性及启用禁用检查保持。Host 侧的对应清理须兼容已安装旧包,不以升级插件为恢复前提。握手行的字节摘要描述当前规范消息;旧 Host 附带的惰性元数据不进入新 binding,原始传输仍受既有 NDJSON 帧大小上限约束。
参数与结果的完整类型以 module-host.ts、FeatureContext 及相应类型入口为准。下表涵盖当前公开 surface;“实例”表示 Host 自动绑定的安装实例与 feature,不能由正文替换。
| 接口 | 用途、参数与结果 | 资源与身份边界 | 生命周期与取消 |
|---|---|---|---|
context.config.get(key) |
读取当前配置,返回 unknown | 实例声明的配置 | 每次读最新值,撤销后拒绝 |
context.secrets.get(key) |
读取声明凭据,返回 string 或 undefined | 实例声明的凭据 | 不把密钥写日志,撤销后拒绝 |
context.storage.get/list/set/compareAndSet/delete |
私有键值;读返回 revision/value,CAS 接 expectedRevision,返回 applied/revision | 实例私有存储 | 持久数据按声明保留;并发用 revision;停用撤销调用 |
context.threads.get(id)、context.threads.list({limit,before}?) |
普通线程摘要或 null/分页摘要,含 Host 生成 deepLinkUrl | 普通用户线程规则 | 实际资源检查、实例存活 |
context.threads.readMessages(id,{limit,before?}) |
分页历史 id/timestamp/content/sender;游标用同一行的 timestamp/id | 普通线程可见性 | 同上,不把分页当永久快照 |
context.threads.getCats(id) |
实际参与者、当前可路由成员与未加入成员事实 | 普通成员与线程可见性 | 实际线程参与者,不是插件自建成员目录 |
context.threads.create({title,parentThreadId?,participants?}) |
创建普通线程;父线程存在时继承项目;返回摘要 | 普通用户线程写入规则 | 写失败不自动重试;成功后业务绑定由插件 storage 管理;响应异常后允许重新创建并覆盖映射 |
context.threads.update(id,{title?,preferredCats?}) |
修改标题或偏好,返回摘要 | 普通用户线程写入规则 | preferredCats 是偏好,不等同实际参与者 |
context.threads.addParticipants({threadId,catIds}) |
添加实际线程参与者,返回摘要 | 普通用户线程写入规则 | Host 复用成员与线程资源校验 |
context.members.list()、context.members.get(catId) |
同一成员目录的列表/单行或 null;catId/displayName/defaultModel/available,及可选 nickname/variantLabel/roleDescription/avatar | 当前实例与普通成员目录 | available 是 quota 事实,不是某插件操作的支持证明;实际调用由 Host 判断 |
context.messaging.get(messageId) |
标准 PluginMessage 或 null,正文、结构化块、来源 meta、时间及修订 | 普通用户消息可见性 | 消息元数据是数据,不能制造权限 |
context.messaging.send(threadId,draft) |
保存可见消息,返回 messageId/threadId;可声明 identity、replyTo、媒体及 wake:auto/catId | 声明身份;当前实例与目标线程 | 普通入站 send+wake 一次完成;按 idempotencyKey 重放原写入 |
context.messaging.subscribe(threadId,options?)、unsubscribe(threadId) |
声明允许的消息订阅/撤销;options 含 contributionId/includeOwnMessages | 声明的订阅 contribution 与方法 | 与实例启停绑定,消息回调见下一节 |
context.streams.read(reference,{signal}?) |
将获准 hmr_… 附件引用读取为 AsyncIterable;用于平台上传/转发附件 |
当前实例和附件引用范围 | 直接 AsyncIterable 字节流;结束/return/abort 只释放 reader,引用由 owner 保留,可重试;撤销后停止读取 |
context.tasks.get(selector)、context.tasks.list(filter) |
selector 为 {id} 或 {subjectKey};filter 可含 threadId/kind;get 返回 Task/null,list 返回 Task 数组;可含 entrustedWork 只读摘要;dataController 的 currentForCaller 是 Host 针对当前实例、lifecycle 与 Work 绑定 计算的即时 fence,不是持久授权 | 当前用户可见的 Task | 数据不等于执行授权;实际请求受实例生命期约束;controller state=active 但 currentForCaller=false 时不得维护、发送或唤醒该 Work,只能经新的 owner-confirmed bind 接续 |
context.tasks.create(input,options?)、context.tasks.update(id,patch,conditions?) |
create 默认 onConflict:error;create 的 onConflict:update 要求非空 subjectKey 并保留原子 subject 确保;update 的 expectedThreadId、entrustedWork.expectedRevision、wait.expectedGeneration 均由 Host 内部原子核验;create 返回 Task,update 返回 Task/null | 当前 Task/线程、CAS 与 canonical entrustedWork admission;数据写入不派发执行 | wait patch 不与普通字段或 entrustedWork 混合,写前拒绝且不拆成两次写;无自动写重试,不派发执行 |
context.tasks.bindDataController(input)、context.tasks.revokeDataController(input) |
用 exact Task/revision、不可变 Work 快照与幂等 operationId 建立或撤销有界数据维护绑定;返回 Host receipt、binding revision 与同一 Task | Host 注入当前实例/模块/绑定坐标及 owner 明确确认,包不能提交可信 actor、confirmed 或 controller 坐标 | 每次调用在同一 inventory 串行边界内复核实例、lifecycle、数据维护绑定与 Task CAS;旧 Host 缺少 typed port 时明确 unavailable,绝不 fallback create;绑定只授权同一 Task 的数据维护,不授消息发送、wake 或外部 Service 权限 |
context.dataPath |
Host 启动上下文注入的稳定实例数据目录 | 实例;路径复用 runtime.dataDirectory 声明 | 插件管理内容,卸载按声明的数据策略处理 |
context.log(level,message,fields?) |
输出诊断;日志字段由调用者显式提供 | 实例 | 不输出密钥;作用域随 feature 生命周期 |
模块 Host 可向经验证的 Collective 安装实例提供可选 collectiveArtifactOwner 接缝,结构见 owner port 类型。它不属于通用 FeatureContext,不是内容 CRUD:Host 保留内容/审核/媒体/custody 状态,绑定 activation 并逐次复核来源授权;尚未 compose 时明确 owner_unavailable,可重试。
| 接口/调用 | 输入、结果与用途 | 身份、声明与取消 |
|---|---|---|
entrypoint.create(manifest) |
建立已加载 module 对象,返回 start 及可选生命周期/settings hooks | Host 核安装包与声明;不是启用动作 |
module.settings(host) |
已加载模块的配置/扫码动作,返回 actions 和可选 dispose | Host 绑定实例与 owner 设置调用;不执行业务 start、不更改 enabled 意图 |
module.start(host) |
启用业务;返回 actions、configRefresh、preStop、stop 及可选 streams | 已声明 feature,Host client 绑定实例;拒绝启动则不宣称 healthy |
actions[method](input,actionContext?,hostContext?) |
工具、配置按钮、测试、schedule、service 或声明 HTTP 请求;返回其已声明结果(包括 render/data/targetValues/advance) | 实际导出的 handler 按 method 路由;上下文由 Host 注入;signal/assertCurrent 与停止撤销 |
| 单一 delivery action | kind 为 message 或 execution_status;公共 deliveryId,执行反馈按 executionId 关联;message 携带 envelope,status 携带 started/blocked/resumed/settled 等状态及 presentation | 声明的 message-subscription 方法;receipt 只确认本 input 处理,不能推断猫执行成功;deliveryId 幂等 |
activation.configRefresh() |
插件按需声明;配置改变后重读 config/secrets、更新自身连接与缓存;没有 hook 时无操作 | Host 校验实际资源与实例存活;刷新失败旧 handler 不继续执行,由管理器记录 error |
activation.streams.read(reference,{signal}?) |
与 Plugin→Host 相同的 AsyncIterable;Host 读取插件提供的附件 | 静态 media-source 声明来源归属;结束/return/abort 只释放本次 reader,不删除附件或使引用失效 |
activation.preStop({reason,deadline})、stop({reason})/settings dispose() |
排空/停止该次运行/释放设置资源;deadline 是 Unix 毫秒 | Host 控制生命周期,插件回收自己的连接、订阅、定时器及 helper 子进程 |
stateDiagram-v2
[*] --> 已安装: 验证并复制制品
已安装 --> 已加载: 解析入口并初始化
已加载 --> 运行中: enable / preStart → start
运行中 --> 已加载: disable / preStop → stop
已加载 --> 已安装: 卸载已加载资源
已安装 --> [*]: preUninstall → uninstall
| 阶段 | Host 保证与实际 hook | 插件职责 | 失败落点 |
|---|---|---|---|
| 安装 | 验声明/版本/摘要,复制不可变安装制品并记录 inventory;当前没有 install hook | 提供完整制品、入口、声明及数据策略 | 不准入或保留安装失败,不能伪报已启用 |
| 初始化/加载 | 加载入口、create(manifest),建立实例 client;按需调用 settings |
只建立所需对象与设置 handler,不把初始化视为启用 | loaded/settings error;不能先启动业务再假装初始化 |
| 启用/启动 | 按启用意图调用 preStart(host)/start(host),登记实际激活状态 | 建立业务连接、订阅;自行启动所需 helper 子进程 | 启动失败为 error/blocked 等实际状态,清理部分资源 |
| 停止/禁用 | 停止接新调用,preStop({reason,deadline}) 后 stop({reason});保留 disabled 意图 | 中止未完成操作,停止自身定时器/连接,回收自建子进程 | 记录真实停止错误;晚到回调不能恢复活性 |
| 卸载/销毁 | 运行时先 preStop/stop;随后 preUninstall(host)/uninstall(host),移除安装并按数据策略处理;停用实例直接用 settings 同型 host 执行卸载,不调用 start | 停止阶段已释放运行资源;不自行删除用户保留数据 | 保留实际失败记录,不凭目录消失伪造成功 |
停用状态仍可读取/编辑/保存正常配置及执行声明设置动作;保存不自动启用。已启用实例保存配置时保持启用意图,调用 configRefresh;没有 configRefresh hook 时不执行隐式 stop/start;缓存配置或持有连接的插件显式实现刷新,重读配置并替换自身资源。刷新失败由 Host 停止旧 generation。卸载上下文仅 pluginInstanceId/dataPath/config/secrets/storage/log,不授予运行能力。插件自建 helper 的 PID 与协议不属于 Host 管理接口;现存 external-runtime 则是 Host 运行插件载体的实现事实。C2 的插件自带界面仍是计划,不能把它写作当前已支持的任意 UI 注入。
test 是任意命名的普通 action。所有 action 返回的图像、轮询 data 和 receipt 都只用于本次显示,不由 Host 持久化或恢复。Host 仅保存通用步骤位置和 manifest 声明的配置/凭据目标;业务 cursor/二维码 challenge 由插件 storage 或既有不透明业务引用持有。操作 receipt 不能恢复为 runtime 健康状态;配置改变、重新安装或新操作使旧结果失效,晚响应不得覆盖更新结果。普通配置与操作顺序按 manifest。runtimeState 的 healthy/degraded/crashed 是载体健康,不是微信登录或外服连通;healthy 与插件 idle 可以同时成立。操作回执不改变这份投影。
实现参考:SDK 类型、feature 生命周期、消息/控制 wire registry、模块 action client。wire registry 说明载体消息,不替代以上作者 API。当前 stdio 的 Host 发起请求只有 host.lifecycle.ping 和 host.messaging.deliver;生命周期 hooks 由 module 载体直接调用,不是额外 stdio 方法。streams.read 在 stdio 内部分片传输,作者仍读取 Uint8Array 迭代器。
0.3.1-candidate.0 在当前 0.3.0 契约上准备了 projectOutputs 有界产物声明。它是待审候选,尚未获得项目产物公共交付范围的采纳或发布批准;不恢复已退役的 API 权限表或原生 MCP 方法白名单。SDK 的对应上下文类型位于 0.3.0-beta.2-candidate.0。
每条声明给出公开 action、内部 producer/lookup 方法、同 feature 的 mcpId 和 media-source、允许的 MIME 和大小上限。这是产物操作的资源归属与生命周期声明,不是通用 API 访问清单。首版仅接受可解析可信调用上下文的原生 MCP 入口,不接受 direct-tool 或设置动作;Host 拦截产物公开 action,并拒绝普通调用直接触发该声明的内部 producer/lookup。调用正文为 {requestId, outputPath, parameters};Host 从认证调用解析用户、线程和项目,在实际安装、显式启用、当前调用与项目归属检查下,将完整音频/回执目标一起独占预留并持久记录,再调用 producer。producer 仅收到 parameters,第二参数带 Host 注入的 outputOperation.id;它不收到项目绝对根或 writer。旧 Host 必须在安装或调用之前拒绝,不能先生成后退回附件。
producer 和 lookup 返回同一个有界 ProjectOutputResult:pending、generation_unknown、rejected 或 produced。后者含归属 PMR、媒体类型、字节数、SHA-256 和来源事实。插件在返回引用前持久保存产物;Host 复用现有媒体 ledger 承接字节,并验证完整性、实例/来源归属和当前权限后交付。Host 控制回执文件名、序列化和归属字段,只有两个文件均持久完成才返回 delivered。
相同 requestId 与相同参数接续原操作;不同参数拒绝。派发后结果未知不得再次调用 producer;恢复只查询原结果或继续交付。授权撤销、目录变化、外部文件占位与磁盘失败不能变成覆盖或付费重试。多个普通文件的依次提交不宣称整体原子性;崩溃按原预留 inode、内容摘要及前缀恢复。插件与 Host 承接的用户产物都不随停用、升级或卸载丢弃。
设置动作可返回 render: url, data: {url},由 Host 显示仅 HTTPS、无内嵌认证的链接;不自动打开浏览器。轮询可携带同一 data.url 供刷新后的卡片恢复显示。Google 等具体来源的固定域名和授权参数校验仍由插件负责。Host 设置结果不接受 ok;只读 test 则返回 {ok, message?, details?},二者不可混用。
Contract 的 schema、公开类型和 wire registry 是同一份接口真相;SDK 和独立 Loopback fixture 消费相同的契约夹具,不各自复制一套常量或方法矩阵。协议字节上限从允许的输入形状推导,并以合法最大值和 N+1、UTF-8 多字节与转义字符重新计算验证。请求与响应关联、错误结果、取消及授权撤销也须覆盖;测试通过不表示 Host 已支持或已启用该能力。