oui-spec 0.4.1 → 0.6.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 (48) hide show
  1. package/README.md +41 -8
  2. package/dist/{chunk-DCVVTP3V.cjs → chunk-5UUJ3F7C.cjs} +53 -16
  3. package/dist/chunk-5UUJ3F7C.cjs.map +1 -0
  4. package/dist/{chunk-GDB56ZQZ.cjs → chunk-AFNWLRKV.cjs} +126 -15
  5. package/dist/chunk-AFNWLRKV.cjs.map +1 -0
  6. package/dist/chunk-B2EFZMGY.js +159 -0
  7. package/dist/chunk-B2EFZMGY.js.map +1 -0
  8. package/dist/chunk-LDECTDPU.cjs +169 -0
  9. package/dist/chunk-LDECTDPU.cjs.map +1 -0
  10. package/dist/{chunk-F3WKIRAU.js → chunk-R3SGP2UY.js} +53 -16
  11. package/dist/chunk-R3SGP2UY.js.map +1 -0
  12. package/dist/{chunk-UQLG55ST.js → chunk-VQETSIY5.js} +124 -13
  13. package/dist/chunk-VQETSIY5.js.map +1 -0
  14. package/dist/core/index.cjs +7 -7
  15. package/dist/core/index.d.cts +5 -5
  16. package/dist/core/index.d.ts +5 -5
  17. package/dist/core/index.js +3 -3
  18. package/dist/index.cjs +40 -8
  19. package/dist/index.d.cts +4 -3
  20. package/dist/index.d.ts +4 -3
  21. package/dist/index.js +3 -3
  22. package/dist/react/index.d.cts +3 -3
  23. package/dist/react/index.d.ts +3 -3
  24. package/dist/spec/index.cjs +34 -2
  25. package/dist/spec/index.d.cts +22 -272
  26. package/dist/spec/index.d.ts +22 -272
  27. package/dist/spec/index.js +1 -1
  28. package/dist/{surface-runtime-BzIKgL_I.d.cts → surface-runtime-CSZ9UtBO.d.cts} +23 -2
  29. package/dist/{surface-runtime-DeByfbKg.d.ts → surface-runtime-D5nINY4b.d.ts} +23 -2
  30. package/dist/transport/index.cjs +2 -2
  31. package/dist/transport/index.d.cts +3 -3
  32. package/dist/transport/index.d.ts +3 -3
  33. package/dist/transport/index.js +1 -1
  34. package/dist/types-CmJb1BQ8.d.cts +403 -0
  35. package/dist/types-CmJb1BQ8.d.ts +403 -0
  36. package/dist/{websocket-YfWILdZ4.d.cts → websocket-Ce3tDSsJ.d.ts} +26 -7
  37. package/dist/{websocket-c5tIUWY5.d.ts → websocket-Ds2Rh_jL.d.cts} +26 -7
  38. package/package.json +4 -3
  39. package/spec/OUI-SPEC-v0.1.md +63 -7
  40. package/spec/approval-vectors.json +88 -0
  41. package/dist/chunk-BMCL2K74.js +0 -12
  42. package/dist/chunk-BMCL2K74.js.map +0 -1
  43. package/dist/chunk-DCVVTP3V.cjs.map +0 -1
  44. package/dist/chunk-F3WKIRAU.js.map +0 -1
  45. package/dist/chunk-GDB56ZQZ.cjs.map +0 -1
  46. package/dist/chunk-UQLG55ST.js.map +0 -1
  47. package/dist/chunk-ZKM4XWAB.cjs +0 -14
  48. package/dist/chunk-ZKM4XWAB.cjs.map +0 -1
