@company-semantics/contracts 0.60.2 → 0.61.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "0.60.2",
3
+ "version": "0.61.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
package/src/chat/index.ts CHANGED
@@ -24,8 +24,25 @@ export type {
24
24
  ChatSummaryExtended,
25
25
  TitleGenerationRequest,
26
26
  TitleGenerationResponse,
27
+ // Event system types
28
+ BaseEvent,
29
+ InvalidationReason,
30
+ ChatChangedField,
31
+ // Domain events (carry full state)
32
+ ChatCreatedEvent,
33
+ ChatUpdatedEvent,
34
+ ChatDeletedEvent,
35
+ ChatDomainEvent,
36
+ // Invalidation events (staleness signals)
37
+ InvalidateChatEvent,
38
+ InvalidateChatListEvent,
39
+ ChatInvalidationEvent,
40
+ // SSE event unions
27
41
  ChatSseEvent,
28
42
  ChatUpdateEvent,
29
- // Chat creation event (server-first navigation contract)
30
- ChatCreatedEvent,
43
+ // Legacy types (deprecated, for migration)
44
+ LegacyChatCreatedEvent,
45
+ LegacyChatInvalidatedEvent,
46
+ LegacyChatListInvalidatedEvent,
47
+ LegacyChatSseEvent,
31
48
  } from './types'
package/src/chat/types.ts CHANGED
@@ -169,14 +169,44 @@ export interface TitleGenerationResponse {
169
169
  isAutoGenerated: boolean
170
170
  }
171
171
 
172
+ // =============================================================================
173
+ // Event System Types
174
+ // =============================================================================
175
+
176
+ /**
177
+ * Base envelope for all SSE events.
178
+ *
179
+ * INV-EVENT-ORDER-1: Domain events for a given entity are emitted in commit order.
180
+ * Clients may assume monotonic updatedAt.
181
+ * INV-EVENT-IDEMPOTENT: Clients must treat events as idempotent. Full payload
182
+ * replacement handles duplicate events naturally.
183
+ * INV-EVENT-CONVERGENCE: Clients must assume events may be missed during SSE
184
+ * disconnects. Invalidation events ensure convergence.
185
+ */
186
+ export interface BaseEvent {
187
+ /** Protocol version for forward compatibility */
188
+ v: 1
189
+ /** ISO 8601 timestamp when event was created */
190
+ timestamp: string
191
+ /** Optional event ID for telemetry/debugging (not for deduplication logic) */
192
+ eventId?: string
193
+ }
194
+
172
195
  /**
173
- * SSE event types for chat updates (convention locked).
174
- * Client receives these and handles accordingly.
196
+ * Reason for cache invalidation.
197
+ * INV-REASON-TELEMETRY: This is for telemetry only. UI behavior must be
198
+ * identical regardless of reason. Do not branch on this.
175
199
  */
176
- export type ChatSseEvent =
177
- | { type: 'chat.invalidated'; chatId: string; timestamp: string }
178
- | { type: 'chat.list.invalidated'; timestamp: string }
179
- | ChatCreatedEvent
200
+ export type InvalidationReason = 'external-mutation' | 'bulk-operation' | 'sync-required'
201
+
202
+ /**
203
+ * Fields that can change on a chat, used in changed[] array.
204
+ */
205
+ export type ChatChangedField = 'title' | 'titleSource' | 'status' | 'pinnedAt'
206
+
207
+ // =============================================================================
208
+ // Domain Events (past-tense facts, carry full state)
209
+ // =============================================================================
180
210
 
181
211
  /**
182
212
  * Emitted exactly once after a chat is successfully persisted.
@@ -185,18 +215,152 @@ export type ChatSseEvent =
185
215
  * INV-EVENT-1: This event is emitted exactly once per successful stream.
186
216
  * INV-PERSIST-1: Chat record + messages are committed atomically before this event.
187
217
  */
