oui-spec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +680 -0
  3. package/dist/chunk-2ON5IEXB.cjs +167 -0
  4. package/dist/chunk-2ON5IEXB.cjs.map +1 -0
  5. package/dist/chunk-3DCIL4FV.cjs +14 -0
  6. package/dist/chunk-3DCIL4FV.cjs.map +1 -0
  7. package/dist/chunk-7EXSMPGR.js +207 -0
  8. package/dist/chunk-7EXSMPGR.js.map +1 -0
  9. package/dist/chunk-CVUUSPF4.js +12 -0
  10. package/dist/chunk-CVUUSPF4.js.map +1 -0
  11. package/dist/chunk-JO5LGOB6.js +88 -0
  12. package/dist/chunk-JO5LGOB6.js.map +1 -0
  13. package/dist/chunk-LEXIU2MH.cjs +90 -0
  14. package/dist/chunk-LEXIU2MH.cjs.map +1 -0
  15. package/dist/chunk-UIGRLZOX.js +164 -0
  16. package/dist/chunk-UIGRLZOX.js.map +1 -0
  17. package/dist/chunk-WZXUKJ7E.cjs +210 -0
  18. package/dist/chunk-WZXUKJ7E.cjs.map +1 -0
  19. package/dist/core/index.cjs +17 -0
  20. package/dist/core/index.cjs.map +1 -0
  21. package/dist/core/index.d.cts +120 -0
  22. package/dist/core/index.d.ts +120 -0
  23. package/dist/core/index.js +4 -0
  24. package/dist/core/index.js.map +1 -0
  25. package/dist/index.cjs +35 -0
  26. package/dist/index.cjs.map +1 -0
  27. package/dist/index.d.cts +4 -0
  28. package/dist/index.d.ts +4 -0
  29. package/dist/index.js +6 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/react/index.cjs +16 -0
  32. package/dist/react/index.cjs.map +1 -0
  33. package/dist/react/index.d.cts +62 -0
  34. package/dist/react/index.d.ts +62 -0
  35. package/dist/react/index.js +3 -0
  36. package/dist/react/index.js.map +1 -0
  37. package/dist/spec/index.cjs +12 -0
  38. package/dist/spec/index.cjs.map +1 -0
  39. package/dist/spec/index.d.cts +249 -0
  40. package/dist/spec/index.d.ts +249 -0
  41. package/dist/spec/index.js +3 -0
  42. package/dist/spec/index.js.map +1 -0
  43. package/dist/transport/index.cjs +16 -0
  44. package/dist/transport/index.cjs.map +1 -0
  45. package/dist/transport/index.d.cts +91 -0
  46. package/dist/transport/index.d.ts +91 -0
  47. package/dist/transport/index.js +3 -0
  48. package/dist/transport/index.js.map +1 -0
  49. package/package.json +109 -0
  50. package/spec/OUI-SPEC-v0.1.md +1323 -0
