create-spatius-app 0.1.0-beta.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.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/cli.js +9046 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/templates.js +2856 -0
  6. package/dist/templates.js.map +1 -0
  7. package/package.json +79 -0
  8. package/schemas/result-v1.schema.json +129 -0
  9. package/schemas/result-v2.schema.json +146 -0
  10. package/templates/cloudflare-livekit/.editorconfig +15 -0
  11. package/templates/cloudflare-livekit/.prettierignore +6 -0
  12. package/templates/cloudflare-livekit/.prettierrc.json +4 -0
  13. package/templates/cloudflare-livekit/AGENTS.md +41 -0
  14. package/templates/cloudflare-livekit/README.md +37 -0
  15. package/templates/cloudflare-livekit/agent/Dockerfile.pip +31 -0
  16. package/templates/cloudflare-livekit/agent/Dockerfile.uv +29 -0
  17. package/templates/cloudflare-livekit/agent/README.md +84 -0
  18. package/templates/cloudflare-livekit/agent/dockerignore +11 -0
  19. package/templates/cloudflare-livekit/agent/env.example +8 -0
  20. package/templates/cloudflare-livekit/agent/pyproject.toml +21 -0
  21. package/templates/cloudflare-livekit/agent/python-version +1 -0
  22. package/templates/cloudflare-livekit/agent/src/__init__.py +1 -0
  23. package/templates/cloudflare-livekit/agent/src/agent.py +97 -0
  24. package/templates/cloudflare-livekit/agent/src/dispatch_metadata.py +64 -0
  25. package/templates/cloudflare-livekit/agent/tests/test_agent_startup.py +211 -0
  26. package/templates/cloudflare-livekit/agent/tests/test_dispatch_metadata.py +97 -0
  27. package/templates/cloudflare-livekit/agent/uv.lock +2272 -0
  28. package/templates/cloudflare-livekit/components.json +23 -0
  29. package/templates/cloudflare-livekit/contracts/agent-dispatch-metadata.schema.json +40 -0
  30. package/templates/cloudflare-livekit/dev.vars.example +12 -0
  31. package/templates/cloudflare-livekit/eslint.config.mjs +41 -0
  32. package/templates/cloudflare-livekit/gitignore +30 -0
  33. package/templates/cloudflare-livekit/index.html +17 -0
  34. package/templates/cloudflare-livekit/npmrc +4 -0
  35. package/templates/cloudflare-livekit/package-lock.json +6514 -0
  36. package/templates/cloudflare-livekit/package.json +66 -0
  37. package/templates/cloudflare-livekit/playwright.config.ts +29 -0
  38. package/templates/cloudflare-livekit/pnpm-lock.yaml +4012 -0
  39. package/templates/cloudflare-livekit/pnpm-workspace.yaml +8 -0
  40. package/templates/cloudflare-livekit/scripts/agent-runtime.mjs +129 -0
  41. package/templates/cloudflare-livekit/scripts/command.mjs +52 -0
  42. package/templates/cloudflare-livekit/scripts/dev-agent.mjs +77 -0
  43. package/templates/cloudflare-livekit/scripts/dev-web.mjs +18 -0
  44. package/templates/cloudflare-livekit/scripts/managed-process.mjs +144 -0
  45. package/templates/cloudflare-livekit/scripts/process-tree.mjs +111 -0
  46. package/templates/cloudflare-livekit/scripts/run-agent-python.mjs +25 -0
  47. package/templates/cloudflare-livekit/tests/dev-agent.test.mjs +327 -0
  48. package/templates/cloudflare-livekit/tests/dev-fixtures.mjs +235 -0
  49. package/templates/cloudflare-livekit/tests/dev-supervision.test.mjs +203 -0
  50. package/templates/cloudflare-livekit/tests/e2e/conversation.spec.ts +376 -0
  51. package/templates/cloudflare-livekit/tests/e2e/fake-avatar.ts +24 -0
  52. package/templates/cloudflare-livekit/tests/e2e/fake-components.tsx +173 -0
  53. package/templates/cloudflare-livekit/tests/e2e/fake-livekit.ts +123 -0
  54. package/templates/cloudflare-livekit/tests/e2e/harness.html +12 -0
  55. package/templates/cloudflare-livekit/tests/e2e/harness.tsx +1 -0
  56. package/templates/cloudflare-livekit/tests/e2e/vite.config.ts +48 -0
  57. package/templates/cloudflare-livekit/tests/web/api.test.ts +64 -0
  58. package/templates/cloudflare-livekit/tests/web/app.test.tsx +125 -0
  59. package/templates/cloudflare-livekit/tests/web/audio-visualizer.test.tsx +53 -0
  60. package/templates/cloudflare-livekit/tests/web/avatar-session.test.ts +119 -0
  61. package/templates/cloudflare-livekit/tests/web/components.test.tsx +152 -0
  62. package/templates/cloudflare-livekit/tests/web/conversation.test.tsx +76 -0
  63. package/templates/cloudflare-livekit/tests/web/session-attempt.test.ts +178 -0
  64. package/templates/cloudflare-livekit/tsconfig.json +9 -0
  65. package/templates/cloudflare-livekit/tsconfig.node.json +23 -0
  66. package/templates/cloudflare-livekit/tsconfig.web.json +26 -0
  67. package/templates/cloudflare-livekit/tsconfig.worker.json +22 -0
  68. package/templates/cloudflare-livekit/vite.config.ts +49 -0
  69. package/templates/cloudflare-livekit/vitest.config.ts +11 -0
  70. package/templates/cloudflare-livekit/web/App.tsx +176 -0
  71. package/templates/cloudflare-livekit/web/api.ts +69 -0
  72. package/templates/cloudflare-livekit/web/assets/FONT-PROVENANCE.md +8 -0
  73. package/templates/cloudflare-livekit/web/assets/OFL.txt +92 -0
  74. package/templates/cloudflare-livekit/web/assets/inter-latin.woff2 +0 -0
  75. package/templates/cloudflare-livekit/web/avatar-session.ts +67 -0
  76. package/templates/cloudflare-livekit/web/components/agents-ui/LICENSE +201 -0
  77. package/templates/cloudflare-livekit/web/components/agents-ui/PROVENANCE.md +32 -0
  78. package/templates/cloudflare-livekit/web/components/agents-ui/agent-audio-visualizer-bar.tsx +43 -0
  79. package/templates/cloudflare-livekit/web/components/agents-ui/agent-chat-input.tsx +78 -0
  80. package/templates/cloudflare-livekit/web/components/agents-ui/agent-chat-transcript.tsx +145 -0
  81. package/templates/cloudflare-livekit/web/components/agents-ui/agent-disconnect-button.tsx +27 -0
  82. package/templates/cloudflare-livekit/web/components/agents-ui/agent-session-provider.tsx +29 -0
  83. package/templates/cloudflare-livekit/web/components/agents-ui/agent-track-toggle.tsx +51 -0
  84. package/templates/cloudflare-livekit/web/components/agents-ui/start-audio-button.tsx +13 -0
  85. package/templates/cloudflare-livekit/web/components/conversation.tsx +352 -0
  86. package/templates/cloudflare-livekit/web/components/icons.tsx +39 -0
  87. package/templates/cloudflare-livekit/web/components/transcript-panel.tsx +12 -0
  88. package/templates/cloudflare-livekit/web/main.tsx +17 -0
  89. package/templates/cloudflare-livekit/web/session-attempt.ts +110 -0
  90. package/templates/cloudflare-livekit/web/styles.css +571 -0
  91. package/templates/cloudflare-livekit/worker/dispatch-metadata.ts +22 -0
  92. package/templates/cloudflare-livekit/worker/index.test.ts +152 -0
  93. package/templates/cloudflare-livekit/worker/index.ts +61 -0
  94. package/templates/cloudflare-livekit/worker/session.ts +103 -0
  95. package/templates/cloudflare-livekit/worker-configuration.d.ts +17184 -0
  96. package/templates/cloudflare-livekit/wrangler.jsonc +27 -0