188
- export interface ChatCreatedEvent {
218
+ export interface ChatCreatedEvent extends BaseEvent {
219
+ type: 'chat.created'
220
+ data: {
221
+ /** Canonical server-generated UUID - use this for URLs */
222
+ chatId: string
223
+ /** Client-provided ID for correlation (optional) */
224
+ interactionId?: string
225
+ /** Chat title */
226
+ title: string
227
+ /** How the title was set */
228
+ titleSource: TitleSource
229
+ /** Chat status */
230
+ status: ChatStatus
231
+ /** When pinned (null if not pinned) */
232
+ pinnedAt: string | null
233
+ /** Message count */
234
+ messageCount: number
235
+ /** ISO timestamp */
236
+ createdAt: string
237
+ /** ISO timestamp */
238
+ updatedAt: string
239
+ /** Whether chat is shared */
240
+ isShared: boolean
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Emitted when chat metadata changes (title, pin, archive, etc.)
246
+ * Carries full current state - clients should replace local state entirely.
247
+ */
248
+ export interface ChatUpdatedEvent extends BaseEvent {
249
+ type: 'chat.updated'
250
+ data: {
251
+ chatId: string
252
+ title: string
253
+ titleSource: TitleSource | null
254
+ titleGeneratedAt: string | null
255
+ status: ChatStatus
256
+ pinnedAt: string | null
257
+ messageCount: number
258
+ createdAt: string
259
+ updatedAt: string
260
+ isShared: boolean
261
+ }
262
+ /** What fields changed - for UI optimization only */
263
+ changed: ChatChangedField[]
264
+ }
265
+
266
+ /**
267
+ * Emitted when a chat is deleted.
268
+ */
269
+ export interface ChatDeletedEvent extends BaseEvent {
270
+ type: 'chat.deleted'
271
+ data: {
272
+ chatId: string
273
+ }
274
+ }
275
+
276
+ // =============================================================================
277
+ // Invalidation Events (staleness signals)
278
+ // =============================================================================
279
+
280
+ /**
281
+ * Single-entity staleness signal.
282
+ * Emitted when a specific chat may be out of sync (e.g., external mutation).
283
+ */
284
+ export interface InvalidateChatEvent extends BaseEvent {
285
+ type: 'invalidate.chat'
286
+ chatId: string
287
+ reason: InvalidationReason
288
+ }
289
+
290
+ /**
291
+ * Collection staleness signal.
292
+ * Emitted when chat list ordering/filtering may be stale.
293
+ */
294
+ export interface InvalidateChatListEvent extends BaseEvent {
295
+ type: 'invalidate.chat-list'
296
+ reason: InvalidationReason
297
+ }
298
+
299
+ // =============================================================================
300
+ // SSE Event Union
301
+ // =============================================================================
302
+
303
+ /**
304
+ * All domain events that carry full state.
305
+ */
306
+ export type ChatDomainEvent = ChatCreatedEvent | ChatUpdatedEvent | ChatDeletedEvent
307
+
308
+ /**
309
+ * All invalidation events that signal staleness.
310
+ */
311
+ export type ChatInvalidationEvent = InvalidateChatEvent | InvalidateChatListEvent
312
+
313
+ /**
314
+ * SSE event types for chat updates.
315
+ * Client receives these via SSE and handles accordingly.
316
+ */
317
+ export type ChatSseEvent = ChatDomainEvent | ChatInvalidationEvent
318
+
319
+ /**
320
+ * Convenience alias for backwards compat.
321
+ */
322
+ export type ChatUpdateEvent = ChatSseEvent
323
+
324
+ // =============================================================================
325
+ // Legacy Event Types (deprecated, for migration period only)
326
+ // =============================================================================
327
+
328
+ /**
329
+ * @deprecated Use ChatCreatedEvent with v:1 instead.
330
+ * Legacy event shape without v field or data wrapper.
331
+ */
332
+ export interface LegacyChatCreatedEvent {
189
333
  type: 'chat.created'
190
- /** Canonical server-generated UUID - use this for URLs */
191
334
  chatId: string
192
- /** Client-provided ID for correlation (optional) */
193
335
  interactionId?: string
194
- /** Auto-generated title from first message */
195
336
  title?: string
196
337
  timestamp: string
197
338
  }
198
339
 
199
340
  /**
200
- * Convenience alias for backwards compat.
341
+ * @deprecated Use InvalidateChatEvent instead.
342
+ * Legacy invalidation event without v field.
201
343
  */
202
- export type ChatUpdateEvent = ChatSseEvent
344
+ export interface LegacyChatInvalidatedEvent {
345
+ type: 'chat.invalidated'
346
+ chatId: string
347
+ timestamp: string
348
+ }
349
+
350
+ /**
351
+ * @deprecated Use InvalidateChatListEvent instead.
352
+ * Legacy list invalidation event without v field.
353
+ */
354
+ export interface LegacyChatListInvalidatedEvent {
355
+ type: 'chat.list.invalidated'
356
+ timestamp: string
357
+ }
358
+
359
+ /**
360
+ * @deprecated Use ChatSseEvent instead.
361
+ * Legacy SSE event union for migration period.
362
+ */
363
+ export type LegacyChatSseEvent =
364
+ | LegacyChatInvalidatedEvent
365
+ | LegacyChatListInvalidatedEvent
366
+ | LegacyChatCreatedEvent