跳转至

宿主 Hook

参与宿主生命周期、而不仅仅是添加 provider、channel 或 tool 的插件的 SDK 扩展点。属于 Plugin SDK 概览 的一部分。

工作流插件的宿主钩子

宿主钩子是那些需要参与宿主生命周期、而不仅仅是添加 provider、channel 或 tool 的插件的 SDK 扩展点。它们是通用契约;Plan Mode 可以使用它们,审批工作流、工作区策略门控、后台监视器、设置向导和 UI 伴随插件也可以使用。

方法 其拥有的契约
api.session.state.registerSessionExtension(...) 插件拥有的、JSON 兼容的会话状态,通过 Gateway 会话进行投影
api.session.workflow.enqueueNextTurnInjection(...) 为单个会话的下一个 agent 轮次注入的持久化、恰好一次上下文
api.registerTrustedToolPolicy(...) 由 Manifest 门控的可信插件前置 tool 策略,可阻止或重写 tool 参数
api.registerToolMetadata(...) tool 目录显示元数据,不更改 tool 实现
api.registerCommand(...) 作用域插件命令;命令结果可设置 continueAgent: true 或 suppressReply: true;Discord 原生命令支持 descriptionLocalizations
api.session.controls.registerControlUiDescriptor(...) 用于会话、tool、run、设置或标签页界面的控制 UI 贡献描述符
api.lifecycle.registerRuntimeLifecycle(...) 在重置/删除/重载路径上,针对插件拥有的运行时资源的清理回调
api.agent.events.registerAgentEventSubscription(...) 用于工作流状态和监视器的已清理事件订阅
api.runContext.setRunContext(...) / getRunContext(...) / clearRunContext(...) 每次运行的插件临时状态,在运行终止生命周期时清除
api.session.workflow.registerSessionSchedulerJob(...) 插件拥有的调度器作业的清理元数据;不调度工作,也不创建任务记录
api.session.workflow.sendSessionAttachment(...) 仅限捆绑包:通过宿主中介,将文件附件投递到活动的直接出站会话路由
api.session.workflow.scheduleSessionTurn(...) / unscheduleSessionTurnsByTag(...) 仅限捆绑包:基于 Cron 的定时会话轮次,以及基于标签的清理
api.session.controls.registerSessionAction(...) 客户端可通过 Gateway 分派的类型化会话操作
api.registerBoardWidgetContentKind(...) 沙箱化 board 小部件源验证、渲染器资源和文档组合

运行时生命周期注册也可以为该注册所捕获的资源提供 dispose()。显式拥有的、未缓存的插件检查会在回滚或释放时调用它,并等待清理,包括无效的异步注册工作。Doctor 在加载用于发现的上下文引擎时使用这种所有权;它仍然不会调用仅用于发现的引擎工厂。dispose() 不得删除持久状态或禁用其他注册。现有 raw loader 和 Gateway 生命周期不会获得自动处置:保留其 cleanup(ctx) 行为。

用于 agent 运行的预配置模型运行时也拥有新的模型选定注册。它们使用相同的发现注册模式和插件选择,但新注册会绕过全局注册表缓存。热调用方共享其预配置代。注册资源在已接纳的工作和清理期间保持持有;dispose() 在最终占用释放后运行,包括运行之间任何有界的空闲保留。

创建已配置或独立发布不会启用这种所有权。现有根注册表和 raw SDK 宿主注册保留其原始所有者;借用已管理的代会保留该源的所有权。api.runtime.state 键控和 blob 存储背后的共享数据库仍由进程拥有。注册的处置器不得关闭该数据库或删除其持久行。

图像和音乐生成也拥有由 api.runtime.imageGeneration.generate(...) 和 api.runtime.musicGeneration.generate(...) 获取的新注册。它们等待 provider 的完整图像或音频缓冲区以及受跟踪操作的清理,然后在解决或拒绝之前等待注册处置。现有受管理的注册为该操作保留;raw 宿主注册和调用方提供的 provider 保留其现有所有者。Provider 列表仍返回调用方拥有的回调,并且不会获取代生命周期。返回异步 provider 工作,并让 dispose() 停止并等待其拥有的任何额外后台工作结束。