@@ -0,0 +1,571 @@
1
+ @import 'tailwindcss';
2
+ @theme inline {
3
+ --font-sans: 'Inter', sans-serif;
4
+ --color-background: var(--background);
5
+ --color-foreground: var(--foreground);
6
+ --color-card: var(--background);
7
+ --color-card-foreground: var(--foreground);
8
+ --color-popover: var(--background);
9
+ --color-popover-foreground: var(--foreground);
10
+ --color-primary: var(--primary);
11
+ --color-primary-foreground: #fafafa;
12
+ --color-secondary: #f4f4f5;
13
+ --color-secondary-foreground: var(--foreground);
14
+ --color-muted: #f4f4f5;
15
+ --color-muted-foreground: #71717a;
16
+ --color-accent: #f4f4f5;
17
+ --color-accent-foreground: var(--foreground);
18
+ --color-destructive: #dc2626;
19
+ --color-border: #e4e4e7;
20
+ --color-input: #e4e4e7;
21
+ --color-ring: #a1a1aa;
22
+ --radius-sm: 4px;
23
+ --radius-md: 6px;
24
+ --radius-lg: 8px;
25
+ --radius-xl: 12px;
26
+ }
27
+ @font-face {
28
+ font-family: 'Inter';
29
+ font-style: normal;
30
+ font-weight: 100 900;
31
+ font-display: swap;
32
+ src: url('./assets/inter-latin.woff2') format('woff2');
33
+ }
34
+ :root {
35
+ --background: #fff;
36
+ --foreground: #18181b;
37
+ --primary: #18181b;
38
+ --border: #e4e4e7;
39
+ --muted: #71717a;
40
+ font-family: 'Inter', sans-serif;
41
+ color: var(--foreground);
42
+ background: #fafafa;
43
+ font-synthesis: none;
44
+ text-rendering: optimizeLegibility;
45
+ }
46
+ * {
47
+ box-sizing: border-box;
48
+ }
49
+ body {
50
+ margin: 0;
51
+ min-width: 320px;
52
+ }
53
+ button,
54
+ textarea,
55
+ select {
56
+ font: inherit;
57
+ }
58
+ button,
59
+ select {
60
+ cursor: pointer;
61
+ }
62
+ button {
63
+ border: 1px solid var(--border);
64
+ }
65
+ button:disabled,
66
+ select:disabled {
67
+ cursor: not-allowed;
68
+ opacity: 0.5;
69
+ }
70
+ :focus-visible {
71
+ outline: 2px solid #a1a1aa;
72
+ outline-offset: 3px;
73
+ }
74
+ .sr-only {
75
+ position: absolute;
76
+ width: 1px;
77
+ height: 1px;
78
+ padding: 0;
79
+ overflow: hidden;
80
+ clip: rect(0, 0, 0, 0);
81
+ white-space: nowrap;
82
+ border: 0;
83
+ }
84
+ .avatar-app {
85
+ position: relative;
86
+ isolation: isolate;
87
+ display: grid;
88
+ grid-template-columns: minmax(0, 1fr) 340px;
89
+ grid-template-rows: 64px minmax(0, 1fr) auto;
90
+ width: min(1040px, calc(100% - 48px));
91
+ height: min(780px, calc(100dvh - 64px));
92
+ min-height: 640px;
93
+ margin: 32px auto;
94
+ overflow: hidden;
95
+ border: 1px solid var(--border);
96
+ border-radius: 12px;
97
+ background: var(--background);
98
+ box-shadow: 0 1px 3px #00000008;
99
+ }
100
+ .session-header {
101
+ grid-column: 1 / -1;
102
+ grid-row: 1;
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: space-between;
106
+ gap: 16px;
107
+ padding: 0 20px;
108
+ border-bottom: 1px solid var(--border);
109
+ background: var(--background);
110
+ }
111
+ .brand-pill {
112
+ display: flex;
113
+ align-items: center;
114
+ gap: 10px;
115
+ text-decoration: none;
116
+ color: inherit;
117
+ font-size: 14px;
118
+ font-weight: 600;
119
+ }
120
+ .icon-button,
121
+ .text-button,
122
+ .type-trigger,
123
+ .microphone-toggle,
124
+ .audio-unlock {
125
+ display: inline-flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ gap: 8px;
129
+ min-height: 36px;
130
+ padding: 8px 12px;
131
+ border-radius: 6px;
132
+ background: var(--background);
133
+ color: var(--foreground);
134
+ font-size: 13px;
135
+ font-weight: 500;
136
+ }
137
+ .icon-button:hover,
138
+ .text-button:hover,
139
+ .type-trigger:hover,
140
+ .microphone-toggle:hover,
141
+ .audio-unlock:hover {
142
+ background: #f4f4f5;
143
+ }
144
+ .avatar-canvas {
145
+ position: relative;
146
+ grid-column: 1;
147
+ grid-row: 2;
148
+ min-width: 0;
149
+ min-height: 0;
150
+ margin: 16px;
151
+ overflow: hidden;
152
+ border: 1px solid var(--border);
153
+ border-radius: 8px;
154
+ background: #f4f4f5;
155
+ background-size: cover;
156
+ background-position: center;
157
+ }
158
+ .avatar-canvas canvas {
159
+ width: 100% !important;
160
+ height: 100% !important;
161
+ display: block;
162
+ }
163
+ .avatar-app:not([data-phase='active']) .avatar-canvas {
164
+ visibility: hidden;
165
+ }
166
+ .welcome-state {
167
+ grid-column: 1;
168
+ grid-row: 2 / 4;
169
+ z-index: 1;
170
+ display: flex;
171
+ flex-direction: column;
172
+ align-items: center;
173
+ justify-content: center;
174
+ text-align: center;
175
+ padding: 32px 24px 64px;
176
+ }
177
+ .welcome-icon {
178
+ width: 48px;
179
+ height: 48px;
180
+ display: grid;
181
+ place-items: center;
182
+ border: 1px solid var(--border);
183
+ border-radius: 10px;
184
+ margin-bottom: 20px;
185
+ }
186
+ .welcome-state h1 {
187
+ font-size: 24px;
188
+ font-weight: 600;
189
+ letter-spacing: -0.025em;
190
+ line-height: 1.3;
191
+ margin: 0;
192
+ }
193
+ .welcome-description {
194
+ font-size: 14px;
195
+ line-height: 1.7;
196
+ color: var(--muted);
197
+ max-width: 370px;
198
+ margin: 12px 0 24px;
199
+ text-wrap: balance;
200
+ }
201
+ .primary-button {
202
+ display: inline-flex;
203
+ align-items: center;
204
+ justify-content: center;
205
+ gap: 8px;
206
+ min-height: 40px;
207
+ padding: 10px 16px;
208
+ border-radius: 6px;
209
+ background: var(--primary);
210
+ color: #fff;
211
+ border-color: var(--primary);
212
+ font-size: 14px;
213
+ font-weight: 500;
214
+ }
215
+ .primary-button:hover:not(:disabled) {
216
+ background: #27272a;
217
+ }
218
+ .privacy-note {
219
+ font-size: 12px;
220
+ color: var(--muted);
221
+ margin: 20px 0 0;
222
+ }
223
+ .welcome-state .text-button {
224
+ margin-top: 12px;
225
+ }
226
+ .stage-footer {
227
+ grid-column: 1;
228
+ grid-row: 3;
229
+ padding: 20px;
230
+ text-align: center;
231
+ font-size: 11px;
232
+ color: var(--muted);
233
+ }
234
+ .stage-footer span {
235
+ margin: 0 6px;
236
+ }
237
+ .identity-pill {
238
+ display: flex;
239
+ gap: 10px;
240
+ align-items: center;
241
+ min-width: 0;
242
+ }
243
+ .identity-pill h1 {
244
+ font-size: 14px;
245
+ font-weight: 600;
246
+ margin: 0 0 3px;
247
+ }
248
+ .identity-pill p {
249
+ font-size: 12px;
250
+ color: var(--muted);
251
+ margin: 0;
252
+ }
253
+ .presence-dot {
254
+ width: 6px;
255
+ height: 6px;
256
+ border-radius: 50%;
257
+ background: #a1a1aa;
258
+ flex: none;
259
+ }
260
+ .presence-dot[data-live='true'] {
261
+ background: #16a34a;
262
+ }
263
+ .session-timer {
264
+ font-variant-numeric: tabular-nums;
265
+ font-size: 12px;
266
+ color: var(--muted);
267
+ border-left: 1px solid var(--border);
268
+ padding-left: 12px;
269
+ margin-left: 4px;
270
+ }
271
+ .connection-overlay {
272
+ grid-column: 1;
273
+ grid-row: 2;
274
+ z-index: 1;
275
+ margin: 16px;
276
+ display: flex;
277
+ flex-direction: column;
278
+ align-items: center;
279
+ justify-content: center;
280
+ padding: 24px;
281
+ text-align: center;
282
+ background: #fafafaf5;
283
+ border: 1px solid var(--border);
284
+ border-radius: 8px;
285
+ }
286
+ .connection-overlay h2 {
287
+ font-size: 16px;
288
+ font-weight: 600;
289
+ margin: 12px 0 4px;
290
+ }
291
+ .connection-overlay p {
292
+ color: var(--muted);
293
+ font-size: 13px;
294
+ }
295
+ .loading-spinner {
296
+ width: 24px;
297
+ height: 24px;
298
+ flex: none;
299
+ border: 2px solid var(--border);
300
+ border-top-color: var(--foreground);
301
+ border-radius: 50%;
302
+ animation: spin 1s linear infinite;
303
+ margin-bottom: 12px;
304
+ }
305
+ @keyframes spin {
306
+ to {
307
+ transform: rotate(360deg);
308
+ }
309
+ }
310
+ @media (prefers-reduced-motion: reduce) {
311
+ .loading-spinner {
312
+ animation: none;
313
+ }
314
+ }
315
+ .conversation-tools {
316
+ grid-column: 1;
317
+ grid-row: 3;
318
+ padding: 0 16px 20px;
319
+ display: flex;
320
+ flex-direction: column;
321
+ align-items: center;
322
+ gap: 12px;
323
+ }
324
+ .call-dock {
325
+ display: flex;
326
+ align-items: center;
327
+ justify-content: center;
328
+ flex-wrap: wrap;
329
+ gap: 8px;
330
+ width: 100%;
331
+ }
332
+ .device-select {
333
+ min-width: 0;
334
+ }
335
+ .device-select select {
336
+ background: var(--background);
337
+ color: var(--foreground);
338
+ border: 1px solid var(--border);
339
+ border-radius: 6px;
340
+ height: 36px;
341
+ max-width: 180px;
342
+ padding: 0 28px 0 10px;
343
+ font-size: 12px;
344
+ }
345
+ .disconnect-button {
346
+ display: inline-flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ gap: 8px;
350
+ min-height: 36px;
351
+ padding: 8px 12px;
352
+ color: #b91c1c;
353
+ background: #fff;
354
+ border: 1px solid #fecaca;
355
+ border-radius: 6px;
356
+ font-size: 13px;
357
+ font-weight: 500;
358
+ }
359
+ .disconnect-button:hover {
360
+ background: #fef2f2;
361
+ }
362
+ .user-audio {
363
+ display: flex;
364
+ align-items: center;
365
+ justify-content: center;
366
+ gap: 12px;
367
+ font-size: 12px;
368
+ min-height: 26px;
369
+ }
370
+ .audio-label {
371
+ color: var(--muted);
372
+ }
373
+ .audio-bars {
374
+ display: flex;
375
+ align-items: center;
376
+ gap: 3px;
377
+ height: 24px;
378
+ width: 32px;
379
+ }
380
+ .audio-bars span {
381
+ width: 4px;
382
+ min-height: 4px;
383
+ border-radius: 2px;
384
+ background: currentColor;
385
+ }
386
+ .audio-bars[data-enabled='false'] {
387
+ color: #a1a1aa;
388
+ }
389
+ .control-error {
390
+ font-size: 12px;
391
+ line-height: 1.5;
392
+ text-align: center;
393
+ color: #b91c1c;
394
+ margin: 0;
395
+ max-width: 480px;
396
+ }
397
+ .transcript-panel {
398
+ grid-column: 2;
399
+ grid-row: 2 / 4;
400
+ min-width: 0;
401
+ min-height: 0;
402
+ display: flex;
403
+ flex-direction: column;
404
+ border-left: 1px solid var(--border);
405
+ background: var(--background);
406
+ overflow: hidden;
407
+ }
408
+
409
+ .transcript-heading {
410
+ display: flex;
411
+ align-items: center;
412
+ justify-content: space-between;
413
+ flex: none;
414
+ padding: 16px;
415
+ border-bottom: 1px solid var(--border);
416
+ }
417
+ .transcript-heading h2 {
418
+ font-size: 14px;
419
+ font-weight: 600;
420
+ margin: 0;
421
+ }
422
+ .transcript-heading .icon-button {
423
+ padding: 8px;
424
+ border: 0;
425
+ }
426
+ .transcript-container {
427
+ display: flex;
428
+ flex: 1;
429
+ min-height: 0;
430
+ position: relative;
431
+ }
432
+ .transcript-scroll {
433
+ overflow-y: auto;
434
+ overscroll-behavior: contain;
435
+ scrollbar-width: thin;
436
+ padding: 20px 16px;
437
+ width: 100%;
438
+ }
439
+ .transcript-empty {
440
+ color: var(--muted);
441
+ font-size: 13px;
442
+ line-height: 1.7;
443
+ margin: 0;
444
+ }
445
+ .transcript-message {
446
+ margin-bottom: 24px;
447
+ }
448
+ .message-meta {
449
+ display: flex;
450
+ justify-content: space-between;
451
+ gap: 8px;
452
+ font-size: 11px;
453
+ margin: 0 0 8px;
454
+ font-weight: 500;
455
+ }
456
+ .message-meta time {
457
+ color: var(--muted);
458
+ font-weight: 400;
459
+ }
460
+ .transcript-message p {
461
+ font-size: 13px;
462
+ line-height: 1.7;
463
+ margin: 0;
464
+ white-space: pre-wrap;
465
+ overflow-wrap: anywhere;
466
+ }
467
+ .transcript-message[data-speaker='user'] p {
468
+ border-radius: 8px;
469
+ background: #f4f4f5;
470
+ padding: 10px 12px;
471
+ }
472
+ .thinking-indicator {
473
+ font-size: 12px;
474
+ color: var(--muted);
475
+ }
476
+ .jump-to-latest {
477
+ position: absolute;
478
+ bottom: 12px;
479
+ left: 50%;
480
+ transform: translateX(-50%);
481
+ border-radius: 6px;
482
+ background: var(--primary);
483
+ color: white;
484
+ padding: 8px 12px;
485
+ white-space: nowrap;
486
+ font-size: 12px;
487
+ }
488
+ .panel-composer {
489
+ flex: none;
490
+ padding: 12px;
491
+ border-top: 1px solid var(--border);
492
+ }
493
+ .chat-input {
494
+ display: flex;
495
+ align-items: flex-end;
496
+ padding: 6px;
497
+ gap: 8px;
498
+ border-radius: 6px;
499
+ background: var(--background);
500
+ border: 1px solid var(--border);
501
+ }
502
+ .chat-input textarea {
503
+ min-width: 0;
504
+ flex: 1;
505
+ resize: none;
506
+ padding: 6px;
507
+ background: transparent;
508
+ color: inherit;
509
+ border: 0;
510
+ font-size: 13px;
511
+ line-height: 1.5;
512
+ max-height: 120px;
513
+ field-sizing: content;
514
+ }
515
+ .chat-input textarea:focus-visible {
516
+ outline: 0;
517
+ }
518
+ .chat-input:focus-within {
519
+ box-shadow: 0 0 0 2px #a1a1aa;
520
+ }
521
+ .send-button {
522
+ display: grid;
523
+ place-items: center;
524
+ width: 32px;
525
+ height: 32px;
526
+ border-radius: 4px;
527
+ background: var(--primary);
528
+ color: white;
529
+ flex: none;
530
+ }
531
+ .chat-input-group .control-error {
532
+ margin-top: 8px;
533
+ }
534
+ .transcript-notice {
535
+ margin: 0;
536
+ padding: 16px;
537
+ border-top: 1px solid var(--border);
538
+ font-size: 12px;
539
+ line-height: 1.6;
540
+ color: var(--muted);
541
+ }
542
+
543
+ .conversation-ui {
544
+ display: contents;
545
+ }
546
+ @media (max-width: 820px) {
547
+ .avatar-app {
548
+ width: calc(100% - 24px);
549
+ margin: 12px auto;
550
+ height: auto;
551
+ min-height: 0;
552
+ grid-template-columns: minmax(0, 1fr);
553
+ grid-template-rows: 64px 360px auto 400px;
554
+ }
555
+ .transcript-panel {
556
+ grid-column: 1;
557
+ grid-row: 4;
558
+ border-left: 0;
559
+ border-top: 1px solid var(--border);
560
+ }
561
+ .session-header {
562
+ padding: 0 16px;
563
+ }
564
+ .avatar-canvas,
565
+ .connection-overlay {
566
+ margin: 12px;
567
+ }
568
+ .device-select select {
569
+ max-width: 160px;
570
+ }
571
+ }
@@ -0,0 +1,22 @@
1
+ export const AGENT_DISPATCH_METADATA_VERSION = 1 as const;
2
+
3
+ export interface AgentDispatchMetadataV1 {
4
+ avatar: {
5
+ id: string;
6
+ };
7
+ voice?: { id: string };
8
+ version: typeof AGENT_DISPATCH_METADATA_VERSION;
9
+ }
10
+
11
+ export function serializeAgentDispatchMetadata(
12
+ avatarId: string,
13
+ voiceId?: string,
14
+ ): string {
15
+ const metadata = {
16
+ avatar: { id: avatarId },
17
+ ...(voiceId?.trim() ? { voice: { id: voiceId.trim() } } : {}),
18
+ version: AGENT_DISPATCH_METADATA_VERSION,
19
+ } satisfies AgentDispatchMetadataV1;
20
+
21
+ return JSON.stringify(metadata);
22
+ }