UIAP Core
UIAP Core v0.1
Section titled “UIAP Core v0.1”| Field | Value |
|---|---|
| Status | Draft |
| Version | 0.1 |
| Date | 2026-03-27 |
| Dependencies | — |
| Editors | Patrick |
1. Purpose and Scope
Section titled “1. Purpose and Scope”UIAP (UI Control Protocol) is a transport-agnostic protocol between a running application and an AI agent or agent bridge layer.
UIAP Core v0.1 defines:
- the common message envelope
- session establishment and session lifecycle
- error format and error codes
- version negotiation
- extension mechanism
- high-level capability discovery
UIAP Core v0.1 does not define:
- concrete web/DOM/ARIA semantics
- concrete browser control
- detailed policy or security rules
- workflow/skill definitions
- visual presentation such as ghost cursors or highlights
These concerns belong in profiles or extensions.
2. Normative Terms
Section titled “2. Normative Terms”The key words MUST, MUST NOT, SHOULD, MAY in this document are to be interpreted as described in RFC 2119 and BCP 14, when and only when they appear in ALL CAPS.
3. Fundamental Principles
Section titled “3. Fundamental Principles”- UIAP is transport-agnostic. It MAY be carried over WebSocket, WebRTC DataChannel,
postMessage, local IPC, or other bindings. - UIAP messages are UTF-8 encoded JSON objects.
- Every UIAP message MUST use a common Envelope.
- UIAP Core is strict-by-envelope, flexible-by-payload: mandatory fields MUST be validated; unknown optional fields SHOULD be ignored.
- UIAP separates protocol contract from execution mechanics.
4. Data Types
Section titled “4. Data Types”4.1 Primitive Types
Section titled “4.1 Primitive Types”type Version = string; // Format: "major.minor", e.g. "0.1"type Timestamp = string; // ISO-8601 UTC, e.g. "2026-03-26T13:12:09.123Z"type MessageId = string; // 1..128 characters, unique within a sessiontype SessionId = string; // 1..128 characters, assigned by the session ownertype ExtensionId = string; // e.g. "uiap.policy" or "x.vendor.foo"type MessageType = string; // e.g. "session.initialize"type JsonObject = Record<string, unknown>;4.2 Identifiers
Section titled “4.2 Identifiers”MessageIdMUST be unique within a session.SessionIdMUST be unique per active session.- Vendor-specific identifiers SHOULD be stable and human-readable.
- Extension names SHOULD be lowercase and dot-segmented, e.g.
uiap.policyorx.acme.billing.
5. Message Envelope
Section titled “5. Message Envelope”Every message MUST use this envelope.
interface UIAPEnvelope { uiap: Version; // protocol version of this message kind: "request" | "response" | "event" | "error"; type: MessageType; // e.g. "session.initialize" id: MessageId; sessionId?: SessionId; // optional before session init correlationId?: MessageId; // mandatory for response/error ts: Timestamp; source: EndpointRef; target?: EndpointRef; seq?: number; // optional, for unordered transports requires?: string[]; // required profiles/extensions payload: JsonObject; // NEVER null, ALWAYS an object ext?: Record<ExtensionId, unknown>; // optional extension data}interface EndpointRef { role: "app" | "agent" | "bridge" | "observer" | string; id: string; instanceId?: string;}5.1 Envelope Rules
Section titled “5.1 Envelope Rules”uiap,kind,type,id,ts,source,payloadMUST be present.payloadMUST be a JSON object, even if empty.correlationIdMUST be set forresponseanderror.sessionIdMAY be absent forsession.initialize; after that it SHOULD always be present.extMUST only contain extension data. Core fields MUST NOT be overridden viaext.- Unknown optional fields SHOULD be ignored.
- Senders SHOULD omit optional fields rather than sending
null, unlessnullcarries its own semantics.
5.2 kind Semantics
Section titled “5.2 kind Semantics”request: expects exactly oneresponseorerrorresponse: successful reply to arequestevent: unidirectional state or status notification, no mandatory replyerror: failed processing of arequest
5.3 type Namespace
Section titled “5.3 type Namespace”Core reserves:
session.*capabilities.*error
Extensions MUST use their own namespace, e.g.:
uiap.policy.*uiap.workflow.*x.vendor.feature.*
6. Session Lifecycle
Section titled “6. Session Lifecycle”6.1 Session States
Section titled “6.1 Session States”A UIAP session has exactly one of these states:
NEWINITIALIZINGACTIVEINTERRUPTEDTERMINATINGTERMINATED
6.2 State Transitions
Section titled “6.2 State Transitions”NEW -> INITIALIZINGviasession.initializeINITIALIZING -> ACTIVEviasession.initializedACTIVE -> INTERRUPTEDviasession.interrupt/session.interruptedINTERRUPTED -> ACTIVEviasession.resume/session.resumedACTIVE|INTERRUPTED -> TERMINATINGviasession.terminateTERMINATING -> TERMINATEDviasession.terminated- Any state MAY transition directly to
TERMINATEDon a fatal transport error
6.3 Handshake Rules
Section titled “6.3 Handshake Rules”- A session MUST be started with
session.initialize. - The remote peer MUST reply with
session.initializedorerror. - Before a successful handshake, no non-session-core messages MUST be processed.
- Capability information MAY be delivered inline during the handshake or deferred.
7. Core Message Types
Section titled “7. Core Message Types”7.1 session.initialize
Section titled “7.1 session.initialize”Starts a session and negotiates versions, profiles, and extensions.
Request
Section titled “Request”interface SessionInitializePayload { supportedVersions: Version[]; // non-empty supportedProfiles?: string[]; // e.g. ["web@0.1"] supportedExtensions?: ExtensionOffer[]; capabilityDelivery?: "inline" | "deferred" | "none"; peer: PeerInfo; metadata?: JsonObject;}interface PeerInfo { role: "app" | "agent" | "bridge" | string; name?: string; version?: string; locale?: string; timezone?: string; tenantId?: string; userRole?: string;}interface ExtensionOffer { id: ExtensionId; versions: Version[]; required?: boolean;}Response: session.initialized
Section titled “Response: session.initialized”interface SessionInitializedPayload { sessionId: SessionId; selectedVersion: Version; selectedProfiles?: string[]; selectedExtensions?: SelectedExtension[]; capabilityDelivery: "inline" | "deferred" | "none"; heartbeatMs?: number; resumeToken?: string; capabilities?: CapabilityDocument; // only when capabilityDelivery="inline" metadata?: JsonObject;}interface SelectedExtension { id: ExtensionId; version: Version;}supportedVersionsMUST contain at least one entry.- If a sender marks an extension as
required=trueand it is not selected, the handshake MUST fail. selectedVersionMUST correspond to exactly one version offered by the initiator and supported by the receiver.selectedProfilesMAY be empty if only Core is used.capabilitiesMUST only be present whencapabilityDelivery = "inline".
7.2 session.resume
Section titled “7.2 session.resume”Resumes an existing session.
Request
Section titled “Request”interface SessionResumePayload { sessionId: SessionId; resumeToken: string; metadata?: JsonObject;}Response: session.resumed
Section titled “Response: session.resumed”interface SessionResumedPayload { sessionId: SessionId; selectedVersion: Version; selectedProfiles?: string[]; selectedExtensions?: SelectedExtension[]; heartbeatMs?: number; metadata?: JsonObject;}resumeTokenis opaque and defined by the session owner.- If
resumeTokenis invalid, anerrorwithcode="unknown_session"orcode="permission_denied"MUST be returned.
7.3 session.interrupt
Section titled “7.3 session.interrupt”Interrupts an active session without terminating it.
Request
Section titled “Request”interface SessionInterruptPayload { reason?: string; by?: "user" | "system" | "policy" | "transport" | string; metadata?: JsonObject;}Response: session.interrupted
Section titled “Response: session.interrupted”interface SessionInterruptedPayload { status: "interrupted"; reason?: string;}- In the
INTERRUPTEDstate, only session messages MUST be processed, unless a profile defines otherwise.
7.4 session.ping
Section titled “7.4 session.ping”Liveness check.
Request
Section titled “Request”interface SessionPingPayload { nonce?: string;}Response: session.pong
Section titled “Response: session.pong”interface SessionPongPayload { nonce?: string;}- If
heartbeatMswas negotiated, each side SHOULD sendsession.pingduring idle periods. - If repeated responses are missing, the session MAY be terminated.
7.5 session.terminate
Section titled “7.5 session.terminate”Terminates the session gracefully.
Request
Section titled “Request”interface SessionTerminatePayload { reason?: "normal" | "timeout" | "error" | "policy" | "disconnect" | string; metadata?: JsonObject;}Response: session.terminated
Section titled “Response: session.terminated”interface SessionTerminatedPayload { status: "terminated"; reason?: string;}- After
session.terminated, no further non-terminating UIAP messages MUST be processed.
7.6 capabilities.get
Section titled “7.6 capabilities.get”Requests the capability document.
Request
Section titled “Request”interface CapabilitiesGetPayload { include?: Array<"roles" | "states" | "affordances" | "actions" | "risk" | "signals" | "all">;}Response: capabilities.list
Section titled “Response: capabilities.list”interface CapabilitiesListPayload { revision?: string; capabilities: CapabilityDocument;}- If
includeis absent, the full capability document MUST be delivered. revisionMAY be used for caching and change detection.
7.7 capabilities.changed
Section titled “7.7 capabilities.changed”Announces a change in the capability landscape.
interface CapabilitiesChangedPayload { revision: string; reason?: "app_update" | "role_change" | "feature_flag" | "configuration" | string; capabilities: CapabilityDocument;}- In v0.1
capabilities.changedMUST always deliver a full replacement document, not patches. - Receivers SHOULD fully replace the previous document.
8. Error Model
Section titled “8. Error Model”Errors are sent as kind="error".
Envelope Rules for Errors
Section titled “Envelope Rules for Errors”typeMUST be"error".correlationIdMUST point to the failed request.
Error Payload
Section titled “Error Payload”interface UIAPErrorPayload { code: ErrorCode; message: string; retryable?: boolean; failedType?: string; details?: JsonObject;}Core Error Codes
Section titled “Core Error Codes”type ErrorCode = | "bad_request" | "invalid_message" | "unknown_message_type" | "unsupported_version" | "unsupported_profile" | "unsupported_extension" | "unknown_session" | "session_not_active" | "permission_denied" | "capability_unavailable" | "timeout" | "rate_limited" | "state_conflict" | "internal_error";Error Semantics
Section titled “Error Semantics”bad_request: structurally valid envelope, but semantically invalid requestinvalid_message: envelope or payload is structurally invalidunknown_message_type:typeis unknown or not permitted in the current stateunsupported_version: no compatible version availableunsupported_profile: requested or required profiles are missingunsupported_extension: required extension is missing or has an incompatible versionunknown_session: session is not known or not resumablesession_not_active: message received in wrong session statepermission_denied: auth/policy violationcapability_unavailable: requested capability is not declared or not activetimeout: request or execution timed outrate_limited: receiver is throttling trafficstate_conflict: request no longer matches the current stateinternal_error: unexpected internal error
Error Rules
Section titled “Error Rules”- Every
requestMUST be followed by either aresponseor anerror. - Error codes MAY be extended by extensions.
- Vendor-specific errors SHOULD be namespaced, e.g.
x.vendor.foo_error.
9. Versioning
Section titled “9. Versioning”9.1 Version Format
Section titled “9.1 Version Format”UIAP Core v0.1 uses major.minor.
Examples:
0.10.21.0
9.2 Negotiation
Section titled “9.2 Negotiation”- The initiator MUST list all supported versions in
session.initialize.supportedVersions. - The receiver MUST select exactly one
selectedVersion. - After a successful handshake, every subsequent message MUST carry
uiap = selectedVersion.
9.3 Compatibility Rules
Section titled “9.3 Compatibility Rules”For v0.x, compatibility is intentionally strict:
- Same major version does not automatically imply full compatibility.
- Compatibility is established only through explicit negotiation.
- After negotiation, the selected version is binding.
9.4 Patch/Errata Rule
Section titled “9.4 Patch/Errata Rule”Purely editorial changes SHOULD NOT alter wire semantics. If wire semantics change, the minor version MUST be incremented at minimum.
10. Extension Mechanism
Section titled “10. Extension Mechanism”UIAP Core is extensible.
10.1 Extension Negotiation
Section titled “10.1 Extension Negotiation”Extensions are offered in session.initialize.supportedExtensions and confirmed in session.initialized.selectedExtensions.
10.2 Envelope Field ext
Section titled “10.2 Envelope Field ext”ext carries extension data.
Example:
{ "ext": { "uiap.policy": { "decision": "confirm_required" } }}extentries MUST be grouped by extension ID.- Unnegotiated optional extensions SHOULD be ignored.
- If a message mandatorily requires an extension, it MUST list it in
requires. - If a message arrives with an unfulfilled
requires,unsupported_extensionorunsupported_profileMUST be returned. - Extensions MUST NOT redefine core fields.
10.3 Extended Message Types
Section titled “10.3 Extended Message Types”Extensions MAY define new type namespaces, e.g.:
uiap.policy.evaluateuiap.workflow.startx.vendor.analytics.sample
11. Minimum Core Conformance
Section titled “11. Minimum Core Conformance”A UIAP Core v0.1-conformant implementation MUST:
- validate the envelope
- process
session.initialize - process
session.terminate - support
session.ping/session.pong - support
capabilities.get/capabilities.list - send errors in the defined format
- ignore unknown optional fields
- be able to negotiate versions, profiles, and extensions
12. Minimal Example Handshake
Section titled “12. Minimal Example Handshake”12.1 Request
Section titled “12.1 Request”{ "uiap": "0.1", "kind": "request", "type": "session.initialize", "id": "msg_1", "ts": "2026-03-26T13:00:00.000Z", "source": { "role": "agent", "id": "agent-runtime" }, "payload": { "supportedVersions": ["0.1"], "supportedProfiles": ["web@0.1"], "supportedExtensions": [ { "id": "uiap.policy", "versions": ["0.1"], "required": false } ], "capabilityDelivery": "deferred", "peer": { "role": "agent", "name": "onboarding-agent", "version": "0.1.0", "locale": "de-CH", "timezone": "Europe/Zurich" } }}12.2 Response
Section titled “12.2 Response”{ "uiap": "0.1", "kind": "response", "type": "session.initialized", "id": "msg_2", "correlationId": "msg_1", "sessionId": "sess_123", "ts": "2026-03-26T13:00:00.040Z", "source": { "role": "app", "id": "videoland-app" }, "payload": { "sessionId": "sess_123", "selectedVersion": "0.1", "selectedProfiles": ["web@0.1"], "selectedExtensions": [ { "id": "uiap.policy", "version": "0.1" } ], "capabilityDelivery": "deferred", "heartbeatMs": 15000 }}Normative References
Section titled “Normative References”- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels, BCP 14
Security Considerations
Section titled “Security Considerations”- Transport security is not part of UIAP Core; securing the transport channel (e.g. TLS for WebSocket) MUST be ensured by the binding.
- Session tokens (
resumeToken) SHOULD be cryptographically random and short-lived. extfields MUST NOT be used to circumvent security mechanisms.- Implementations SHOULD support rate limiting for incoming messages.
Changelog
Section titled “Changelog”| Version | Date | Changes |
|---|---|---|
| 0.1 | 2026-03-27 | Initial draft |