视频生成对 api.runtime.videoGeneration.generate(...) 使用相同的所有权,从模型能力查找,到已完成的视频资产和结果元数据。视频资产可能包含缓冲区或提供方托管的 URL。调用返回后的下载归属于调用方;注册释放不得使这些已完成工件失效。

api.runtime.tts.textToSpeechTelephony(...)、缓冲 TTS 以及宿主 Talk 语音 也拥有新的提供方注册,贯穿配置、角色准备、合成、结果元数据以及受跟踪的提供方清理。 这些操作保留受管注册并保持原始宿主所有权。 配置的回退目录与直接的偏好和覆盖查找保持分离。返回的音频缓冲区在注册释放后仍然存活;标准 TTS 在释放提供方后转码并保存这些已完成的缓冲区。独立的同步语音查找和指令解析 API 保持 其现有的调用方生命周期;流式语音不属于此有限操作。

流式语音拥有其提供方注册,直到流清理完成。 api.runtime.tts.textToSpeechStream(...) 在 EOF 或读取错误后保留显式提供方 release() 回调的生命周期:调用方仍必须调用并等待返回的 release()。如果没有提供方释放回调, EOF 或读取错误会自动释放注册。流取消和显式释放在等待两者之前启动源取消和提供方清理, 包括受跟踪的生产者工作。释放还会处理未打开的流。现有的原始宿主注册保持其宿主生命周期。

api.runtime.tts.prepareTtsRequest(...) 可以返回不透明的提供方覆盖,供后续合成使用。准备会保留借用的受管注册, 直到 SDK 宿主关闭,即使原始检查先被释放。来自同一源的重复准备共享宿主占用。原始加载器和 Gateway 注册保持其现有生命周期和缓存复用;准备不会为每个话语创建新的注册。宿主在释放其占用之前等待受跟踪的 准备工作,包括由失败投影启动的工作。

对于从拥有的检查准备的 image_generate、music_generate 和 video_generate 工具, 资源会保持持有,贯穿预检,并在接受后贯穿生成、媒体保存以及 任何回滚。started 结果确认接受;它并不意味着 工作或清理已完成。如果原始检查在 预检期间退役,新的任务准入将被拒绝。在重试之前,请从当前提供方 设置准备工具。 已接受的任务会保持其捕获的资源,直到其工作结束。 当准备好的视图从另一个受管注册复制回调时,该 源在整个任务以及准备好视图的最终释放器期间保持可用, 包括回滚已启动的清理。最终资源释放会等待 借用源的清理并报告释放失败。这些物理持有不会 恢复已退役注册接受新工作的权限。 原始准备注册表保留其现有宿主生命周期;这不会为所有准备运行时启用 自动物理释放。

当模型支持的图像理解报告请求超时或取消时, 其准备运行时借用会保持持有,直到实际的提供方操作和 受跟踪的传输清理结束。 提供的受管快照保留其原始准备资源,包括 已采用的捐赠者;原始快照保持其调用方拥有的生命周期。超时报告 一个未完成的请求,而不是已完成的资源清理。

可执行 CLI 命令注册也使用拥有的、未缓存的注册表。其 资源在异步注册、命令操作 以及它们的受跟踪清理期间保持可用,然后运行 dispose()。在解析之前关闭命令准备 不会释放这些资源。从注册器和操作中返回异步工作;插件的释放器还必须停止并等待其拥有的任何后台 工作,然后再关闭这些工作使用的资源。CLI 的有界清理 宽限期可以报告待处理工作,而不将该工作视为已完成。 独立的编程式 CLI 调用和调用方拥有的 Commander 程序保留 其现有生命周期。cli-metadata 保持惰性,仅接受 CLI 描述符;保持其 machineOutput 解析器纯且同步。

