Zum Inhalt springen

UIAP Web Profile

FeldWert
StatusDraft
Version0.1
Datum2026-03-27
Abhängigkeiten[UIAP-CORE], [UIAP-CAP]
EditorenPatrick

UIAP Web Profile v0.1 definiert, wie eine Webanwendung ihren aktuellen, semantisch reduzierten UI-Zustand als PageGraph publiziert, damit ein UIAP-Agent oder eine Runtime:

  • die sichtbare UI verstehen kann,
  • steuerbare Ziele stabil adressieren kann,
  • Laufzeitänderungen als Deltas erhält,
  • Feedback-Signale strukturiert beobachten kann,
  • Browsergrenzen wie Frames und Shadow DOM korrekt modelliert.

Dieses Profil setzt auf UIAP Core v0.1 und UIAP Capability Model v0.1 auf.

type ProfileId = "[email protected]";

Eine Implementierung, die dieses Profil unterstützt, MUSS [email protected] bei session.initialize.supportedProfiles bzw. session.initialized.selectedProfiles verhandeln.

Ein Web Publisher ist typischerweise das in der Anwendung laufende SDK oder die Bridge. Er MUSS:

  • einen PageGraph erzeugen können,
  • Snapshots und Deltas publizieren können,
  • Frames und Shadow-DOM-Grenzen korrekt markieren,
  • elementbezogene Semantik und Zustände bereitstellen,
  • Web-Signale wie Route-Wechsel, Toasts, Validierungsfehler und Fokuswechsel emittieren können.

Ein Web Consumer ist eine Agent-Runtime oder ein Planner. Er MUSS:

  • Snapshots und Deltas verarbeiten,
  • Revisionen korrekt nachhalten,
  • Targets aus stableId, Scope und Semantik auflösen können,
  • Opaque Boundaries respektieren.

Eine Web Bridge ist optional. Sie verbindet Parent/Child-Dokumente oder mehrere Sessions, insbesondere bei eingebetteten UIs.


  1. Das Web-Profil publiziert kein Vollabbild des DOM, sondern einen reduzierten semantischen Graphen.
  2. Standardmäßig SOLLTEN nur sichtbare und relevante UI-Objekte enthalten sein.
  3. Stabile App-IDs schlagen flüchtige DOM-Identitäten.
  4. Semantik schlägt CSS/XPath.
  5. Cross-Origin- und Closed-Shadow-Boundaries MÜSSEN als echte Grenzen behandelt werden.
  6. Snapshots MÜSSEN versions- und revisionsfähig sein.
  7. Deltas MÜSSEN auf eine bekannte Basisrevision verweisen.

type RevisionId = string;
type DocumentId = string;
type FrameId = string;
type ScopeId = string;
type ElementInstanceId = string;
type StableId = string;
type RelationId = string;
type SignalId = string;
interface DOMRectLike {
x: number;
y: number;
width: number;
height: number;
}

Koordinaten MÜSSEN in CSS-Pixeln relativ zum Top-Level-Viewport angegeben werden.


interface PageGraph {
modelVersion: "0.1";
profile: "[email protected]";
revision: RevisionId;
rootDocumentId: DocumentId;
route?: RouteContext;
viewport: ViewportState;
documents: WebDocument[];
scopes: UIScope[];
elements: UIElement[];
relations?: ElementRelation[];
signals?: WebSignal[];
focus?: FocusState;
selection?: SelectionState;
metadata?: Record<string, unknown>;
}
  • revision MUSS innerhalb einer Session monoton fortschreiten.
  • documents, scopes und elements MÜSSEN vollständig für diese Revision gelten.
  • Ein Snapshot MUSS in sich konsistent sein.

interface ViewportState {
width: number;
height: number;
scrollX: number;
scrollY: number;
devicePixelRatio?: number;
}

interface RouteContext {
routeId?: string; // app-stabil
url?: string;
pathname?: string;
title?: string;
params?: Record<string, string>;
query?: Record<string, string | string[]>;
appState?: Record<string, unknown>;
}
  • routeId SOLLTE vorhanden sein, wenn die App eine stabile Router-Semantik hat.
  • url DARF fehlen, wenn die Anwendung keine klassische URL-Navigation verwendet.