@@ -0,0 +1,403 @@
1
+ /**
2
+ * Approvals: an irreversible action runs only on an approval the user gave,
3
+ * bound to the exact request.
4
+ *
5
+ * An agent runtime stops at an action that needs approval and shows the user
6
+ * a confirmation. Its approval store issues a single-use grant when the user
7
+ * confirms, and the request that then reaches the tab carries `approval:
8
+ * { approvalId, argsHash }`. The surface runtime runs the action only when
9
+ * that matches a grant this tab received from the user's own confirmation.
10
+ *
11
+ * Everything that checks a request computes the same args hash: SHA-256 over
12
+ * the RFC 8785 (JCS) canonical JSON of its params, lowercase hex. The
13
+ * published vectors (`oui-spec/approval-vectors.json`) hold every
14
+ * implementation, in any language, to it.
15
+ */
16
+ /**
17
+ * What using an action does. One vocabulary for every kind of action, so an
18
+ * action is told apart by what it changes:
19
+ *
20
+ * | Effect | What it changes | Access |
21
+ * |---|---|---|
22
+ * | `view`, `selection`, `navigate`, `open` | What is shown | read |
23
+ * | `edit` | The document, one undo step | write |
24
+ * | `file` | Imports or exports a file | write |
25
+ * | `mutate` | Backend data, through an API operation | write |
26
+ * | `job` | Starts work that outlives the call | write |
27
+ * | `transaction` | An irreversible external act: an order, a payment, a send, a publish | write, approved |
28
+ */
29
+ type OUIEffectKind = "view" | "selection" | "navigate" | "open" | "edit" | "file" | "mutate" | "job" | "transaction";
30
+ /** Every effect, in the order of the table above. */
31
+ declare const OUI_EFFECT_KINDS: readonly OUIEffectKind[];
32
+ /** Whether an effect only changes what is shown, or changes something. */
33
+ declare const OUI_EFFECT_ACCESS: Readonly<Record<OUIEffectKind, "read" | "write">>;
34
+ /** `read` or `write`. An action that declares no effect is a `write`: nothing says it only shows. */
35
+ declare function effectAccess(effect: OUIEffectKind | undefined): "read" | "write";
36
+ /**
37
+ * Whether running an action needs the user's approval of the exact request: a
38
+ * `transaction` always, and a `write` declared destructive. An agent
39
+ * runtime's policy may require it of more; nothing waives these.
40
+ */
41
+ declare function requiresApproval(effect: OUIEffectKind | undefined, destructive?: boolean): boolean;
42
+ /** What a request for an approved action carries. */
43
+ interface OUIActionApproval {
44
+ approvalId: string;
45
+ /** `argsHash(params)` of the request the user approved. */
46
+ argsHash: string;
47
+ }
48
+ /** A grant this tab holds: the user approved this request here. */
49
+ interface OUIApprovalGrant extends OUIActionApproval {
50
+ /** Epoch ms after which the grant no longer admits anything. */
51
+ expiresAt: number;
52
+ }
53
+ /** An args hash: 64 lowercase hex digits. */
54
+ declare const ARGS_HASH_PATTERN: RegExp;
55
+ /**
56
+ * The RFC 8785 canonical form of a JSON value:
57
+ * - object members sorted by their names' UTF-16 code units, with no
58
+ * whitespace;
59
+ * - numbers in ECMAScript's shortest round-trip form (`-0` is `0`);
60
+ * - strings escaped as ECMAScript's `JSON.stringify` escapes them.
61
+ *
62
+ * The value must be I-JSON (RFC 7493): anything JSON cannot carry — `NaN`,
63
+ * infinities, `undefined` outside an object member, a bigint, a function, a
64
+ * symbol, a class instance, or a string with a lone surrogate — is refused
65
+ * rather than hashed as something the other end never receives. An object
66
+ * member whose value is `undefined` is left out, as it is on the wire.
67
+ */
68
+ declare function canonicalJson(value: unknown): string;
69
+ /** SHA-256 of the canonical JSON of a request's params, lowercase hex. Runs wherever WebCrypto does. */
70
+ declare function argsHash(params: unknown): Promise<string>;
71
+
72
+ /**
73
+ * OUI — Open UI Specification
74
+ *
75
+ * The machine-readable contract for agent-controllable user interfaces.
76
+ * Any UI application that implements this interface can be controlled by
77
+ * any OUI-compatible agent runtime.
78
+ */
79
+ /** JSON Schema type (subset used by OUI) */
80
+ type JSONSchema = {
81
+ type?: string | string[];
82
+ properties?: Record<string, JSONSchema>;
83
+ items?: JSONSchema;
84
+ required?: string[];
85
+ enum?: (string | number | boolean)[];
86
+ description?: string;
87
+ default?: unknown;
88
+ oneOf?: JSONSchema[];
89
+ anyOf?: JSONSchema[];
90
+ allOf?: JSONSchema[];
91
+ const?: unknown;
92
+ minimum?: number;
93
+ maximum?: number;
94
+ minLength?: number;
95
+ maxLength?: number;
96
+ pattern?: string;
97
+ format?: string;
98
+ additionalProperties?: boolean | JSONSchema;
99
+ };
100
+ /**
101
+ * A Surface is a controllable boundary within an application.
102
+ * It declares what an agent can do (actions), what it can observe (state),
103
+ * and when it's available (activation conditions).
104
+ *
105
+ * Think of it as "one page" or "one feature" of an application, described
106
+ * in a way that an agent can understand and operate.
107
+ */
108
+ interface OUISurface {
109
+ /** Unique identifier for this surface (e.g., 'dataviz-wizard', 'document-editor') */
110
+ id: string;
111
+ /** Human-readable name */
112
+ name: string;
113
+ /** Description of what this surface does — included in agent context */
114
+ description: string;
115
+ /** Version of this surface's contract (semver) */
116
+ version?: string;
117
+ /** Actions the agent can invoke on this surface */
118
+ actions: OUIAction[];
119
+ /** Observable state the agent can read */
120
+ observations?: OUIObservation[];
121
+ /** When this surface is active/available */
122
+ activation?: OUIActivation;
123
+ /** Additional metadata for agent context */
124
+ metadata?: Record<string, unknown>;
125
+ }
126
+ /**
127
+ * Declarative polling configuration for async actions.
128
+ * The OUI runtime in the browser manages the polling lifecycle automatically
129
+ * after the action handler returns its dispatch result.
130
+ */
131
+ interface OUIActionPolling {
132
+ /** How often to check (milliseconds) */
133
+ intervalMs: number;
134
+ /** Max poll attempts before giving up (optional) */
135
+ maxAttempts?: number;
136
+ /** Max total duration before giving up in ms (alternative to maxAttempts) */
137
+ maxDurationMs?: number;
138
+ /**
139
+ * Subscribe to a realtime event instead of polling.
140
+ * If provided, the runtime listens for this event instead of using setInterval.
141
+ * More efficient when realtime infrastructure exists.
142
+ */
143
+ subscribe?: {
144
+ /** Event name to listen for */
145
+ event: string;
146
+ /**
147
+ * Filter to match the event. Values prefixed with '$dispatchResult.'
148
+ * are resolved from the handler's return value.
149
+ * Example: { jobId: '$dispatchResult.jobId' }
150
+ */
151
+ filter?: Record<string, unknown>;
152
+ };
153
+ }
154
+ /**
155
+ * An Action is a single operation an agent can perform on a surface.
156
+ * Each action becomes a "tool" that the LLM can call.
157
+ *
158
+ * The action's handler executes in the UI (or API) and returns a result
159
+ * that the agent sees as the tool's output.
160
+ */
161
+ interface OUIAction {
162
+ /** Unique action identifier (becomes the tool name) */
163
+ id: string;
164
+ /** Human-readable description — this is what the LLM reads to decide when to use it */
165
+ description: string;
166
+ /** The action's name as a person reads it, for a confirmation the user approves. */
167
+ title?: string;
168
+ /**
169
+ * What using the action does. A `transaction` runs only on the user's
170
+ * approval of the exact request (see `requiresApproval`). Undeclared counts
171
+ * as a write.
172
+ */
173
+ effect?: OUIEffectKind;
174
+ /** JSON Schema for the action's input parameters */
175
+ input: JSONSchema;
176
+ /** JSON Schema for the action's return value (optional — agent still gets the result) */
177
+ output?: JSONSchema;
178
+ /**
179
+ * Destructive: the action removes or replaces something the person made.
180
+ * Unless its effect only reads, it runs only on the user's approval of the
181
+ * exact request, which the surface runtime checks before it runs.
182
+ */
183
+ confirm?: boolean;
184
+ /** If true, the action is async — returns immediately with a job/tracking ID */
185
+ async?: boolean;
186
+ /** Declarative polling configuration — runtime manages the polling lifecycle after dispatch */
187
+ polling?: OUIActionPolling;
188
+ /** Human-readable hint about when this action is appropriate to use */
189
+ usage?: string;
190
+ /**
191
+ * Preconditions that should be met before this action is called.
192
+ * Expressed as a description (for the LLM) not as executable logic.
193
+ */
194
+ preconditions?: string;
195
+ /** Estimated duration hint (e.g., "instant", "2-5s", "30-120s") */
196
+ estimatedDuration?: string;
197
+ /** Tags for categorization */
198
+ tags?: string[];
199
+ }
200
+ /**
201
+ * An Observation is a piece of application state that the agent can read.
202
+ * Observations are pushed to the agent in real-time as the app state changes,
203
+ * giving it situational awareness without needing screenshots.
204
+ */
205
+ interface OUIObservation {
206
+ /** Unique identifier for this observation */
207
+ id: string;
208
+ /** Description of what this observation represents */
209
+ description: string;
210
+ /** JSON Schema for the observation's value */
211
+ schema: JSONSchema;
212
+ /** How frequently this observation updates */
213
+ updateFrequency?: "realtime" | "on-change" | "polling";
214
+ }
215
+ /**
216
+ * Activation conditions — when a surface is available to the agent.
217
+ * A surface might only be active on a specific page, when certain
218
+ * entities are loaded, or under specific application states.
219
+ */
220
+ interface OUIActivation {
221
+ /** URL path(s) where this surface is active */
222
+ routes?: string | string[];
223
+ /** Entity type/ID that must be present for this surface to activate */
224
+ entity?: {
225
+ type: string;
226
+ id?: string;
227
+ };
228
+ /** Custom predicate description (for documentation, not execution) */
229
+ condition?: string;
230
+ }
231
+ /**
232
+ * A request from the agent runtime to execute an action on a surface.
233
+ */
234
+ interface OUIActionRequest {
235
+ /** Unique request ID for correlation */
236
+ requestId: string;
237
+ /** Which surface this request targets */
238
+ surfaceId: string;
239
+ /** Which action to invoke */
240
+ actionId: string;
241
+ /** The action's input parameters (validated against the action's input schema) */
242
+ params: Record<string, unknown>;
243
+ /** Timestamp of the request */
244
+ timestamp: number;
245
+ /**
246
+ * The user's approval this request runs on, for an action that needs one.
247
+ * The surface runtime runs such an action only when this matches a grant
248
+ * the tab received from the user's own confirmation, for exactly `params`.
249
+ */
250
+ approval?: OUIActionApproval;
251
+ /**
252
+ * The `surfacesHash` of the surfaces the agent runtime already holds for
253
+ * this client: from its snapshot, or from an earlier result. A client whose
254
+ * surfaces still hash to this answers without `surfaces`, so a result
255
+ * carries the client's surfaces only when they changed (§7.3.4).
256
+ */
257
+ knownSurfaces?: string;
258
+ }
259
+ /**
260
+ * Why a client's answer is shorter than the result it produced: the receiving
261
+ * side refused the full answer (too large, or otherwise not accepted), so the
262
+ * client sent it again without the parts named in `omitted`. The action's own
263
+ * outcome — `success`, `error`, and `data` unless omitted — is still the one
264
+ * that happened (§7.3.6).
265
+ */
266
+ interface OUIResultDelivery {
267
+ trimmed: true;
268
+ /** What the receiving side said when it refused the full answer. */
269
+ reason: string;
270
+ /** The fields left out of this answer. */
271
+ omitted: Array<"surfaces" | "observations" | "data">;
272
+ }
273
+ /**
274
+ * The result of an action execution, returned from the surface to the agent.
275
+ */
276
+ interface OUIActionResult {
277
+ /** Correlates to the request */
278
+ requestId: string;
279
+ /** Whether the action succeeded */
280
+ success: boolean;
281
+ /** The action's return data (if successful) */
282
+ data?: unknown;
283
+ /** Error information (if failed) */
284
+ error?: {
285
+ code: string;
286
+ message: string;
287
+ details?: unknown;
288
+ };
289
+ /** Progress indicator for interim polling results (0-100) */
290
+ progress?: number;
291
+ /** Whether this is an interim update (polling still in progress) or final */
292
+ interim?: boolean;
293
+ /** Duration of execution in milliseconds */
294
+ durationMs?: number;
295
+ /** Timestamp of the result */
296
+ timestamp: number;
297
+ /**
298
+ * The surfaces active on the client once the action's effects settled.
299
+ *
300
+ * An action can change what the agent is able to do next: navigating
301
+ * unmounts one page's surfaces and mounts another's. Without this, the agent
302
+ * would act on the capability set it had before the action, which is the
303
+ * set the action just replaced.
304
+ */
305
+ surfaces?: OUISurface[];
306
+ /** Latest observation values per surface once the action's effects settled. */
307
+ observations?: OUIObservationSnapshot;
308
+ /**
309
+ * The hash of the client's surfaces once the action's effects settled
310
+ * (`surfacesHash`). Always present from a client that sends it; `surfaces`
311
+ * is then present only when this differs from the request's
312
+ * `knownSurfaces`, and an agent runtime that holds surfaces with this hash
313
+ * keeps using them.
314
+ */
315
+ surfacesHash?: string;
316
+ /** Present when this answer was trimmed to be delivered (§7.3.6). */
317
+ delivery?: OUIResultDelivery;
318
+ /**
319
+ * False when the client's deadline passed before its UI settled, so
320
+ * `surfaces` and `observations` may still be changing. Absent or true
321
+ * otherwise.
322
+ */
323
+ settled?: boolean;
324
+ }
325
+ /** Latest observation values, keyed by surface id and then observation id. */
326
+ type OUIObservationSnapshot = Record<string, Record<string, unknown>>;
327
+ /**
328
+ * What a client can do right now: the surfaces it has mounted and their latest
329
+ * observation values. A client sends this to its agent runtime (for example,
330
+ * with each turn), and returns it on every action result.
331
+ */
332
+ interface OUISurfaceSnapshot {
333
+ surfaces: OUISurface[];
334
+ observations: OUIObservationSnapshot;
335
+ /**
336
+ * `surfacesHash(surfaces)`: what an agent runtime sends back as a request's
337
+ * `knownSurfaces`, so the client's answer need not repeat surfaces the
338
+ * runtime already holds.
339
+ */
340
+ surfacesHash?: string;
341
+ }
342
+ /**
343
+ * An observation update pushed from the surface to the agent.
344
+ */
345
+ interface OUIObservationUpdate {
346
+ /** Which surface this observation belongs to */
347
+ surfaceId: string;
348
+ /** Which observation updated */
349
+ observationId: string;
350
+ /** The new value */
351
+ value: unknown;
352
+ /** Timestamp of the update */
353
+ timestamp: number;
354
+ }
355
+ /**
356
+ * Surface registration event — sent when a surface becomes active.
357
+ */
358
+ interface OUISurfaceRegistration {
359
+ /** The full surface manifest */
360
+ surface: OUISurface;
361
+ /** Timestamp of registration */
362
+ timestamp: number;
363
+ }
364
+ /**
365
+ * Surface deregistration event — sent when a surface becomes inactive.
366
+ */
367
+ interface OUISurfaceDeregistration {
368
+ /** Which surface is being deregistered */
369
+ surfaceId: string;
370
+ /** Timestamp */
371
+ timestamp: number;
372
+ }
373
+ /**
374
+ * All protocol events exchanged between agent runtime and surfaces.
375
+ */
376
+ type OUIProtocolEvent = {
377
+ type: "surface:register";
378
+ payload: OUISurfaceRegistration;
379
+ } | {
380
+ type: "surface:deregister";
381
+ payload: OUISurfaceDeregistration;
382
+ } | {
383
+ type: "action:request";
384
+ payload: OUIActionRequest;
385
+ } | {
386
+ type: "action:result";
387
+ payload: OUIActionResult;
388
+ } | {
389
+ type: "observation:update";
390
+ payload: OUIObservationUpdate;
391
+ };
392
+ /**
393
+ * Protocol event type strings (for type guards and routing).
394
+ */
395
+ declare const OUI_PROTOCOL_EVENTS: {
396
+ readonly SURFACE_REGISTER: "surface:register";
397
+ readonly SURFACE_DEREGISTER: "surface:deregister";
398
+ readonly ACTION_REQUEST: "action:request";
399
+ readonly ACTION_RESULT: "action:result";
400
+ readonly OBSERVATION_UPDATE: "observation:update";
401
+ };
402
+
403
+ export { ARGS_HASH_PATTERN as A, type JSONSchema as J, type OUIAction as O, type OUIActionApproval as a, type OUIActionPolling as b, type OUIActionRequest as c, type OUIActionResult as d, type OUIActivation as e, type OUIApprovalGrant as f, type OUIEffectKind as g, type OUIObservation as h, type OUIObservationSnapshot as i, type OUIObservationUpdate as j, type OUIProtocolEvent as k, type OUIResultDelivery as l, type OUISurface as m, type OUISurfaceDeregistration as n, type OUISurfaceRegistration as o, type OUISurfaceSnapshot as p, OUI_EFFECT_ACCESS as q, OUI_EFFECT_KINDS as r, OUI_PROTOCOL_EVENTS as s, argsHash as t, canonicalJson as u, effectAccess as v, requiresApproval as w };
@@ -1,4 +1,4 @@
1
- import { OUIActionRequest, OUIActionResult, OUIObservationUpdate, OUISurface } from './spec/index.cjs';
1
+ import { c as OUIActionRequest, d as OUIActionResult, j as OUIObservationUpdate, m as OUISurface } from './types-CmJb1BQ8.js';
2
2
 
