@company-semantics/contracts 0.27.0 → 0.29.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.27.0",
3
+ "version": "0.29.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -325,6 +325,62 @@ export interface EvolutionBaselines {
325
325
  subdirectoryAffinity?: SubdirectoryAffinityBaseline;
326
326
  }
327
327
 
328
+ // =============================================================================
329
+ // Aggregate Evolution Types
330
+ // =============================================================================
331
+
332
+ /**
333
+ * Domain-level aggregate baseline for evolution tracking.
334
+ * Replaces per-file baselines with domain-aggregated metrics.
335
+ *
336
+ * Design: Domains are extracted from file paths (first 2 segments).
337
+ * Example: 'src/chat/execution/types.ts' → domain 'chat/execution'
338
+ */
339
+ export interface DomainAggregateBaseline {
340
+ /** Total exports across all files in domain */
341
+ exports: number;
342
+ /** Total imports across all files in domain */
343
+ imports: number;
344
+ /** Diagnostic only, not thresholded */
345
+ fileCount: number;
346
+ /** ISO timestamp when baseline was captured */
347
+ capturedAt?: string;
348
+ }
349
+
350
+ /**
351
+ * Configuration for aggregate evolution guard.
352
+ * Uses domain-level metrics instead of per-file tracking.
353
+ *
354
+ * Benefits over per-file:
355
+ * - ~22 entries vs ~300 entries
356
+ * - Stable under refactors within domains
357
+ * - Grows O(domains) not O(files)
358
+ */
359
+ export interface AggregateEvolutionConfig {
360
+ /** Source directory to scan */
361
+ srcDir: string;
362
+ /** Domain baselines keyed by domain path */
363
+ domains: Record<string, DomainAggregateBaseline>;
364
+ /** Global totals for sanity checking */
365
+ totals?: {
366
+ exports: number;
367
+ imports: number;
368
+ files: number;
369
+ capturedAt?: string;
370
+ };
371
+ /** Warning thresholds (sensible defaults applied if omitted) */
372
+ thresholds?: {
373
+ /** Absolute export growth before warning (default: 10) */
374
+ domainExportGrowth?: number;
375
+ /** Percentage growth before warning (default: 0.25 = 25%) */
376
+ domainGrowthPercent?: number;
377
+ };
378
+ }
379
+
380
+ // =============================================================================
381
+ // Coverage Baseline Types
382
+ // =============================================================================
383
+
328
384
  /**
329
385
  * Coverage baseline configuration.
330
386
  * Used by CI orchestrator to inject coverage drift guard.
@@ -54,6 +54,9 @@ export type {
54
54
  FileClusterBaseline,
55
55
  SubdirectoryAffinityBaseline,
56
56
  MetaBaselines,
57
+ // Aggregate evolution types
58
+ DomainAggregateBaseline,
59
+ AggregateEvolutionConfig,
57
60
  // SOC 2 Guard Configuration types
58
61
  SecretsDetectionConfig,
59
62
  StructuredLoggingConfig,
package/src/index.ts CHANGED
@@ -70,6 +70,9 @@ export type {
70
70
  ContractsFreshnessBaseline,
71
71
  CoverageBaseline,
72
72
  MetaBaselines,
73
+ // Aggregate evolution types
74
+ DomainAggregateBaseline,
75
+ AggregateEvolutionConfig,
73
76
  // SOC 2 Compliance types
74
77
  Soc2ControlArea,
75
78
  Soc2ControlStatus,
@@ -152,6 +155,19 @@ export type {
152
155
  ExecutionResultData,
153
156
  ExecutionResultPart,
154
157
  ExecutionResultDataPart,
158
+ // Message lifecycle types (Phase 1 streaming)
159
+ // @see ADR-CONT-027 for design rationale
160
+ StreamPhase,
161
+ MessageLifecycleEventType,
162
+ MessageLifecycleEvent,
163
+ MessageStartEvent,
164
+ MessageDeltaEvent,
165
+ MessageCompleteEvent,
166
+ ToolResultRenderClass,
167
+ ToolResultPart,
168
+ MessageStartDataPart,
169
+ MessageDeltaDataPart,
170
+ MessageCompleteDataPart,
155
171
  } from './message-parts/index.js'
156
172
 
157
173
  export {
@@ -47,6 +47,21 @@ export type {
47
47
  ExecutionResultDataPart,
48
48
  } from './execution.js'
49
49
 
50
+ // Lifecycle types (Phase 1 streaming)
51
+ export type {
52
+ StreamPhase,
53
+ MessageLifecycleEventType,
54
+ MessageLifecycleEvent,
55
+ MessageStartEvent,
56
+ MessageDeltaEvent,
57
+ MessageCompleteEvent,
58
+ ToolResultRenderClass,
59
+ ToolResultPart,
60
+ MessageStartDataPart,
61
+ MessageDeltaDataPart,
62
+ MessageCompleteDataPart,
63
+ } from './lifecycle.js'
64
+
50
65
  // Type guards
51
66
  export { isTextPart, isSurfacePart } from './types.js'
52
67
 
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Message Lifecycle Event Vocabulary
3
+ *
4
+ * Explicit lifecycle events for message streaming.
5
+ * Backend is the single authority for message lifecycle state.
6
+ *
7
+ * INVARIANTS:
8
+ * - Every message stream MUST emit: start -> delta* -> complete
9
+ * - messageId MUST be consistent across all events for a single message
10
+ * - version field enables protocol evolution without breaking changes
11
+ *
12
+ * @see ADR-CONT-027 for design rationale
13
+ */
14
+
15
+ // =============================================================================
16
+ // Stream Phase
17
+ // =============================================================================
18
+
19
+ /**
20
+ * Derived stream phase for UI state management.
21
+ * Frontend derives this from lifecycle events, not vice versa.
22
+ *
23
+ * - idle: No active stream (initial state, or after complete)
24
+ * - streaming: Between message.start and message.complete
25
+ * - complete: After message.complete received
26
+ */
27
+ export type StreamPhase = 'idle' | 'streaming' | 'complete'
28
+
29
+ // =============================================================================
30
+ // Lifecycle Event Types
31
+ // =============================================================================
32
+
33
+ /**
34
+ * Lifecycle event type discriminator.
35
+ */
36
+ export type MessageLifecycleEventType =
37
+ | 'message.start'
38
+ | 'message.delta'
39
+ | 'message.complete'
40
+
41
+ /**
42
+ * Base interface for all message lifecycle events.
43
+ *
44
+ * @property type - Event discriminator
45
+ * @property version - Protocol version (add now, expensive to retrofit later)
46
+ * @property messageId - Correlation identifier for the message
47
+ * @property timestamp - ISO 8601 timestamp of event emission
48
+ */
49
+ export interface MessageLifecycleEvent {
50
+ type: MessageLifecycleEventType
51
+ version: 1
52
+ messageId: string
53
+ timestamp: string
54
+ }
55
+
56
+ /**
57
+ * Message start event.
58
+ * Emitted when a new message stream begins.
59
+ */
60
+ export interface MessageStartEvent extends MessageLifecycleEvent {
61
+ type: 'message.start'
62
+ }
63
+
64
+ /**
65
+ * Message delta event.
66
+ * Emitted for each text chunk during streaming.
67
+ * Only contains narrative text, not surface parts.
68
+ */
69
+ export interface MessageDeltaEvent extends MessageLifecycleEvent {
70
+ type: 'message.delta'
71
+ /** The text delta (narrative content only) */
72
+ delta: string
73
+ }
74
+
75
+ /**
76
+ * Message complete event.
77
+ * Emitted when the message stream ends.
78
+ * Signals that all narrative content has been streamed.
79
+ */
80
+ export interface MessageCompleteEvent extends MessageLifecycleEvent {
81
+ type: 'message.complete'
82
+ /** Total length of narrative content for validation */
83
+ narrativeLength: number
84
+ }
85
+
86
+ // =============================================================================
87
+ // Tool Result Rendering Classification
88
+ // =============================================================================
89
+
90
+ /**
91
+ * Tool result rendering classification.
92
+ * This is protocol metadata, not a UI decision.
93
+ *
94
+ * - inline: Render during streaming (narrative-like)
95
+ * Examples: search snippets, numbers, short text, intermediate reasoning
96
+ * No atomicity guarantee, safe to interleave with text
97
+ *
98
+ * - surface: Render only after message.complete (UI commitment)
99
+ * Examples: tool lists, cards, dashboards, MCP UI components
100
+ * Atomic, deterministic, never stream
101
+ */
102
+ export type ToolResultRenderClass = 'inline' | 'surface'
103
+
104
+ /**
105
+ * Tool result part with rendering classification.
106
+ */
107
+ export interface ToolResultPart {
108
+ type: 'tool-result'
109
+ /** Tool identifier */
110
+ toolName: string
111
+ /** Rendering classification */
112
+ render: ToolResultRenderClass
113
+ /** Tool-specific payload */
114
+ payload: unknown
115
+ /** Optional message correlation (enables out-of-band arrival, multi-message chains) */
116
+ messageId?: string
117
+ }
118
+
119
+ // =============================================================================
120
+ // Wire Format Types
121
+ // =============================================================================
122
+
123
+ /**
124
+ * Wire format for message start event.
125
+ * Follows AI SDK's data-{name} convention.
126
+ */
127
+ export interface MessageStartDataPart {
128
+ type: 'data-message-start'
129
+ data: Omit<MessageStartEvent, 'type'>
130
+ }
131
+
132
+ /**
133
+ * Wire format for message delta event.
134
+ * Note: Standard text-delta may be used instead for AI SDK compatibility.
135
+ */
136
+ export interface MessageDeltaDataPart {
137
+ type: 'data-message-delta'
138
+ data: Omit<MessageDeltaEvent, 'type'>
139
+ }
140
+
141
+ /**
142
+ * Wire format for message complete event.
143
+ */
144
+ export interface MessageCompleteDataPart {
145
+ type: 'data-message-complete'
146
+ data: Omit<MessageCompleteEvent, 'type'>
147
+ }
@@ -19,6 +19,10 @@ import type {
19
19
  ConfirmationResponseDataPart,
20
20
  } from './confirmation.js'