type DocumentAccess =
| "same-origin"
| "bridged"
| "opaque";
interface WebDocument {
documentId: DocumentId;
frameId: FrameId;
parentFrameId?: FrameId;
parentDocumentId?: DocumentId;
access: DocumentAccess;
origin?: string;
url?: string;
title?: string;
readyState?: "loading" | "interactive" | "complete";
bbox?: DOMRectLike; // Frame-Box im Top-Level-Viewport
rootScopeId?: ScopeId;
bridgeSessionId?: string; // bei access="bridged"
metadata?: Record<string, unknown>;
}
  • access="same-origin" bedeutet: Publisher kann den DOM-Inhalt direkt modellieren.
  • access="opaque" bedeutet: nur die Frame-Grenze ist bekannt, der innere Inhalt nicht.
  • access="bridged" bedeutet: der innere Inhalt wird durch eine kooperierende Gegenstelle publiziert.
  • Für opaque Dokumente DÜRFEN keine inneren Elemente publiziert werden.

Scopes gruppieren UI-Objekte logisch.

type ScopeKind =
| "route"
| "region"
| "form"
| "dialog"
| "drawer"
| "popover"
| "menu"
| "toolbar"
| "tabset"
| "tabpanel"
| "collection"
| "rowgroup"
| "iframe-root"
| "custom";
interface UIScope {
scopeId: ScopeId;
kind: ScopeKind;
documentId: DocumentId;
parentScopeId?: ScopeId;
stableId?: StableId;
name?: string;
description?: string;
state?: Partial<UIState>;
bbox?: DOMRectLike;
metadata?: Record<string, unknown>;
}
  • Jeder UIScope MUSS genau einem documentId angehören.
  • Ein dialog- oder drawer-Scope SOLLTE state.open=true publizieren, wenn sichtbar.
  • stableId SOLLTE für wichtige Scopes gesetzt werden.

interface UIElement {
instanceId: ElementInstanceId;
stableId?: StableId;
documentId: DocumentId;
scopeId?: ScopeId;
role: UIRole;
name?: string;
description?: string;
state: UIState;
affordances: UIAffordance[];
supportedActions: ActionId[];
bbox?: DOMRectLike;
zIndexHint?: number;
textValue?: string;
semanticValue?: string | number | boolean | null;
targetHints?: TargetHints;
semantics?: WebSemantics;
risk?: RiskDescriptor;
success?: SuccessSignal[];
metadata?: Record<string, unknown>;
}
  • instanceId MUSS innerhalb einer Revision eindeutig sein.
  • stableId SOLLTE vorhanden sein, wenn die App das Element absichtlich für Agent-Steuerung exponiert.
  • role, state, affordances und supportedActions MÜSSEN konsistent sein.
  • bbox SOLLTE für sichtbare Elemente vorhanden sein.
  • supportedActions MUSS nur Actions enthalten, die für dieses Element tatsächlich zulässig sind.

interface TargetHints {
semantic?: {
role?: UIRole;
name?: string;
scopeId?: ScopeId;
ordinal?: number;
};
annotations?: {
meaning?: string;
defaultAction?: ActionId;
};
runtime?: {
css?: string; // lokale Optimierung, nicht kanonisch
xpath?: string; // lokale Optimierung, nicht kanonisch
};
}
  • css und xpath DÜRFEN nur als lokale Runtime-Hints verwendet werden.
  • css und xpath DÜRFEN NICHT als interoperable Kernidentität behandelt werden.

type SemanticSource =
| "native-html"
| "aria"
| "label-association"
| "visible-text"
| "agent-annotation"
| "app-registry"
| "inferred";
interface WebSemantics {
sources: SemanticSource[];
tagName?: string;
inputType?: string;
ariaRole?: string;
shadowHostId?: ElementInstanceId;
framePath?: FrameId[];
interactable?: boolean;
attached?: boolean;
inViewport?: boolean;
obscured?: boolean;
stable?: boolean;
metadata?: Record<string, unknown>;
}
  • sources MUSS mindestens eine Quelle enthalten.
  • Wenn Heuristiken verwendet wurden, MUSS sources den Wert inferred enthalten.
  • attached, inViewport, obscured, stable SOLLTEN publiziert werden, wenn die Runtime später Actionability Checks ausführen soll.