registerBoardWidgetContentKind(...) 用于拥有声明式 小部件源格式的插件。注册提供全局唯一的小写 kind、一个简短标签、一个能力作用域的插件表面及其渲染器 资源路径、一个同步的 validateSource(source) 回调,以及一个 同步的 composeDocument(...) 回调。核心添加文档外壳、 沙箱、主题和票据绑定的操作桥。注册仅在其 插件处于活动状态时存在;无效、保留或重复的 kind 会导致插件加载失败。 使用 dashboard.dataBindings 和 dashboard.actionVerbs 表示宿主能力, 而不是用于渲染器注册。

对于内联渲染,resources.readPublicResource(path) 可以可选地返回 { body: Uint8Array, contentType: string },用于已注册的资源路径。 这些字节是公开的:隔离的沙箱监听器在没有 Gateway 凭据的情况下提供它们。只返回静态渲染器资产,绝不返回用户数据或 机密。未注册的路径和没有此回调的注册保持私有。 选择加入会在一个全局沙箱命名空间中保留每个声明的路径:没有其他 内容类型可以声明相同的路径,即使没有公开读取器。注册 会拒绝这些冲突,无论顺序如何;只有私有注册可以 共享路径。公开路径必须已经是规范的 URL 路径名,不含点 段、反斜杠、查询字符串或片段。沙箱宿主端点 /mcp-app-sandbox 是保留的。这些额外的路径限制仅适用于 带有 readPublicResource 的注册;私有路径保留其能力 URL 编码。

surface: "tab" 描述符会为 Control UI 添加一个侧边栏标签页。活动插件的标签页描述符会通过 gateway hello(controlUiTabs)通告给 dashboard 客户端,因此该标签页仅在插件启用期间显示。内置插件可以为自己的标签页提供一等 dashboard 视图;其他插件可以将 path 设置为插件 HTTP 路由(参见 api.registerHttpRoute(...)),dashboard 会在沙箱化 frame 中渲染该路由。icon 是 dashboard 图标名称提示,group 选择侧边栏分区(control 或 agent),order 在插件标签页之间排序,requiredScopes 会将标签页从缺少相应 operator 权限范围的连接中隐藏:

如果内置插件的页面已有匹配的 native Control UI 路由,可以设置 placement: "route:<pluginId>"。host 会拒绝来自外部插件或 ID 未拥有该路由的内置插件的 native 路由声明。当描述符存在时,侧边栏会打开 native 路由,而不是挂载通用插件标签页页面。

可选的 slug 会为标签页提供一个 Control UI 地址,例如 /reports,并带有 gateway.controlUi.basePath 前缀。它必须是一个至多 64 个字符的段,且匹配 ^[a-z0-9]+(?:-[a-z0-9]+)*$。只有 surface: "tab" 接受它,并且不能与 placement: "route:<pluginId>" 组合使用。注册会拒绝来自其他活动插件的重复 slug(首次注册优先)以及 Gateway 保留名称:api、plugins、plugin、focus、approve、ask、share、j、v1、ui、mcp-app-sandbox、__openclaw__、__openclaw、sessions、agent、agents,以及探测名称 health、healthz、ready、readyz、startup 和 startupz。

Control UI 会忽略与任何原生路由或别名第一段匹配的 slug。如果任何插件的精确或前缀 HTTP 路由都会匹配已挂载的 slug 路径,Gateway 会从 hello 中省略该 slug 并记录诊断日志。在这两种情况下,标签页都会改用 /plugin?plugin=<pluginId>&id=<tabId>。对于具有可用 slug 的标签页,通用链接会在浏览器历史记录中一次性替换为其 slug 路径,同时保留 p.* 参数和 fragment。slug 仅更改 Control UI 地址;它从不更改 HTTP 路由或身份验证。