21
21
  import type { ExecutionResultData, ExecutionResultDataPart } from './execution.js'
22
+ import type {
23
+ MessageStartDataPart,
24
+ MessageCompleteDataPart,
25
+ } from './lifecycle.js'
22
26
 
23
27
  /**
24
28
  * Factory for creating wire-format surface parts.
@@ -119,4 +123,55 @@ export const WireSurfaceBuilder = {
119
123
  data,
120
124
  }
121
125
  },
126
+
127
+ // =========================================================================
128
+ // Message Lifecycle Events (Phase 1)
129
+ // =========================================================================
130
+
131
+ /**
132
+ * Build a message start event for streaming.
133
+ * Emit at the beginning of a new message stream.
134
+ *
135
+ * INVARIANTS:
136
+ * - Must be emitted before any text-delta or surface parts
137
+ * - messageId must be consistent across all events for this message
138
+ *
139
+ * @param messageId - Unique identifier for this message
140
+ * @returns Wire-format message start event
141
+ */
142
+ messageStart(messageId: string): MessageStartDataPart {
143
+ return {
144
+ type: 'data-message-start',
145
+ data: {
146
+ version: 1,
147
+ messageId,
148
+ timestamp: new Date().toISOString(),
149
+ },
150
+ }
151
+ },
152
+
153
+ /**
154
+ * Build a message complete event for streaming.
155
+ * Emit after all narrative content has been streamed.
156
+ *
157
+ * INVARIANTS:
158
+ * - Must be emitted after all text-delta events
159
+ * - Surface parts (if any) should be emitted after this
160
+ * - narrativeLength must match total text content length
161
+ *
162
+ * @param messageId - Message identifier (must match start event)
163
+ * @param narrativeLength - Total length of streamed narrative text
164
+ * @returns Wire-format message complete event
165
+ */
166
+ messageComplete(messageId: string, narrativeLength: number): MessageCompleteDataPart {
167
+ return {
168
+ type: 'data-message-complete',
169
+ data: {
170
+ version: 1,
171
+ messageId,
172
+ narrativeLength,
173
+ timestamp: new Date().toISOString(),
174
+ },
175
+ }
176
+ },
122
177
  } as const