3
3
  /**
4
4
  * OUI Transport — the wire protocol between agent runtime and surfaces.
@@ -19,8 +19,14 @@ interface OUITransport {
19
19
  dispatch(request: OUIActionRequest): void;
20
20
  /** Receive action requests (client side). */
21
21
  onAction(handler: OUIActionHandler): () => void;
22
- /** Answer an action request (client side). */
23
- sendResult(result: OUIActionResult): void;
22
+ /**
23
+ * Answer an action request (client side). `onAcknowledged` is called with
24
+ * the receiving side's acknowledgment: accepted, or refused with its reason
25
+ * — too large, invalid, over a rate limit — so the client can answer again
26
+ * in a form it accepts (§7.3.6). A receiver that does not acknowledge
27
+ * answers never calls it.
28
+ */
29
+ sendResult(result: OUIActionResult, onAcknowledged?: (ack: OUIAcknowledgment) => void): void;
24
30
  /** Receive action results (runtime side). */
25
31
  onResult(handler: OUIResultHandler): () => void;
26
32
  /** Push an observation update from the surface to the agent runtime */
@@ -47,8 +53,20 @@ interface OUITransport {
47
53
  */
48
54
  dispose(): void;
49
55
  }
50
- /** Handler for incoming action requests (client side) */
51
- type OUIActionHandler = (request: OUIActionRequest) => void;
56
+ /**
57
+ * Handler for incoming action requests (client side). `receipt`, when the
58
+ * sender asked for one, acknowledges the request: received and accepted, or
59
+ * refused with why (§7.3.7). The sender can then tell a request that reached a
60
+ * client from one that reached nobody, and need not send it again.
61
+ */
62
+ type OUIActionHandler = (request: OUIActionRequest, receipt?: (ack: OUIAcknowledgment) => void) => void;
63
+ /** An acknowledgment: accepted, or refused with a reason. */
64
+ type OUIAcknowledgment = {
65
+ ok: true;
66
+ } | {
67
+ ok: false;
68
+ reason: string;
69
+ };
52
70
  /** Handler for incoming action results (runtime side) */