对于受 gateway 保护的外部标签页,请将描述符 path 注册在同一插件的 auth: "gateway" HTTP 路由下。经过身份验证的 bootstrap 后,浏览器会获得一个短生命周期的 HttpOnly 授权,其范围限定于该插件和路由根,以便沙箱化 frame 可以在不将 Gateway bearer token 复制到其 URL 或 JavaScript 中的情况下加载。已认证父页面会在外部标签页处于活动状态时,以及在导航或浏览器恢复后挂载它之前,更新该授权。它还会在挂载前从同一不透明沙箱中探测该授权,因此阻止 cookie 的浏览器隐私模式会以不可用面板的形式失败关闭。frame 授权仅接受 GET 和 HEAD,并且始终携带 operator.read;requiredScopes 控制标签页可见性,但从不扩大 cookie 授权。变更操作仍保留在显式 Gateway 认证的父页面或 bearer 接口上。外部标签页要求 HTTPS/Tailscale Serve 或浏览器信任的 loopback 源;在 LAN 主机上使用纯 HTTP 会显示 secure-context 错误,而不是挂载一个无法进行身份验证的面板。完全阻止第三方 cookie 也会使受 gateway 保护的标签页不可用。与所有原生插件接口一样,frame 仍位于已安装插件的信任边界内;OpenClaw 不会将已安装插件视为相互隔离的浏览器安全主体。Cookie 授权使用浏览器的 hostname 边界,而不是其端口边界。请勿在 Gateway hostname 上共同托管相互不受信任的服务,即使在其他端口上也是如此。由插件管理的身份验证支持的标签页会保留其直接 iframe 行为,并且不会请求或需要此 Gateway 授权。

已认证的、同源插件标签页可以在不放宽 iframe 沙箱的情况下请求会话导航。在用户点击后,向父页面发送此仅限会话的消息:

window.parent.postMessage(
  { type: "openclaw-plugin-session-open", sessionKey: "agent:writer:project-review" },
  window.location.origin,
);

仅接受 type、sessionKey 和可选的 agentId。省略不存在的字段。该 key 必须可路由,至多 512 个 UTF-16 码元,并且不包含控制字符或周围空白。显式 agent 必须与限定 key 中的 agent 匹配。host 在使用常规会话导航之前,会检查当前已挂载的 frame、已认证描述符、连接和 frame 授权生命周期。此消息不授予任何会话访问权限,不接受任意 URL,也不返回任何凭据或会话内容。独立页面应保留一个普通 Control UI 链接作为其非嵌入路径。使用 openclaw/plugin-sdk/session-discussion 中的 buildControlUiSessionPath 来构建该路径。

api.session.controls.registerControlUiDescriptor({
  surface: "tab",
  id: "logbook",
  label: "Logbook",
  description: "Your day as a timeline, built from screen snapshots.",
  icon: "sun",
  group: "control",
  requiredScopes: ["operator.write"],
});

新的插件代码请使用分组命名空间:

  • api.session.state.registerSessionExtension(...)
  • api.session.workflow.enqueueNextTurnInjection(...)
  • api.session.workflow.registerSessionSchedulerJob(...)
  • api.session.workflow.sendSessionAttachment(...)
  • api.session.workflow.scheduleSessionTurn(...)
  • api.session.workflow.unscheduleSessionTurnsByTag(...)
  • api.session.controls.registerSessionAction(...)
  • api.session.controls.registerControlUiDescriptor(...)
  • api.agent.events.registerAgentEventSubscription(...)
  • api.agent.events.emitAgentEvent(...)
  • api.runContext.setRunContext(...) / getRunContext(...) / clearRunContext(...)
  • api.lifecycle.registerRuntimeLifecycle(...)

等效的扁平方法仍可作为已弃用的兼容别名供现有插件使用。兼容注册表已于 2026-07-25 将它们弃用,removeAfter 日期为 2026-10-01;参见移除时间线。请勿添加直接调用 api.registerSessionExtension、api.enqueueNextTurnInjection、api.registerControlUiDescriptor、api.registerRuntimeLifecycle、api.registerAgentEventSubscription、api.emitAgentEvent、api.setRunContext、api.getRunContext、api.clearRunContext、api.registerSessionSchedulerJob、api.registerSessionAction、api.sendSessionAttachment、api.scheduleSessionTurn 或 api.unscheduleSessionTurnsByTag 的新插件代码。

