@oscharko-dev/keiko-contracts 0.2.8 → 0.2.9

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 (106) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bff-wire.d.ts +33 -0
  3. package/dist/bff-wire.d.ts.map +1 -1
  4. package/dist/command-runner.d.ts +81 -0
  5. package/dist/command-runner.d.ts.map +1 -0
  6. package/dist/command-runner.js +209 -0
  7. package/dist/container-runtime.d.ts +125 -0
  8. package/dist/container-runtime.d.ts.map +1 -0
  9. package/dist/container-runtime.js +287 -0
  10. package/dist/context-engineering.js +2 -2
  11. package/dist/discussion-intelligence.d.ts +70 -0
  12. package/dist/discussion-intelligence.d.ts.map +1 -0
  13. package/dist/discussion-intelligence.js +440 -0
  14. package/dist/editor-agent-governance.d.ts +65 -0
  15. package/dist/editor-agent-governance.d.ts.map +1 -0
  16. package/dist/editor-agent-governance.js +180 -0
  17. package/dist/editor-agent.d.ts +29 -1
  18. package/dist/editor-agent.d.ts.map +1 -1
  19. package/dist/editor-agent.js +183 -6
  20. package/dist/editor-builtin-capabilities.d.ts +13 -0
  21. package/dist/editor-builtin-capabilities.d.ts.map +1 -0
  22. package/dist/editor-builtin-capabilities.js +135 -0
  23. package/dist/editor-language-mode-map.d.ts +11 -0
  24. package/dist/editor-language-mode-map.d.ts.map +1 -0
  25. package/dist/editor-language-mode-map.js +89 -0
  26. package/dist/editor-layout.d.ts +37 -0
  27. package/dist/editor-layout.d.ts.map +1 -1
  28. package/dist/editor-layout.js +125 -8
  29. package/dist/editor-workspace-path.d.ts +20 -0
  30. package/dist/editor-workspace-path.d.ts.map +1 -0
  31. package/dist/editor-workspace-path.js +133 -0
  32. package/dist/evidence.d.ts +3 -1
  33. package/dist/evidence.d.ts.map +1 -1
  34. package/dist/gateway.d.ts +72 -3
  35. package/dist/gateway.d.ts.map +1 -1
  36. package/dist/gateway.js +73 -3
  37. package/dist/git-commit-intent.d.ts +28 -0
  38. package/dist/git-commit-intent.d.ts.map +1 -0
  39. package/dist/git-commit-intent.js +155 -0
  40. package/dist/git-commit-policy.d.ts +29 -0
  41. package/dist/git-commit-policy.d.ts.map +1 -0
  42. package/dist/git-commit-policy.js +173 -0
  43. package/dist/git-delivery-action-sheet.d.ts +157 -0
  44. package/dist/git-delivery-action-sheet.d.ts.map +1 -0
  45. package/dist/git-delivery-action-sheet.js +430 -0
  46. package/dist/git-delivery-evidence.d.ts +92 -0
  47. package/dist/git-delivery-evidence.d.ts.map +1 -0
  48. package/dist/git-delivery-evidence.js +272 -0
  49. package/dist/git-delivery-policy.d.ts +40 -0
  50. package/dist/git-delivery-policy.d.ts.map +1 -0
  51. package/dist/git-delivery-policy.js +183 -0
  52. package/dist/git-delivery-provider.d.ts +53 -0
  53. package/dist/git-delivery-provider.d.ts.map +1 -0
  54. package/dist/git-delivery-provider.js +96 -0
  55. package/dist/git-delivery.d.ts +202 -0
  56. package/dist/git-delivery.d.ts.map +1 -0
  57. package/dist/git-delivery.js +410 -0
  58. package/dist/git-history.d.ts +27 -0
  59. package/dist/git-history.d.ts.map +1 -0
  60. package/dist/git-history.js +73 -0
  61. package/dist/git-merge.d.ts +67 -0
  62. package/dist/git-merge.d.ts.map +1 -0
  63. package/dist/git-merge.js +323 -0
  64. package/dist/git-pull-request.d.ts +112 -0
  65. package/dist/git-pull-request.d.ts.map +1 -0
  66. package/dist/git-pull-request.js +351 -0
  67. package/dist/git-repository-agent.d.ts +46 -0
  68. package/dist/git-repository-agent.d.ts.map +1 -0
  69. package/dist/git-repository-agent.js +198 -0
  70. package/dist/git-repository-summary.d.ts +54 -0
  71. package/dist/git-repository-summary.d.ts.map +1 -0
  72. package/dist/git-repository-summary.js +105 -0
  73. package/dist/git-repository.d.ts +60 -0
  74. package/dist/git-repository.d.ts.map +1 -0
  75. package/dist/git-repository.js +106 -0
  76. package/dist/git-sync.d.ts +49 -0
  77. package/dist/git-sync.d.ts.map +1 -0
  78. package/dist/git-sync.js +110 -0
  79. package/dist/index.d.ts +63 -9
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +33 -6
  82. package/dist/lsp-process.d.ts +38 -0
  83. package/dist/lsp-process.d.ts.map +1 -0
  84. package/dist/lsp-process.js +103 -0
  85. package/dist/runtime-capabilities.d.ts +44 -0
  86. package/dist/runtime-capabilities.d.ts.map +1 -0
  87. package/dist/runtime-capabilities.js +141 -0
  88. package/dist/task-workspace.d.ts +302 -0
  89. package/dist/task-workspace.d.ts.map +1 -0
  90. package/dist/task-workspace.js +1236 -0
  91. package/dist/voice-action-intent.d.ts +86 -0
  92. package/dist/voice-action-intent.d.ts.map +1 -0
  93. package/dist/voice-action-intent.js +387 -0
  94. package/dist/voice-playback.d.ts +50 -0
  95. package/dist/voice-playback.d.ts.map +1 -0
  96. package/dist/voice-playback.js +240 -0
  97. package/dist/voice-protocol.d.ts +165 -0
  98. package/dist/voice-protocol.d.ts.map +1 -0
  99. package/dist/voice-protocol.js +312 -0
  100. package/dist/voice-transcript.d.ts +57 -0
  101. package/dist/voice-transcript.d.ts.map +1 -0
  102. package/dist/voice-transcript.js +221 -0
  103. package/package.json +5 -1
  104. package/dist/conversation-budget.d.ts +0 -37
  105. package/dist/conversation-budget.d.ts.map +0 -1
  106. package/dist/conversation-budget.js +0 -97