type RelationType =
| "contains"
| "labels"
| "describes"
| "controls"
| "owns"
| "opens"
| "submits"
| "invokes"
| "error-for"
| "next"
| "previous";
interface ElementRelation {
relationId: RelationId;
type: RelationType;
from: ElementInstanceId | ScopeId;
to: ElementInstanceId | ScopeId;
}

interface FocusState {
documentId: DocumentId;
target?: ElementInstanceId;
}
interface SelectionState {
anchorTarget?: ElementInstanceId;
focusTarget?: ElementInstanceId;
text?: string;
}

type WebSignalKind =
| "route.changed"
| "toast.shown"
| "status.changed"
| "validation.changed"
| "dialog.opened"
| "dialog.closed"
| "submission.started"
| "submission.finished"
| "custom";
interface WebSignal {
signalId: SignalId;
kind: WebSignalKind;
documentId?: DocumentId;
scopeId?: ScopeId;
target?: TargetRef;
level?: "info" | "success" | "warning" | "error";
text?: string;
detail?: Record<string, unknown>;
}

Ein Publisher MUSS role wie folgt ableiten:

  1. aus nativem HTML, wenn die Rolle eindeutig ist,
  2. aus ARIA, wenn die Anwendung gültige ARIA-Semantik bereitstellt,
  3. aus expliziter App-Annotierung,
  4. aus Heuristiken nur als letzter Fallback.

Ein Publisher DARF durch Annotation ergänzen, MUSS aber Widersprüche vermeiden. data-uiap-role="button" auf einem nicht aktivierbaren <div> ist genau die Sorte Lüge, die später in Meetings „Edge Case“ genannt wird.

Ein Publisher SOLLTE name als zugänglichen Namen des Elements publizieren. Wenn kein belastbarer Accessible Name bestimmbar ist, DARF sichtbarer, benachbarter oder explizit annotierter Text genutzt werden.

description SOLLTE die zugängliche Beschreibung oder eine explizite App-Beschreibung widerspiegeln.

state SOLLTE aus nativen Kontrollzuständen, ARIA-States und Laufzeitbeobachtung abgeleitet werden.

affordances SOLLTEN aus Rolle, Elementtyp, Zuständen und App-Annotierung abgeleitet werden.


Das Profil REKOMMENDIERT folgende HTML-Datenattribute:

data-uiap-id="video.submit"
data-uiap-scope="create-video-dialog"
data-uiap-meaning="use_case"
data-uiap-action="video.create"
data-uiap-risk="confirm"
data-uiap-sensitive="true"
  • data-uiap-id: stabile Ziel-ID
  • data-uiap-scope: logische Scope-Zuordnung
  • data-uiap-meaning: Domänenbedeutung eines Feldes
  • data-uiap-action: fachliche Default-Action
  • data-uiap-risk: safe | confirm | blocked
  • data-uiap-sensitive: sensible Daten oder Controls
  • Diese Attribute SIND NICHT verpflichtend.
  • Wenn vorhanden, SOLLTEN sie Vorrang vor Heuristiken haben.
  • Sie DÜRFEN native und ARIA-Semantik nicht grob widersprechen.

Standardmäßig SOLLTE ein Snapshot enthalten:

  • sichtbare interaktive Elemente,
  • sichtbare Status- und Feedback-Elemente,
  • relevante Container wie Dialoge, Forms, Popovers und Drawer,
  • fokussierte Elemente, auch wenn sie gerade nicht sichtbar sind,
  • invalidierte oder blockierte Elemente, wenn sie für den aktuellen Flow relevant sind.