scheduleSessionTurn(...) 是 Gateway Cron 调度器之上的会话作用域便捷封装。Cron 负责计时和运行历史;Plugin SDK 仅约束目标会话、插件拥有的命名和清理。

在会话扩展中,openclaw/plugin-sdk/agent-sessions 提供宿主的模型选择辅助函数。精确的 provider/model ID 优先于不区分大小写的匹配;歧义引用需要精确的 provider 和 model ID。当不同身份共享组合引用时,请单独传递 provider。人类名称匹配、别名/日期版本选择以及不区分大小写的 glob 作用域仍然可用。

ModelRegistry.fork(authStorage, publishedModels?) 创建隔离的注册表。authStorage 提供该调用方的凭据。可选的 publishedModels 是从 provider ID 到完整已验证运行时模型行的只读映射;空数组会撤回该 provider 的行。省略的 provider 保留已捕获的目录。Fork 保留当前源中编写好的请求设置和运行时注册,后续 refresh() 调用保留已捕获的模型发布。已发布的模型元数据不提供凭据,也不授权账户。可选参数要求宿主版本包含可执行的目录发布;v2026.9.4 宿主仅支持 fork(authStorage)。此会话扩展子路径仅运行时可用,且不发布 TypeScript 声明。

会话扩展 SDK 和受支持的 TypeBox 导入共享宿主的模块。

这些契约有意拆分权限:

  • 外部插件可以拥有会话扩展、UI 描述符、命令、工具元数据、下一轮注入和常规钩子。
  • 受信任的工具策略在普通 before_tool_call 钩子之前运行,并且由宿主信任。捆绑策略最先运行;已安装插件的策略需要显式启用,并在 contracts.trustedToolPolicies 中包含其本地 id,然后按插件加载顺序运行。策略 id 的作用域限定为注册插件。
  • 保留命令所有权仅限捆绑插件。外部插件应使用自己的命令名称或别名。
  • allowPromptInjection=false 会禁用提示词变更钩子,包括 agent_turn_prepare、before_prompt_build、heartbeat_prompt_contribution 和 enqueueNextTurnInjection。

非 Plan 使用者示例:

插件原型 使用的钩子
审批工作流 会话扩展、命令延续、下一轮注入、UI 描述符
预算/工作区策略门控 受信任工具策略、工具元数据、会话投影
后台生命周期监视器 运行时生命周期清理、代理事件订阅、会话调度器所有权/清理、心跳提示词贡献、UI 描述符
设置或入门向导 会话扩展、作用域命令、Control UI 描述符

Note

保留的核心管理员命名空间(config.*、exec.approvals.*、wizard.*、 update.*)始终保持 operator.admin,即使插件尝试分配更窄的 gateway 方法作用域。对于插件拥有的方法,优先使用插件特定前缀。

何时使用工具结果中间件

具有匹配 manifest 契约的捆绑插件和显式启用的已安装插件,在需要在执行后、 运行时将该结果反馈给模型之前重写工具结果时,可以使用 api.registerAgentToolResultMiddleware(...)。这是用于 tokenjuice 等异步输出归约器的受信任运行时无关接缝。

插件必须为每个目标运行时声明 contracts.agentToolResultMiddleware。受支持的 id 是 agentsapi、codex 和 openclaw;例如, ["agentsapi", "codex", "openclaw"]。省略注册 runtimes 会使用 manifest 中声明的所有受支持运行时。显式注册作用域可以选择这些已声明运行时的子集。

没有该契约或未显式启用的已安装插件不能 注册此中间件;对于不需要模型前工具结果时序的工作,请继续使用 常规 OpenClaw 插件钩子。旧的 仅限嵌入式运行时的扩展工厂注册路径已被移除。

沙箱后端