@@ -0,0 +1,312 @@
1
+ // Public type contracts for the optional Voice Digital Twin control / media protocol (Epic #491,
2
+ // Issue #496, ADR-0059). This module DEFINES the wire protocol; it implements no transport. It is
3
+ // pure data + pure validators only — nothing performs IO, crypto, clock reads, randomness, or audio
4
+ // processing, and no provider base URL, credential, or raw audio buffer is ever a field on these
5
+ // types. The `VOICE_PROTOCOL_VERSION` discriminant follows the same evolution rule as
6
+ // `WORKFLOW_HANDOFF_SCHEMA_VERSION` / `CONNECTED_CONTEXT_SCHEMA_VERSION`: a breaking change introduces
7
+ // a NEW literal member rather than mutating "1". It is INDEPENDENT of
8
+ // `CONVERSATION_CAPABILITY_CONTRACT_VERSION` (the capability registry contract, gateway.ts) and never
9
+ // bumps it. Leaf-package rule (ADR-0019 direction 1): no `@oscharko-dev/keiko-*` imports may appear
10
+ // here; sibling types are reached by relative path.
11
+ //
12
+ // The protocol separates two planes (AC1): the WebSocket control / signaling plane (every message
13
+ // kind in this module) and the WebRTC media plane (raw audio frames). Raw audio is never a control
14
+ // message and is never replayed or persisted by default (AC5). Today the control-plane role is
15
+ // realized on the existing loopback HTTP + Server-Sent Events seam; a bidirectional WebSocket upgrade
16
+ // is an explicit, ADR-gated transport decision owned by Issue #497 (ADR-0058 D3 / ADR-0059).
17
+ // ─── Protocol version ─────────────────────────────────────────────────────────
18
+ export const VOICE_PROTOCOL_VERSION = "1";
19
+ // Compatibility / negotiation rule: a peer accepts a message only when its declared protocol version
20
+ // is one this build understands. v1 understands exactly "1"; a future v2 build widens this set.
21
+ export function isVoiceProtocolVersionSupported(version) {
22
+ return version === VOICE_PROTOCOL_VERSION;
23
+ }
24
+ export const VOICE_PLANES = ["control", "media"];
25
+ export const VOICE_CONTROL_TRANSPORTS = [
26
+ "loopback-http-sse",
27
+ "loopback-websocket",
28
+ ];
29
+ // The realization in effect for v1. Recorded as a constant so the spec and the implementing transport
30
+ // issue (#497) share one source of truth and any change is an explicit edit, not a silent drift.
31
+ export const VOICE_CONTROL_TRANSPORT_V1 = "loopback-http-sse";
32
+ export const VOICE_MEDIA_TRANSPORTS = [
33
+ "none",
34
+ "gateway-batch",
35
+ "webrtc",
36
+ ];
37
+ export const VOICE_NEGOTIATION_MODES = [
38
+ "proxied-sdp",
39
+ "direct-ephemeral",
40
+ "disabled",
41
+ ];
42
+ export const PREFERRED_VOICE_NEGOTIATION_MODE = "proxied-sdp";
43
+ export const VOICE_REPLAY_CLASSES = [
44
+ "replayable",
45
+ "ephemeral",
46
+ "never-persisted",
47
+ ];
48
+ export const VOICE_REDACTION_CLASSES = [
49
+ "content-free",
50
+ "reviewable-text",
51
+ "secret-bearing",
52
+ "raw-media",
53
+ ];
54
+ export const VOICE_MESSAGE_DIRECTIONS = [
55
+ "client-to-host",
56
+ "host-to-client",
57
+ ];
58
+ export const VOICE_MEDIA_TRACK_KINDS = [
59
+ "audio-in",
60
+ "audio-out",
61
+ ];
62
+ export const VOICE_MEDIA_PLANE = {
63
+ plane: "media",
64
+ transport: "webrtc",
65
+ trackKinds: VOICE_MEDIA_TRACK_KINDS,
66
+ replay: "never-persisted",
67
+ redaction: "raw-media",
68
+ };
69
+ export const VOICE_CONTROL_MESSAGE_KINDS = [
70
+ "session.create",
71
+ "session.created",
72
+ "session.close",
73
+ "session.closed",
74
+ "capability.offer",
75
+ "capability.select",
76
+ "signal.sdp.offer",
77
+ "signal.sdp.answer",
78
+ "signal.ice.candidate",
79
+ "media.track.state",
80
+ "control.cancel",
81
+ "control.interrupt",
82
+ "transcript.partial",
83
+ "transcript.committed",
84
+ "transcript.discarded",
85
+ "playback.state",
86
+ "policy.decision",
87
+ "error",
88
+ ];
89
+ export const VOICE_DATA_CHANNEL_EVENT_KINDS = [
90
+ "control.interrupt",
91
+ "transcript.partial",
92
+ "playback.state",
93
+ ];
94
+ export const VOICE_SESSION_CLOSE_REASONS = [
95
+ "client-request",
96
+ "host-request",
97
+ "policy-disabled",
98
+ "provider-unreachable",
99
+ "timeout",
100
+ "protocol-error",
101
+ ];
102
+ export const VOICE_MEDIA_TRACK_STATES = [
103
+ "negotiating",
104
+ "live",
105
+ "muted",
106
+ "ended",
107
+ ];
108
+ export const VOICE_PLAYBACK_STATES = [
109
+ "idle",
110
+ "playing",
111
+ "paused",
112
+ "stopped",
113
+ "interrupted",
114
+ ];
115
+ export const VOICE_POLICY_DECISIONS = [
116
+ "allow",
117
+ "deny",
118
+ "degrade",
119
+ ];
120
+ export const VOICE_PROTOCOL_ERROR_CODES = [
121
+ "unsupported-version",
122
+ "invalid-message",
123
+ "capability-unavailable",
124
+ "not-allowed-for-profile",
125
+ "negotiation-failed",
126
+ "rate-limited",
127
+ "internal",
128
+ ];
129
+ // ─── Per-kind classification tables ─────────────────────────────────────────────
130
+ // Keyed by `VoiceControlMessageKind` so adding a kind without classifying it is a compile error
131
+ // (totality). No control kind is ever `raw-media` — that classification is media-plane only (AC1).
132
+ export const VOICE_CONTROL_MESSAGE_REPLAY = {
133
+ "session.create": "replayable",
134
+ "session.created": "replayable",
135
+ "session.close": "replayable",
136
+ "session.closed": "replayable",
137
+ "capability.offer": "replayable",
138
+ "capability.select": "replayable",
139
+ "signal.sdp.offer": "ephemeral",
140
+ "signal.sdp.answer": "ephemeral",
141
+ "signal.ice.candidate": "ephemeral",
142
+ "media.track.state": "replayable",
143
+ "control.cancel": "replayable",
144
+ "control.interrupt": "replayable",
145
+ "transcript.partial": "ephemeral",
146
+ "transcript.committed": "replayable",
147
+ "transcript.discarded": "replayable",
148
+ "playback.state": "replayable",
149
+ "policy.decision": "replayable",
150
+ error: "replayable",
151
+ };
152
+ export const VOICE_CONTROL_MESSAGE_REDACTION = {
153
+ "session.create": "content-free",
154
+ "session.created": "content-free",
155
+ "session.close": "content-free",
156
+ "session.closed": "content-free",
157
+ "capability.offer": "content-free",
158
+ "capability.select": "content-free",
159
+ "signal.sdp.offer": "secret-bearing",
160
+ "signal.sdp.answer": "secret-bearing",
161
+ "signal.ice.candidate": "secret-bearing",
162
+ "media.track.state": "content-free",
163
+ "control.cancel": "content-free",
164
+ "control.interrupt": "content-free",
165
+ "transcript.partial": "reviewable-text",
166
+ "transcript.committed": "reviewable-text",
167
+ "transcript.discarded": "content-free",
168
+ "playback.state": "content-free",
169
+ "policy.decision": "content-free",
170
+ error: "content-free",
171
+ };
172
+ // ─── Capability-gating & fallback state table (Deliverable; AC2, AC3) ───────────
173
+ // The control-message kinds permitted for each effective voice profile. `none` permits NOTHING — the
174
+ // deterministic disabled behavior of AC2. `speech-to-text` permits the controlled-dictation control
175
+ // subset WITHOUT any WebRTC signaling, media-track, interruption, or playback message — STT-only
176
+ // dictation never requires the full-realtime media path (AC3). `full-realtime` permits every kind.
177
+ export const VOICE_PROFILE_ALLOWED_MESSAGE_KINDS = {
178
+ none: [],
179
+ "speech-to-text": [
180
+ "session.create",
181
+ "session.created",
182
+ "session.close",
183
+ "session.closed",
184
+ "capability.offer",
185
+ "capability.select",
186
+ "control.cancel",
187
+ "transcript.partial",
188
+ "transcript.committed",
189
+ "transcript.discarded",
190
+ "policy.decision",
191
+ "error",
192
+ ],
193
+ "speech-output": [
194
+ "session.create",
195
+ "session.created",
196
+ "session.close",
197
+ "session.closed",
198
+ "capability.offer",
199
+ "capability.select",
200
+ "control.cancel",
201
+ "control.interrupt",
202
+ "playback.state",
203
+ "policy.decision",
204
+ "error",
205
+ ],
206
+ "full-realtime": [...VOICE_CONTROL_MESSAGE_KINDS],
207
+ };
208
+ // The media-plane transport and browser↔provider negotiation mode that each profile resolves to.
209
+ export const VOICE_PROFILE_MEDIA_TRANSPORT = {
210
+ none: "none",
211
+ "speech-to-text": "gateway-batch",
212
+ "speech-output": "gateway-batch",
213
+ "full-realtime": "webrtc",
214
+ };
215
+ export const VOICE_PROFILE_NEGOTIATION_MODE = {
216
+ none: "disabled",
217
+ "speech-to-text": "disabled",
218
+ "speech-output": "disabled",
219
+ "full-realtime": "proxied-sdp",
220
+ };
221
+ // Defaults are conservative, controlled-network-friendly bounds. The implementing transport issue
222
+ // (#497) polices them against its own clock; the contracts package is a leaf and reads no clock.
223
+ export const DEFAULT_VOICE_PROTOCOL_TIMEOUTS = {
224
+ sessionCreateMs: 10_000,
225
+ signalingMs: 15_000,
226
+ heartbeatIntervalMs: 15_000,
227
+ reconnectBackoffInitialMs: 500,
228
+ reconnectBackoffMaxMs: 10_000,
229
+ maxReconnectAttempts: 5,
230
+ };
231
+ // ─── Type guards & lookups ───────────────────────────────────────────────────────
232
+ function isRecord(value) {
233
+ return typeof value === "object" && value !== null && !Array.isArray(value);
234
+ }
235
+ function isNonEmptyTrimmed(value) {
236
+ return typeof value === "string" && value.trim().length > 0;
237
+ }
238
+ function isFiniteNonNegativeInteger(value) {
239
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
240
+ }
241
+ export function isVoiceControlMessageKind(value) {
242
+ return (typeof value === "string" && VOICE_CONTROL_MESSAGE_KINDS.includes(value));
243
+ }
244
+ export function isVoiceMessageDirection(value) {
245
+ return (typeof value === "string" && VOICE_MESSAGE_DIRECTIONS.includes(value));
246
+ }
247
+ export function isVoiceNegotiationMode(value) {
248
+ return (typeof value === "string" && VOICE_NEGOTIATION_MODES.includes(value));
249
+ }
250
+ // The replay class for a control-message kind. `replayable` events are the ones a reconnect
251
+ // re-delivers (AC5); `ephemeral` and the media plane's `never-persisted` are excluded.
252
+ export function voiceControlMessageReplayClass(kind) {
253
+ return VOICE_CONTROL_MESSAGE_REPLAY[kind];
254
+ }
255
+ export function voiceControlMessageRedactionClass(kind) {
256
+ return VOICE_CONTROL_MESSAGE_REDACTION[kind];
257
+ }
258
+ // AC5: a control-message kind is replay-eligible only when classified `replayable`. Raw audio is
259
+ // media-plane (`never-persisted`) and has no control kind, so it can never be replay-eligible.
260
+ export function isVoiceReplayEligible(kind) {
261
+ return VOICE_CONTROL_MESSAGE_REPLAY[kind] === "replayable";
262
+ }
263
+ // AC2 / AC3: whether a kind is permitted for an effective profile. `none` permits nothing.
264
+ export function voiceMessageAllowedForProfile(kind, profile) {
265
+ return VOICE_PROFILE_ALLOWED_MESSAGE_KINDS[profile].includes(kind);
266
+ }
267
+ // Exhaustiveness guard: a `default`/fall-through that reaches this fails to type-check if a new
268
+ // `VoiceControlMessageKind` is added without handling, mirroring `assertNeverMemoryType`.
269
+ export function assertNeverVoiceControlMessageKind(kind) {
270
+ throw new Error(`unhandled voice control message kind: ${JSON.stringify(kind)}`);
271
+ }
272
+ // ─── Envelope validation ─────────────────────────────────────────────────────────
273
+ function validateEnvelope(value, reasons) {
274
+ if (!isVoiceProtocolVersionSupported(value.protocolVersion)) {
275
+ reasons.push("protocolVersion unsupported");
276
+ }
277
+ if (!isNonEmptyTrimmed(value.sessionId)) {
278
+ reasons.push("sessionId must be a non-empty string");
279
+ }
280
+ if (!isFiniteNonNegativeInteger(value.seq)) {
281
+ reasons.push("seq must be a non-negative integer");
282
+ }
283
+ if (!isVoiceMessageDirection(value.direction)) {
284
+ reasons.push("direction invalid");
285
+ }
286
+ if (!isVoiceControlMessageKind(value.kind)) {
287
+ reasons.push("kind invalid");
288
+ }
289
+ }
290
+ // Structural guard for the shared envelope (version + sessionId + seq + direction + kind). Per-kind
291
+ // payload typing is recovered by narrowing on `kind` once the envelope is known to be well-formed.
292
+ export function isVoiceControlMessage(value) {
293
+ if (!isRecord(value)) {
294
+ return false;
295
+ }
296
+ return (isVoiceProtocolVersionSupported(value.protocolVersion) &&
297
+ isNonEmptyTrimmed(value.sessionId) &&
298
+ isFiniteNonNegativeInteger(value.seq) &&
299
+ isVoiceMessageDirection(value.direction) &&
300
+ isVoiceControlMessageKind(value.kind));
301
+ }
302
+ // Deep validation of the shared envelope, returning every reason it is malformed. Per-kind payload
303
+ // fields are owned and validated by the implementing transport issue (#497); the contract pins the
304
+ // envelope, the kind catalog, and the version-compatibility rule.
305
+ export function validateVoiceControlMessage(value) {
306
+ if (!isRecord(value)) {
307
+ return { ok: false, reasons: ["message must be an object"] };
308
+ }
309
+ const reasons = [];
310
+ validateEnvelope(value, reasons);
311
+ return reasons.length === 0 ? { ok: true } : { ok: false, reasons };
312
+ }
@@ -0,0 +1,57 @@
1
+ import type { VoiceProfile } from "./gateway.js";
2
+ import type { VoiceControlMessageKind, VoiceRedactionClass, VoiceReplayClass } from "./voice-protocol.js";
3
+ export declare const VOICE_TRANSCRIPT_SCHEMA_VERSION: "1";
4
+ export declare function isVoiceTranscriptSchemaVersionSupported(version: unknown): boolean;
5
+ export type VoiceTranscriptSegmentState = "partial" | "stable" | "committed" | "corrected" | "discarded" | "redacted" | "provider-error";
6
+ export declare const VOICE_TRANSCRIPT_SEGMENT_STATES: readonly VoiceTranscriptSegmentState[];
7
+ export declare const VOICE_TRANSCRIPT_CONSUMABLE_STATES: readonly VoiceTranscriptSegmentState[];
8
+ export type VoiceTranscriptProviderErrorKind = "rate-limited" | "timeout" | "provider-error" | "unavailable" | "internal";
9
+ export declare const VOICE_TRANSCRIPT_PROVIDER_ERROR_KINDS: readonly VoiceTranscriptProviderErrorKind[];
10
+ export type VoiceTranscriptSource = "dictation" | "realtime";
11
+ export declare const VOICE_TRANSCRIPT_SOURCES: readonly VoiceTranscriptSource[];
12
+ export interface VoiceTranscriptSegment {
13
+ readonly id: string;
14
+ readonly seq: number;
15
+ readonly state: VoiceTranscriptSegmentState;
16
+ readonly text: string;
17
+ readonly source: VoiceTranscriptSource;
18
+ readonly revision: number;
19
+ readonly replayClass: VoiceReplayClass;
20
+ readonly redactionClass: VoiceRedactionClass;
21
+ readonly supersedesId?: string | undefined;
22
+ readonly providerErrorKind?: VoiceTranscriptProviderErrorKind | undefined;
23
+ }
24
+ export declare const VOICE_TRANSCRIPT_SEGMENT_REPLAY: Record<VoiceTranscriptSegmentState, VoiceReplayClass>;
25
+ export declare const VOICE_TRANSCRIPT_SEGMENT_REDACTION: Record<VoiceTranscriptSegmentState, VoiceRedactionClass>;
26
+ export declare const VOICE_TRANSCRIPT_SEGMENT_TRANSITIONS: Record<VoiceTranscriptSegmentState, readonly VoiceTranscriptSegmentState[]>;
27
+ export declare function isVoiceTranscriptSegmentState(value: unknown): value is VoiceTranscriptSegmentState;
28
+ export declare function isVoiceTranscriptProviderErrorKind(value: unknown): value is VoiceTranscriptProviderErrorKind;
29
+ export declare function isVoiceTranscriptSource(value: unknown): value is VoiceTranscriptSource;
30
+ export declare function voiceTranscriptSegmentReplayClass(state: VoiceTranscriptSegmentState): VoiceReplayClass;
31
+ export declare function voiceTranscriptSegmentRedactionClass(state: VoiceTranscriptSegmentState): VoiceRedactionClass;
32
+ export declare function canTransitionVoiceTranscriptSegment(from: VoiceTranscriptSegmentState, to: VoiceTranscriptSegmentState): boolean;
33
+ export declare function isCommittedVoiceTranscriptState(state: VoiceTranscriptSegmentState): boolean;
34
+ export declare function assertNeverVoiceTranscriptSegmentState(state: never): never;
35
+ export declare function mapWireKindToVoiceTranscriptSegmentState(kind: VoiceControlMessageKind): VoiceTranscriptSegmentState | undefined;
36
+ export declare function voiceTranscriptCaptureAllowed(profile: VoiceProfile): boolean;
37
+ export declare function voiceTranscriptPreviewAllowed(profile: VoiceProfile): boolean;
38
+ export interface CommittedVoiceTranscriptProjection {
39
+ readonly schemaVersion: typeof VOICE_TRANSCRIPT_SCHEMA_VERSION;
40
+ readonly segments: readonly VoiceTranscriptSegment[];
41
+ readonly text: string;
42
+ readonly segmentCount: number;
43
+ }
44
+ export declare function selectCommittedVoiceTranscript(segments: readonly VoiceTranscriptSegment[]): CommittedVoiceTranscriptProjection;
45
+ export interface VoiceTranscriptEvidenceSummary {
46
+ readonly schemaVersion: typeof VOICE_TRANSCRIPT_SCHEMA_VERSION;
47
+ readonly segmentCount: number;
48
+ readonly committedCount: number;
49
+ readonly correctedCount: number;
50
+ readonly discardedCount: number;
51
+ readonly redactedCount: number;
52
+ readonly providerErrorCount: number;
53
+ readonly committedChars: number;
54
+ readonly highestSeq: number;
55
+ }
56
+ export declare function summarizeVoiceTranscript(segments: readonly VoiceTranscriptSegment[]): VoiceTranscriptEvidenceSummary;
57
+ //# sourceMappingURL=voice-transcript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice-transcript.d.ts","sourceRoot":"","sources":["../src/voice-transcript.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAI7B,eAAO,MAAM,+BAA+B,EAAG,GAAY,CAAC;AAE5D,wBAAgB,uCAAuC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEjF;AAcD,MAAM,MAAM,2BAA2B,GACnC,SAAS,GACT,QAAQ,GACR,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,gBAAgB,CAAC;AAErB,eAAO,MAAM,+BAA+B,EAAE,SAAS,2BAA2B,EAQxE,CAAC;AAKX,eAAO,MAAM,kCAAkC,EAAE,SAAS,2BAA2B,EAG3E,CAAC;AAOX,MAAM,MAAM,gCAAgC,GACxC,cAAc,GACd,SAAS,GACT,gBAAgB,GAChB,aAAa,GACb,UAAU,CAAC;AAEf,eAAO,MAAM,qCAAqC,EAAE,SAAS,gCAAgC,EAMnF,CAAC;AAMX,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG,UAAU,CAAC;AAE7D,eAAO,MAAM,wBAAwB,EAAE,SAAS,qBAAqB,EAG3D,CAAC;AAUX,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,2BAA2B,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,cAAc,EAAE,mBAAmB,CAAC;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gCAAgC,GAAG,SAAS,CAAC;CAC3E;AAUD,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAClD,2BAA2B,EAC3B,gBAAgB,CASR,CAAC;AAMX,eAAO,MAAM,kCAAkC,EAAE,MAAM,CACrD,2BAA2B,EAC3B,mBAAmB,CASX,CAAC;AAWX,eAAO,MAAM,oCAAoC,EAAE,MAAM,CACvD,2BAA2B,EAC3B,SAAS,2BAA2B,EAAE,CAS9B,CAAC;AAGX,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,2BAA2B,CAKtC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,gCAAgC,CAK3C;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAItF;AAED,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,2BAA2B,GACjC,gBAAgB,CAElB;AAED,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,2BAA2B,GACjC,mBAAmB,CAErB;AAGD,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,2BAA2B,EACjC,EAAE,EAAE,2BAA2B,GAC9B,OAAO,CAET;AAGD,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAE3F;AAID,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE1E;AAOD,wBAAgB,wCAAwC,CACtD,IAAI,EAAE,uBAAuB,GAC5B,2BAA2B,GAAG,SAAS,CAWzC;AAOD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAE5E;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAE5E;AAWD,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,aAAa,EAAE,OAAO,+BAA+B,CAAC;IAC/D,QAAQ,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAwBD,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAC1C,kCAAkC,CAkBpC;AAMD,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,aAAa,EAAE,OAAO,+BAA+B,CAAC;IAC/D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAK9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AASD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAC1C,8BAA8B,CAiBhC"}
@@ -0,0 +1,221 @@
1
+ // Public type contract for the optional Voice Digital Twin transcript segment lifecycle (Epic #491,
2
+ // Issue #500, ADR-0063). This module DEFINES the provider-neutral semantics that let Keiko distinguish
3
+ // uncertain partial text, provider-stabilised text, user/turn-committed text, provider corrections, and
4
+ // discarded / redacted / failed text. It is pure data + pure functions only — nothing performs IO,
5
+ // crypto, clock reads, randomness, or audio processing, and no provider base URL, credential, or raw
6
+ // audio buffer is ever a field on these types.
7
+ //
8
+ // It is layered ABOVE the #496 wire protocol (voice-protocol.ts): the wire carries only three transcript
9
+ // message kinds (`transcript.partial`, `transcript.committed`, `transcript.discarded`), whereas the
10
+ // lifecycle here has seven segment STATES. Only the three states that have a wire kind round-trip on the
11
+ // wire (`mapWireKindToVoiceTranscriptSegmentState`); `stable`, `corrected`, `redacted`, and
12
+ // `provider-error` are derived/internal states with no wire kind, exactly as the #499 turn manager maps
13
+ // a richer semantic signal set onto the same wire catalog (ADR-0062). The stateful reducer that drives
14
+ // these states lives in keiko-ui (`voice-transcript-segments.ts`) as a synchronous deterministic engine;
15
+ // this leaf contract holds the state catalog, the classification tables, the legal-transition table, the
16
+ // validators, and the AC5 committed-only integration boundary so server-side consumers (#503 governed
17
+ // spoken actions and memory review) can enforce committed-only consumption without forking the reducer.
18
+ //
19
+ // `VOICE_TRANSCRIPT_SCHEMA_VERSION` follows the same evolution rule as `VOICE_PROTOCOL_VERSION`: a
20
+ // breaking change introduces a NEW literal rather than mutating "1". It is INDEPENDENT of the wire
21
+ // `VOICE_PROTOCOL_VERSION` and of `CONVERSATION_CAPABILITY_CONTRACT_VERSION`. Leaf-package rule
22
+ // (ADR-0019 direction 1): no `@oscharko-dev/keiko-*` imports may appear here; sibling types are reached
23
+ // by relative path.
24
+ import { voiceMessageAllowedForProfile } from "./voice-protocol.js";
25
+ // ─── Schema version ───────────────────────────────────────────────────────────
26
+ export const VOICE_TRANSCRIPT_SCHEMA_VERSION = "1";
27
+ export function isVoiceTranscriptSchemaVersionSupported(version) {
28
+ return version === VOICE_TRANSCRIPT_SCHEMA_VERSION;
29
+ }
30
+ export const VOICE_TRANSCRIPT_SEGMENT_STATES = [
31
+ "partial",
32
+ "stable",
33
+ "committed",
34
+ "corrected",
35
+ "discarded",
36
+ "redacted",
37
+ "provider-error",
38
+ ];
39
+ // The two states whose text downstream integrations (memory, workflow, evaluation) may consume.
40
+ // This is the load-bearing AC5 invariant expressed as data: a segment's text crosses the integration
41
+ // boundary if and only if its state is in this set.
42
+ export const VOICE_TRANSCRIPT_CONSUMABLE_STATES = [
43
+ "committed",
44
+ "corrected",
45
+ ];
46
+ export const VOICE_TRANSCRIPT_PROVIDER_ERROR_KINDS = [
47
+ "rate-limited",
48
+ "timeout",
49
+ "provider-error",
50
+ "unavailable",
51
+ "internal",
52
+ ];
53
+ export const VOICE_TRANSCRIPT_SOURCES = [
54
+ "dictation",
55
+ "realtime",
56
+ ];
57
+ // ─── Per-state classification tables (aligned to voice-protocol.ts) ──────────────
58
+ // Keyed by `VoiceTranscriptSegmentState` so adding a state without classifying it is a compile error
59
+ // (totality). The classes are the same `VoiceReplayClass` / `VoiceRedactionClass` the wire protocol
60
+ // uses, so a segment and the wire message it derives from agree on replay and redaction treatment.
61
+ //
62
+ // Replay (AC5 reconnect semantics): the durable, committed lifecycle facts (`committed`, `corrected`,
63
+ // `discarded`, `redacted`, `provider-error`) are `replayable`; the in-flight `partial` / `stable`
64
+ // previews are `ephemeral` and a reconnect never re-delivers them, mirroring `transcript.partial`.
65
+ export const VOICE_TRANSCRIPT_SEGMENT_REPLAY = {
66
+ partial: "ephemeral",
67
+ stable: "ephemeral",
68
+ committed: "replayable",
69
+ corrected: "replayable",
70
+ discarded: "replayable",
71
+ redacted: "replayable",
72
+ "provider-error": "replayable",
73
+ };
74
+ // Redaction: the text-bearing states (`partial`, `stable`, `committed`, `corrected`) are
75
+ // `reviewable-text` — they must pass through the existing `stripUnsafeFormatChars` + redact-at-persist
76
+ // seam before any log or evidence manifest. The
77
+ // content-free states carry no reviewable text, so they are `content-free`.
78
+ export const VOICE_TRANSCRIPT_SEGMENT_REDACTION = {
79
+ partial: "reviewable-text",
80
+ stable: "reviewable-text",
81
+ committed: "reviewable-text",
82
+ corrected: "reviewable-text",
83
+ discarded: "content-free",
84
+ redacted: "content-free",
85
+ "provider-error": "content-free",
86
+ };
87
+ // ─── Legal transition table (Deliverable: reducer/state machine rules) ───────────
88
+ // The set of states reachable from a given state by a state-CHANGING transition. Repeated operations
89
+ // that keep the same state (partial-text churn, re-correction) are text updates handled by the reducer,
90
+ // not transitions, so they are intentionally absent here. `discarded` and `redacted` are terminal.
91
+ // Keyed by state for totality.
92
+ // A correction targets already-finalised text, so `partial` has no direct edge to `corrected`
93
+ // (an in-flight partial is refined by further partials, then stabilised/committed, then corrected).
94
+ // `committed`/`corrected` never regress to `provider-error`: a provider failure can only affect text
95
+ // that has not yet been committed.
96
+ export const VOICE_TRANSCRIPT_SEGMENT_TRANSITIONS = {
97
+ partial: ["stable", "committed", "discarded", "redacted", "provider-error"],
98
+ stable: ["committed", "corrected", "discarded", "redacted", "provider-error"],
99
+ committed: ["corrected", "discarded", "redacted"],
100
+ corrected: ["discarded", "redacted"],
101
+ discarded: [],
102
+ redacted: [],
103
+ "provider-error": ["discarded", "redacted"],
104
+ };
105
+ // ─── Type guards & lookups ───────────────────────────────────────────────────────
106
+ export function isVoiceTranscriptSegmentState(value) {
107
+ return (typeof value === "string" &&
108
+ VOICE_TRANSCRIPT_SEGMENT_STATES.includes(value));
109
+ }
110
+ export function isVoiceTranscriptProviderErrorKind(value) {
111
+ return (typeof value === "string" &&
112
+ VOICE_TRANSCRIPT_PROVIDER_ERROR_KINDS.includes(value));
113
+ }
114
+ export function isVoiceTranscriptSource(value) {
115
+ return (typeof value === "string" && VOICE_TRANSCRIPT_SOURCES.includes(value));
116
+ }
117
+ export function voiceTranscriptSegmentReplayClass(state) {
118
+ return VOICE_TRANSCRIPT_SEGMENT_REPLAY[state];
119
+ }
120
+ export function voiceTranscriptSegmentRedactionClass(state) {
121
+ return VOICE_TRANSCRIPT_SEGMENT_REDACTION[state];
122
+ }
123
+ // Whether `to` is reachable from `from` by a legal state-changing transition.
124
+ export function canTransitionVoiceTranscriptSegment(from, to) {
125
+ return VOICE_TRANSCRIPT_SEGMENT_TRANSITIONS[from].includes(to);
126
+ }
127
+ // AC5: a segment's text is consumable downstream iff it is committed or corrected.
128
+ export function isCommittedVoiceTranscriptState(state) {
129
+ return VOICE_TRANSCRIPT_CONSUMABLE_STATES.includes(state);
130
+ }
131
+ // Exhaustiveness guard: a `default`/fall-through that reaches this fails to type-check if a new
132
+ // `VoiceTranscriptSegmentState` is added without handling, mirroring `assertNeverVoiceControlMessageKind`.
133
+ export function assertNeverVoiceTranscriptSegmentState(state) {
134
+ throw new Error(`unhandled voice transcript segment state: ${JSON.stringify(state)}`);
135
+ }
136
+ // ─── Wire-kind ↔ state mapping ───────────────────────────────────────────────────
137
+ // The initial segment state a wire transcript kind establishes. Only the three transcript wire kinds
138
+ // map; every other control kind (and the derived states `stable`/`corrected`/`redacted`/
139
+ // `provider-error`, which have no wire kind) returns `undefined`. A strict 1:1 wire↔state map is
140
+ // impossible by design, the same lesson the #499 turn manager records (ADR-0062).
141
+ export function mapWireKindToVoiceTranscriptSegmentState(kind) {
142
+ switch (kind) {
143
+ case "transcript.partial":
144
+ return "partial";
145
+ case "transcript.committed":
146
+ return "committed";
147
+ case "transcript.discarded":
148
+ return "discarded";
149
+ default:
150
+ return undefined;
151
+ }
152
+ }
153
+ // ─── Capability-gating predicates (AC1/AC2/AC3) — derived from the contract ──────
154
+ // The single source of truth for capability gating is `voiceMessageAllowedForProfile`; these predicates
155
+ // derive the transcript-relevant capabilities from it rather than re-encoding the profile table.
156
+ // `none` permits neither (transcript handling is dormant); `speech-output` permits neither (it is
157
+ // playback-only); `speech-to-text` and `full-realtime` permit both.
158
+ export function voiceTranscriptCaptureAllowed(profile) {
159
+ return voiceMessageAllowedForProfile("transcript.committed", profile);
160
+ }
161
+ export function voiceTranscriptPreviewAllowed(profile) {
162
+ return voiceMessageAllowedForProfile("transcript.partial", profile);
163
+ }
164
+ // Segments carry unique ids, so the sort never compares an id to itself; a `seq` tie is broken by id.
165
+ function bySeqThenId(a, b) {
166
+ if (a.seq !== b.seq) {
167
+ return a.seq - b.seq;
168
+ }
169
+ return a.id < b.id ? -1 : 1;
170
+ }
171
+ // Supersession is a PERMANENT fact: once a correction replaces a prior segment's text, that prior text
172
+ // must never re-enter the consumable projection — even after the superseding correction is itself
173
+ // discarded or redacted. We therefore collect `supersedesId` from ANY segment that carries it,
174
+ // regardless of the carrier's current state (the reducer preserves `supersedesId` across transitions).
175
+ function supersededSegmentIds(segments) {
176
+ const ids = new Set();
177
+ for (const segment of segments) {
178
+ if (segment.supersedesId !== undefined) {
179
+ ids.add(segment.supersedesId);
180
+ }
181
+ }
182
+ return ids;
183
+ }
184
+ export function selectCommittedVoiceTranscript(segments) {
185
+ const superseded = supersededSegmentIds(segments);
186
+ const committed = segments
187
+ .filter((segment) => isCommittedVoiceTranscriptState(segment.state) && !superseded.has(segment.id))
188
+ .slice()
189
+ .sort(bySeqThenId);
190
+ const text = committed
191
+ .map((segment) => segment.text.trim())
192
+ .filter((value) => value.length > 0)
193
+ .join(" ");
194
+ return {
195
+ schemaVersion: VOICE_TRANSCRIPT_SCHEMA_VERSION,
196
+ segments: committed,
197
+ text,
198
+ segmentCount: committed.length,
199
+ };
200
+ }
201
+ function countState(segments, state) {
202
+ return segments.filter((segment) => segment.state === state).length;
203
+ }
204
+ export function summarizeVoiceTranscript(segments) {
205
+ const projection = selectCommittedVoiceTranscript(segments);
206
+ // The committed character count is the length of the consumable projection text, so the evidence
207
+ // summary, the projection, and the reducer's onCommit observation all report the same number.
208
+ const committedChars = projection.text.length;
209
+ const highestSeq = segments.reduce((max, segment) => (segment.seq > max ? segment.seq : max), 0);
210
+ return {
211
+ schemaVersion: VOICE_TRANSCRIPT_SCHEMA_VERSION,
212
+ segmentCount: segments.length,
213
+ committedCount: countState(segments, "committed"),
214
+ correctedCount: countState(segments, "corrected"),
215
+ discardedCount: countState(segments, "discarded"),
216
+ redactedCount: countState(segments, "redacted"),
217
+ providerErrorCount: countState(segments, "provider-error"),
218
+ committedChars,
219
+ highestSeq,
220
+ };
221
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscharko-dev/keiko-contracts",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Internal workspace package: shared Keiko contracts. Not published independently.",
@@ -62,6 +62,10 @@
62
62
  "./memory-audit-events": {
63
63
  "types": "./dist/memory-audit-events.d.ts",
64
64
  "import": "./dist/memory-audit-events.js"
65
+ },
66
+ "./task-workspace": {
67
+ "types": "./dist/task-workspace.d.ts",
68
+ "import": "./dist/task-workspace.js"
65
69
  }
66
70
  },
