From 68f9e7198e1dd05f43d711a7df5a11600556bc5e Mon Sep 17 00:00:00 2001 From: alanxtl Date: Wed, 26 Aug 2026 14:57:59 +0800 Subject: [PATCH 1/3] add contract doc --- docs/en/development/plugin-contract.md | 147 +++++++++++++++++++++++++ docs/zh/development/plugin-contract.md | 135 +++++++++++++++++++++++ zensical.toml | 2 + 3 files changed, 284 insertions(+) create mode 100644 docs/en/development/plugin-contract.md create mode 100644 docs/zh/development/plugin-contract.md diff --git a/docs/en/development/plugin-contract.md b/docs/en/development/plugin-contract.md new file mode 100644 index 000000000..bede62efa --- /dev/null +++ b/docs/en/development/plugin-contract.md @@ -0,0 +1,147 @@ +--- +title: Host-visible diagnostics contract +description: Contract for the host-visible integration diagnostics RFC. +--- + +# Host-visible diagnostics contract + +This page records the contract introduced by the host-visible integration diagnostics RFC. It covers the diagnostic +slice only; shared service state, service lifecycle, installation, and platform adapter contracts belong to their +respective RFCs and implementation slices. + +The contract currently applies to Codex, Claude Code, DeepSeek Harness (DSH), OpenClaw, Pi, and Hermes. Bub is out of +scope until it has a host channel, implementation, tests, and support qualification. + +The words **MUST**, **SHOULD**, and **MAY** are normative requirements for plugin implementation and review. + +This contract is derived from [RFC 1299: Local Server availability and service installation](../rfcs/1299_local_server_availability_and_service_installation.md). +The original [RFC PR #1299](https://github.com/oceanbase/powercontext/pull/1299) is tracked by [issue #1298](https://github.com/oceanbase/powercontext/issues/1298). + +## What a plugin must report + +A plugin MUST report a PowerContext failure when a host-visible operation cannot complete because of one of the +classified backend failures. The operation may be context preparation, recall, capture, flush, a direct tool or slash +command, or a health/status check. + +The plugin MUST use typed client errors to make the classification. It MUST NOT classify failures by matching text in +an exception message. + +| Outcome | Classification | +| --- | --- | +| `authentication_failed` | HTTP 401. | +| `version_mismatch` | HTTP 404, normally an incompatible or missing endpoint. | +| `server_unavailable` | Connection failure, timeout, aborted request, or HTTP 503. | +| `invalid_response` | Other HTTP failures, malformed JSON, invalid response shape, or decoding/schema failures. | + +An empty but valid result is not a failure diagnostic. In particular, an empty memory result MUST NOT be reported as +`server_unavailable`. + +## Diagnostic event format + +Each diagnostic MUST be one JSON object written as one line through the host's supported channel: + +```json +{ + "component": "powercontext.openclaw", + "event": "context_prepare", + "outcome": "server_unavailable", + "recovery": "powercontext doctor" +} +``` + +### Fields + +| Field | Requirement | +| --- | --- | +| `component` | Stable host-qualified name, such as `powercontext.dsh` or `powercontext.claude_code.recall`. | +| `event` | Short lower-snake-case event, such as `context_prepare`, `capture_source`, `tool_call`, or `status`. It MUST NOT contain a prompt, query, URL, or identifier. | +| `outcome` | One of the four outcomes defined above. | +| `http_status` | Optional integer for an HTTP response. It MUST NOT be fabricated for a transport failure. | +| `recovery` | MUST equal `powercontext doctor` for `server_unavailable`; normally omitted for other outcomes. | + +Additional fields MAY be included only when they are bounded, non-sensitive, and useful to interpret the lifecycle +event. For example, a numeric `content_bytes` or a bounded `context_status` is acceptable. + +### Examples + +```json +{"component":"powercontext.codex.recall","event":"context_prepare","outcome":"authentication_failed","http_status":401} +{"component":"powercontext.pi","event":"context_prepare","outcome":"version_mismatch","http_status":404} +{"component":"powercontext.hermes","event":"tool_call","outcome":"server_unavailable","recovery":"powercontext doctor"} +{"component":"powercontext.dsh","event":"capture_source","outcome":"invalid_response","http_status":500} +``` + +## Host presentation contract + +Each plugin MUST use the native host channel. Diagnostics MUST NOT be inserted into model content, recalled context, +or a successful tool result. + +| Host | Channel | Component prefix | +| --- | --- | --- | +| Codex | Hook `stderr` | `powercontext.codex.recall` | +| Claude Code | Hook `stderr` | `powercontext.claude_code.recall` | +| DSH | Host logger warning | `powercontext.dsh` | +| OpenClaw | Plugin API logger warning | `powercontext.openclaw` | +| Pi | Host terminal warning (`console.warn`) | `powercontext.pi` | +| Hermes | Plugin logger warning | `powercontext.hermes` | + +The host-facing operation result MAY remain a generic error such as `PowerContext operation failed`. The structured +diagnostic is the recovery signal; the generic result is only for host/model control flow. + +## Fail-open, privacy, and presentation bounds + +When a PowerContext operation fails: + +- recall/context preparation MUST return no recalled context rather than partial or fabricated context; +- capture and flush MUST not terminate or block the host session indefinitely; +- direct tools and commands MUST return a generic failure result without exposing request details; +- diagnostic emission itself MUST be best effort and MUST NOT turn a backend failure into a host failure. + +Diagnostics MUST NOT contain endpoint URLs, authorization headers, tokens, cookies, filesystem paths, prompts, queries, +captured text, recalled text, response bodies, or stack traces. + +Repeated failures MUST have bounded presentation. Long-lived plugins SHOULD deduplicate by `outcome` for 60 seconds. +Short-lived hooks MAY deduplicate within one invocation, but MUST NOT emit an unbounded stream for one failure. The +deduplication key is the outcome, not user input or the request payload. + +## Plugin implementation conventions for this RFC + +Every plugin implementation in this RFC MUST: + +1. Reuse the shared client error types and the outcome mapping above. +2. Attach diagnostics to every relevant failure exit, including lifecycle callbacks and direct tool/command paths. +3. Use a stable component and event name; never put user or request data in either field. +4. Keep the diagnostic formatter independent from model-facing content formatting. +5. Preserve the host's normal behavior when PowerContext is unavailable. +6. Add or update documentation and tests in the same implementation slice. + +The plugin MAY choose its language and internal helper shape. It MUST preserve the observable JSON contract and the +host channel listed above. + +## Required test matrix + +Each plugin PR that implements this RFC MUST test the following observable behavior: + +1. Transport failure or timeout produces `server_unavailable` and `powercontext doctor`. +2. HTTP 503 produces `server_unavailable` with `http_status: 503`. +3. HTTP 401 produces `authentication_failed`. +4. HTTP 404 produces `version_mismatch`. +5. Other HTTP failures and malformed responses produce `invalid_response`. +6. Repeated failures are deduplicated within the documented bound. +7. Recall, capture, flush, direct tool, slash command, and status paths remain fail-open where the host exposes them. +8. The diagnostic contains no URL, token, prompt, query, response body, or stack trace. +9. The matching host runner, type checker, or smoke test passes. + +Tests SHOULD assert the parsed event and the host-visible channel. They SHOULD NOT freeze private call order or +internal helper names. + +## Out of scope + +This contract does not define: + +- shared service state or native service lifecycle; +- service installation, ownership, restart policy, or platform support qualification; +- a common UI for every host; +- the Bub integration. + +Those decisions require their own implementation evidence and review boundary. diff --git a/docs/zh/development/plugin-contract.md b/docs/zh/development/plugin-contract.md new file mode 100644 index 000000000..f3a5ca351 --- /dev/null +++ b/docs/zh/development/plugin-contract.md @@ -0,0 +1,135 @@ +--- +title: 宿主可见诊断契约 +description: 宿主可见集成诊断 RFC 的契约。 +--- + +# 宿主可见诊断契约 + +本文记录 Host-visible integration diagnostics RFC 引入的契约,只覆盖本次诊断切片。共享 service state、service lifecycle、安装方式和平台 adapter +契约属于各自的 RFC 与实现切片,不在本文定义。 + +当前契约适用于 Codex、Claude Code、DeepSeek Harness(DSH)、OpenClaw、Pi 和 Hermes。Bub 尚未具备宿主通道、实现、测试和支持资格,暂不在范围内。 + +文中的“必须(MUST)”“应该(SHOULD)”和“可以(MAY)”是插件实现与评审的规范性要求。 + +本契约来源于 [RFC 1299:Local Server availability and service installation](../rfcs/1299_local_server_availability_and_service_installation.md)。 +原始 [RFC PR #1299](https://github.com/oceanbase/powercontext/pull/1299) 对应的跟踪 issue 是 [#1298](https://github.com/oceanbase/powercontext/issues/1298)。 + +## 插件必须报告什么 + +当宿主可见操作因为下列已分类的 backend failure 无法完成时,插件必须报告 PowerContext failure。适用操作包括 context preparation、recall、capture、flush、 +direct tool、slash command 以及 health/status check。 + +插件必须使用 typed client error 进行分类,不能通过匹配 exception message 文本来分类。 + +| Outcome | 分类规则 | +| --- | --- | +| `authentication_failed` | HTTP 401。 | +| `version_mismatch` | HTTP 404,通常表示 endpoint 不兼容或不存在。 | +| `server_unavailable` | 连接失败、超时、请求中止或 HTTP 503。 | +| `invalid_response` | 其他 HTTP failure、JSON 损坏、响应结构错误或解码/schema failure。 | + +合法但为空的结果不是失败诊断。尤其是空的 memory 结果不能报告为 `server_unavailable`。 + +## 诊断事件格式 + +每条诊断必须通过宿主支持的通道写出一个单行 JSON object: + +```json +{ + "component": "powercontext.openclaw", + "event": "context_prepare", + "outcome": "server_unavailable", + "recovery": "powercontext doctor" +} +``` + +### 字段 + +| 字段 | 要求 | +| --- | --- | +| `component` | 稳定且包含宿主名称,例如 `powercontext.dsh` 或 `powercontext.claude_code.recall`。 | +| `event` | 简短的 lower-snake-case 事件,例如 `context_prepare`、`capture_source`、`tool_call` 或 `status`。不能包含 prompt、query、URL 或 identifier。 | +| `outcome` | 必须是上面定义的四种 outcome 之一。 | +| `http_status` | HTTP 响应可以携带的整数。传输失败不能伪造该字段。 | +| `recovery` | `server_unavailable` 时必须为 `powercontext doctor`;其他 outcome 通常省略。 | + +可以增加 bounded、非敏感且有助于解释生命周期事件的字段,例如数字类型的 `content_bytes` 或受限的 `context_status`。 + +### 示例 + +```json +{"component":"powercontext.codex.recall","event":"context_prepare","outcome":"authentication_failed","http_status":401} +{"component":"powercontext.pi","event":"context_prepare","outcome":"version_mismatch","http_status":404} +{"component":"powercontext.hermes","event":"tool_call","outcome":"server_unavailable","recovery":"powercontext doctor"} +{"component":"powercontext.dsh","event":"capture_source","outcome":"invalid_response","http_status":500} +``` + +## 宿主展示契约 + +每个插件必须使用宿主原生通道。诊断不能插入 model content、recalled context 或成功的 tool result。 + +| 宿主 | 通道 | Component 前缀 | +| --- | --- | --- | +| Codex | Hook `stderr` | `powercontext.codex.recall` | +| Claude Code | Hook `stderr` | `powercontext.claude_code.recall` | +| DSH | 宿主 logger warning | `powercontext.dsh` | +| OpenClaw | Plugin API logger warning | `powercontext.openclaw` | +| Pi | 宿主终端 warning(`console.warn`) | `powercontext.pi` | +| Hermes | Plugin logger warning | `powercontext.hermes` | + +宿主侧 operation result 可以继续返回 `PowerContext operation failed` 这类通用错误。结构化诊断是恢复信号,通用错误只服务于 host/model 控制流。 + +## Fail-open、隐私和展示边界 + +PowerContext operation 失败时: + +- recall/context preparation 必须返回空的 recalled context,不能返回 partial 或伪造的 context; +- capture 和 flush 不能终止宿主 session,也不能无限期阻塞宿主; +- direct tool 和 command 必须返回不包含请求细节的通用 failure result; +- 诊断发送本身必须是 best effort,日志失败不能把 backend failure 变成宿主 failure。 + +诊断不能包含 endpoint URL、authorization header、token、cookie、filesystem path、prompt、query、capture text、recall text、response body 或 stack trace。 + +重复失败必须有展示边界。长生命周期插件应该按 `outcome` 去重 60 秒。短生命周期 hook 可以在一次 invocation 内去重,但不能为一个失败无限输出诊断。 +去重 key 是 outcome,不能使用 user input 或 request payload。 + +## 本 RFC 的插件实现约定 + +本 RFC 中的每个插件实现都必须: + +1. 复用 shared client 的 typed error 和上面的 outcome mapping。 +2. 在所有相关 failure exit 接入诊断,包括 lifecycle callback 和 direct tool/command path。 +3. 使用稳定的 component 和 event name;不能把用户数据或请求数据放进字段。 +4. 让 diagnostic formatter 与 model-facing content formatter 解耦。 +5. PowerContext 不可用时保持宿主的正常行为。 +6. 在同一个 implementation slice 中同步更新文档和测试。 + +插件可以选择语言和内部 helper 结构,但必须保持可观察的 JSON 契约和上表中的宿主通道。 + +## 必须覆盖的测试矩阵 + +实现本 RFC 的每个插件 PR 都必须测试以下可观察行为: + +1. 传输失败或超时产生 `server_unavailable` 和 `powercontext doctor`。 +2. HTTP 503 产生带有 `http_status: 503` 的 `server_unavailable`。 +3. HTTP 401 产生 `authentication_failed`。 +4. HTTP 404 产生 `version_mismatch`。 +5. 其他 HTTP failure 和 malformed response 产生 `invalid_response`。 +6. 重复失败在约定边界内去重。 +7. 在宿主提供这些入口时,recall、capture、flush、direct tool、slash command 和 status path 都保持 fail-open。 +8. 诊断不包含 URL、token、prompt、query、response body 或 stack trace。 +9. 对应的宿主 runner、type checker 或 smoke test 通过。 + +测试应该断言解析后的 event 和宿主可见通道,不应该冻结 private call order 或内部 helper name。 + +## 不在范围内 + +本文不定义: + +- shared service state 或 native service lifecycle; +- service 安装、ownership、restart policy 或平台支持资格; +- 所有宿主共用的 UI; +- Bub 集成。 + +这些决定需要独立的实现证据和评审边界。 diff --git a/zensical.toml b/zensical.toml index 6ddfbb4b0..c795a75f9 100644 --- a/zensical.toml +++ b/zensical.toml @@ -41,6 +41,7 @@ nav = [ ] }, { "Development" = [ { "Core Protocol" = "en/development/core-protocol.md" }, + { "Host Integration Contract" = "en/development/plugin-contract.md" }, { "Memory Layer" = "en/development/memory-layer.md" }, { "Pydantic AI Inference" = "en/development/pydantic-ai-inference.md" }, { "Remote Access Implementation" = "en/development/remote-access-implementation.md" }, @@ -113,6 +114,7 @@ nav = [ ] }, { "开发" = [ { "Core Protocol" = "zh/development/core-protocol.md" }, + { "宿主集成契约" = "zh/development/plugin-contract.md" }, { "Memory Layer" = "zh/development/memory-layer.md" }, { "Pydantic AI 推理" = "zh/development/pydantic-ai-inference.md" }, { "远程访问实现" = "zh/development/remote-access-implementation.md" }, From 50d3fef29093c8a8efee7976b0eed9ceb6c58f17 Mon Sep 17 00:00:00 2001 From: alanxtl Date: Wed, 26 Aug 2026 15:02:40 +0800 Subject: [PATCH 2/3] rename --- docs/en/development/plugin-contract.md | 46 ++++++++++++------------ docs/zh/development/plugin-contract.md | 48 +++++++++++++------------- zensical.toml | 4 +-- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/docs/en/development/plugin-contract.md b/docs/en/development/plugin-contract.md index bede62efa..d91fd5aae 100644 --- a/docs/en/development/plugin-contract.md +++ b/docs/en/development/plugin-contract.md @@ -1,16 +1,16 @@ --- -title: Host-visible diagnostics contract -description: Contract for the host-visible integration diagnostics RFC. +title: Plugin-visible diagnostics contract +description: Contract for the Plugin-visible integration diagnostics RFC. --- -# Host-visible diagnostics contract +# Plugin-visible diagnostics contract -This page records the contract introduced by the host-visible integration diagnostics RFC. It covers the diagnostic +This page records the contract introduced by the Plugin-visible integration diagnostics RFC. It covers the diagnostic slice only; shared service state, service lifecycle, installation, and platform adapter contracts belong to their respective RFCs and implementation slices. The contract currently applies to Codex, Claude Code, DeepSeek Harness (DSH), OpenClaw, Pi, and Hermes. Bub is out of -scope until it has a host channel, implementation, tests, and support qualification. +scope until it has a Plugin channel, implementation, tests, and support qualification. The words **MUST**, **SHOULD**, and **MAY** are normative requirements for plugin implementation and review. @@ -19,7 +19,7 @@ The original [RFC PR #1299](https://github.com/oceanbase/powercontext/pull/1299) ## What a plugin must report -A plugin MUST report a PowerContext failure when a host-visible operation cannot complete because of one of the +A plugin MUST report a PowerContext failure when a Plugin-visible operation cannot complete because of one of the classified backend failures. The operation may be context preparation, recall, capture, flush, a direct tool or slash command, or a health/status check. @@ -38,7 +38,7 @@ An empty but valid result is not a failure diagnostic. In particular, an empty m ## Diagnostic event format -Each diagnostic MUST be one JSON object written as one line through the host's supported channel: +Each diagnostic MUST be one JSON object written as one line through the Plugin's supported channel: ```json { @@ -53,7 +53,7 @@ Each diagnostic MUST be one JSON object written as one line through the host's s | Field | Requirement | | --- | --- | -| `component` | Stable host-qualified name, such as `powercontext.dsh` or `powercontext.claude_code.recall`. | +| `component` | Stable Plugin-qualified name, such as `powercontext.dsh` or `powercontext.claude_code.recall`. | | `event` | Short lower-snake-case event, such as `context_prepare`, `capture_source`, `tool_call`, or `status`. It MUST NOT contain a prompt, query, URL, or identifier. | | `outcome` | One of the four outcomes defined above. | | `http_status` | Optional integer for an HTTP response. It MUST NOT be fabricated for a transport failure. | @@ -71,31 +71,31 @@ event. For example, a numeric `content_bytes` or a bounded `context_status` is a {"component":"powercontext.dsh","event":"capture_source","outcome":"invalid_response","http_status":500} ``` -## Host presentation contract +## Plugin presentation contract -Each plugin MUST use the native host channel. Diagnostics MUST NOT be inserted into model content, recalled context, +Each plugin MUST use the native Plugin channel. Diagnostics MUST NOT be inserted into model content, recalled context, or a successful tool result. -| Host | Channel | Component prefix | +| Plugin | Channel | Component prefix | | --- | --- | --- | | Codex | Hook `stderr` | `powercontext.codex.recall` | | Claude Code | Hook `stderr` | `powercontext.claude_code.recall` | -| DSH | Host logger warning | `powercontext.dsh` | +| DSH | Plugin logger warning | `powercontext.dsh` | | OpenClaw | Plugin API logger warning | `powercontext.openclaw` | -| Pi | Host terminal warning (`console.warn`) | `powercontext.pi` | +| Pi | Plugin terminal warning (`console.warn`) | `powercontext.pi` | | Hermes | Plugin logger warning | `powercontext.hermes` | -The host-facing operation result MAY remain a generic error such as `PowerContext operation failed`. The structured -diagnostic is the recovery signal; the generic result is only for host/model control flow. +The Plugin-facing operation result MAY remain a generic error such as `PowerContext operation failed`. The structured +diagnostic is the recovery signal; the generic result is only for Plugin/model control flow. ## Fail-open, privacy, and presentation bounds When a PowerContext operation fails: - recall/context preparation MUST return no recalled context rather than partial or fabricated context; -- capture and flush MUST not terminate or block the host session indefinitely; +- capture and flush MUST not terminate or block the Plugin session indefinitely; - direct tools and commands MUST return a generic failure result without exposing request details; -- diagnostic emission itself MUST be best effort and MUST NOT turn a backend failure into a host failure. +- diagnostic emission itself MUST be best effort and MUST NOT turn a backend failure into a Plugin failure. Diagnostics MUST NOT contain endpoint URLs, authorization headers, tokens, cookies, filesystem paths, prompts, queries, captured text, recalled text, response bodies, or stack traces. @@ -112,11 +112,11 @@ Every plugin implementation in this RFC MUST: 2. Attach diagnostics to every relevant failure exit, including lifecycle callbacks and direct tool/command paths. 3. Use a stable component and event name; never put user or request data in either field. 4. Keep the diagnostic formatter independent from model-facing content formatting. -5. Preserve the host's normal behavior when PowerContext is unavailable. +5. Preserve the Plugin's normal behavior when PowerContext is unavailable. 6. Add or update documentation and tests in the same implementation slice. The plugin MAY choose its language and internal helper shape. It MUST preserve the observable JSON contract and the -host channel listed above. +Plugin channel listed above. ## Required test matrix @@ -128,11 +128,11 @@ Each plugin PR that implements this RFC MUST test the following observable behav 4. HTTP 404 produces `version_mismatch`. 5. Other HTTP failures and malformed responses produce `invalid_response`. 6. Repeated failures are deduplicated within the documented bound. -7. Recall, capture, flush, direct tool, slash command, and status paths remain fail-open where the host exposes them. +7. Recall, capture, flush, direct tool, slash command, and status paths remain fail-open where the Plugin exposes them. 8. The diagnostic contains no URL, token, prompt, query, response body, or stack trace. -9. The matching host runner, type checker, or smoke test passes. +9. The matching Plugin runner, type checker, or smoke test passes. -Tests SHOULD assert the parsed event and the host-visible channel. They SHOULD NOT freeze private call order or +Tests SHOULD assert the parsed event and the Plugin-visible channel. They SHOULD NOT freeze private call order or internal helper names. ## Out of scope @@ -141,7 +141,7 @@ This contract does not define: - shared service state or native service lifecycle; - service installation, ownership, restart policy, or platform support qualification; -- a common UI for every host; +- a common UI for every Plugin; - the Bub integration. Those decisions require their own implementation evidence and review boundary. diff --git a/docs/zh/development/plugin-contract.md b/docs/zh/development/plugin-contract.md index f3a5ca351..6efb81644 100644 --- a/docs/zh/development/plugin-contract.md +++ b/docs/zh/development/plugin-contract.md @@ -1,23 +1,23 @@ --- -title: 宿主可见诊断契约 -description: 宿主可见集成诊断 RFC 的契约。 +title: 插件集成约定 +description: 插件可见集成诊断 RFC 的约定。 --- -# 宿主可见诊断契约 +# 插件可见诊断约定 -本文记录 Host-visible integration diagnostics RFC 引入的契约,只覆盖本次诊断切片。共享 service state、service lifecycle、安装方式和平台 adapter -契约属于各自的 RFC 与实现切片,不在本文定义。 +本文记录 Host-visible integration diagnostics RFC 引入的约定,只覆盖本次诊断切片。共享 service state、service lifecycle、安装方式和平台 adapter +约定属于各自的 RFC 与实现切片,不在本文定义。 -当前契约适用于 Codex、Claude Code、DeepSeek Harness(DSH)、OpenClaw、Pi 和 Hermes。Bub 尚未具备宿主通道、实现、测试和支持资格,暂不在范围内。 +当前约定适用于 Codex、Claude Code、DeepSeek Harness(DSH)、OpenClaw、Pi 和 Hermes。Bub 尚未具备插件通道、实现、测试和支持资格,暂不在范围内。 文中的“必须(MUST)”“应该(SHOULD)”和“可以(MAY)”是插件实现与评审的规范性要求。 -本契约来源于 [RFC 1299:Local Server availability and service installation](../rfcs/1299_local_server_availability_and_service_installation.md)。 +本约定来源于 [RFC 1299:Local Server availability and service installation](../rfcs/1299_local_server_availability_and_service_installation.md)。 原始 [RFC PR #1299](https://github.com/oceanbase/powercontext/pull/1299) 对应的跟踪 issue 是 [#1298](https://github.com/oceanbase/powercontext/issues/1298)。 ## 插件必须报告什么 -当宿主可见操作因为下列已分类的 backend failure 无法完成时,插件必须报告 PowerContext failure。适用操作包括 context preparation、recall、capture、flush、 +当插件可见操作因为下列已分类的 backend failure 无法完成时,插件必须报告 PowerContext failure。适用操作包括 context preparation、recall、capture、flush、 direct tool、slash command 以及 health/status check。 插件必须使用 typed client error 进行分类,不能通过匹配 exception message 文本来分类。 @@ -33,7 +33,7 @@ direct tool、slash command 以及 health/status check。 ## 诊断事件格式 -每条诊断必须通过宿主支持的通道写出一个单行 JSON object: +每条诊断必须通过插件支持的通道写出一个单行 JSON object: ```json { @@ -48,7 +48,7 @@ direct tool、slash command 以及 health/status check。 | 字段 | 要求 | | --- | --- | -| `component` | 稳定且包含宿主名称,例如 `powercontext.dsh` 或 `powercontext.claude_code.recall`。 | +| `component` | 稳定且包含插件名称,例如 `powercontext.dsh` 或 `powercontext.claude_code.recall`。 | | `event` | 简短的 lower-snake-case 事件,例如 `context_prepare`、`capture_source`、`tool_call` 或 `status`。不能包含 prompt、query、URL 或 identifier。 | | `outcome` | 必须是上面定义的四种 outcome 之一。 | | `http_status` | HTTP 响应可以携带的整数。传输失败不能伪造该字段。 | @@ -65,29 +65,29 @@ direct tool、slash command 以及 health/status check。 {"component":"powercontext.dsh","event":"capture_source","outcome":"invalid_response","http_status":500} ``` -## 宿主展示契约 +## 插件展示约定 -每个插件必须使用宿主原生通道。诊断不能插入 model content、recalled context 或成功的 tool result。 +每个插件必须使用插件原生通道。诊断不能插入 model content、recalled context 或成功的 tool result。 -| 宿主 | 通道 | Component 前缀 | +| 插件 | 通道 | Component 前缀 | | --- | --- | --- | | Codex | Hook `stderr` | `powercontext.codex.recall` | | Claude Code | Hook `stderr` | `powercontext.claude_code.recall` | -| DSH | 宿主 logger warning | `powercontext.dsh` | +| DSH | 插件 logger warning | `powercontext.dsh` | | OpenClaw | Plugin API logger warning | `powercontext.openclaw` | -| Pi | 宿主终端 warning(`console.warn`) | `powercontext.pi` | +| Pi | 插件终端 warning(`console.warn`) | `powercontext.pi` | | Hermes | Plugin logger warning | `powercontext.hermes` | -宿主侧 operation result 可以继续返回 `PowerContext operation failed` 这类通用错误。结构化诊断是恢复信号,通用错误只服务于 host/model 控制流。 +插件侧 operation result 可以继续返回 `PowerContext operation failed` 这类通用错误。结构化诊断是恢复信号,通用错误只服务于 host/model 控制流。 ## Fail-open、隐私和展示边界 PowerContext operation 失败时: - recall/context preparation 必须返回空的 recalled context,不能返回 partial 或伪造的 context; -- capture 和 flush 不能终止宿主 session,也不能无限期阻塞宿主; +- capture 和 flush 不能终止插件 session,也不能无限期阻塞插件; - direct tool 和 command 必须返回不包含请求细节的通用 failure result; -- 诊断发送本身必须是 best effort,日志失败不能把 backend failure 变成宿主 failure。 +- 诊断发送本身必须是 best effort,日志失败不能把 backend failure 变成插件 failure。 诊断不能包含 endpoint URL、authorization header、token、cookie、filesystem path、prompt、query、capture text、recall text、response body 或 stack trace。 @@ -102,10 +102,10 @@ PowerContext operation 失败时: 2. 在所有相关 failure exit 接入诊断,包括 lifecycle callback 和 direct tool/command path。 3. 使用稳定的 component 和 event name;不能把用户数据或请求数据放进字段。 4. 让 diagnostic formatter 与 model-facing content formatter 解耦。 -5. PowerContext 不可用时保持宿主的正常行为。 +5. PowerContext 不可用时保持插件的正常行为。 6. 在同一个 implementation slice 中同步更新文档和测试。 -插件可以选择语言和内部 helper 结构,但必须保持可观察的 JSON 契约和上表中的宿主通道。 +插件可以选择语言和内部 helper 结构,但必须保持可观察的 JSON 约定和上表中的插件通道。 ## 必须覆盖的测试矩阵 @@ -117,11 +117,11 @@ PowerContext operation 失败时: 4. HTTP 404 产生 `version_mismatch`。 5. 其他 HTTP failure 和 malformed response 产生 `invalid_response`。 6. 重复失败在约定边界内去重。 -7. 在宿主提供这些入口时,recall、capture、flush、direct tool、slash command 和 status path 都保持 fail-open。 +7. 在插件提供这些入口时,recall、capture、flush、direct tool、slash command 和 status path 都保持 fail-open。 8. 诊断不包含 URL、token、prompt、query、response body 或 stack trace。 -9. 对应的宿主 runner、type checker 或 smoke test 通过。 +9. 对应的插件 runner、type checker 或 smoke test 通过。 -测试应该断言解析后的 event 和宿主可见通道,不应该冻结 private call order 或内部 helper name。 +测试应该断言解析后的 event 和插件可见通道,不应该冻结 private call order 或内部 helper name。 ## 不在范围内 @@ -129,7 +129,7 @@ PowerContext operation 失败时: - shared service state 或 native service lifecycle; - service 安装、ownership、restart policy 或平台支持资格; -- 所有宿主共用的 UI; +- 所有插件共用的 UI; - Bub 集成。 这些决定需要独立的实现证据和评审边界。 diff --git a/zensical.toml b/zensical.toml index c795a75f9..b787d2e50 100644 --- a/zensical.toml +++ b/zensical.toml @@ -41,7 +41,7 @@ nav = [ ] }, { "Development" = [ { "Core Protocol" = "en/development/core-protocol.md" }, - { "Host Integration Contract" = "en/development/plugin-contract.md" }, + { "Plugin Integration Contract" = "en/development/plugin-contract.md" }, { "Memory Layer" = "en/development/memory-layer.md" }, { "Pydantic AI Inference" = "en/development/pydantic-ai-inference.md" }, { "Remote Access Implementation" = "en/development/remote-access-implementation.md" }, @@ -114,7 +114,7 @@ nav = [ ] }, { "开发" = [ { "Core Protocol" = "zh/development/core-protocol.md" }, - { "宿主集成契约" = "zh/development/plugin-contract.md" }, + { "插件集成约定" = "zh/development/plugin-contract.md" }, { "Memory Layer" = "zh/development/memory-layer.md" }, { "Pydantic AI 推理" = "zh/development/pydantic-ai-inference.md" }, { "远程访问实现" = "zh/development/remote-access-implementation.md" }, From 6aec3571169d57ff31bd3f75832164e0c59a483b Mon Sep 17 00:00:00 2001 From: alanxtl Date: Thu, 27 Aug 2026 09:59:08 +0800 Subject: [PATCH 3/3] docs: scope plugin error classifications --- docs/en/development/plugin-contract.md | 49 +++++++++++++++++++------- docs/zh/development/plugin-contract.md | 42 ++++++++++++++++------ 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/docs/en/development/plugin-contract.md b/docs/en/development/plugin-contract.md index d91fd5aae..042c8dc1d 100644 --- a/docs/en/development/plugin-contract.md +++ b/docs/en/development/plugin-contract.md @@ -28,17 +28,35 @@ an exception message. | Outcome | Classification | | --- | --- | -| `authentication_failed` | HTTP 401. | -| `version_mismatch` | HTTP 404, normally an incompatible or missing endpoint. | +| `authentication_failed` | A typed authentication failure, normally HTTP 401. | +| `version_mismatch` | HTTP 404 from a required compatibility or availability endpoint; it MUST NOT be inferred from a direct resource lookup. | | `server_unavailable` | Connection failure, timeout, aborted request, or HTTP 503. | -| `invalid_response` | Other HTTP failures, malformed JSON, invalid response shape, or decoding/schema failures. | +| `invalid_response` | Malformed JSON, invalid response shape, decoding/schema failure, or an otherwise unclassified HTTP failure after operation-specific domain classification. | An empty but valid result is not a failure diagnostic. In particular, an empty memory result MUST NOT be reported as `server_unavailable`. +### Operation-specific domain errors + +Direct tools and commands can receive valid domain errors after the Server has processed the request. The client MUST +classify typed domain errors before applying the Plugin-visible diagnostic mapping: + +| Domain result | Direct operation meaning | +| --- | --- | +| `not_found` | HTTP 404 for a missing Memory entry, citation, or other requested resource. | +| `conflict` | HTTP 409 for a revision, source, citation, or other operation conflict. | +| `invalid_request` | HTTP 422 for a request that violates the wire or application contract. | + +These domain results MUST be preserved in the direct operation result and MUST NOT be rewritten as +`version_mismatch` or `invalid_response`. A 404 from an explicitly identified compatibility or availability endpoint +remains `version_mismatch`; the operation identifier or endpoint contract, not the status code alone, determines that +classification. + ## Diagnostic event format -Each diagnostic MUST be one JSON object written as one line through the Plugin's supported channel: +Each diagnostic MUST be one JSON object written as one line through the Plugin's supported channel. For hook-based +hosts, the event is encoded as the top-level `systemMessage` value in the successful stdout hook JSON; the event is +not required to be rendered as a standalone stdout line. ```json { @@ -78,15 +96,17 @@ or a successful tool result. | Plugin | Channel | Component prefix | | --- | --- | --- | -| Codex | Hook `stderr` | `powercontext.codex.recall` | -| Claude Code | Hook `stderr` | `powercontext.claude_code.recall` | +| Codex | Hook stdout top-level `systemMessage` | `powercontext.codex.recall` | +| Claude Code | Hook stdout top-level `systemMessage` | `powercontext.claude_code.recall` | | DSH | Plugin logger warning | `powercontext.dsh` | | OpenClaw | Plugin API logger warning | `powercontext.openclaw` | | Pi | Plugin terminal warning (`console.warn`) | `powercontext.pi` | | Hermes | Plugin logger warning | `powercontext.hermes` | The Plugin-facing operation result MAY remain a generic error such as `PowerContext operation failed`. The structured -diagnostic is the recovery signal; the generic result is only for Plugin/model control flow. +diagnostic is the recovery signal; the generic result is only for Plugin/model control flow. When a hook also injects +context, its stdout JSON MUST retain `hookSpecificOutput` alongside `systemMessage`. Hook diagnostics MAY still be +written to stderr for local debugging, but stderr is not the user-visible channel for Codex or Claude Code. ## Fail-open, privacy, and presentation bounds @@ -100,9 +120,10 @@ When a PowerContext operation fails: Diagnostics MUST NOT contain endpoint URLs, authorization headers, tokens, cookies, filesystem paths, prompts, queries, captured text, recalled text, response bodies, or stack traces. -Repeated failures MUST have bounded presentation. Long-lived plugins SHOULD deduplicate by `outcome` for 60 seconds. -Short-lived hooks MAY deduplicate within one invocation, but MUST NOT emit an unbounded stream for one failure. The -deduplication key is the outcome, not user input or the request payload. +Repeated failures MUST have bounded presentation across invocations. Long-lived plugins SHOULD deduplicate by `outcome` +for 60 seconds. Short-lived hooks MUST use a host-level or durable local state mechanism to enforce the bound across +invocations; an invocation-local set MAY provide additional deduplication. The deduplication key is the outcome, not +user input or the request payload. ## Plugin implementation conventions for this RFC @@ -125,9 +146,11 @@ Each plugin PR that implements this RFC MUST test the following observable behav 1. Transport failure or timeout produces `server_unavailable` and `powercontext doctor`. 2. HTTP 503 produces `server_unavailable` with `http_status: 503`. 3. HTTP 401 produces `authentication_failed`. -4. HTTP 404 produces `version_mismatch`. -5. Other HTTP failures and malformed responses produce `invalid_response`. -6. Repeated failures are deduplicated within the documented bound. +4. A missing compatibility or availability endpoint produces `version_mismatch`, while a direct resource 404 preserves + `not_found`. +5. Direct 409 and 422 responses preserve `conflict` and `invalid_request`; other unclassified HTTP failures and + malformed responses produce `invalid_response`. +6. Two separate hook invocations within the documented cooldown produce at most one identical diagnostic. 7. Recall, capture, flush, direct tool, slash command, and status paths remain fail-open where the Plugin exposes them. 8. The diagnostic contains no URL, token, prompt, query, response body, or stack trace. 9. The matching Plugin runner, type checker, or smoke test passes. diff --git a/docs/zh/development/plugin-contract.md b/docs/zh/development/plugin-contract.md index 6efb81644..ad3dcbb50 100644 --- a/docs/zh/development/plugin-contract.md +++ b/docs/zh/development/plugin-contract.md @@ -24,16 +24,32 @@ direct tool、slash command 以及 health/status check。 | Outcome | 分类规则 | | --- | --- | -| `authentication_failed` | HTTP 401。 | -| `version_mismatch` | HTTP 404,通常表示 endpoint 不兼容或不存在。 | +| `authentication_failed` | typed authentication failure,通常为 HTTP 401。 | +| `version_mismatch` | 必须来自兼容性或 availability endpoint 的 HTTP 404;不能从 direct resource lookup 的状态码推断。 | | `server_unavailable` | 连接失败、超时、请求中止或 HTTP 503。 | -| `invalid_response` | 其他 HTTP failure、JSON 损坏、响应结构错误或解码/schema failure。 | +| `invalid_response` | JSON 损坏、响应结构错误、解码/schema failure,或完成 operation-specific domain 分类后仍未分类的 HTTP failure。 | 合法但为空的结果不是失败诊断。尤其是空的 memory 结果不能报告为 `server_unavailable`。 +### Operation-specific domain error + +Direct tool 和 command 在 Server 已处理请求后,可能返回合法的 domain error。Client 必须先根据 typed domain +error 分类,再应用插件可见诊断映射: + +| Domain result | Direct operation 含义 | +| --- | --- | +| `not_found` | HTTP 404,表示 Memory entry、citation 或其他请求资源不存在。 | +| `conflict` | HTTP 409,表示 revision、source、citation 或其他 operation conflict。 | +| `invalid_request` | HTTP 422,表示 request 违反 wire 或 application contract。 | + +这些 domain result 必须保留在 direct operation result 中,不能改写成 `version_mismatch` 或 `invalid_response`。 +明确标识为兼容性或 availability endpoint 的 404 仍然是 `version_mismatch`;应由 operation identifier 或 endpoint +contract,而不是单独的 status code,决定分类。 + ## 诊断事件格式 -每条诊断必须通过插件支持的通道写出一个单行 JSON object: +每条诊断必须通过插件支持的通道写出一个单行 JSON object。对于基于 Hook 的宿主,事件编码在成功 stdout +Hook JSON 顶层的 `systemMessage` 值中,不要求作为独立的 stdout 行输出。 ```json { @@ -71,14 +87,16 @@ direct tool、slash command 以及 health/status check。 | 插件 | 通道 | Component 前缀 | | --- | --- | --- | -| Codex | Hook `stderr` | `powercontext.codex.recall` | -| Claude Code | Hook `stderr` | `powercontext.claude_code.recall` | +| Codex | Hook stdout 顶层 `systemMessage` | `powercontext.codex.recall` | +| Claude Code | Hook stdout 顶层 `systemMessage` | `powercontext.claude_code.recall` | | DSH | 插件 logger warning | `powercontext.dsh` | | OpenClaw | Plugin API logger warning | `powercontext.openclaw` | | Pi | 插件终端 warning(`console.warn`) | `powercontext.pi` | | Hermes | Plugin logger warning | `powercontext.hermes` | 插件侧 operation result 可以继续返回 `PowerContext operation failed` 这类通用错误。结构化诊断是恢复信号,通用错误只服务于 host/model 控制流。 +Hook 同时注入 context 时,stdout JSON 必须保留 `hookSpecificOutput`,并与 `systemMessage` 并列。Hook 可以继续 +向 stderr 写本地调试信息,但 Codex 和 Claude Code 的用户可见通道不是 stderr。 ## Fail-open、隐私和展示边界 @@ -91,8 +109,9 @@ PowerContext operation 失败时: 诊断不能包含 endpoint URL、authorization header、token、cookie、filesystem path、prompt、query、capture text、recall text、response body 或 stack trace。 -重复失败必须有展示边界。长生命周期插件应该按 `outcome` 去重 60 秒。短生命周期 hook 可以在一次 invocation 内去重,但不能为一个失败无限输出诊断。 -去重 key 是 outcome,不能使用 user input 或 request payload。 +重复失败必须在跨 invocation 的范围内有展示边界。长生命周期插件应该按 `outcome` 去重 60 秒。短生命周期 hook +必须使用宿主级或持久化本地状态跨 invocation 执行该限制;一次 invocation 内的 set 可以作为额外去重手段。去重 +key 是 outcome,不能使用 user input 或 request payload。 ## 本 RFC 的插件实现约定 @@ -114,9 +133,10 @@ PowerContext operation 失败时: 1. 传输失败或超时产生 `server_unavailable` 和 `powercontext doctor`。 2. HTTP 503 产生带有 `http_status: 503` 的 `server_unavailable`。 3. HTTP 401 产生 `authentication_failed`。 -4. HTTP 404 产生 `version_mismatch`。 -5. 其他 HTTP failure 和 malformed response 产生 `invalid_response`。 -6. 重复失败在约定边界内去重。 +4. 缺失的兼容性或 availability endpoint 产生 `version_mismatch`,direct resource 404 保留 `not_found`。 +5. Direct 409 和 422 分别保留 `conflict` 与 `invalid_request`;其他未分类 HTTP failure 和 malformed response 产生 + `invalid_response`。 +6. 在约定 cooldown 内执行两次独立 Hook invocation 时,最多产生一条相同诊断。 7. 在插件提供这些入口时,recall、capture、flush、direct tool、slash command 和 status path 都保持 fail-open。 8. 诊断不包含 URL、token、prompt、query、response body 或 stack trace。 9. 对应的插件 runner、type checker 或 smoke test 通过。