@etazio/agent-sdk 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.
@@ -0,0 +1,235 @@
1
+ import type { AuthorKind } from './agents.js';
2
+ import type { ChatBlock } from './chat.js';
3
+ import type { WorkspaceRole } from './roles.js';
4
+ export interface UserDto {
5
+ id: string;
6
+ email: string;
7
+ displayName: string;
8
+ emailVerified: boolean;
9
+ createdAt: string;
10
+ }
11
+ export interface SessionDto {
12
+ id: string;
13
+ createdAt: string;
14
+ lastSeenAt: string;
15
+ expiresAt: string;
16
+ userAgent: string | null;
17
+ current: boolean;
18
+ }
19
+ export interface WorkspaceDto {
20
+ id: string;
21
+ name: string;
22
+ slug: string;
23
+ ownerId: string;
24
+ createdAt: string;
25
+ /** Rolle des anfragenden Nutzers in diesem Workspace */
26
+ role: WorkspaceRole;
27
+ /** Mitglieder dürfen sich freie Schreibtische selbst sichern (SPA-125) */
28
+ deskSelfClaim: boolean;
29
+ /**
30
+ * SPA-94: Die aktuelle Sitzung reicht für diesen Workspace nicht (z. B. „nur SSO“) – dann stehen hier
31
+ * die zulässigen Anmeldearten, sonst `null`.
32
+ */
33
+ loginRequired: Array<'password' | 'magic' | 'google' | 'oidc'> | null;
34
+ }
35
+ /** Workspace-Einstellungen geändert (Socket `workspace:settings`, SPA-125) */
36
+ export interface WorkspaceSettingsPayload {
37
+ workspaceId: string;
38
+ deskSelfClaim: boolean;
39
+ }
40
+ export interface MemberDto {
41
+ userId: string;
42
+ email: string;
43
+ displayName: string;
44
+ role: WorkspaceRole;
45
+ joinedAt: string;
46
+ }
47
+ export interface InvitationDto {
48
+ id: string;
49
+ email: string;
50
+ role: WorkspaceRole;
51
+ invitedById: string;
52
+ createdAt: string;
53
+ expiresAt: string;
54
+ acceptedAt: string | null;
55
+ revokedAt: string | null;
56
+ /** Letzter Mailversand und wie oft schon gesendet wurde (SPA-99) */
57
+ lastSentAt: string | null;
58
+ sentCount: number;
59
+ /** Abgeleitet: offen, angenommen, zurückgezogen oder abgelaufen */
60
+ status: 'open' | 'accepted' | 'revoked' | 'expired';
61
+ }
62
+ export interface OfficeRevisionDto {
63
+ id: string;
64
+ number: number;
65
+ schemaVersion: number;
66
+ /** OfficeLayout aus @huddle/scene-schema (hier untypisiert, damit shared nicht von scene-schema abhängt) */
67
+ layout: unknown;
68
+ publishedAt: string;
69
+ }
70
+ export interface DeskAssignmentDto {
71
+ deskId: string;
72
+ userId: string;
73
+ displayName: string;
74
+ assignedAt: string;
75
+ }
76
+ export interface OfficeDto {
77
+ id: string;
78
+ workspaceId: string;
79
+ name: string;
80
+ revision: OfficeRevisionDto;
81
+ /** Arbeitsplatzzuweisungen (Schreibtisch-Instanz → Mitglied) */
82
+ assignments: DeskAssignmentDto[];
83
+ /** Persönliche Einrichtung je Schreibtisch (Phase 4) */
84
+ customizations: DeskCustomizationDto[];
85
+ }
86
+ export interface EditorLockDto {
87
+ userId: string;
88
+ displayName: string;
89
+ /** ISO-Zeitpunkt, bis zu dem die Sperre ohne Verlängerung gilt */
90
+ expiresAt: string;
91
+ }
92
+ export interface OfficeDraftDto {
93
+ officeId: string;
94
+ /** Fortlaufende Versionsnummer des Entwurfs; jede Operation erhöht sie (Basis für Konflikterkennung) */
95
+ version: number;
96
+ /** Veröffentlichte Revision, auf der der Entwurf aufsetzt */
97
+ baseRevisionId: string;
98
+ baseRevisionNumber: number;
99
+ /** OfficeLayout aus @huddle/scene-schema */
100
+ layout: unknown;
101
+ /** Entwurf weicht von der veröffentlichten Revision ab */
102
+ dirty: boolean;
103
+ updatedAt: string;
104
+ updatedBy: {
105
+ userId: string;
106
+ displayName: string;
107
+ } | null;
108
+ lock: EditorLockDto | null;
109
+ }
110
+ export interface LayoutIssueDto {
111
+ code: string;
112
+ message: string;
113
+ targetId?: string;
114
+ otherId?: string;
115
+ }
116
+ export interface RevisionSummaryDto {
117
+ id: string;
118
+ number: number;
119
+ publishedAt: string;
120
+ createdBy: {
121
+ userId: string;
122
+ displayName: string;
123
+ } | null;
124
+ note: string | null;
125
+ /** Aus dieser Revision wiederhergestellt (Rollback) */
126
+ restoredFrom: number | null;
127
+ active: boolean;
128
+ placements: number;
129
+ }
130
+ export interface PublishResultDto {
131
+ office: OfficeDto;
132
+ /** Zuweisungen, deren Schreibtisch in der neuen Revision fehlt (wurden entfernt) */
133
+ removedAssignments: DeskAssignmentDto[];
134
+ /** Teilnehmer, die auf einen gültigen Punkt versetzt wurden */
135
+ relocatedParticipants: number;
136
+ /** Persönliche Gegenstände, die ins Inventar ihrer Besitzer gewandert sind (Schreibtisch/Anker entfernt) */
137
+ inventoried: number;
138
+ }
139
+ export interface MaterialOverrideDto {
140
+ color?: string;
141
+ roughness?: number;
142
+ metalness?: number;
143
+ }
144
+ /** Persönlicher Gegenstand an einem Schreibtisch-Anker (relativ zum Anker gespeichert, Asset-Vertrag). */
145
+ export interface DeskItemDto {
146
+ id: string;
147
+ asset: string;
148
+ /** Anker-Name ohne Präfix (monitor, lamp, plant, deco) */
149
+ anchor: string;
150
+ /** Versatz auf der Ankerebene in Metern [x, z], begrenzt durch DESK_LIMITS.maxOffset; bei `anchor: 'surface'` lokale Position relativ zur Plattenmitte (SPA-95) */
151
+ offset: [number, number];
152
+ rotationY: number;
153
+ overrides?: Record<string, MaterialOverrideDto>;
154
+ }
155
+ export interface DeskCustomizationDto {
156
+ deskId: string;
157
+ userId: string;
158
+ displayName: string;
159
+ /** Persönliche Namensanzeige am Tisch (null = Anzeigename des Mitglieds) */
160
+ label: string | null;
161
+ items: DeskItemDto[];
162
+ /** Material-Overrides des Schreibtischs (desk_top, desk_frame) */
163
+ overrides: Record<string, MaterialOverrideDto>;
164
+ /** Material-Overrides des zugehörigen Stuhls (chair_seat, chair_frame) */
165
+ chair: Record<string, MaterialOverrideDto>;
166
+ updatedAt: string;
167
+ }
168
+ export type InventoryReason = 'desk_removed' | 'anchor_removed' | 'reassigned' | 'admin_reset' | 'released';
169
+ /** Gegenstand, der nicht mehr platziert ist (Schreibtisch entfernt, Anker weg, Platz neu vergeben). */
170
+ export interface InventoryItemDto {
171
+ id: string;
172
+ asset: string;
173
+ overrides?: Record<string, MaterialOverrideDto>;
174
+ fromDeskId: string;
175
+ reason: InventoryReason;
176
+ removedAt: string;
177
+ }
178
+ /** Kurzlebiges LiveKit-Token, an Nutzer und Raum gebunden (Roadmap §7). */
179
+ export interface MeetingTokenDto {
180
+ url: string;
181
+ token: string;
182
+ /** LiveKit-Raumname (`<workspaceId>:office` oder `<workspaceId>:zone:<zoneId>`) */
183
+ room: string;
184
+ /** 'office' oder Zonen-ID */
185
+ target: string;
186
+ expiresAt: string;
187
+ }
188
+ export interface ZoneAccessDto {
189
+ zoneId: string;
190
+ /** Dauerhaft freigegebene Mitglieder (Owner/Admin sind immer berechtigt) */
191
+ userIds: string[];
192
+ }
193
+ export interface ChatReactionDto {
194
+ glyph: string;
195
+ userIds: string[];
196
+ }
197
+ export interface ChatMessageDto {
198
+ id: string;
199
+ /** Konversation: `room:office`, `room:<zoneId>` oder `dm:<userA>:<userB>` (IDs sortiert) */
200
+ conv: string;
201
+ authorId: string;
202
+ authorName: string;
203
+ /** Mensch oder KI-Agent (SPA-66); fehlt bei alten Nachrichten und gilt dann als 'user' */
204
+ authorKind?: AuthorKind;
205
+ text: string;
206
+ /** Thread: ID der Wurzelnachricht, null bei Wurzelnachrichten */
207
+ parentId: string | null;
208
+ replyCount: number;
209
+ lastReplyAt: string | null;
210
+ /** Beteiligte des Threads (an der Wurzel gepflegt) – Avatare in der Zusammenfassung (SPA-117) */
211
+ replyAuthors?: {
212
+ userId: string;
213
+ name: string;
214
+ }[];
215
+ /** Thread-Antwort, die zusätzlich im Kanal steht („Auch an den Kanal senden“) */
216
+ alsoToChannel?: boolean;
217
+ reactions: ChatReactionDto[];
218
+ /** Strukturierte Blöcke (Bestätigungs-Karten); fehlt bei allen Bestandsnachrichten */
219
+ blocks?: ChatBlock[];
220
+ createdAt: string;
221
+ }
222
+ /** Thread, dem jemand folgt – Grundlage des eigenständigen Ungelesen-Zählers (SPA-117). */
223
+ export interface ChatThreadDto {
224
+ rootId: string;
225
+ conv: string;
226
+ unread: number;
227
+ lastReplyAt: string | null;
228
+ lastReadAt: string | null;
229
+ }
230
+ export interface ConversationDto {
231
+ conv: string;
232
+ unread: number;
233
+ lastMessageAt: string | null;
234
+ lastReadAt: string | null;
235
+ }
@@ -0,0 +1,15 @@
1
+ export declare const ERROR_CODES: readonly ['validation_error', 'unauthenticated', 'forbidden', 'not_found', 'conflict',
2
+ /** Entwurf ist durch einen anderen Admin gesperrt */
3
+ 'locked',
4
+ /** Layout verletzt Platzierungsregeln (Details: LayoutIssueDto[]) */
5
+ 'layout_invalid', 'invitation_expired', 'invitation_used', 'invitation_email_mismatch', 'token_invalid', 'rate_limited',
6
+ /** Ein nachgelagerter Dienst fehlt oder antwortet nicht (Phase 7f: Renderer) */
7
+ 'unavailable', 'internal'];
8
+ export type ErrorCode = (typeof ERROR_CODES)[number];
9
+ export interface ApiErrorBody {
10
+ error: {
11
+ code: ErrorCode;
12
+ message: string;
13
+ details?: unknown;
14
+ };
15
+ }
@@ -0,0 +1,391 @@
1
+ import type { AgentBadge } from './agents.js';
2
+ import type { AvatarProfile } from './avatar.js';
3
+ import type { ChatMessageDto, ChatReactionDto, DeskCustomizationDto } from './dto.js';
4
+ /** Socket.IO-Ereignisnamen. Client → Server sind Anfragen mit Ack; Server → Client sind Benachrichtigungen. */
5
+ export declare const SOCKET_EVENTS: {
6
+ readonly workspaceJoin: 'workspace:join';
7
+ readonly workspaceLeave: 'workspace:leave';
8
+ readonly worldJoin: 'world:join';
9
+ readonly worldLeave: 'world:leave';
10
+ readonly worldMove: 'world:move';
11
+ readonly worldSit: 'world:sit';
12
+ readonly worldStand: 'world:stand';
13
+ /** Winken (Phase 4): Server verteilt world:emote */
14
+ readonly worldWave: 'world:wave';
15
+ /** Eigener Medienstatus (Phase 5): Server verteilt world:media */
16
+ readonly worldMedia: 'world:media';
17
+ /** An einer privaten Zone klopfen (Phase 5) – Hosts in der Zone bekommen zone:knocked */
18
+ readonly zoneKnock: 'zone:knock';
19
+ /** Host lässt einen Klopfenden ein oder lehnt ab (Phase 5) */
20
+ readonly zoneAdmit: 'zone:admit';
21
+ /** Reaktion über dem Kopf (Phase 7): Server verteilt world:emote mit Glyph */
22
+ readonly worldReact: 'world:react';
23
+ /** Objektzustand setzen (Client → Server mit Ack) und verteilen (Server → Client), Payload: ObjectStatePayload */
24
+ readonly objectState: 'object:state';
25
+ /** Einmalige Aktion an einem Objekt (Client → Server mit Ack) und verteilen (Server → Client), Payload: ObjectActionPayload */
26
+ readonly objectAction: 'object:action';
27
+ /** Whiteboard-Operation (Client → Server mit Ack {version}) und verteilen (Server → Client), Payload: BoardOpPayload */
28
+ readonly boardOp: 'board:op';
29
+ /** Whiteboard laden (Client → Server mit Ack BoardSyncPayload) */
30
+ readonly boardSync: 'board:sync';
31
+ /** Zeigerposition auf einem Whiteboard (flüchtig, ohne Ack, beide Richtungen), Payload: BoardCursorPayload */
32
+ readonly boardCursor: 'board:cursor';
33
+ /** Web-Anzeige eines TVs abonnieren/abbestellen (Client → Server mit Ack WebStatusPayload), Payload: WebWatchPayload */
34
+ readonly webWatch: 'web:watch';
35
+ /** Steuerung übernehmen/abgeben (Client → Server mit Ack WebControlPayload), Payload: { placementId, take } */
36
+ readonly webControl: 'web:control';
37
+ /** Eingabe an den Server-Browser (Client → Server, ohne Ack), Payload: { placementId, event } */
38
+ readonly webInput: 'web:input';
39
+ /** Navigation im Server-Browser (Client → Server mit Ack), Payload: { placementId, action, url? } */
40
+ readonly webNavigate: 'web:navigate';
41
+ /** Sichtfeld des Server-Browsers an die Anzeigegröße des Steuernden angleichen (Client → Server mit Ack), Payload: WebResizePayload */
42
+ readonly webResize: 'web:resize';
43
+ /** Handshake eines Agenten (Agent → Server mit Ack AgentHelloResult), Payload: AgentHelloInput */
44
+ readonly agentHello: 'agent:hello';
45
+ /** Laufbefehl mit serverseitiger Wegfindung (Agent → Server mit Ack), Payload: AgentGoToInput */
46
+ readonly agentGoTo: 'agent:goTo';
47
+ /** LiveKit-Token für den Raum, in dem der Agent steht (Agent → Server mit Ack MeetingTokenDto) */
48
+ readonly agentMeetingToken: 'agent:meetingToken';
49
+ /** Mensch spricht einen Agenten an (Browser → Server mit Ack), Payload: agentInvokeSchema */
50
+ readonly agentInvoke: 'agent:invoke';
51
+ /** Der Agent meldet, was er gerade tut (Agent → Server mit Ack), Payload: agentVoiceStateSchema */
52
+ readonly agentVoiceState: 'agent:voice:state';
53
+ /** Mensch beendet das Sprachgespräch (Browser → Server mit Ack), Payload: voiceCloseSchema */
54
+ readonly voiceClose: 'voice:close';
55
+ /** Chat-Nachricht senden (Client → Server mit Ack ChatMessageDto), Payload: sendMessageSchema – der Weg für Agenten */
56
+ readonly chatSend: 'chat:send';
57
+ /** „Schreibt gerade“ melden (Client → Server, ohne Ack), Payload: chatTypingSchema – Menschen und Agenten (SPA-133) */
58
+ readonly chatTyping: 'chat:typing';
59
+ readonly presenceJoined: 'presence:joined';
60
+ readonly presenceLeft: 'presence:left';
61
+ readonly sessionRevoked: 'session:revoked';
62
+ readonly worldTick: 'world:tick';
63
+ readonly worldParticipantJoined: 'world:participantJoined';
64
+ readonly worldParticipantLeft: 'world:participantLeft';
65
+ readonly worldPresence: 'world:presence';
66
+ /** Dieselbe Person ist woanders beigetreten – diese Verbindung wird abgelöst (SPA-104) */
67
+ readonly worldSuperseded: 'world:superseded';
68
+ readonly worldSeat: 'world:seat';
69
+ readonly worldCorrect: 'world:correct';
70
+ /** Neue Office-Revision veröffentlicht (Payload: OfficePublishedPayload) */
71
+ readonly officePublished: 'office:published';
72
+ /** Arbeitsplatzzuweisungen geändert (Payload: { assignments: DeskAssignmentDto[] }) */
73
+ readonly officeAssignments: 'office:assignments';
74
+ /** Entwurf geändert (Payload: OfficeDraftChangedPayload) – für parallel geöffnete Editoren */
75
+ readonly officeDraftChanged: 'office:draftChanged';
76
+ /** Avatar-Profil eines Teilnehmers geändert (Payload: WorldAvatarPayload) */
77
+ readonly worldAvatar: 'world:avatar';
78
+ /** Einmalige Geste eines Teilnehmers (Payload: WorldEmotePayload) */
79
+ readonly worldEmote: 'world:emote';
80
+ /** Persönliche Einrichtung eines Schreibtischs geändert (Payload: OfficeDeskPayload) */
81
+ readonly officeDesk: 'office:desk';
82
+ /** Inventar eines Mitglieds geändert (Payload: { workspaceId, userId }) – Client lädt sein Inventar neu */
83
+ readonly officeInventory: 'office:inventory';
84
+ /** Zone eines Teilnehmers geändert (Payload: WorldZonePayload) */
85
+ readonly worldZone: 'world:zone';
86
+ /** Es klopft an einer privaten Zone (Payload: ZoneKnockedPayload) – nur an Hosts in der Zone */
87
+ readonly zoneKnocked: 'zone:knocked';
88
+ /** Antwort auf das eigene Klopfen (Payload: ZoneAdmittedPayload) */
89
+ readonly zoneAdmitted: 'zone:admitted';
90
+ /** Dauerhafte oder sitzungsweite Freigaben einer Zone geändert (Payload: ZoneAccessPayload) */
91
+ readonly zoneAccess: 'zone:access';
92
+ /** Neue Chat-Nachricht (Payload: ChatMessagePayload) */
93
+ readonly chatMessage: 'chat:message';
94
+ /** Reaktionen einer Nachricht geändert (Payload: ChatReactionPayload) */
95
+ readonly chatReaction: 'chat:reaction';
96
+ /** Jemand schreibt gerade bzw. nicht mehr (Payload: ChatTypingPayload) – verfällt beim Empfänger nach CHAT_TYPING_TTL_MS */
97
+ readonly chatTypingUpdate: 'chat:typingUpdate';
98
+ /** Der Nutzer wurde aus dem Workspace entfernt (Payload: { workspaceId }) – Client verlässt das Office */
99
+ readonly workspaceRemoved: 'workspace:removed';
100
+ /** Workspace-Einstellungen geändert (Payload: WorkspaceSettingsPayload, SPA-125) */
101
+ readonly workspaceSettings: 'workspace:settings';
102
+ /** Fortschritt eines KI-Generator-Jobs (Payload: GeneratorProgressPayload) – an den Workspace-Raum */
103
+ readonly generatorProgress: 'generator:progress';
104
+ /** Neues Bild der Web-Anzeige (Payload: WebFramePayload) – nur an Abonnenten */
105
+ readonly webFrame: 'web:frame';
106
+ /** Zustand der Web-Anzeige (Payload: WebStatusPayload) – Laden, Fehler, Adresse, Steuerung */
107
+ readonly webStatus: 'web:status';
108
+ /** Jemand spricht den Agenten an (Payload: AgentAddressPayload) – nur an den betroffenen Agenten */
109
+ readonly agentAddress: 'agent:address';
110
+ /** Der Agent wurde gedrosselt (Payload: AgentThrottledPayload) */
111
+ readonly agentThrottled: 'agent:throttled';
112
+ /** Token rotiert, Agent deaktiviert oder gelöscht (Payload: AgentRevokedPayload) – Verbindung endet danach */
113
+ readonly agentRevoked: 'agent:revoked';
114
+ /** Sprachgespräch eröffnet oder beendet (Payload: AgentVoicePayload) – nur an den betroffenen Agenten (SPA-83) */
115
+ readonly agentVoice: 'agent:voice';
116
+ /** Gesprächszustand für die Oberfläche (Payload: VoiceSessionPayload) – an den Workspace-Raum (SPA-83) */
117
+ readonly voiceSession: 'voice:session';
118
+ };
119
+ export interface SocketAck<T = unknown> {
120
+ ok: boolean;
121
+ data?: T;
122
+ error?: {
123
+ code: string;
124
+ message: string;
125
+ };
126
+ }
127
+ export interface PresencePayload {
128
+ workspaceId: string;
129
+ userId: string;
130
+ displayName: string;
131
+ }
132
+ export declare function workspaceRoom(workspaceId: string): string;
133
+ /** Socket-Raum eines Nutzers (alle seine Verbindungen) – Direktnachrichten, Einlass, Entfernen */
134
+ export declare function userRoom(userId: string): string;
135
+ /** Animationszustand; `sit` wird nur vom Server gesetzt (Sitzplatz belegt). */
136
+ export type AnimState = 'idle' | 'walk' | 'run' | 'sit';
137
+ export declare const MOVE_ANIMS: readonly ['idle', 'walk', 'run'];
138
+ export interface ParticipantState {
139
+ /** Stabil über Reconnects hinweg (vom Client erzeugt, an den Nutzer gebunden) */
140
+ id: string;
141
+ userId: string;
142
+ displayName: string;
143
+ x: number;
144
+ z: number;
145
+ /** Blickrichtung in Grad um die Hochachse */
146
+ rotY: number;
147
+ anim: AnimState;
148
+ seatId: string | null;
149
+ /** false, solange die Verbindung unterbrochen ist (Grace-Period läuft) */
150
+ connected: boolean;
151
+ /** Avatar-Profil (Phase 4) */
152
+ avatar: AvatarProfile;
153
+ /** Zone, in der der Teilnehmer steht (Phase 5); null = außerhalb aller Zonen */
154
+ zoneId: string | null;
155
+ /** Medienstatus (Phase 5), vom Client gemeldet */
156
+ media: MediaState;
157
+ /** Mensch oder KI-Agent (SPA-66); fehlt bei alten Snapshots und gilt dann als 'user' */
158
+ kind?: 'user' | 'agent';
159
+ /** Kennzeichnung eines Agenten (SPA-66) – die UI zeigt Badge, Kartenmarker und Chat-Hinweis daraus */
160
+ agent?: AgentBadge;
161
+ }
162
+ /** Präsenzstatus, vom Nutzer gesetzt (Design: Frei / Fokus / Weg) */
163
+ export type PresenceStatus = 'frei' | 'fokus' | 'weg';
164
+ /** Medienstatus eines Teilnehmers (Phase 5). `screen` = teilt gerade den Bildschirm. */
165
+ export interface MediaState {
166
+ mic: boolean;
167
+ cam: boolean;
168
+ screen: boolean;
169
+ presence: PresenceStatus;
170
+ }
171
+ export interface WorldJoinPayload {
172
+ workspaceId: string;
173
+ /** Bei Reconnect: bisherige Teilnehmer-ID, um Position/Sitzplatz zu behalten */
174
+ participantId?: string;
175
+ }
176
+ export interface WorldSnapshot {
177
+ workspaceId: string;
178
+ officeId: string;
179
+ revisionId: string;
180
+ /** Letzte Tick-Sequenznummer der Welt; ältere Ticks nach dem Snapshot ignorieren */
181
+ seq: number;
182
+ serverTime: number;
183
+ self: string;
184
+ participants: ParticipantState[];
185
+ /** Serverseitige Parameter, damit der Client dieselben Grenzen nutzt */
186
+ tickHz: number;
187
+ graceMs: number;
188
+ /** Freigaben privater Zonen (Phase 5) */
189
+ zoneAccess: ZoneAccessPayload[];
190
+ /** Zustände interaktiver Objekte (Phase 7): Instanz-ID → Zustand */
191
+ objects: Record<string, ObjectStateValue>;
192
+ }
193
+ export interface WorldMovePayload {
194
+ /** Monotone Client-Sequenz; der Server verwirft ältere/duplizierte Pakete */
195
+ seq: number;
196
+ x: number;
197
+ z: number;
198
+ rotY: number;
199
+ anim: (typeof MOVE_ANIMS)[number];
200
+ /** Client-Zeitstempel (performance.now), wird im Tick für den Sender zurückgegeben */
201
+ t?: number;
202
+ }
203
+ /** Kompaktes Update: [participantId, x, z, rotY, anim, clientSeq, clientT] */
204
+ export type TickUpdate = [string, number, number, number, AnimState, number, number];
205
+ export interface WorldTickPayload {
206
+ seq: number;
207
+ t: number;
208
+ u: TickUpdate[];
209
+ }
210
+ export interface WorldCorrectPayload {
211
+ x: number;
212
+ z: number;
213
+ rotY: number;
214
+ anim: AnimState;
215
+ seatId: string | null;
216
+ reason: 'speed' | 'collision' | 'seated' | 'invalid' | 'private' | 'full';
217
+ }
218
+ export interface WorldSeatPayload {
219
+ participantId: string;
220
+ seatId: string | null;
221
+ x: number;
222
+ z: number;
223
+ rotY: number;
224
+ anim: AnimState;
225
+ }
226
+ export interface WorldPresencePayload {
227
+ participantId: string;
228
+ connected: boolean;
229
+ }
230
+ /**
231
+ * Ein Nutzer, ein Avatar (SPA-104): dieselbe Person ist in einem anderen Fenster, Tab oder Gerät
232
+ * beigetreten. Diese Verbindung hat die Welt verlassen und baut bewusst keinen Reconnect auf.
233
+ */
234
+ export interface WorldSupersededPayload {
235
+ workspaceId: string;
236
+ /** Zeitpunkt der Übernahme */
237
+ at: number;
238
+ }
239
+ export interface WorldSitPayload {
240
+ seatId: string;
241
+ }
242
+ export interface OfficePublishedPayload {
243
+ workspaceId: string;
244
+ officeId: string;
245
+ revisionId: string;
246
+ number: number;
247
+ publishedAt: string;
248
+ by: {
249
+ userId: string;
250
+ displayName: string;
251
+ };
252
+ }
253
+ export interface OfficeDraftChangedPayload {
254
+ workspaceId: string;
255
+ officeId: string;
256
+ version: number;
257
+ by: {
258
+ userId: string;
259
+ displayName: string;
260
+ };
261
+ /** 'op' = Operation angewendet, 'reset' = Entwurf verworfen/neu aufgesetzt, 'lock' = Sperre geändert */
262
+ kind: 'op' | 'reset' | 'lock';
263
+ }
264
+ export interface WorldAvatarPayload {
265
+ participantId: string;
266
+ avatar: AvatarProfile;
267
+ }
268
+ /** `wave` = Wink-Clip (Phase 4); `glyph` = Reaktion/Objekt-Emote über dem Kopf (Phase 7) */
269
+ export type Emote = 'wave' | 'glyph';
270
+ export interface WorldEmotePayload {
271
+ participantId: string;
272
+ emote: Emote;
273
+ /** Glyph über dem Kopf (Phase 7); beim Winken 👋 */
274
+ glyph?: string;
275
+ /** Auslösendes Objekt (Phase 7c) */
276
+ objectId?: string;
277
+ }
278
+ /** Objektzustand: flaches JSON ≤ 2 KB (Lampe `{ on: false }`, später weitere) */
279
+ export type ObjectStateValue = Record<string, unknown>;
280
+ export interface ObjectStatePayload {
281
+ placementId: string;
282
+ state: ObjectStateValue;
283
+ /** Monoton je Objekt (Server); veraltete Broadcasts ignorieren */
284
+ version: number;
285
+ /** Teilnehmer, der den Zustand gesetzt hat (fehlt beim Snapshot) */
286
+ by?: string;
287
+ }
288
+ export interface ObjectActionPayload {
289
+ placementId: string;
290
+ action: string;
291
+ data?: unknown;
292
+ /** Auslösender Teilnehmer (Server → Client) */
293
+ participantId: string;
294
+ /** Serverseitiger Zufallswert (Katalog `action.roll`), z. B. Dart-Wurf */
295
+ roll?: number;
296
+ /** Serverzeit der Aktion */
297
+ at: number;
298
+ }
299
+ export interface BoardOpPayload {
300
+ placementId: string;
301
+ version: number;
302
+ /** BoardOp aus @huddle/shared (board.ts) */
303
+ op: unknown;
304
+ by: string;
305
+ }
306
+ export interface BoardSyncPayload {
307
+ placementId: string;
308
+ version: number;
309
+ /** Snapshot-PNG als data:-URL (null = leeres Board) und seine Version */
310
+ snapshot: string | null;
311
+ snapshotVersion: number;
312
+ /** Operationen seit dem Snapshot (in Reihenfolge) */
313
+ ops: Array<{
314
+ v: number;
315
+ op: unknown;
316
+ by: string;
317
+ }>;
318
+ }
319
+ export interface BoardCursorPayload {
320
+ placementId: string;
321
+ participantId: string;
322
+ displayName: string;
323
+ /** Normiert 0–1; null = Zeiger verlassen */
324
+ x: number | null;
325
+ y: number | null;
326
+ }
327
+ export interface OfficeDeskPayload {
328
+ workspaceId: string;
329
+ officeId: string;
330
+ deskId: string;
331
+ /** null = Einrichtung entfernt (Zurücksetzen, Platz neu vergeben, Schreibtisch weg) */
332
+ customization: DeskCustomizationDto | null;
333
+ }
334
+ export interface WorldZonePayload {
335
+ participantId: string;
336
+ zoneId: string | null;
337
+ }
338
+ export interface WorldMediaPayload extends MediaState {
339
+ participantId: string;
340
+ }
341
+ export interface ZoneKnockPayload {
342
+ zoneId: string;
343
+ }
344
+ export interface ZoneAdmitPayload {
345
+ zoneId: string;
346
+ userId: string;
347
+ allow: boolean;
348
+ }
349
+ export interface ZoneKnockedPayload {
350
+ zoneId: string;
351
+ userId: string;
352
+ displayName: string;
353
+ }
354
+ export interface ZoneAdmittedPayload {
355
+ zoneId: string;
356
+ allowed: boolean;
357
+ by: {
358
+ userId: string;
359
+ displayName: string;
360
+ };
361
+ }
362
+ /** Freigaben einer privaten Zone: dauerhaft (`persistent`, per HTTP verwaltet) und für diese Sitzung der Welt (`session`, per Einlass) */
363
+ export interface ZoneAccessPayload {
364
+ workspaceId: string;
365
+ zoneId: string;
366
+ persistent: string[];
367
+ session: string[];
368
+ }
369
+ export interface ChatMessagePayload {
370
+ workspaceId: string;
371
+ message: ChatMessageDto;
372
+ }
373
+ /** Wie lange eine „schreibt“-Meldung gilt, wenn keine Auffrischung kommt (SPA-133). Sender frischen alle ~3 s auf. */
374
+ export declare const CHAT_TYPING_TTL_MS = 6000;
375
+ export declare const CHAT_TYPING_REFRESH_MS = 3000;
376
+ export interface ChatTypingPayload {
377
+ workspaceId: string;
378
+ conv: string;
379
+ /** Thread, in dem geschrieben wird – `null` für den Kanal selbst */
380
+ parentId: string | null;
381
+ userId: string;
382
+ name: string;
383
+ kind: 'user' | 'agent';
384
+ typing: boolean;
385
+ }
386
+ export interface ChatReactionPayload {
387
+ workspaceId: string;
388
+ conv: string;
389
+ messageId: string;
390
+ reactions: ChatReactionDto[];
391
+ }