Standardmäßig SOLLTE ein Snapshot NICHT enthalten:

  • rein dekorative Elemente,
  • redundante Textknoten ohne Steuerungsrelevanz,
  • versteckte Alt-Zustände vergangener UI-Bäume,
  • komplette, unstrukturierte HTML-Fragmente.

Same-Origin-Policy beschränkt, wie ein Dokument oder Skript mit Ressourcen oder DOM anderer Origins interagieren kann; deshalb dürfen Cross-Origin-Frames ohne Kooperation nicht einfach ausgelesen werden. Shadow Roots haben einen mode von open oder closed; bei closed sind die internen Knoten von außen per JavaScript nicht zugänglich. UIAP muss diese Grenzen modellieren, statt sie wegzuphantasieren. ([MDN Web Docs][3])

Ein Publisher DARF Inhalte gleichoriginärer Frames direkt als zusätzliche WebDocument-Einträge publizieren.

Ein Publisher MUSS fremde, nicht kooperierende Frames als access="opaque" publizieren. Dabei DÜRFEN nur Frame-Metadaten und die äußere Bounding Box bekannt sein.

Ein Publisher DARF einen kooperierenden Cross-Origin-Frame als access="bridged" modellieren. In diesem Fall SOLLTE bridgeSessionId gesetzt sein.

Open Shadow Roots DÜRFEN traversiert werden. Elemente innerhalb eines offenen Shadow Roots SOLLTEN shadowHostId im WebSemantics-Block referenzieren.

Closed Shadow Roots MÜSSEN als opaque behandelt werden. Nur der Host und explizit vom Host publizierte Agent-Metadaten DÜRFEN sichtbar sein.


Fordert einen Snapshot an.

interface WebStateGetPayload {
includeHidden?: boolean; // default false
includeNonInteractive?: boolean; // default false
scopes?: ScopeId[];
documents?: DocumentId[];
maxNodes?: number;
}
interface WebStateSnapshotPayload {
graph: PageGraph;
}

Startet eine Beobachtung.

interface WebObserveStartPayload {
mode?: "snapshot+delta" | "delta-only"; // default snapshot+delta
includeHidden?: boolean;
includeNonInteractive?: boolean;
throttleMs?: number; // default implementation-defined
signals?: WebSignalKind[]; // default all supported
}
interface WebObserveStartedPayload {
subscriptionId: string;
initialRevision?: RevisionId;
}
  • Wenn mode="snapshot+delta" gesetzt ist, MUSS zuerst ein web.state.snapshot folgen.
  • Deltas MÜSSEN auf die zuletzt bekannte Revision referenzieren.

interface WebStateDeltaPayload {
subscriptionId: string;
revision: RevisionId;
baseRevision: RevisionId;
ops: WebDeltaOp[];
signals?: WebSignal[];
}
type WebDeltaOp =
| { op: "upsertDocument"; document: WebDocument }
| { op: "removeDocument"; documentId: DocumentId }
| { op: "upsertScope"; scope: UIScope }
| { op: "removeScope"; scopeId: ScopeId }
| { op: "upsertElement"; element: UIElement }
| { op: "removeElement"; instanceId: ElementInstanceId }
| { op: "setRoute"; route: RouteContext }
| { op: "setFocus"; focus?: FocusState }
| { op: "setSelection"; selection?: SelectionState };
  • baseRevision MUSS auf die unmittelbar vorherige bekannte Revision verweisen.
  • Bei Revisionslücke MUSS der Consumer einen neuen Snapshot anfordern.
  • Ein Delta DARF keine Operation enthalten, die auf unbekannte Dokumente oder Scopes verweist.

interface WebSignalPayload {
signal: WebSignal;
}

Diese Nachricht DARF zusätzlich zu Deltas verwendet werden, wenn Signale zeitkritisch sind.


interface WebObserveStopPayload {
subscriptionId: string;
}
interface WebObserveStoppedPayload {
subscriptionId: string;
}

Ein konformer Web Publisher MUSS:

  1. mindestens einen Snapshot publizieren können,
  2. Elemente mit role, state, affordances und supportedActions publizieren,
  3. documentId, scopeId und instanceId sauber verwalten,
  4. Revisionswechsel monoton publizieren,
  5. Opaque Boundaries korrekt markieren.