openclaw/plugin-sdk/sandbox 拥有后端注册、远程文件系统桥接和远程 shell 执行。使用 registerSandboxBackend(id, { factory, manager, resolveWorkdir }) 注册后端,并随插件生命周期释放该注册。

分配外部资源的后端可以提供 reserveRuntimeId(params),在不联系其 provider 的情况下生成新的候选 ID。Core 在调用工厂之前,会在沙箱注册表中为每个后端/作用域保留一个代。重放会收到最初保留的 workspaceDir,包括从不同调用方工作区到达的共享作用域。ReservedSandboxBackendFactoryV1 契约要求通过 CreateReservedSandboxBackendParamsV1 提供 runtimeId 和 assertRuntimeCurrent。权限检查是同步的:配置该精确 ID,并在产生副作用之前,在等待的工作完成后重新检查。准备好的 exec 规范将此检查作为 assertCurrent 携带,进程监督者会在排队准入和原生进程构建过程中保留它。Recreate 会拒绝仍在等待准入的工作;已准入的命令遵循后端的正常关闭生命周期。未知的配置失败会保留该 ID 以供重放。仅在 provider 确认该精确代已永久释放后,才抛出 SandboxRuntimeRetiredError(runtimeId)。Core 每个请求最多替换一次。Recreate 和 prune 会保留失败的清理记录,并防止迟到的发布。

使用 createRemoteShellSandboxBackend(params, options) 来复用共享的工作区引导、技能刷新、工作目录校验以及文件系统桥接。options.createSession 返回一个 RemoteShellSandboxSession。对于保留后端,将 options.runtimeId 设置为 params.runtimeId;backendId 默认为 params.cfg.backend。configLabel 和 configLabelKind 用于描述运行时。默认情况下,路径仍从 params.cfg.ssh.workspaceRoot 和沙箱作用域派生。preprovisionedWorkdir: { runtimeId, remoteWorkspaceDir } 会采用一个已存在的、由 placement 拥有的工作树,而不会为其播种或刷新文件。

初始播种会在最终运行时根目录旁边暂存所有必需的工作区树,并以原子方式发布完整目录,而不进行替换。并发发布会保留第一个工作区,即使调用方稍后清空其根目录。现有根目录在没有新的完成标记的情况下仍然保持权威。正常失败和发布竞争失败只会删除其确切的临时目录,恢复所有者对只读暂存目录的访问,而不跟随符号链接。进程突然丢失或无法访问的 provider 可能会留下一个 <runtime-root>.bootstrap-<uuid> 同级目录;它永远不会被视为已完成的工作区。仅在初始化停止后删除已知的孤儿目录。释放 Crabbox 租约会随机器一起删除这些产物;静态 SSH 在运行时清理期间不会通过 glob 删除同级目录。

createRemoteShellSandboxSession({ buildCommand, assertCurrent, dispose }) 从一个传输适配器派生命令执行、受保护的 tar 上传以及私有 exec-script 暂存。buildCommand({ remoteCommand, tty }) 返回本地 argv、env 和可选的 cwd。本地环境属于传输进程;所请求的远程环境会单独暂存。当仓库准入依赖于它时,cwd 必须标识 provider 拥有的工作区。它还会通过 SandboxBackendExecSpec 传递到进程监督者,独立于远程工作目录。返回的会话公开 runCommand、uploadDirectory、prepareExec 和 dispose。可选的 dispose 在完成或失败后释放本地会话资源;可选的 formatFailure(stderr, exitCode) 用于自定义命令失败消息。远程 PTY 请求会影响适配器构建的命令;本地传输仍使用管道输入运行。

将保留的 assertRuntimeCurrent 作为会话的 assertCurrent 传入。共享所有者会在异步准备前后检查它,上传会在本地遍历后、派生进程前立即重新检查。provider 权限仍归属于传输命令:准备本地 argv 或保留连接凭据并不会授权后续效果。暂存、执行、上传和清理都必须跨越该 provider 边界。即使清理跟随一个失败或已撤销的核心操作,它仍保留相同的 provider 准入。