@@ -0,0 +1,1323 @@
1
+ # OUI Specification
2
+
3
+ **Version:** 0.1.0
4
+ **Status:** Draft
5
+ **Date:** 2026-07-27
6
+ **Authors:** Closure Studio
7
+
8
+ ---
9
+
10
+ ## Abstract
11
+
12
+ This document defines the **Open UI Specification (OUI)** — a machine-readable contract that allows any user interface application to declare a semantic control surface for consumption by AI agent runtimes. OUI enables agents to discover, understand, and invoke application capabilities through typed, structured interactions rather than brittle visual or DOM-based approaches.
13
+
14
+ ---
15
+
16
+ ## Table of Contents
17
+
18
+ 1. [Introduction](#1-introduction)
19
+ 2. [Terminology](#2-terminology)
20
+ 3. [Surface Object](#3-surface-object)
21
+ 4. [Action Object](#4-action-object)
22
+ 5. [Observation Object](#5-observation-object)
23
+ 6. [Activation Object](#6-activation-object)
24
+ 7. [Protocol](#7-protocol)
25
+ 8. [Transport Layer](#8-transport-layer)
26
+ 9. [Security Considerations](#9-security-considerations)
27
+ 10. [Conformance](#10-conformance)
28
+
29
+ ---
30
+
31
+ ## 1. Introduction
32
+
33
+ ### 1.1 Purpose
34
+
35
+ OUI defines a standard interface through which AI agents can programmatically control user interfaces. It replaces fragile vision-based or DOM-scraping approaches with an application-declared, typed, semantic contract.
36
+
37
+ An application that implements OUI declares:
38
+ - **What it can do** (Actions)
39
+ - **What state is observable** (Observations)
40
+ - **When capabilities are available** (Activation)
41
+
42
+ An agent runtime that consumes OUI:
43
+ - **Discovers** available capabilities at runtime
44
+ - **Invokes** typed operations and receives structured results
45
+ - **Observes** application state changes in real-time
46
+
47
+ ### 1.2 Problem Statement
48
+
49
+ Current approaches for AI agents to interact with user interfaces are fundamentally unreliable:
50
+
51
+ | Approach | Failure Mode |
52
+ |----------|-------------|
53
+ | Screenshot + Vision Model | Breaks on theme changes, responsive layouts, overlays |
54
+ | DOM Scraping | Breaks on framework updates, CSS refactors, dynamic rendering |
55
+ | Coordinate Clicking | Breaks on window resize, scroll position, element reflow |
56
+ | Accessibility Tree | Incomplete for custom components, missing semantics |
57
+
58
+ All of these approaches attempt to **infer** application capabilities from implementation details. OUI inverts this: the application **declares** its capabilities explicitly.
59
+
60
+ ### 1.3 Relationship to OpenAPI
61
+
62
+ OUI is the UI-side complement to the [OpenAPI Specification](https://spec.openapis.org/oas/latest.html):
63
+
64
+ ```
65
+ OpenAPI Spec + HTTP Server = any client can consume the API
66
+ OUI Manifest + UI App = any agent can control the UI
67
+ ```
68
+
69
+ | Dimension | OpenAPI | OUI |
70
+ |-----------|---------|-----|
71
+ | Describes | HTTP endpoints | UI capabilities |
72
+ | Consumer | HTTP clients | Agent runtimes |
73
+ | Transport | HTTP request/response | Protocol events (WebSocket, postMessage, etc.) |
74
+ | Discovery | Static spec document (JSON/YAML) | Dynamic registration (surfaces appear/disappear) |
75
+ | Statefulness | Stateless (per-request) | Stateful (observations persist, surfaces have lifecycle) |
76
+
77
+ An application MAY expose both an OpenAPI spec for its REST API and an OUI manifest for its UI. These serve different consumers: programmatic API clients vs. interactive agent runtimes.
78
+
79
+ ### 1.4 Design Principles
80
+
81
+ 1. **App-declared, not inferred.** The application explicitly states what it supports.
82
+ 2. **Semantic, not structural.** Actions are named operations, not DOM paths.
83
+ 3. **Typed end-to-end.** Inputs and outputs conform to JSON Schema.
84
+ 4. **Bidirectional.** Actions return structured results; agents know what happened.
85
+ 5. **Framework-agnostic.** Works with any UI technology that can declare actions and handle invocations.
86
+ 6. **Consent-based.** The application controls what is exposed to agents.
87
+ 7. **Observable.** Agents read structured state, not pixels.
88
+ 8. **Composable.** Multiple surfaces coexist; complex apps expose many surfaces.
89
+
90
+ ### 1.5 Notational Conventions
91
+
92
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
93
+
94
+ Field types reference [JSON Schema Draft 2020-12](https://json-schema.org/draft/2020-12/json-schema-core.html). All schemas in this specification are expressed as JSON Schema unless otherwise noted.
95
+
96
+ ---
97
+
98
+ ## 2. Terminology
99
+
100
+ | Term | Definition |
101
+ |------|-----------|
102
+ | **Surface** | A controllable boundary within an application that declares a set of actions and observations. Represents a cohesive unit of functionality (e.g., a page, a feature, a component) that an agent can interact with. |
103
+ | **Action** | A single, named operation that an agent can invoke on a surface. Has typed input parameters and a typed return value. Becomes a "tool" in the agent runtime's tool set. |
104
+ | **Observation** | A piece of application state that the agent can read. Observations are pushed to the agent when state changes, providing situational awareness without screenshots. |
105
+ | **Manifest** | The serializable, schema-only representation of a surface — its metadata, actions (without handlers), and observations. This is what crosses the wire to the agent runtime. |
106
+ | **Agent Runtime** | The system that consumes OUI manifests, presents actions as tools to an LLM, and coordinates invocations. Examples: Closure Agent SDK, LangChain, custom orchestrators. |
107
+ | **Transport** | The communication layer between a surface and the agent runtime. OUI is transport-agnostic; bindings are defined for WebSocket, postMessage, and direct function call. |
108
+ | **Activation** | Conditions under which a surface is available. A surface that is not activated MUST NOT be presented to the agent as available. |
109
+ | **Protocol Event** | A typed message exchanged between surfaces and the agent runtime over the transport layer. |
110
+ | **Correlation ID** | A unique identifier (`requestId`) that links an action request to its corresponding result, enabling asynchronous request/response over any transport. |
111
+ | **Handler** | The implementation function that executes when an action is invoked. Handlers are local to the surface and never cross the wire. |
112
+
113
+ ---
114
+
115
+ ## 3. Surface Object
116
+
117
+ A Surface is the top-level object in OUI. It represents a self-contained, controllable boundary within an application.
118
+
119
+ ### 3.1 Schema
120
+
121
+ | Field | Type | Required | Description |
122
+ |-------|------|----------|-------------|
123
+ | `id` | `string` | ✅ | Unique identifier for this surface. MUST be unique within the application scope. SHOULD use kebab-case (e.g., `document-editor`, `dataviz-wizard`). |
124
+ | `name` | `string` | ✅ | Human-readable display name for the surface. Used in agent UIs and logs. |
125
+ | `description` | `string` | ✅ | Natural-language description of what this surface does. This text is included in the agent's LLM context to inform tool selection. SHOULD be concise but descriptive (1–3 sentences). |
126
+ | `version` | `string` | | Semantic version of this surface's contract (e.g., `1.0.0`). When present, agent runtimes MAY use this for compatibility checking. |
127
+ | `actions` | `OUIAction[]` | ✅ | Array of actions available on this surface. MUST contain at least one action. |
128
+ | `observations` | `OUIObservation[]` | | Array of observable state exposed by this surface. MAY be empty or omitted. |
129
+ | `activation` | `OUIActivation` | | Conditions under which this surface is active. If omitted, the surface is considered always active when registered. |
130
+ | `metadata` | `object` | | Arbitrary key-value pairs for implementation-specific extensions. Agent runtimes SHOULD pass metadata to the LLM context if it aids decision-making. |
131
+
132
+ ### 3.2 Identifier Requirements
133
+
134
+ Surface IDs:
135
+ - MUST match the pattern `^[a-z][a-z0-9-]*[a-z0-9]$` (lowercase letters, digits, hyphens; must start with a letter and end with a letter or digit)
136
+ - MUST be between 2 and 64 characters in length
137
+ - MUST be unique within a single application's registration scope
138
+
139
+ ### 3.3 Description Best Practices
140
+
141
+ The `description` field is critical — it is the primary signal an LLM uses to decide whether to interact with a surface. Effective descriptions:
142
+ - State the surface's **purpose** (what it lets you do)
143
+ - Mention the **domain** (what kind of data/entities it works with)
144
+ - Are **concise** (avoid implementation details)
145
+
146
+ ### 3.4 Example
147
+
148
+ ```json
149
+ {
150
+ "id": "document-editor",
151
+ "name": "Document Editor",
152
+ "description": "Rich text document editor. Supports creating, editing, formatting, and exporting documents.",
153
+ "version": "1.2.0",
154
+ "actions": [ ... ],
155
+ "observations": [ ... ],
156
+ "activation": {
157
+ "routes": ["/documents/*"]
158
+ },
159
+ "metadata": {
160
+ "maxDocumentSize": "10MB",
161
+ "supportedFormats": ["markdown", "html", "pdf"]
162
+ }
163
+ }
164
+ ```
165
+
166
+ ### 3.5 Lifecycle
167
+
168
+ A surface has the following lifecycle states:
169
+
170
+ ```
171
+ [Defined] → [Registered] → [Active] → [Deregistered]
172
+ ↕
173
+ [Inactive]
174
+ ```
175
+
176
+ - **Defined:** The surface is declared in code but not yet connected to a transport.
177
+ - **Registered:** The surface has been announced to the agent runtime via a `surface:register` event.
178
+ - **Active:** The surface's activation conditions are met; its actions are available for invocation.
179
+ - **Inactive:** The surface is registered but its activation conditions are not currently met. The agent runtime MUST NOT invoke actions on an inactive surface.
180
+ - **Deregistered:** The surface has been removed via a `surface:deregister` event. All references MUST be cleaned up.
181
+
182
+ ---
183
+
184
+ ## 4. Action Object
185
+
186
+ An Action represents a single operation an agent can perform on a surface. Each action is presented to the LLM as a callable tool.
187
+
188
+ ### 4.1 Schema
189
+
190
+ | Field | Type | Required | Description |
191
+ |-------|------|----------|-------------|
192
+ | `id` | `string` | ✅ | Unique action identifier within this surface. Becomes the tool name in the agent runtime. MUST use snake_case (e.g., `create_document`, `set_font_size`). |
193
+ | `description` | `string` | ✅ | Natural-language description of what this action does. Included in LLM context. SHOULD clearly state the effect and when to use it. |
194
+ | `input` | `JSONSchema` | ✅ | JSON Schema defining the action's input parameters. The agent runtime validates payloads against this schema before dispatching. |
195
+ | `output` | `JSONSchema` | | JSON Schema defining the action's return value. When present, agent runtimes MAY validate results. If omitted, the result is still returned to the agent as untyped data. |
196
+ | `confirm` | `boolean` | | Default: `false`. If `true`, the agent runtime MUST obtain user confirmation before executing this action. Use for destructive, irreversible, or high-impact operations. |
197
+ | `async` | `boolean` | | Default: `false`. If `true`, the action returns immediately with a tracking identifier. The actual result is delivered later via an `action:result` event. |
198
+ | `usage` | `string` | | Human-readable hint about when this action is appropriate. Supplements `description` with situational guidance (e.g., "Use after the user has selected a document"). |
199
+ | `preconditions` | `string` | | Natural-language description of conditions that should be met before invocation. Expressed for the LLM's benefit, not as executable validation logic. |
200
+ | `estimatedDuration` | `string` | | Human-readable duration hint. One of: `"instant"`, `"< 1s"`, `"1-5s"`, `"5-30s"`, `"30-120s"`, `"> 2min"`, or a custom string. Helps agents manage user expectations. |
201
+ | `tags` | `string[]` | | Categorization tags (e.g., `["write", "destructive"]`, `["read-only"]`). Agent runtimes MAY use tags for filtering or policy enforcement. |
202
+
203
+ ### 4.2 Identifier Requirements
204
+
205
+ Action IDs:
206
+ - MUST match the pattern `^[a-z][a-z0-9_]*[a-z0-9]$` (lowercase letters, digits, underscores; must start with a letter and end with a letter or digit)
207
+ - MUST be between 2 and 64 characters in length
208
+ - MUST be unique within a single surface
209
+
210
+ ### 4.3 Input Schema
211
+
212
+ The `input` field MUST be a valid JSON Schema object. The top-level schema SHOULD have `type: "object"` with a `properties` map. The `required` array specifies which properties are mandatory.
213
+
214
+ **Requirements:**
215
+ - Every property SHOULD include a `description` field to inform the LLM
216
+ - The schema MUST be self-contained (no external `$ref` references in manifest form)
217
+ - Default values SHOULD be specified where applicable
218
+
219
+ **Example:**
220
+
221
+ ```json
222
+ {
223
+ "type": "object",
224
+ "properties": {
225
+ "title": {
226
+ "type": "string",
227
+ "description": "The document title",
228
+ "minLength": 1,
229
+ "maxLength": 255
230
+ },
231
+ "format": {
232
+ "type": "string",
233
+ "enum": ["markdown", "html", "plain"],
234
+ "default": "markdown",
235
+ "description": "The document format"
236
+ },
237
+ "tags": {
238
+ "type": "array",
239
+ "items": { "type": "string" },
240
+ "description": "Optional categorization tags"
241
+ }
242
+ },
243
+ "required": ["title"]
244
+ }
245
+ ```
246
+
247
+ ### 4.4 Output Schema
248
+
249
+ The `output` field, when present, defines the shape of the successful result's `data` payload. It follows the same JSON Schema rules as `input`.
250
+
251
+ If `output` is omitted, the action still returns a result — the `data` field is simply untyped from the spec's perspective.
252
+
253
+ ### 4.5 Execution Semantics
254
+
255
+ #### 4.5.1 Synchronous Actions (`async: false` or omitted)
256
+
257
+ 1. Agent runtime sends an `action:request` event with a unique `requestId`
258
+ 2. Surface validates the input against the action's `input` schema
259
+ 3. Surface executes the handler
260
+ 4. Surface sends an `action:result` event with the same `requestId`
261
+ 5. Agent runtime correlates the result and returns it to the LLM
262
+
263
+ The entire cycle MUST complete within the transport's configured timeout (see [Section 7.5](#75-timeouts)).
264
+
265
+ #### 4.5.2 Asynchronous Actions (`async: true`)
266
+
267
+ 1. Agent runtime sends an `action:request` event with a unique `requestId`
268
+ 2. Surface acknowledges receipt immediately with an `action:result` containing a tracking ID in `data`
269
+ 3. When the operation completes, the surface sends a follow-up `action:result` with the same `requestId` and the final result
270
+
271
+ The initial acknowledgment MUST arrive within the transport timeout. The final result has no protocol-level timeout — the agent runtime tracks pending async operations independently.
272
+
273
+ #### 4.5.3 Confirmed Actions (`confirm: true`)
274
+
275
+ When an action has `confirm: true`:
276
+
277
+ 1. Agent runtime receives the action invocation from the LLM
278
+ 2. Agent runtime presents a confirmation prompt to the human user
279
+ 3. If confirmed: proceeds with normal execution flow
280
+ 4. If denied: returns a result with `success: false` and error code `ACTION_DENIED`
281
+
282
+ The confirmation UX is the agent runtime's responsibility. The surface is not involved in the confirmation flow.
283
+
284
+ ### 4.6 Error Handling
285
+
286
+ When an action fails, the `action:result` MUST have `success: false` and include an `error` object:
287
+
288
+ ```json
289
+ {
290
+ "requestId": "req-abc123",
291
+ "success": false,
292
+ "error": {
293
+ "code": "VALIDATION_ERROR",
294
+ "message": "Parameter 'title' must not be empty",
295
+ "details": {
296
+ "field": "title",
297
+ "constraint": "minLength",
298
+ "expected": 1,
299
+ "actual": 0
300
+ }
301
+ },
302
+ "timestamp": 1706000000000
303
+ }
304
+ ```
305
+
306
+ #### Standard Error Codes
307
+
308
+ | Code | Description |
309
+ |------|-------------|
310
+ | `ACTION_NOT_FOUND` | The specified `actionId` does not exist on this surface |
311
+ | `VALIDATION_ERROR` | Input parameters failed schema validation |
312
+ | `PRECONDITION_FAILED` | Action preconditions are not met |
313
+ | `ACTION_DENIED` | User denied confirmation for a `confirm: true` action |
314
+ | `EXECUTION_ERROR` | Handler threw an unhandled exception |
315
+ | `TIMEOUT` | Action exceeded its execution timeout |
316
+ | `SURFACE_UNAVAILABLE` | The target surface is not currently active |
317
+ | `INTERNAL_ERROR` | An unexpected internal error occurred |
318
+
319
+ Implementations MAY define additional error codes. Custom codes SHOULD use a namespaced format (e.g., `app:quota_exceeded`).
320
+
321
+ ### 4.7 Example
322
+
323
+ ```json
324
+ {
325
+ "id": "create_document",
326
+ "description": "Create a new document in the workspace. Returns the document ID and URL.",
327
+ "input": {
328
+ "type": "object",
329
+ "properties": {
330
+ "title": { "type": "string", "description": "Document title", "minLength": 1 },
331
+ "template": { "type": "string", "enum": ["blank", "meeting-notes", "project-brief"], "default": "blank" }
332
+ },
333
+ "required": ["title"]
334
+ },
335
+ "output": {
336
+ "type": "object",
337
+ "properties": {
338
+ "documentId": { "type": "string" },
339
+ "url": { "type": "string", "format": "uri" },
340
+ "createdAt": { "type": "string", "format": "date-time" }
341
+ }
342
+ },
343
+ "confirm": false,
344
+ "async": false,
345
+ "usage": "Use when the user wants to create a new document. Prefer a descriptive title.",
346
+ "estimatedDuration": "< 1s",
347
+ "tags": ["write", "create"]
348
+ }
349
+ ```
350
+
351
+ ---
352
+
353
+ ## 5. Observation Object
354
+
355
+ An Observation exposes a piece of application state to the agent runtime. Observations give agents situational awareness — they know what's on screen, what data is loaded, and what the current context is — without needing screenshots or DOM access.
356
+
357
+ ### 5.1 Schema
358
+
359
+ | Field | Type | Required | Description |
360
+ |-------|------|----------|-------------|
361
+ | `id` | `string` | ✅ | Unique identifier within the surface. MUST use snake_case. |
362
+ | `description` | `string` | ✅ | Natural-language description of what this observation represents. Included in LLM context. |
363
+ | `schema` | `JSONSchema` | ✅ | JSON Schema defining the shape of the observation's value. |
364
+ | `updateFrequency` | `string` | | One of `"realtime"`, `"on-change"`, or `"polling"`. Defaults to `"on-change"`. Informs the agent runtime about expected update patterns. |
365
+
366
+ ### 5.2 Identifier Requirements
367
+
368
+ Observation IDs follow the same rules as action IDs:
369
+ - MUST match `^[a-z][a-z0-9_]*[a-z0-9]$`
370
+ - MUST be between 2 and 64 characters
371
+ - MUST be unique within a single surface
372
+ - MUST NOT collide with action IDs on the same surface
373
+
374
+ ### 5.3 Update Semantics
375
+
376
+ Observations are state snapshots pushed from the surface to the agent runtime.
377
+
378
+ #### 5.3.1 Update Frequencies
379
+
380
+ | Frequency | Behavior |
381
+ |-----------|----------|
382
+ | `realtime` | Value is pushed on every frame/tick/render cycle. Use sparingly — appropriate for continuously changing values (cursor position, audio level). |
383
+ | `on-change` | Value is pushed whenever the underlying state changes. This is the default and RECOMMENDED mode for most observations. |
384
+ | `polling` | The agent runtime must explicitly request the value. The surface does not push updates proactively. |
385
+
386
+ #### 5.3.2 Push Model (Default)
387
+
388
+ For `realtime` and `on-change` observations:
389
+
390
+ 1. When the surface is registered, it SHOULD emit an initial `observation:update` for each observation with the current value
391
+ 2. When the underlying state changes, the surface emits an `observation:update` event
392
+ 3. The agent runtime maintains the latest value for each observation
393
+ 4. Values are included in the LLM's context on the next interaction
394
+
395
+ #### 5.3.3 Pull Model (`polling`)
396
+
397
+ For `polling` observations:
398
+
399
+ 1. The agent runtime sends an `observation:request` event specifying the surface and observation IDs
400
+ 2. The surface responds with an `observation:update` event containing the current value
401
+
402
+ Pull-mode observations are useful for expensive-to-compute state that shouldn't be pushed continuously.
403
+
404
+ ### 5.4 Value Semantics
405
+
406
+ - Each `observation:update` carries the **complete** current value — not a delta or patch
407
+ - Implementations SHOULD deduplicate updates (don't push if the value hasn't changed)
408
+ - The value MUST conform to the observation's declared `schema`
409
+ - A value of `null` indicates the observation is currently unavailable (e.g., no document is open)
410
+
411
+ ### 5.5 Example
412
+
413
+ ```json
414
+ {
415
+ "id": "document_state",
416
+ "description": "Current state of the active document including title, word count, and selection",
417
+ "schema": {
418
+ "type": "object",
419
+ "properties": {
420
+ "documentId": { "type": "string" },
421
+ "title": { "type": "string" },
422
+ "wordCount": { "type": "integer" },
423
+ "selectedText": { "type": "string", "description": "Currently selected text, empty if no selection" },
424
+ "cursorPosition": {
425
+ "type": "object",
426
+ "properties": {
427
+ "line": { "type": "integer" },
428
+ "column": { "type": "integer" }
429
+ }
430
+ },
431
+ "isDirty": { "type": "boolean", "description": "True if document has unsaved changes" }
432
+ }
433
+ },
434
+ "updateFrequency": "on-change"
435
+ }
436
+ ```
437
+
438
+ ---
439
+
440
+ ## 6. Activation Object
441
+
442
+ Activation defines the conditions under which a surface is available to the agent. Surfaces that fail their activation conditions MUST NOT be presented to the agent as available tools.
443
+
444
+ ### 6.1 Schema
445
+
446
+ | Field | Type | Required | Description |
447
+ |-------|------|----------|-------------|
448
+ | `routes` | `string \| string[]` | | URL path pattern(s) where this surface is active. Supports glob patterns (e.g., `/documents/*`, `/settings/**`). |
449
+ | `entity` | `object` | | An entity that must be loaded/present for this surface to activate. |
450
+ | `entity.type` | `string` | ✅ (if `entity` present) | The entity type (e.g., `"document"`, `"project"`, `"user-profile"`). |
451
+ | `entity.id` | `string` | | A specific entity ID. If omitted, any entity of the given type satisfies the condition. |
452
+ | `condition` | `string` | | Free-form natural-language description of additional activation conditions. Not executable — serves as documentation for the agent runtime and developers. |
453
+
454
+ ### 6.2 Evaluation Rules
455
+
456
+ - If `activation` is omitted entirely, the surface is **always active** when registered
457
+ - If multiple fields are present, **all conditions** must be met (logical AND)
458
+ - Route patterns are matched against the current application URL path
459
+ - Entity conditions are evaluated by the application and communicated via registration/deregistration events
460
+
461
+ ### 6.3 Route Patterns
462
+
463
+ Route patterns follow simplified glob syntax:
464
+
465
+ | Pattern | Matches |
466
+ |---------|---------|
467
+ | `/documents` | Exactly `/documents` |
468
+ | `/documents/*` | `/documents/abc` but not `/documents/abc/edit` |
469
+ | `/documents/**` | `/documents/abc`, `/documents/abc/edit`, `/documents/abc/edit/v2` |
470
+ | `/users/*/settings` | `/users/123/settings` |
471
+
472
+ ### 6.4 Dynamic Activation
473
+
474
+ Applications with complex activation logic SHOULD manage surface lifecycle explicitly:
475
+ - Register the surface (via `surface:register`) when conditions are met
476
+ - Deregister (via `surface:deregister`) when conditions are no longer met
477
+
478
+ The `activation` field in the manifest serves as **documentation** of when the surface is expected to be available. The actual lifecycle is managed by the application.
479
+
480
+ ### 6.5 Example
481
+
482
+ ```json
483
+ {
484
+ "routes": ["/projects/*/dataviz", "/projects/*/charts"],
485
+ "entity": {
486
+ "type": "project"
487
+ },
488
+ "condition": "User must have edit permissions on the project"
489
+ }
490
+ ```
491
+
492
+ ---
493
+
494
+ ## 7. Protocol
495
+
496
+ The OUI Protocol defines the message exchange between agent runtimes and surfaces. It is transport-agnostic — the same message types and semantics apply regardless of whether communication occurs over WebSocket, postMessage, or direct function calls.
497
+
498
+ ### 7.1 Protocol Events
499
+
500
+ All communication consists of typed protocol events. Each event has a `type` string and a `payload` object.
501
+
502
+ | Event Type | Direction | Description |
503
+ |-----------|-----------|-------------|
504
+ | `surface:register` | Surface → Runtime | A surface is announcing itself as available |
505
+ | `surface:deregister` | Surface → Runtime | A surface is announcing it is no longer available |
506
+ | `action:request` | Runtime → Surface | The agent is invoking an action |
507
+ | `action:result` | Surface → Runtime | The surface is returning an action's result |
508
+ | `observation:update` | Surface → Runtime | An observation value has changed |
509
+ | `observation:request` | Runtime → Surface | The agent is requesting a polling observation's current value |
510
+
511
+ ### 7.2 Surface Registration and Deregistration
512
+
513
+ #### 7.2.1 Registration
514
+
515
+ When a surface becomes active, it MUST send a `surface:register` event:
516
+
517
+ ```json
518
+ {
519
+ "type": "surface:register",
520
+ "payload": {
521
+ "surface": {
522
+ "id": "document-editor",
523
+ "name": "Document Editor",
524
+ "description": "Rich text editor for documents",
525
+ "version": "1.2.0",
526
+ "actions": [ ... ],
527
+ "observations": [ ... ],
528
+ "activation": { ... }
529
+ },
530
+ "timestamp": 1706000000000
531
+ }
532
+ }
533
+ ```
534
+
535
+ **Requirements:**
536
+ - The `surface` field contains the complete manifest (schema only, no handlers)
537
+ - The agent runtime MUST acknowledge registration by making the surface's actions available as tools
538
+ - If a surface with the same `id` is already registered, the new registration MUST replace the old one (hot-reload semantics)
539
+
540
+ #### 7.2.2 Deregistration
541
+
542
+ When a surface becomes inactive or is destroyed, it MUST send a `surface:deregister` event:
543
+
544
+ ```json
545
+ {
546
+ "type": "surface:deregister",
547
+ "payload": {
548
+ "surfaceId": "document-editor",
549
+ "timestamp": 1706000000000
550
+ }
551
+ }
552
+ ```
553
+
554
+ **Requirements:**
555
+ - The agent runtime MUST remove the surface's actions from the available tool set
556
+ - Any pending action requests targeting this surface MUST be resolved with error code `SURFACE_UNAVAILABLE`
557
+ - Observation state for this surface MUST be discarded
558
+
559
+ ### 7.3 Action Request and Result
560
+
561
+ #### 7.3.1 Action Request
562
+
563
+ When the agent invokes an action, the runtime sends an `action:request`:
564
+
565
+ ```json
566
+ {
567
+ "type": "action:request",
568
+ "payload": {
569
+ "requestId": "req-f47ac10b-58cc-4372-a567-0e02b2c3d479",
570
+ "surfaceId": "document-editor",
571
+ "actionId": "create_document",
572
+ "params": {
573
+ "title": "Q4 Planning",
574
+ "template": "meeting-notes"
575
+ },
576
+ "timestamp": 1706000000000
577
+ }
578
+ }
579
+ ```
580
+
581
+ **Requirements:**
582
+ - `requestId` MUST be a globally unique string. UUID v4 is RECOMMENDED.
583
+ - `params` MUST conform to the action's declared `input` schema
584
+ - The agent runtime SHOULD validate `params` before sending; the surface MUST validate upon receipt
585
+
586
+ #### 7.3.2 Action Result (Success)
587
+
588
+ ```json
589
+ {
590
+ "type": "action:result",
591
+ "payload": {
592
+ "requestId": "req-f47ac10b-58cc-4372-a567-0e02b2c3d479",
593
+ "success": true,
594
+ "data": {
595
+ "documentId": "doc-789",
596
+ "url": "/documents/doc-789",
597
+ "createdAt": "2026-07-27T10:30:00Z"
598
+ },
599
+ "durationMs": 142,
600
+ "timestamp": 1706000000142
601
+ }
602
+ }
603
+ ```
604
+
605
+ #### 7.3.3 Action Result (Failure)
606
+
607
+ ```json
608
+ {
609
+ "type": "action:result",
610
+ "payload": {
611
+ "requestId": "req-f47ac10b-58cc-4372-a567-0e02b2c3d479",
612
+ "success": false,
613
+ "error": {
614
+ "code": "VALIDATION_ERROR",
615
+ "message": "Parameter 'title' is required",
616
+ "details": { "field": "title" }
617
+ },
618
+ "durationMs": 3,
619
+ "timestamp": 1706000000003
620
+ }
621
+ }
622
+ ```
623
+
624
+ **Requirements:**
625
+ - The `requestId` in the result MUST exactly match the request's `requestId`
626
+ - Exactly one `action:result` MUST be sent for each `action:request` (for sync actions)
627
+ - For async actions: one immediate acknowledgment result, then one final result (both with the same `requestId`)
628
+ - If `success` is `false`, the `error` field MUST be present
629
+ - If `success` is `true`, the `error` field MUST NOT be present
630
+
631
+ ### 7.4 Observation Updates
632
+
633
+ #### 7.4.1 Push Update
634
+
635
+ ```json
636
+ {
637
+ "type": "observation:update",
638
+ "payload": {
639
+ "surfaceId": "document-editor",
640
+ "observationId": "document_state",
641
+ "value": {
642
+ "documentId": "doc-789",
643
+ "title": "Q4 Planning",
644
+ "wordCount": 1247,
645
+ "selectedText": "",
646
+ "cursorPosition": { "line": 42, "column": 8 },
647
+ "isDirty": true
648
+ },
649
+ "timestamp": 1706000001000
650
+ }
651
+ }
652
+ ```
653
+
654
+ #### 7.4.2 Pull Request
655
+
656
+ ```json
657
+ {
658
+ "type": "observation:request",
659
+ "payload": {
660
+ "surfaceId": "document-editor",
661
+ "observationId": "document_state",
662
+ "requestId": "obs-req-001",
663
+ "timestamp": 1706000002000
664
+ }
665
+ }
666
+ ```
667
+
668
+ The surface responds with a standard `observation:update` event.
669
+
670
+ **Requirements:**
671
+ - The `value` MUST conform to the observation's declared `schema`
672
+ - Surfaces SHOULD batch rapid observation updates (e.g., debounce to max 10 updates/second for `on-change` observations)
673
+ - The agent runtime MUST handle out-of-order updates gracefully — always use the latest `timestamp`
674
+
675
+ ### 7.5 Timeouts
676
+
677
+ Timeouts prevent resource leaks and provide predictable failure modes.
678
+
679
+ | Scenario | Default Timeout | Configurable |
680
+ |----------|----------------|--------------|
681
+ | Synchronous action execution | 30,000 ms | Yes |
682
+ | Action acknowledgment (async) | 5,000 ms | Yes |
683
+ | Surface registration response | 10,000 ms | Yes |
684
+ | Observation pull request | 5,000 ms | Yes |
685
+
686
+ When a timeout expires:
687
+ - The agent runtime MUST generate a synthetic `action:result` with `success: false` and error code `TIMEOUT`
688
+ - The agent runtime MUST NOT retry automatically (retry policy is the agent's decision)
689
+ - The surface MAY still complete the operation; late results SHOULD be silently discarded by the runtime
690
+
691
+ ### 7.6 Error Handling
692
+
693
+ #### 7.6.1 Transport-Level Errors
694
+
695
+ If the transport connection is lost:
696
+ - All registered surfaces MUST be considered deregistered
697
+ - All pending action requests MUST be resolved with error code `SURFACE_UNAVAILABLE`
698
+ - On reconnection, surfaces MUST re-register
699
+
700
+ #### 7.6.2 Protocol-Level Errors
701
+
702
+ If a protocol event is malformed:
703
+ - The receiving party MUST ignore the event
704
+ - The receiving party SHOULD log a warning
705
+ - The receiving party MUST NOT crash or close the connection
706
+
707
+ #### 7.6.3 Application-Level Errors
708
+
709
+ Errors within action handlers are captured and returned as `action:result` with `success: false`. Unhandled exceptions MUST be caught by the surface runtime and returned with error code `EXECUTION_ERROR`.
710
+
711
+ ### 7.7 Ordering Guarantees
712
+
713
+ - Events from a single surface MUST be delivered in order
714
+ - Events from different surfaces have no ordering guarantee relative to each other
715
+ - The agent runtime MUST process `action:result` events before dispatching subsequent `action:request` events for the same surface (no concurrent actions on a single surface unless explicitly supported)
716
+
717
+ ---
718
+
719
+ ## 8. Transport Layer
720
+
721
+ OUI is transport-agnostic. This section defines normative bindings for three transports. Implementations MAY support additional transports provided they satisfy the protocol semantics defined in [Section 7](#7-protocol).
722
+
723
+ ### 8.1 Transport Interface
724
+
725
+ All transports MUST implement the following abstract interface:
726
+
727
+ ```typescript
728
+ interface OUITransport {
729
+ /** Send a protocol event to the remote side */
730
+ send(event: OUIProtocolEvent): void;
731
+
732
+ /** Register a handler for incoming protocol events */
733
+ onEvent(handler: (event: OUIProtocolEvent) => void): void;
734
+
735
+ /** Establish the transport connection */
736
+ connect(): Promise<void>;
737
+
738
+ /** Tear down the transport connection */
739
+ disconnect(): void;
740
+
741
+ /** Current connection state */
742
+ readonly state: 'disconnected' | 'connecting' | 'connected';
743
+ }
744
+ ```
745
+
746
+ ### 8.2 WebSocket Binding
747
+
748
+ **Use case:** Browser application communicating with a server-side agent runtime, or any networked scenario.
749
+
750
+ #### 8.2.1 Connection
751
+
752
+ - The surface connects to the agent runtime's WebSocket endpoint
753
+ - URL format: `ws[s]://{host}:{port}/oui/v1`
754
+ - The connection MUST use TLS (`wss://`) in production environments
755
+
756
+ #### 8.2.2 Message Format
757
+
758
+ Each WebSocket message is a UTF-8 encoded JSON string representing a single `OUIProtocolEvent`:
759
+
760
+ ```json
761
+ {
762
+ "type": "action:request",
763
+ "payload": { ... }
764
+ }
765
+ ```
766
+
767
+ - Messages MUST be valid JSON
768
+ - Binary messages MUST NOT be used for protocol events
769
+ - Maximum message size: 1 MB (configurable)
770
+
771
+ #### 8.2.3 Connection Lifecycle
772
+
773
+ ```
774
+ Client (Surface) Server (Agent Runtime)
775
+ | |
776
+ |--- WebSocket Connect --------------->|
777
+ |<-- 101 Switching Protocols ----------|
778
+ | |
779
+ |--- surface:register ---------------->|
780
+ | |
781
+ |<-- action:request -------------------|
782
+ |--- action:result ------------------->|
783
+ | |
784
+ |--- observation:update -------------->|
785
+ | |
786
+ |--- surface:deregister -------------->|
787
+ |--- Close Connection ---------------->|
788
+ ```
789
+
790
+ #### 8.2.4 Reconnection
791
+
792
+ - On unexpected disconnection, the surface SHOULD attempt reconnection with exponential backoff
793
+ - Initial retry delay: 1 second
794
+ - Maximum retry delay: 30 seconds
795
+ - Backoff multiplier: 2x
796
+ - On successful reconnection, the surface MUST re-register all active surfaces
797
+
798
+ #### 8.2.5 Heartbeat
799
+
800
+ - The transport SHOULD implement WebSocket ping/pong frames
801
+ - Ping interval: 30 seconds
802
+ - If no pong is received within 10 seconds, the connection SHOULD be considered dead
803
+
804
+ ### 8.3 postMessage Binding
805
+
806
+ **Use case:** Surface running in an iframe or web worker communicating with a parent/host page agent runtime.
807
+
808
+ #### 8.3.1 Message Format
809
+
810
+ ```javascript
811
+ window.parent.postMessage({
812
+ __oui__: true,
813
+ event: {
814
+ type: "surface:register",
815
+ payload: { ... }
816
+ }
817
+ }, targetOrigin);
818
+ ```
819
+
820
+ - All OUI messages MUST include the `__oui__: true` discriminator field
821
+ - The `targetOrigin` MUST be set to the specific expected origin (never `"*"` in production)
822
+ - Receivers MUST verify `event.origin` matches the expected source
823
+
824
+ #### 8.3.2 Channel Establishment
825
+
826
+ 1. The host page creates an iframe or web worker
827
+ 2. The embedded surface sends a handshake message: `{ __oui__: true, handshake: "oui-v1" }`
828
+ 3. The host acknowledges: `{ __oui__: true, handshake: "oui-v1-ack" }`
829
+ 4. The surface proceeds with `surface:register`
830
+
831
+ #### 8.3.3 Security
832
+
833
+ - Messages from unexpected origins MUST be silently ignored
834
+ - The host SHOULD validate that the iframe's `src` matches a trusted allowlist before accepting registration
835
+ - Structured clone serialization ensures no prototype pollution
836
+
837
+ ### 8.4 Direct Function Call Binding
838
+
839
+ **Use case:** Surface and agent runtime in the same JavaScript process (e.g., API surfaces, server-side rendering, testing).
840
+
841
+ #### 8.4.1 Interface
842
+
843
+ When both sides are in the same process, the transport reduces to direct function invocation:
844
+
845
+ ```typescript
846
+ interface DirectTransport {
847
+ /** Surface calls this to emit events to the runtime */
848
+ emitToRuntime(event: OUIProtocolEvent): void;
849
+
850
+ /** Runtime calls this to emit events to the surface */
851
+ emitToSurface(event: OUIProtocolEvent): void;
852
+ }
853
+ ```
854
+
855
+ #### 8.4.2 Synchronous vs. Asynchronous
856
+
857
+ Even in direct-call mode:
858
+ - Action handlers MUST be invoked asynchronously (via `await` or microtask)
859
+ - The protocol's event-based semantics are preserved — the runtime still receives `action:result` events
860
+ - This ensures behavioral consistency regardless of transport
861
+
862
+ #### 8.4.3 Testing
863
+
864
+ Direct function call transport is the RECOMMENDED approach for unit and integration testing of surfaces. It allows tests to:
865
+ - Register a surface
866
+ - Send action requests
867
+ - Assert on action results
868
+ - Verify observation updates
869
+
870
+ All without network overhead or browser infrastructure.
871
+
872
+ ### 8.5 Transport Selection
873
+
874
+ | Scenario | Recommended Transport |
875
+ |----------|----------------------|
876
+ | Web app ↔ remote agent server | WebSocket |
877
+ | Iframe plugin ↔ host app | postMessage |
878
+ | Web worker ↔ main thread | postMessage |
879
+ | Same-process SDK integration | Direct function call |
880
+ | Server-side rendering | Direct function call |
881
+ | Automated testing | Direct function call |
882
+ | Electron main ↔ renderer | postMessage or WebSocket |
883
+ | React Native | WebSocket |
884
+
885
+ ---
886
+
887
+ ## 9. Security Considerations
888
+
889
+ OUI surfaces expose application capabilities to external agent runtimes. Security is a first-class concern.
890
+
891
+ ### 9.1 Consent Model
892
+
893
+ #### 9.1.1 Developer Consent
894
+
895
+ - Only capabilities that are **explicitly declared** in a surface are exposed. OUI never infers or auto-discovers capabilities.
896
+ - Developers choose what to expose. If an action is not in the manifest, it does not exist to the agent.
897
+ - The `metadata` field MUST NOT be used to expose sensitive information (API keys, internal IDs not meant for agents).
898
+
899
+ #### 9.1.2 User Consent
900
+
901
+ - Agent runtimes SHOULD inform users which surfaces are active and what actions are available
902
+ - Agent runtimes MUST honor the `confirm` flag on actions
903
+ - Users SHOULD be able to disable specific surfaces or actions at runtime
904
+ - Agent runtimes SHOULD log all action invocations for user auditability
905
+
906
+ ### 9.2 Action Confirmation
907
+
908
+ The `confirm: true` flag is the primary mechanism for protecting high-impact operations:
909
+
910
+ | Operation Type | Confirm Recommended |
911
+ |---------------|-------------------|
912
+ | Read-only / query | No |
913
+ | Create / add | Situational |
914
+ | Update / modify | Situational |
915
+ | Delete / destroy | Yes |
916
+ | External side effects (send email, publish) | Yes |
917
+ | Financial transactions | Yes |
918
+ | Permission changes | Yes |
919
+
920
+ Agent runtimes MUST NOT bypass confirmation. The confirmation prompt MUST clearly describe:
921
+ - Which action is being invoked
922
+ - What parameters are being passed
923
+ - The expected effect (from the action's `description`)
924
+
925
+ ### 9.3 Scope Limiting
926
+
927
+ #### 9.3.1 Surface Scoping
928
+
929
+ Applications SHOULD scope surfaces narrowly:
930
+ - One surface per feature or page (not one monolithic surface for the entire app)
931
+ - Only expose actions relevant to the surface's domain
932
+ - Prefer many small surfaces over few large ones
933
+
934
+ #### 9.3.2 Input Validation
935
+
936
+ - Surfaces MUST validate all input parameters against the declared `input` schema before execution
937
+ - Surfaces MUST NOT trust input from the agent runtime without validation
938
+ - String inputs SHOULD be bounded (`maxLength`) to prevent injection attacks
939
+ - Array inputs SHOULD be bounded (`maxItems`) to prevent resource exhaustion
940
+
941
+ #### 9.3.3 Output Sanitization
942
+
943
+ - Action results MUST NOT include sensitive data not intended for the agent (e.g., internal tokens, other users' PII)
944
+ - Observation values MUST NOT expose data beyond what the current user is authorized to see
945
+
946
+ ### 9.4 Transport Security
947
+
948
+ | Transport | Security Requirement |
949
+ |-----------|---------------------|
950
+ | WebSocket | MUST use TLS (`wss://`) in production. SHOULD authenticate the connection (e.g., via token in initial handshake). |
951
+ | postMessage | MUST validate `origin`. MUST NOT use `targetOrigin: "*"` in production. |
952
+ | Direct call | Inherits the process's security context. No additional requirements. |
953
+
954
+ ### 9.5 Rate Limiting
955
+
956
+ - Surfaces SHOULD implement rate limiting on action execution to prevent abuse
957
+ - Agent runtimes SHOULD implement rate limiting on action requests to prevent runaway loops
958
+ - Observation updates SHOULD be throttled to prevent flooding (RECOMMENDED: max 10 updates/second per observation)
959
+
960
+ ### 9.6 Capability Enumeration
961
+
962
+ The manifest is readable by the agent runtime. This means:
963
+ - Do not include security-sensitive information in action descriptions
964
+ - Do not expose admin-only actions to non-admin agent sessions
965
+ - Consider per-role surface variants if different users have different capabilities
966
+
967
+ ---
968
+
969
+ ## 10. Conformance
970
+
971
+ ### 10.1 Conformance Levels
972
+
973
+ This specification defines two conformance targets:
974
+
975
+ #### 10.1.1 OUI-Compliant Surface
976
+
977
+ An application surface is **OUI-compliant** if it:
978
+
979
+ 1. **Declares a valid manifest.** The surface's manifest conforms to the schemas defined in Sections 3–6. All REQUIRED fields are present. All field values conform to their specified types and constraints.
980
+
981
+ 2. **Implements the protocol.** The surface correctly sends and receives protocol events as defined in Section 7. Specifically:
982
+ - Sends `surface:register` on activation with a complete manifest
983
+ - Sends `surface:deregister` on deactivation
984
+ - Receives `action:request` events and returns `action:result` events with correct `requestId` correlation
985
+ - Emits `observation:update` events according to declared `updateFrequency`
986
+
987
+ 3. **Validates inputs.** The surface validates `action:request` parameters against the declared `input` schema before executing the handler.
988
+
989
+ 4. **Handles errors gracefully.** Action handlers never throw unhandled exceptions that crash the surface. All failures are reported as `action:result` with `success: false`.
990
+
991
+ 5. **Respects timeouts.** Synchronous actions complete within 30 seconds (or the configured timeout). Async actions acknowledge within 5 seconds.
992
+
993
+ 6. **Implements at least one transport.** The surface is reachable via at least one of the defined transport bindings (WebSocket, postMessage, or direct call).
994
+
995
+ #### 10.1.2 OUI-Compliant Agent Runtime
996
+
997
+ An agent runtime is **OUI-compliant** if it:
998
+
999
+ 1. **Consumes manifests correctly.** Parses surface manifests and presents actions as tools to the LLM with correct descriptions and input schemas.
1000
+
1001
+ 2. **Implements the protocol.** Correctly sends and receives all protocol events:
1002
+ - Processes `surface:register` and `surface:deregister` events
1003
+ - Sends well-formed `action:request` events with unique `requestId` values
1004
+ - Correlates `action:result` events by `requestId`
1005
+ - Processes `observation:update` events and makes values available to the LLM
1006
+
1007
+ 3. **Validates before dispatch.** Validates action parameters against the declared `input` schema before sending `action:request`.
1008
+
1009
+ 4. **Honors confirmation.** Presents confirmation prompts for `confirm: true` actions. Never bypasses.
1010
+
1011
+ 5. **Implements timeouts.** Enforces timeout limits and generates synthetic timeout results when exceeded.
1012
+
1013
+ 6. **Cleans up on disconnect.** Removes surfaces and resolves pending requests on transport disconnection.
1014
+
1015
+ ### 10.2 Versioning
1016
+
1017
+ This specification is versioned using [Semantic Versioning](https://semver.org/):
1018
+
1019
+ - **MAJOR** version: incompatible protocol changes
1020
+ - **MINOR** version: backwards-compatible additions (new optional fields, new event types)
1021
+ - **PATCH** version: clarifications and editorial fixes
1022
+
1023
+ Surfaces and runtimes SHOULD declare which spec version they implement. Compatibility negotiation is out of scope for v0.1 but is expected in a future version.
1024
+
1025
+ ### 10.3 Extension Points
1026
+
1027
+ The specification provides explicit extension mechanisms:
1028
+
1029
+ | Extension Point | Mechanism |
1030
+ |----------------|-----------|
1031
+ | Surface metadata | The `metadata` field on `OUISurface` accepts arbitrary key-value pairs |
1032
+ | Custom error codes | Namespaced codes (e.g., `app:custom_error`) alongside standard codes |
1033
+ | Custom protocol events | Implementations MAY define additional event types prefixed with `x-` (e.g., `x-analytics:track`) |
1034
+ | Transport extensions | Custom transports that implement the `OUITransport` interface |
1035
+
1036
+ Extensions MUST NOT conflict with the standard protocol. Receivers that encounter unknown event types MUST silently ignore them.
1037
+
1038
+ ### 10.4 Compliance Testing
1039
+
1040
+ A reference test suite will be provided in the `@oui/compliance` package (planned). It will verify:
1041
+
1042
+ - Manifest schema validity
1043
+ - Protocol event correctness (registration, action round-trip, observation delivery)
1044
+ - Timeout behavior
1045
+ - Error handling
1046
+ - Transport binding correctness
1047
+
1048
+ Until the compliance suite is available, implementations SHOULD self-verify against the requirements in Sections 10.1.1 and 10.1.2.
1049
+
1050
+ ---
1051
+
1052
+ ## Appendix A: Full Protocol Event Schema
1053
+
1054
+ ```typescript
1055
+ type OUIProtocolEvent =
1056
+ | { type: 'surface:register'; payload: OUISurfaceRegistration }
1057
+ | { type: 'surface:deregister'; payload: OUISurfaceDeregistration }
1058
+ | { type: 'action:request'; payload: OUIActionRequest }
1059
+ | { type: 'action:result'; payload: OUIActionResult }
1060
+ | { type: 'observation:update'; payload: OUIObservationUpdate }
1061
+ | { type: 'observation:request'; payload: OUIObservationRequest };
1062
+ ```
1063
+
1064
+ ### OUISurfaceRegistration
1065
+
1066
+ ```typescript
1067
+ {
1068
+ surface: OUISurface; // Complete manifest
1069
+ timestamp: number; // Unix epoch milliseconds
1070
+ }
1071
+ ```
1072
+
1073
+ ### OUISurfaceDeregistration
1074
+
1075
+ ```typescript
1076
+ {
1077
+ surfaceId: string; // ID of surface being removed
1078
+ timestamp: number;
1079
+ }
1080
+ ```
1081
+
1082
+ ### OUIActionRequest
1083
+
1084
+ ```typescript
1085
+ {
1086
+ requestId: string; // UUID v4 recommended
1087
+ surfaceId: string; // Target surface
1088
+ actionId: string; // Target action
1089
+ params: object; // Input conforming to action's input schema
1090
+ timestamp: number;
1091
+ }
1092
+ ```
1093
+
1094
+ ### OUIActionResult
1095
+
1096
+ ```typescript
1097
+ {
1098
+ requestId: string; // Correlates to request
1099
+ success: boolean;
1100
+ data?: unknown; // Present when success = true
1101
+ error?: { // Present when success = false
1102
+ code: string;
1103
+ message: string;
1104
+ details?: unknown;
1105
+ };
1106
+ durationMs?: number; // Execution time
1107
+ timestamp: number;
1108
+ }
1109
+ ```
1110
+
1111
+ ### OUIObservationUpdate
1112
+
1113
+ ```typescript
1114
+ {
1115
+ surfaceId: string;
1116
+ observationId: string;
1117
+ value: unknown; // Conforms to observation's schema
1118
+ timestamp: number;
1119
+ }
1120
+ ```
1121
+
1122
+ ### OUIObservationRequest
1123
+
1124
+ ```typescript
1125
+ {
1126
+ surfaceId: string;
1127
+ observationId: string;
1128
+ requestId: string;
1129
+ timestamp: number;
1130
+ }
1131
+ ```
1132
+
1133
+ ---
1134
+
1135
+ ## Appendix B: Complete Example — Todo Application
1136
+
1137
+ This appendix shows a full OUI manifest for a todo application, demonstrating all object types.
1138
+
1139
+ ```json
1140
+ {
1141
+ "id": "todo-app",
1142
+ "name": "Todo Application",
1143
+ "description": "A task management application. Supports creating, completing, prioritizing, and filtering todo items.",
1144
+ "version": "1.0.0",
1145
+ "actions": [
1146
+ {
1147
+ "id": "add_todo",
1148
+ "description": "Add a new todo item to the list",
1149
+ "input": {
1150
+ "type": "object",
1151
+ "properties": {
1152
+ "title": {
1153
+ "type": "string",
1154
+ "description": "The todo text",
1155
+ "minLength": 1,
1156
+ "maxLength": 500
1157
+ },
1158
+ "priority": {
1159
+ "type": "string",
1160
+ "enum": ["low", "medium", "high"],
1161
+ "default": "medium",
1162
+ "description": "Priority level"
1163
+ },
1164
+ "dueDate": {
1165
+ "type": "string",
1166
+ "format": "date",
1167
+ "description": "Optional due date (ISO 8601 date)"
1168
+ }
1169
+ },
1170
+ "required": ["title"]
1171
+ },
1172
+ "output": {
1173
+ "type": "object",
1174
+ "properties": {
1175
+ "id": { "type": "string" },
1176
+ "created": { "type": "boolean" }
1177
+ }
1178
+ },
1179
+ "estimatedDuration": "instant",
1180
+ "tags": ["write", "create"]
1181
+ },
1182
+ {
1183
+ "id": "complete_todo",
1184
+ "description": "Mark a todo item as completed",
1185
+ "input": {
1186
+ "type": "object",
1187
+ "properties": {
1188
+ "todoId": {
1189
+ "type": "string",
1190
+ "description": "The ID of the todo to complete"
1191
+ }
1192
+ },
1193
+ "required": ["todoId"]
1194
+ },
1195
+ "output": {
1196
+ "type": "object",
1197
+ "properties": {
1198
+ "success": { "type": "boolean" },
1199
+ "completedAt": { "type": "string", "format": "date-time" }
1200
+ }
1201
+ },
1202
+ "estimatedDuration": "instant",
1203
+ "tags": ["write", "update"]
1204
+ },
1205
+ {
1206
+ "id": "delete_todo",
1207
+ "description": "Permanently delete a todo item",
1208
+ "input": {
1209
+ "type": "object",
1210
+ "properties": {
1211
+ "todoId": {
1212
+ "type": "string",
1213
+ "description": "The ID of the todo to delete"
1214
+ }
1215
+ },
1216
+ "required": ["todoId"]
1217
+ },
1218
+ "confirm": true,
1219
+ "estimatedDuration": "instant",
1220
+ "tags": ["write", "destructive"]
1221
+ },
1222
+ {
1223
+ "id": "bulk_complete",
1224
+ "description": "Mark multiple todo items as completed at once",
1225
+ "input": {
1226
+ "type": "object",
1227
+ "properties": {
1228
+ "todoIds": {
1229
+ "type": "array",
1230
+ "items": { "type": "string" },
1231
+ "minItems": 1,
1232
+ "maxItems": 100,
1233
+ "description": "Array of todo IDs to complete"
1234
+ }
1235
+ },
1236
+ "required": ["todoIds"]
1237
+ },
1238
+ "output": {
1239
+ "type": "object",
1240
+ "properties": {
1241
+ "completed": { "type": "integer" },
1242
+ "failed": { "type": "integer" }
1243
+ }
1244
+ },
1245
+ "async": true,
1246
+ "estimatedDuration": "1-5s",
1247
+ "tags": ["write", "bulk"]
1248
+ }
1249
+ ],
1250
+ "observations": [
1251
+ {
1252
+ "id": "todo_list",
1253
+ "description": "Current state of all visible todo items, reflecting active filters",
1254
+ "schema": {
1255
+ "type": "array",
1256
+ "items": {
1257
+ "type": "object",
1258
+ "properties": {
1259
+ "id": { "type": "string" },
1260
+ "title": { "type": "string" },
1261
+ "completed": { "type": "boolean" },
1262
+ "priority": { "type": "string", "enum": ["low", "medium", "high"] },
1263
+ "dueDate": { "type": "string", "format": "date" },
1264
+ "createdAt": { "type": "string", "format": "date-time" }
1265
+ }
1266
+ }
1267
+ },
1268
+ "updateFrequency": "on-change"
1269
+ },
1270
+ {
1271
+ "id": "active_filter",
1272
+ "description": "Currently active filter applied to the todo list",
1273
+ "schema": {
1274
+ "type": "object",
1275
+ "properties": {
1276
+ "status": { "type": "string", "enum": ["all", "active", "completed"] },
1277
+ "priority": { "type": "string", "enum": ["all", "low", "medium", "high"] }
1278
+ }
1279
+ },
1280
+ "updateFrequency": "on-change"
1281
+ },
1282
+ {
1283
+ "id": "stats",
1284
+ "description": "Summary statistics for the todo list",
1285
+ "schema": {
1286
+ "type": "object",
1287
+ "properties": {
1288
+ "total": { "type": "integer" },
1289
+ "active": { "type": "integer" },
1290
+ "completed": { "type": "integer" },
1291
+ "overdue": { "type": "integer" }
1292
+ }
1293
+ },
1294
+ "updateFrequency": "on-change"
1295
+ }
1296
+ ],
1297
+ "activation": {
1298
+ "routes": ["/todos", "/todos/**"]
1299
+ },
1300
+ "metadata": {
1301
+ "maxTodos": 1000,
1302
+ "supportedPriorities": ["low", "medium", "high"]
1303
+ }
1304
+ }
1305
+ ```
1306
+
1307
+ ---
1308
+
1309
+ ## Appendix C: Relationship to Existing Standards
1310
+
1311
+ | Standard | Relationship to OUI |
1312
+ |----------|-------------------|
1313
+ | [OpenAPI](https://spec.openapis.org/oas/latest.html) | Complementary. OpenAPI describes HTTP APIs; OUI describes UI capabilities. An app may expose both. |
1314
+ | [JSON Schema](https://json-schema.org/) | Foundation. OUI uses JSON Schema for all input/output/observation type definitions. |
1315
+ | [JSON-RPC](https://www.jsonrpc.org/specification) | Inspiration. OUI's request/result correlation is similar to JSON-RPC's `id` field, but OUI adds lifecycle (registration, observations). |
1316
+ | [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) | Inspiration. LSP's capability negotiation and typed messages influenced OUI's protocol design. |
1317
+ | [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) | Orthogonal. Web Components define UI encapsulation at the DOM level; OUI defines semantic capability exposure at the agent level. |
1318
+ | [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) | Related. MCP defines tools/resources for LLMs in a server context. OUI is specifically designed for UI surfaces with lifecycle, activation, and real-time observations. |
1319
+ | [WAI-ARIA](https://www.w3.org/WAI/ARIA/apg/) | Complementary. ARIA describes UI semantics for assistive technology; OUI describes UI capabilities for agent technology. |
1320
+
1321
+ ---
1322
+
1323
+ *End of specification.*