Ein Web Publisher SOLLTE:

  1. DOM-Änderungen in Deltas übersetzen,
  2. Route-Wechsel signalisieren,
  3. Fokus und Validierung publizieren,
  4. stabile data-uiap-*-Annotationen unterstützen.

{
"uiap": "0.1",
"kind": "response",
"type": "web.state.snapshot",
"id": "msg_42",
"correlationId": "msg_41",
"sessionId": "sess_123",
"ts": "2026-03-26T14:00:00.000Z",
"source": { "role": "app", "id": "videoland-web-sdk" },
"payload": {
"graph": {
"modelVersion": "0.1",
"profile": "[email protected]",
"revision": "rev_19",
"rootDocumentId": "doc_root",
"route": {
"routeId": "videos.new",
"url": "https://app.example.com/videos/new",
"pathname": "/videos/new",
"title": "Neues Video"
},
"viewport": {
"width": 1440,
"height": 900,
"scrollX": 0,
"scrollY": 180
},
"documents": [
{
"documentId": "doc_root",
"frameId": "frame_root",
"access": "same-origin",
"url": "https://app.example.com/videos/new",
"title": "Neues Video",
"readyState": "complete",
"rootScopeId": "scope_page"
}
],
"scopes": [
{
"scopeId": "scope_page",
"kind": "route",
"documentId": "doc_root",
"stableId": "videos.new",
"name": "Neues Video"
},
{
"scopeId": "scope_form",
"kind": "form",
"documentId": "doc_root",
"parentScopeId": "scope_page",
"stableId": "video.create.form",
"name": "Video erstellen"
}
],
"elements": [
{
"instanceId": "el_title",
"stableId": "video.title",
"documentId": "doc_root",
"scopeId": "scope_form",
"role": "textbox",
"name": "Titel",
"state": { "visible": true, "enabled": true, "required": true },
"affordances": ["read", "focus", "edit"],
"supportedActions": ["ui.focus", "ui.enterText", "ui.clearText"],
"bbox": { "x": 120, "y": 220, "width": 480, "height": 40 },
"semantics": {
"sources": ["native-html", "label-association"],
"tagName": "input",
"inputType": "text",
"attached": true,
"inViewport": true,
"obscured": false,
"stable": true
}
},
{
"instanceId": "el_submit",
"stableId": "video.submit",
"documentId": "doc_root",
"scopeId": "scope_form",
"role": "button",
"name": "Video erstellen",
"state": { "visible": true, "enabled": true },
"affordances": ["read", "focus", "activate", "invoke"],
"supportedActions": ["ui.focus", "ui.activate", "video.create"],
"bbox": { "x": 120, "y": 420, "width": 180, "height": 40 },
"risk": { "level": "confirm", "tags": ["external_effect"] },
"success": [
{ "kind": "route.changed", "pattern": "/videos/:id" },
{ "kind": "toast.contains", "text": "erstellt" }
],
"semantics": {
"sources": ["native-html", "visible-text", "agent-annotation"],
"tagName": "button",
"attached": true,
"inViewport": true,
"obscured": false,
"stable": true
}
}
]
}
}
}

  • [UIAP-CORE] UIAP Core v0.1
  • [UIAP-CAP] UIAP Capability Model v0.1
  • [RFC2119] Key words for use in RFCs to Indicate Requirement Levels, BCP 14
  • PageGraph-Snapshots KÖNNEN sensitive Daten enthalten (Formularwerte, Benutzernamen). Implementierungen SOLLTEN PII vor der Übertragung redaktieren oder maskieren.
  • Cross-Origin-Frames MÜSSEN als opaque behandelt werden, wenn keine explizite Bridge besteht.
  • Same-Origin-Policy und CSP-Regeln der Host-Anwendung MÜSSEN respektiert werden.
  • Bounding-Box-Daten SOLLTEN nicht für Fingerprinting missbraucht werden.
VersionDatumÄnderungen
0.12026-03-27Initialer Entwurf