Crabbox 适配器使用 crabbox exec --id <lease-id> [--pty] -- /bin/sh -c ... 和 stop --current-repo --id <lease-id>,从原始拥有工作区执行。其预分配 exec --check 探测要求 execution 和 currentRepoStop 都为 true;初始支持直接 Daytona 租约。静态 SSH 继续通过适配器使用其现有设置,接入同一工作区所有者。

链接读取器允许已启用的插件声明受支持的 HTTPS 链接,并在聊天旁边渲染一个被动文档。核心拥有停靠区、浏览器式标签页、历史记录、键盘行为以及安全的 Markdown 渲染。插件拥有 URL 策略、服务请求、缓存和文档数据。这不是插件 JavaScript 加载器,也不是带框架的外部网站。

注册读取作用域的 Gateway 方法和一个贡献描述符:

import type { ControlUiLinkReaderDocument } from "openclaw/plugin-sdk/control-ui-link-reader";

api.registerGatewayMethod(
  "notes.read",
  async ({ params, respond }) => {
    // Validate params.url against your service and bound the response before returning it.
    const document: ControlUiLinkReaderDocument = await readNotesDocument(params);
    respond(true, document, undefined);
  },
  { scope: "operator.read" },
);

api.session.controls.registerControlUiDescriptor({
  surface: "link-reader",
  id: "notes",
  label: "Notes",
  icon: "book",
  requiredScopes: ["operator.read"],
  linkReader: {
    hosts: ["notes.example"],
    pathPattern: "^/documents/[a-z0-9-]+$",
    detailMethod: "notes.read",
  },
});

描述符仅在以下条件满足时,才会在 hello.controlUiLinkReaders 和实时插件能力快照中通告:其插件已加载,调用方具有所需作用域,并且每个被引用的方法都属于同一插件且具有 operator.read 作用域。隐藏方法和控制平面写入方法不会通告读取器。注册可以在方法注册之前或之后发生;投影会检查已完成的注册表。插件启用和重新加载通过现有的 plugins.changed 能力刷新流程更新贡献。UI 会清除已移除的贡献并忽略过期的请求结果。

linkReader 字段如下:

字段 约定
hosts 一到十六个精确的小写 DNS 主机名;不包含协议、通配符或端口。
pathPattern 一个锚定的 JavaScript Unicode 正则表达式,最多 1,024 个字符,针对 URL 路径名进行匹配。已安装的插件代码拥有该模式;请保持其简单且可预测。
detailMethod 同一插件的读取方法,接收 { url, agentId?, refresh? } 并返回一个 ControlUiLinkReaderDocument。
字段 契约
previewMethod 可选的同一插件读取方法,接收 { url, agentId? } 并返回 ControlUiLinkReaderPreview,用于悬停或键盘焦点。对于不应获取预览的 URL,请省略它。
imageMethod 可选的同一插件读取方法,接收 { url } 并返回 { url, dataUrl },用于内联图像。

预览和详情请求在可用时会包含所选的 agentId;详情请求还接受 refresh: true。接收方所有者必须授权身份选择,而不是将此提示视为访问权限。

方法名称限制为 128 个字符。URL 中的凭据以及非 HTTPS URL 永远不会被拦截。描述符是路由提示,而不是授权或输入验证:每个插件方法仍会验证其 URL、源访问和请求参数。普通的修饰键点击、下载、不支持的链接以及显式外部操作会保留其原生目标。

