diff --git a/.changeset/public-capability-table-types.md b/.changeset/public-capability-table-types.md new file mode 100644 index 000000000..b45468494 --- /dev/null +++ b/.changeset/public-capability-table-types.md @@ -0,0 +1,5 @@ +--- +'agent-bundle': patch +--- + +Export the `CapabilityRow` and `HostCapabilityTable` capability-table types. (#626) diff --git a/packages/agent-bundle/src/adapters/capability-state.ts b/packages/agent-bundle/src/adapters/capability-state.ts index 930c4a363..7c49383ab 100644 --- a/packages/agent-bundle/src/adapters/capability-state.ts +++ b/packages/agent-bundle/src/adapters/capability-state.ts @@ -1,6 +1,7 @@ import { CapabilityStateError, unknownCapabilityStateError } from '../core/capabilities.ts'; import type { CapabilityEvidence, CapabilityState } from '../core/capabilities.ts'; import { featureCapabilityName } from '../core/components.ts'; +import type { JsonObject } from '../core/strict-json.ts'; import { NOTICE_DELIVERY_ROUTES, NOTICE_SENSITIVITIES, @@ -43,6 +44,24 @@ export const unavailableCapability = (reason: string): CapabilityState => Object state: 'unavailable', }); +/** A row from a pinned host capability table. */ +export interface CapabilityRow { + readonly availability?: Readonly>; + readonly evidence?: readonly string[]; + readonly nativeEvent?: string; + readonly payload?: JsonObject; + readonly reason?: string; + readonly state?: string; +} + +/** A loaded pinned host capability table and its source identity. */ +export interface HostCapabilityTable { + readonly data: JsonObject; + readonly fileName: string; + readonly host: string; + readonly version: string; +} + export interface EventRouteCapabilityTableEntry { readonly nativeEvent?: string; /** @@ -90,8 +109,7 @@ export const supportedEventRouteNamesFrom = ( )); /** A pinned capability-table row: JSON imports widen the state literal, so unknown states fail closed. */ -export interface CapabilityTableRow { - readonly reason?: string; +export interface CapabilityTableRow extends CapabilityRow { readonly state: string; } diff --git a/packages/agent-bundle/src/index.ts b/packages/agent-bundle/src/index.ts index cde6b978a..9904f8cf1 100644 --- a/packages/agent-bundle/src/index.ts +++ b/packages/agent-bundle/src/index.ts @@ -191,6 +191,7 @@ export type AgentBundleConfig = CoreAgentBundleConfig & PortableConfigExtension; export type { PortableAuthorConfig, PortableManifestConfig } from './adapters/portable.ts'; +export type { CapabilityRow, HostCapabilityTable } from './adapters/capability-state.ts'; // The config hook handler contract (#488): the payload a `hooks..handler` // receives and the result the generated wrapper admits, per canonical event. diff --git a/website/docs/en/index.mdx b/website/docs/en/index.mdx index 41352b0a5..383d5160d 100644 --- a/website/docs/en/index.mdx +++ b/website/docs/en/index.mdx @@ -119,32 +119,32 @@ declaration, because a handler has to be bound to an event. ```text title="artifact/" artifact/ ├── .claude-plugin/ -│ ├── plugin.json # Claude Code manifest +│ ├── plugin.json │ └── marketplace.json ├── .codex-plugin/ -│ ├── plugin.json # Codex manifest -│ ├── hooks.json # Codex hook document -│ └── mcp.json # Codex MCP document -├── .agents/plugins/marketplace.json # Codex marketplace -├── .mcp.json # Claude Code MCP document -├── plugin.json # portable (Agent Plugins) manifest -├── mcp.json # portable MCP document +│ ├── plugin.json +│ ├── hooks.json +│ └── mcp.json +├── .agents/plugins/marketplace.json +├── .mcp.json +├── plugin.json +├── mcp.json ├── hooks/ -│ ├── hooks.json # Claude Code hook document -│ ├── session-start-….claude.mjs # one wrapper per host the hook reaches +│ ├── hooks.json +│ ├── session-start-….claude.mjs │ ├── session-start-….codex.mjs │ └── hooks-flight.mjs ├── mcp/ -│ ├── mcp-status-….mjs # compiled once, shared by every host +│ ├── mcp-status-….mjs │ └── mcp-status-…-flight.mjs ├── scripts/check-service.mjs ├── skills/release-review/ │ ├── SKILL.md │ └── references/policy.md -├── INSTALL.md # one section per selected host -├── install.mjs # installer for portable and Cursor -├── agent-bundle.manifest.json # every emitted file with its SHA-256 -└── agent-bundle.hooks.json # hook index over the selected hosts +├── INSTALL.md +├── install.mjs +├── agent-bundle.manifest.json +└── agent-bundle.hooks.json ``` Every selected host reads this one directory as its plugin root: Claude Code finds diff --git a/website/docs/zh/index.mdx b/website/docs/zh/index.mdx index 5369d2c20..013adfa46 100644 --- a/website/docs/zh/index.mdx +++ b/website/docs/zh/index.mdx @@ -116,32 +116,32 @@ Skill、MCP 服务器与脚本都按约定被发现。只有钩子需要声明 ```text title="artifact/" artifact/ ├── .claude-plugin/ -│ ├── plugin.json # Claude Code 清单 +│ ├── plugin.json │ └── marketplace.json ├── .codex-plugin/ -│ ├── plugin.json # Codex 清单 -│ ├── hooks.json # Codex 钩子文档 -│ └── mcp.json # Codex MCP 文档 -├── .agents/plugins/marketplace.json # Codex marketplace -├── .mcp.json # Claude Code MCP 文档 -├── plugin.json # portable(Agent Plugins)清单 -├── mcp.json # portable MCP 文档 +│ ├── plugin.json +│ ├── hooks.json +│ └── mcp.json +├── .agents/plugins/marketplace.json +├── .mcp.json +├── plugin.json +├── mcp.json ├── hooks/ -│ ├── hooks.json # Claude Code 钩子文档 -│ ├── session-start-….claude.mjs # 钩子到达的每个宿主各一个包装脚本 +│ ├── hooks.json +│ ├── session-start-….claude.mjs │ ├── session-start-….codex.mjs │ └── hooks-flight.mjs ├── mcp/ -│ ├── mcp-status-….mjs # 只编译一次,所有宿主共用 +│ ├── mcp-status-….mjs │ └── mcp-status-…-flight.mjs ├── scripts/check-service.mjs ├── skills/release-review/ │ ├── SKILL.md │ └── references/policy.md -├── INSTALL.md # 每个所选宿主一节 -├── install.mjs # portable 与 Cursor 的安装器 -├── agent-bundle.manifest.json # 每个输出文件及其 SHA-256 -└── agent-bundle.hooks.json # 覆盖所选宿主的钩子索引 +├── INSTALL.md +├── install.mjs +├── agent-bundle.manifest.json +└── agent-bundle.hooks.json ``` 每个所选宿主都把这同一个目录当作自己的插件根目录来读取:Claude Code 找到 `.claude-plugin/`、 diff --git a/website/plugins/generated-reference.ts b/website/plugins/generated-reference.ts index 10cb902c6..cedde87e3 100644 --- a/website/plugins/generated-reference.ts +++ b/website/plugins/generated-reference.ts @@ -1,6 +1,11 @@ import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'; import path from 'node:path'; import type { RspressPlugin } from '@rspress/core'; +import type { + CapabilityRow, + HostCapabilityTable, +} from '../../packages/agent-bundle/src/adapters/capability-state.ts'; +import type { JsonObject, JsonValue } from '../../packages/agent-bundle/src/core/strict-json.ts'; /** * Build-time reference pages rendered from repository sources of truth: @@ -15,26 +20,6 @@ import type { RspressPlugin } from '@rspress/core'; * artifacts exactly like authored pages, while never being committed. */ -type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; -type JsonObject = { [key: string]: JsonValue }; - -interface CapabilityRow { - readonly state?: string; - readonly reason?: string; - readonly nativeEvent?: string; - readonly evidence?: readonly string[]; - readonly availability?: Readonly>; - /** Canonical payload field → host key (or `{ nativeKey, decode }`), on supported event-route rows. */ - readonly payload?: JsonObject; -} - -interface HostCapabilityTable { - readonly fileName: string; - readonly host: string; - readonly version: string; - readonly data: JsonObject; -} - export interface GeneratedReferenceLocale { /** Locale key, such as `en` or `zh`. */ readonly lang: 'en' | 'zh'; @@ -79,7 +64,7 @@ const mcpPathTokenFields = (host: JsonObject): JsonObject => { return {}; } const groups = asObject(substitution.fields); - const derived: JsonObject = {}; + const derived: Record = {}; for (const group of ['mcpStdio', 'mcpRemote']) { const fields = groups[group]; if (Array.isArray(fields)) {