67
71
  "scripts": {
@@ -1,37 +0,0 @@
1
- export interface ConversationBudgetMessage {
2
- readonly role: string;
3
- readonly content: string;
4
- }
5
- export interface ConversationBudgetDocumentContext {
6
- readonly extractedBytes: number;
7
- }
8
- export interface ConversationBudgetInputs {
9
- readonly modelContextWindow: number;
10
- readonly modelMaxOutputTokens: number;
11
- readonly userDraftText: string;
12
- readonly conversationHistory: readonly ConversationBudgetMessage[];
13
- readonly documentContext?: readonly ConversationBudgetDocumentContext[] | undefined;
14
- readonly repoContextPackBytes?: number | undefined;
15
- readonly knowledgeCapsuleBytes?: number | undefined;
16
- readonly memoryContextBytes?: number | undefined;
17
- }
18
- export type ConversationBudgetPressure = "low" | "moderate" | "high" | "exceeded";
19
- export interface ConversationBudgetBreakdown {
20
- readonly draftBytes: number;
21
- readonly historyBytes: number;
22
- readonly documentBytes: number;
23
- readonly repoContextBytes: number;
24
- readonly knowledgeBytes: number;
25
- readonly memoryBytes: number;
26
- }
27
- export interface ConversationBudgetEstimate {
28
- readonly approximateBytes: number;
29
- readonly approximateTokens: number;
30
- readonly contextWindowTokens: number;
31
- readonly reservedOutputTokens: number;
32
- readonly availableInputTokens: number;
33
- readonly pressure: ConversationBudgetPressure;
34
- readonly breakdown: ConversationBudgetBreakdown;
35
- }
36
- export declare function estimateConversationBudget(input: ConversationBudgetInputs): ConversationBudgetEstimate;
37
- //# sourceMappingURL=conversation-budget.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conversation-budget.d.ts","sourceRoot":"","sources":["../src/conversation-budget.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,mBAAmB,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACnE,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,iCAAiC,EAAE,GAAG,SAAS,CAAC;IACpF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnD,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClD;AAED,MAAM,MAAM,0BAA0B,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAElF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,2BAA2B,CAAC;CACjD;AA6DD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,wBAAwB,GAC9B,0BAA0B,CAmC5B"}