@beeos-ai/beeos-types
v4.1.0
Published
Domain-namespaced TypeScript wire types for BeeOS protocols and shared agent/conversation surfaces. Independent of upstream @agentclientprotocol/sdk.
Maintainers
Readme
@beeos-ai/beeos-types
Domain-namespaced TypeScript wire types for BeeOS protocols.
Today this package ships one domain — acp — which covers the full agent client protocol
spoken between beeos-claw agents and the web / mobile SDKs. The layout is designed so
future domains (platform, a2a, …) can be added without churn in existing consumers.
This package is the single source of truth for the JSON-over-WebSocket protocol that flows
between BeeOS agents and their front-end clients. It is independent of the upstream
@agentclientprotocol/sdk — BeeOS operates a proxy layer that adapts ACP for multi-tenant
cloud deployment.
Install
npm install @beeos-ai/beeos-typesUsage
All protocol types live under a domain namespace. The package root intentionally does not re-export types flat: this forces the domain to appear at the call site, which is what makes the package extensible without collisions.
Pick whichever style suits the file:
Option A — subpath import (preferred for heavy type files)
import type {
ServerSessionUpdate,
ClientSessionUpdate,
AgentMessageChunkUpdate,
ToolCallStartUpdate,
StreamCompleteOk,
StreamCompleteErr,
} from "@beeos-ai/beeos-types/acp";
function handle(update: ClientSessionUpdate) {
switch (update.sessionUpdate) {
case "agent_message_chunk":
// update.content is narrowed to TextBlock | ImageBlock | ResourceLinkBlock
break;
case "stream_complete":
if (update.stopReason === "error") {
// update.error is narrowed to string (required)
}
break;
}
}Option B — namespace import (preferred when mixing multiple future domains)
import { acp } from "@beeos-ai/beeos-types";
const update: acp.ClientSessionUpdate = /* ... */;Produce messages (factories, type-safe, no as any)
import {
makeAgentMessageChunk,
makeToolCallStart,
makeStreamCompleteError,
} from "@beeos-ai/beeos-types/acp/factories";
const chunk = makeAgentMessageChunk("Hello", { _turnId: "run-abc" });
const err = makeStreamCompleteError("Provider timed out");Normalize snake_case payloads (legacy interop)
import { normalizeSessionUpdate } from "@beeos-ai/beeos-types/acp/normalize";
// Converts upstream snake_case keys (tool_call_id, raw_input) to camelCase
// and trims unknown fields before feeding to chat-store.
const camel = normalizeSessionUpdate(rawWireMessage);Design principles
- Domain-namespaced — every protocol lives under its own directory / namespace. The root barrel only re-exports namespaces, never flat types. This is Interface Segregation at the package level: adding a new domain can never rename or shadow an existing symbol.
- Protocol is a contract, not an implementation — types live in a versioned package so
beeos-claw,web, andmobiledepend on the same source. - Discriminated unions — every
sessionUpdatevariant and JSON-RPCmethodis a separate interface keyed by a string literal;switchstatements are provably exhaustive. - LSP subset —
ServerSessionUpdate ⊂ ClientSessionUpdate. The transport signature uses the narrower type, the reducer signature uses the wider one. - Zero runtime deps —
@a2ui/reactand other UI frameworks are decoupled via genericCanvasUpdateEnvelope<TMessage = unknown>; consumers instantiate with their message type. - Branded IDs —
JsonRpcId,SessionId,ToolCallId,MessageIdare nominal string types preventing accidental cross-use.
Package layout
src/
├── index.ts # root barrel: `export * as acp` only
└── acp/ # ACP protocol domain
├── index.ts # acp barrel
├── meta.ts # ACP_PROTOCOL_VERSION, ResponseMeta
├── ids.ts # branded IDs
├── enums.ts # ToolKind / ToolStatus / StopReason / MessageRole
├── run-outcome.ts # RunOutcomeShape for prompt termination
├── normalize.ts # runtime snake_case ↔ camelCase helper
├── content/ # ContentBlock variants (Text/Image/Audio/File/Resource/ResourceLink)
├── session-update/ # 16 sessionUpdate variants + ServerSessionUpdate / ClientSessionUpdate
├── json-rpc/ # envelope + 29 method-specific params/results + registry
└── factories/ # type-safe builders (replaces all `as unknown as ACPUpdatePayload`)Subpath exports in package.json:
| Subpath | Contents |
| ------------------------------------ | ------------------------------------------- |
| @beeos-ai/beeos-types | root barrel — acp namespace only |
| @beeos-ai/beeos-types/acp | all ACP types (flat) |
| @beeos-ai/beeos-types/acp/factories| message factories |
| @beeos-ai/beeos-types/acp/normalize| snake_case ↔ camelCase normalizer |
Versioning
1.x— API stable. Breaking changes to a domain surface only ship at majors.- Adding a new domain (e.g.
platform,a2a) is a minor bump: existing consumers are unaffected because the root barrel only exposes namespaces.
Compatible with:
beeos-claw>= 0.2.41@beeos/sdk(web/mobile) via facade re-exports
Migration from 0.x
1.0.0 is a breaking change on the import path only; the runtime wire protocol and all
type shapes are identical to 0.1.0-beta.4. Pick the style that fits each file and do a
whole-repo find/replace:
| 0.x | 1.0 |
| ------------------------------------------------------ | ------------------------------------------------------------ |
| from "@beeos-ai/beeos-types" | from "@beeos-ai/beeos-types/acp" |
| from "@beeos-ai/beeos-types/factories" | from "@beeos-ai/beeos-types/acp/factories" |
| from "@beeos-ai/beeos-types/normalize" | from "@beeos-ai/beeos-types/acp/normalize" |
If you prefer namespaced access, the alternative is:
import { acp } from "@beeos-ai/beeos-types";
type X = acp.ClientSessionUpdate;License
MIT
Runtime-control contracts are available from @beeos-ai/beeos-types/runtime.
They intentionally remain separate from the frozen ACP wire domain: Service
callers use platform identifiers and credential references, while adapters map
both transports to canonical commands. The exported parity manifest records
every currently accepted ACP request/notification and remains gap until its
Service implementation, ACP golden fixture, and semantic parity tests pass on
the same release-candidate SHA.
Backend and beeos-claw internal adapters import authority DTOs from
@beeos-ai/beeos-types/runtime/internal. The generated endpoint schema is
available at @beeos-ai/beeos-types/contracts/runtime-internal-api.v1.schema.json.
These internal registration, claim and resource-redemption routes are not
Public API v1 routes and do not add ACP parity entries.
Migration to 4.0.0
RuntimeRegistrationRequest.manifestDigest is now required. A runtime computes
it as lowercase SHA-256 over the UTF-8 RFC 8785/JCS canonical JSON of the
frozen runtime-method-manifest.v1.json artifact. The digest is external to the
manifest and must never be inserted into the manifest being hashed. Internal
adapters reject missing, non-canonical, or unexpected digests before activating
a runtime lease. ACP wire types and public Service DTOs are unchanged.
Cross-language registration bytes and hash preimages are frozen in
runtime-registration-request.v4.json and
runtime-registration-vectors.v4.json under the contracts export.
Resource-binding carrier in 4.1.0
runtime_rpc_request.payload.resourceBindings carries only immutable,
operation-scoped redemption authority: identity binding, resource ref, version,
digest, required Skill size, and expiresAt. It never carries URLs, runtime
configuration, credentials, tokens, or artifact bytes. mcp/set,
skills/install, and agent/applyTemplate use the generated frozen manifest
requirements to bind exactly their canonical definition or artifact before a
runtime may redeem it.
The behavior is selected only by the explicit
resourceBindingContractRevision: "2026-07-15.1" discriminator. A Gateway
may add that discriminator and bindings only after the target runtime
capability manifest advertises the same revision; it never rewrites an
already-dispatched request. A legacy request omits both fields and retains the
old semantics. A request with bindings but no discriminator, or with an
unknown revision, is rejected. New-revision resource methods require exactly
the frozen binding; non-resource methods may omit it.