53
71
  type OUIResultHandler = (result: OUIActionResult) => void;
54
72
  /** Handler for incoming observation updates (server side) */
@@ -85,7 +103,8 @@ declare function createWebSocketTransport(socket: SocketLike, config?: OUITransp
85
103
  */
86
104
  interface SocketLike {
87
105
  readonly connected?: boolean;
88
- emit(event: string, data: unknown): void;
106
+ /** An optional last argument is the receiver's acknowledgment callback, as Socket.IO takes it. */
107
+ emit(event: string, ...args: any[]): void;
89
108
  on(event: string, handler: (...args: any[]) => void): void;
90
109
  off(event: string, handler: (...args: any[]) => void): void;
91
110
  once(event: string, handler: (...args: any[]) => void): void;
@@ -93,4 +112,4 @@ interface SocketLike {
93
112
  disconnect?(): void;
94
113
  }
95
114
 
96
- export { type OUIActionHandler as O, type SocketLike as S, type OUIObservationHandler as a, type OUIResultHandler as b, type OUITransport as c, type OUITransportConfig as d, createWebSocketTransport as e };
115
+ export { type OUIAcknowledgment as O, type SocketLike as S, type OUIActionHandler as a, type OUIObservationHandler as b, type OUIResultHandler as c, type OUITransport as d, type OUITransportConfig as e, createWebSocketTransport as f };
@@ -1,4 +1,4 @@
1
- import { OUIActionRequest, OUIActionResult, OUIObservationUpdate, OUISurface } from './spec/index.js';
1
+ import { c as OUIActionRequest, d as OUIActionResult, j as OUIObservationUpdate, m as OUISurface } from './types-CmJb1BQ8.cjs';
2
2
 
3
3
  /**
4
4
  * OUI Transport — the wire protocol between agent runtime and surfaces.
@@ -19,8 +19,14 @@ interface OUITransport {
19
19
  dispatch(request: OUIActionRequest): void;
20
20
  /** Receive action requests (client side). */
21
21
  onAction(handler: OUIActionHandler): () => void;
22
- /** Answer an action request (client side). */
23
- sendResult(result: OUIActionResult): void;
22
+ /**
23
+ * Answer an action request (client side). `onAcknowledged` is called with
24
+ * the receiving side's acknowledgment: accepted, or refused with its reason
25
+ * — too large, invalid, over a rate limit — so the client can answer again
26
+ * in a form it accepts (§7.3.6). A receiver that does not acknowledge
27
+ * answers never calls it.
28
+ */
29
+ sendResult(result: OUIActionResult, onAcknowledged?: (ack: OUIAcknowledgment) => void): void;
24
30
  /** Receive action results (runtime side). */
25
31
  onResult(handler: OUIResultHandler): () => void;
26
32
  /** Push an observation update from the surface to the agent runtime */
@@ -47,8 +53,20 @@ interface OUITransport {
47
53
  */
48
54
  dispose(): void;
49
55
  }
50
- /** Handler for incoming action requests (client side) */
51
- type OUIActionHandler = (request: OUIActionRequest) => void;
56
+ /**
57
+ * Handler for incoming action requests (client side). `receipt`, when the
58
+ * sender asked for one, acknowledges the request: received and accepted, or
59
+ * refused with why (§7.3.7). The sender can then tell a request that reached a
60
+ * client from one that reached nobody, and need not send it again.
61
+ */
62
+ type OUIActionHandler = (request: OUIActionRequest, receipt?: (ack: OUIAcknowledgment) => void) => void;
63
+ /** An acknowledgment: accepted, or refused with a reason. */
64
+ type OUIAcknowledgment = {
65
+ ok: true;
66
+ } | {
67
+ ok: false;
68
+ reason: string;
69
+ };
52
70
  /** Handler for incoming action results (runtime side) */
53
71
  type OUIResultHandler = (result: OUIActionResult) => void;
54
72
  /** Handler for incoming observation updates (server side) */
@@ -85,7 +103,8 @@ declare function createWebSocketTransport(socket: SocketLike, config?: OUITransp
85
103
  */
86
104
  interface SocketLike {
87
105
  readonly connected?: boolean;
88
- emit(event: string, data: unknown): void;
106
+ /** An optional last argument is the receiver's acknowledgment callback, as Socket.IO takes it. */
107
+ emit(event: string, ...args: any[]): void;
89
108
  on(event: string, handler: (...args: any[]) => void): void;
90
109
  off(event: string, handler: (...args: any[]) => void): void;
91
110
  once(event: string, handler: (...args: any[]) => void): void;
@@ -93,4 +112,4 @@ interface SocketLike {
93
112
  disconnect?(): void;
94
113
  }
95
114
 
96
- export { type OUIActionHandler as O, type SocketLike as S, type OUIObservationHandler as a, type OUIResultHandler as b, type OUITransport as c, type OUITransportConfig as d, createWebSocketTransport as e };
115
+ export { type OUIAcknowledgment as O, type SocketLike as S, type OUIActionHandler as a, type OUIObservationHandler as b, type OUIResultHandler as c, type OUITransport as d, type OUITransportConfig as e, createWebSocketTransport as f };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oui-spec",
3
- "version": "0.4.1",
4
- "description": "OUI — The OpenUI Specification. A machine-readable contract for agent-controllable user interfaces.",
3
+ "version": "0.6.0",
4
+ "description": "OUI \u2014 The OpenUI Specification. A machine-readable contract for agent-controllable user interfaces.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -30,7 +30,8 @@
30
30
  "types": "./dist/transport/index.d.ts",
31
31
  "import": "./dist/transport/index.js",
32
32
  "require": "./dist/transport/index.cjs"
33
- }
33
+ },
34
+ "./approval-vectors.json": "./spec/approval-vectors.json"
34
35
  },
35
36
  "typesVersions": {
36
37
  "*": {
@@ -607,6 +607,7 @@ When the agent invokes an action, the runtime sends an `action:request`:
607
607
  - `requestId` MUST be a globally unique string. UUID v4 is RECOMMENDED.
608
608
  - `params` MUST conform to the action's declared `input` schema
609
609
  - The agent runtime SHOULD validate `params` before sending; the surface MUST validate upon receipt
610
+ - `knownSurfaces` (OPTIONAL) is the `surfacesHash` of the client's surfaces that the agent runtime already holds, from the client's snapshot (§7.3.5) or an earlier result. A runtime that holds the client's surfaces SHOULD send it (§7.3.4).
610
611
 
611
612
  #### 7.3.2 Action Result (Success)
612
613
 
@@ -659,11 +660,29 @@ When the agent invokes an action, the runtime sends an `action:request`:
659
660
 
660
661
  A result MAY carry the client's state once the action's effects settled:
661
662
 
662
- | Field | Type | Description |
663
- | -------------- | --------------------------------------------- | ------------------------------------------------------------- |
664
- | `surfaces` | `OUISurface[]` | Every surface active on the client after the action |
665
- | `observations` | `{ [surfaceId]: { [observationId]: value } }` | The latest observation values of those surfaces |
666
- | `settled` | `boolean` | `false` if the client's deadline passed before its UI settled |
663
+ | Field | Type | Description |
664
+ | -------------- | --------------------------------------------- | -------------------------------------------------------------------------------- |
665
+ | `surfaces` | `OUISurface[]` | Every surface active on the client after the action, when the runtime lacks them |
666
+ | `surfacesHash` | `string` | The hash of those surfaces, always: `fnv1a64:` and 16 hex digits (see below) |
667
+ | `observations` | `{ [surfaceId]: { [observationId]: value } }` | The latest observation values of those surfaces |
668
+ | `settled` | `boolean` | `false` if the client's deadline passed before its UI settled |
669
+
670
+ A client's surfaces can weigh hundreds of kilobytes (every action's schema),
671
+ while what changed for one click is usually nothing. So the client sends
672
+ `surfaces` only when they differ from what the agent runtime holds:
673
+
674
+ - The client MUST send `surfacesHash`. It is `fnv1a64:` followed by the 64-bit
675
+ FNV-1a hash, in 16 lowercase hex digits, of the surfaces' JSON with every
676
+ object's keys sorted, taken over its UTF-16 code units.
677
+ - The client MUST include `surfaces` when `surfacesHash` differs from the
678
+ request's `knownSurfaces`, or the request carried none; it MAY leave them out
679
+ otherwise.
680
+ - For an async action's final result, the comparison is with the
681
+ `surfacesHash` of its acknowledgment, which the runtime received first.
682
+ - A runtime that receives a result without `surfaces` MUST keep the surfaces it
683
+ holds under that `surfacesHash`. If it holds none under that hash, it MUST
684
+ treat the client's surfaces as unknown, and send no `knownSurfaces` until it
685
+ has them again.
667
686
 
668
687
  An action can change what the agent may do next: navigating unmounts one
669
688
  page's surfaces and mounts another's. A client that reports `surfaces` lets
@@ -678,8 +697,9 @@ regardless, with `settled: false`.
678
697
  #### 7.3.5 Client Snapshot
679
698
 
680
699
  A client MAY provide its surfaces and observations to the agent runtime as a
681
- snapshot (`{ surfaces, observations }`), for example with each request the
682
- user makes of the agent, instead of through registration events (§7.2). A
700
+ snapshot (`{ surfaces, observations, surfacesHash }`), for example with each
701
+ request the user makes of the agent, instead of through registration events
702
+ (§7.2). Its `surfacesHash` is what the runtime then sends as `knownSurfaces`. A
683
703
  runtime that works from snapshots needs no registry of its own, and cannot
684
704
  hold a registration the client no longer has.
685
705
 
@@ -687,6 +707,42 @@ A client's snapshot is authoritative for that client. A runtime MUST NOT merge
687
707
  snapshots from different clients of the same user into one capability set:
688
708
  each client can act only on what it has mounted.
689
709
 
710
+ #### 7.3.6 A Refused Result
711
+
712
+ A relay between client and runtime can refuse a result: too large, invalid, or
713
+ over a rate limit. A result refused in silence leaves the runtime waiting out
714
+ its deadline and reporting an action that ran as one that did not answer. So:
715
+
716
+ - A receiver that refuses a result SHOULD acknowledge it as refused, with a
717
+ reason (`{ "ok": false, "error": "<reason>" }` on a Socket.IO
718
+ acknowledgment).
719
+ - A client whose result is refused MUST send it again, trimmed, with
720
+ `delivery: { trimmed: true, reason, omitted }`. It first leaves out
721
+ `surfaces` and `observations`, and then also `data`. The action's outcome
722
+ (`success`, `error`) is never trimmed.
723
+ - A runtime that receives a trimmed result MUST report the action's outcome as
724
+ it happened, and SHOULD tell its agent what was left out, and why.
725
+
726
+ #### 7.3.7 Receipts and Repeated Requests
727
+
728
+ An agent runtime may send a request again when no answer has come, since a
729
+ client can join late. Each copy of a request MUST NOT produce another copy of
730
+ its answer while the first is on its way. Copies of a large answer queue on one
731
+ connection, ahead of the next request's answer, and turn a slow answer into
732
+ missed ones. So:
733
+
734
+ - The client MAY acknowledge receipt of a request when the sender asks for an
735
+ acknowledgment. It acknowledges with `{ "ok": true }`, or with
736
+ `{ "ok": false, "reason" }` for a request it does not accept. A sender that
737
+ holds a receipt SHOULD wait for the answer rather than send the request
738
+ again, and SHOULD back off between sends while it holds none.
739
+ - The client MUST run a request id once. It MUST NOT answer a repeat while the
740
+ first answer is being worked out, or once the receiver acknowledged it.
741
+ - The client MUST NOT answer a repeat while the first answer is unacknowledged
742
+ and was sent less than a re-answer interval ago (default 8 s). After that
743
+ interval the answer may have been lost, and the client answers the repeat
744
+ with the same result.
745
+
690
746
  ### 7.4 Observation Updates
691
747
 
692
748
  #### 7.4.1 Push Update