导出的被动模型包括源 url、title、可选副标题、作者、日期、徽章以及标签/值元数据。徽章可以包含一个可选的 timestamp,用于其状态事件(例如合并或关闭)。阅读器会在徽章旁以浏览器本地时间显示该时间戳;如果缺失,则回退到 createdAt。保持 createdAt 为原始创建时间;插件负责选择事件时间戳。文档会添加 Markdown body、可选的评论和已更改文件补丁、总计,以及显式的部分或截断标志。评论 ID 和源链接、审查上下文标签以及徽章文本来自插件,而不是核心中的服务特定条件。filesExpanded 可选地选择初始文件差异视图。徽章色调为 neutral、positive、negative、attention 和 accent。元数据条目可以包含 tone: "positive" | "negative",以在预览和阅读器中使用主题的绿色/红色强调其值。对于中性值,请省略 tone;宿主不会根据标签或带符号数字推断它。使用空的元数据标签可获得仅包含值的紧凑预览;如需更完整的元数据列表,请在详情文档中返回。

authorUrl 可选地将主要作者链接到源站上的 HTTPS 个人资料。coAuthors 承载一个有界的 { name, imageUrl? } 条目列表,当未包含所有姓名时,使用 coAuthorCount 表示总数。悬停卡片最多显示三个可用头像和一个 +N 剩余数量;缺失的头像仍计入该数量。图像加载失败时保留首字母缩写,而不会丢弃作者。姓名也可供辅助技术使用,并显示在完整阅读器中。作者图像遵循预览的匿名图像规则;这些不是 Gateway 用户身份。

文档还可以包含被动 checks:

checks?: {
  state: "success" | "failure" | "pending" | "neutral" | "unavailable";
  summary: string;
  total: number;
  items: Array<{
    name: string;
    state: "success" | "failure" | "pending" | "neutral";
    detail?: string;
    url?: string;
  }>;
  truncated?: boolean;
  url?: string;
  commit?: string;
};

插件负责摘要、条目详情、有界的 HTTPS 源链接、聚合以及精确的源修订版本(commit)。宿主渲染这些事实,而不是服务规则或可合并性决定。total 是已知的检查上下文数量,在 truncated 或 unavailable 时可能不完整。当条目列表不完整时设置 truncated,包括源无法读取时。如果可选的 checks 请求失败,请保留文档正文,并且永远不要从不完整数据中报告成功。一个完整的空列表为 neutral。

内置的 GitHub 阅读器会匿名读取拉取请求的精确 head SHA 的检查运行和旧版提交状态,而不是其 base 或 test-merge 提交。它从每个 API 读取一页最多 100 个条目,并返回最多 100 个条目;它不会跟随分页链接。GitHub 的 filter=latest 选择检查运行;阅读器会保留每个不同的运行 ID,而不是根据应用和作业名称推断工作流身份。来自不同工作流的同名作业仍保持独立,因此较新的成功不会掩盖独立的失败。旧版状态与检查运行保持独立,并使用最新的忽略大小写的上下文。已知失败的优先级高于待处理工作,待处理工作又高于不可用数据;只有完整数据才能产生成功或中性。已取消、超时、过期和需要操作的运行计为失败;跳过和中性的运行保持中性。部分结果保留已知条目和显式的不完整摘要。PR 快照共享现有文档缓存 30 秒;显式刷新会重新读取该响应 head 的 PR 以及两个 CI 源。此界面既不评估必需检查规则,也不声称 PR 可以合并。

仅返回适合调用方的有界数据。渲染内容无法激活嵌入式应用小部件、脚本、文件操作或代码执行。内联远程图像使用匿名 CORS 且不带 referrer,除非阅读器声明了 imageMethod。当源不支持浏览器 CORS 时,该方法通过插件解析图像。它必须验证源和每个重定向,限制响应大小和时间,并返回请求的 URL 以及规范的 base64 光栅图像 data URL;不支持 SVG 和 HTML。不要将浏览器 cookie 或服务凭据转发到图像主机。宿主显示经过验证的图像数据,而不执行远程内容。宿主接受每张图像最多 2 MiB 的 PNG、JPEG、GIF 和 WebP 数据,最多排队四个并发请求,并将每个文档解析器限制为 32 个唯一图像和 8 MiB 的编码图像数据。解析器无法提供的图像保留原始匿名 CORS 路径。如果这也失败,则保留外部链接。对于不可用的内容,使用显式错误响应,以便 UI 可以提供重试和原始 URL。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw