@tt-a1i/openpi 0.4.0 → 0.5.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 (109) hide show
  1. package/README.md +102 -40
  2. package/SETUP.md +22 -6
  3. package/assets/openpi-launch-card-v1.webp +0 -0
  4. package/bin/openpi.js +145 -0
  5. package/extensions/background-terminals/index.ts +30 -2
  6. package/extensions/background-terminals/src/domain.ts +2 -0
  7. package/extensions/background-terminals/src/manager.ts +486 -106
  8. package/extensions/background-terminals/src/output.ts +33 -0
  9. package/extensions/background-terminals/src/prompt.ts +13 -5
  10. package/extensions/background-terminals/src/result-delivery.ts +4 -1
  11. package/extensions/clear-context/index.ts +83 -0
  12. package/extensions/context-pivot/index.ts +16 -6
  13. package/extensions/cron/schedule.ts +7 -1
  14. package/extensions/file-mutation-display/render.ts +17 -257
  15. package/extensions/file-search/src/binaries.ts +57 -41
  16. package/extensions/git-read/index.ts +1 -3
  17. package/extensions/model-info/index.ts +21 -33
  18. package/extensions/model-info/session-metrics.ts +96 -0
  19. package/extensions/plan-mode/bash-policy.ts +54 -9
  20. package/extensions/plan-mode/index.ts +7 -2
  21. package/extensions/post-edit/index.ts +16 -6
  22. package/extensions/sessions/git-stats.ts +258 -72
  23. package/extensions/sessions/index.ts +153 -86
  24. package/extensions/sessions/preview-cache.ts +104 -0
  25. package/extensions/sessions/preview-loader.ts +856 -0
  26. package/extensions/sessions/sessions.ts +43 -4
  27. package/extensions/setup/index.ts +123 -127
  28. package/extensions/shared/activity-status.ts +30 -0
  29. package/extensions/shared/agent-session-page.ts +319 -0
  30. package/extensions/shared/agent-tool-renderer.ts +218 -0
  31. package/extensions/shared/agent-transcript.ts +524 -0
  32. package/extensions/shared/capability-intent.ts +1 -1
  33. package/extensions/shared/child-session.ts +437 -21
  34. package/extensions/shared/result-delivery.ts +34 -0
  35. package/extensions/shared/setup-config.ts +73 -33
  36. package/extensions/shared/setup-episode-state.ts +1 -1
  37. package/extensions/shared/terminal-text.ts +110 -23
  38. package/extensions/shared/text-projection.ts +72 -15
  39. package/extensions/shared/tool-activity.ts +382 -0
  40. package/extensions/shared/tool-surface.ts +29 -2
  41. package/extensions/shared/transcript-viewport.ts +46 -0
  42. package/extensions/shared/web-observer-registry.ts +390 -0
  43. package/extensions/shared/worktree.ts +11 -0
  44. package/extensions/subagents/index.ts +270 -59
  45. package/extensions/subagents/navigation.ts +34 -5
  46. package/extensions/subagents/src/backend.ts +12 -1
  47. package/extensions/subagents/src/backends/pi.ts +375 -66
  48. package/extensions/subagents/src/domain.ts +5 -0
  49. package/extensions/subagents/src/manager.ts +34 -2
  50. package/extensions/subagents/src/prompt.ts +32 -4
  51. package/extensions/subagents/src/result-artifact.ts +4 -0
  52. package/extensions/subagents/src/result-delivery.ts +7 -1
  53. package/extensions/subagents/src/runtime.ts +15 -1
  54. package/extensions/subagents/src/ui/takeover.ts +73 -257
  55. package/extensions/subagents/src/ui/transcript.ts +38 -535
  56. package/extensions/subagents/src/ui/wait-result.ts +103 -15
  57. package/extensions/suggestions/src/ui.ts +10 -4
  58. package/extensions/tasks/index.ts +0 -3
  59. package/extensions/ui-customization/footer.ts +0 -40
  60. package/extensions/ui-customization/index.ts +0 -4
  61. package/extensions/user-input-fold/index.ts +1 -1
  62. package/extensions/web/index.ts +234 -0
  63. package/extensions/workflows/artifacts.ts +137 -47
  64. package/extensions/workflows/completion-projection.ts +457 -0
  65. package/extensions/workflows/coordinator.ts +8 -10
  66. package/extensions/workflows/dashboard.ts +167 -228
  67. package/extensions/workflows/handoff.ts +70 -16
  68. package/extensions/workflows/index.ts +488 -198
  69. package/extensions/workflows/journal.ts +148 -13
  70. package/extensions/workflows/model.ts +74 -4
  71. package/extensions/workflows/navigation.ts +32 -8
  72. package/extensions/workflows/progress-projection.ts +306 -0
  73. package/extensions/workflows/prompt.ts +66 -6
  74. package/extensions/workflows/replay-safety.ts +42 -21
  75. package/extensions/workflows/result-delivery.ts +128 -64
  76. package/extensions/workflows/retention.ts +593 -0
  77. package/extensions/workflows/runner.ts +388 -279
  78. package/extensions/workflows/sandbox-child.cjs +25 -3
  79. package/extensions/workflows/sandbox.ts +62 -8
  80. package/extensions/workflows/serialization.ts +325 -17
  81. package/extensions/workflows/tool-renderer.ts +22 -0
  82. package/extensions/workflows/transcript.ts +149 -0
  83. package/extensions/workspace-cleanup-guard/index.ts +54 -0
  84. package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
  85. package/package.json +28 -8
  86. package/skills/subagents/REFERENCE.md +189 -0
  87. package/skills/subagents/SKILL.md +1 -1
  88. package/skills/workflows/REFERENCE.md +4 -2
  89. package/web/adapter/pi-adapter.ts +661 -0
  90. package/web/host/browser-launcher.ts +20 -0
  91. package/web/host/static-assets.ts +4 -0
  92. package/web/host/terminal-status.ts +38 -0
  93. package/web/host/web-host.ts +789 -0
  94. package/web/http-dispatcher.ts +125 -0
  95. package/web/protocol/types.ts +462 -0
  96. package/web/runtime/pi-runtime.ts +991 -0
  97. package/web/runtime/types.ts +71 -0
  98. package/web/runtime/web-host-lease.ts +497 -0
  99. package/web/trace.ts +18 -0
  100. package/web/ui/app.js +1398 -0
  101. package/web/ui/index.html +139 -0
  102. package/web/ui/styles.css +598 -0
  103. package/web/vite.config.mjs +34 -0
  104. package/extensions/execution-convergence/active-evidence.ts +0 -129
  105. package/extensions/execution-convergence/index.ts +0 -442
  106. package/extensions/execution-convergence/workspace-provenance.ts +0 -338
  107. package/extensions/setup/intercom-fs-helper.cjs +0 -130
  108. package/extensions/setup/intercom.ts +0 -603
  109. package/extensions/subagents/src/backends/stub.ts +0 -303
@@ -0,0 +1,598 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --page: #ffffff;
4
+ --sidebar: #faf7ed;
5
+ --sidebar-hover: #f3ead0;
6
+ --sidebar-active: #eee9dc;
7
+ --border: #e3e7eb;
8
+ --border-strong: #d2d8de;
9
+ --text: #1f252c;
10
+ --muted: #69737d;
11
+ --subtle: #929ba4;
12
+ --accent: #2867b2;
13
+ --warm-accent: #faf7ed;
14
+ --warm-accent-hover: #f3ead0;
15
+ --warm-accent-disabled: #faf7ed;
16
+ --focus: #4c88cf;
17
+ --healthy: #28835a;
18
+ --error: #b42318;
19
+ }
20
+
21
+ * { box-sizing: border-box; }
22
+ html, body { width: 100%; height: 100%; overflow: hidden; }
23
+ body {
24
+ margin: 0;
25
+ background-color: #faf7ed;
26
+ background-image:
27
+ linear-gradient(rgba(182, 165, 119, .2) 1px, transparent 1px),
28
+ linear-gradient(90deg, rgba(182, 165, 119, .2) 1px, transparent 1px);
29
+ background-size: 24px 24px;
30
+ color: var(--text);
31
+ font: 14px/1.5 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
32
+ }
33
+ button, input, textarea { font: inherit; letter-spacing: 0; }
34
+ button { color: inherit; cursor: pointer; }
35
+ button:disabled { cursor: default; }
36
+ .icon {
37
+ display: block;
38
+ fill: none;
39
+ stroke: currentColor;
40
+ stroke-linecap: round;
41
+ stroke-linejoin: round;
42
+ stroke-width: 1.8;
43
+ vector-effect: non-scaling-stroke;
44
+ }
45
+ .visually-hidden {
46
+ position: absolute;
47
+ width: 1px;
48
+ height: 1px;
49
+ padding: 0;
50
+ margin: -1px;
51
+ overflow: hidden;
52
+ clip: rect(0, 0, 0, 0);
53
+ white-space: nowrap;
54
+ border: 0;
55
+ }
56
+ button:focus-visible, input:focus-visible, textarea:focus-visible {
57
+ outline: 2px solid var(--focus);
58
+ outline-offset: 2px;
59
+ }
60
+
61
+ .app-shell {
62
+ display: grid;
63
+ grid-template-columns: 280px minmax(0, 1fr);
64
+ width: 100%;
65
+ height: 100dvh;
66
+ transition: grid-template-columns 180ms ease;
67
+ }
68
+ body.sidebar-collapsed .app-shell { grid-template-columns: 56px minmax(0, 1fr); }
69
+
70
+ .session-sidebar {
71
+ position: relative;
72
+ z-index: 20;
73
+ display: flex;
74
+ min-height: 0;
75
+ flex-direction: column;
76
+ padding: 8px 10px;
77
+ overflow: hidden;
78
+ border-right: 1px solid #e8ebee;
79
+ background: var(--sidebar);
80
+ }
81
+ .sidebar-brand {
82
+ display: flex;
83
+ height: 44px;
84
+ align-items: center;
85
+ justify-content: space-between;
86
+ padding: 0 4px 0 6px;
87
+ }
88
+ .sidebar-brand strong, .landing-brand strong {
89
+ display: inline-flex;
90
+ align-items: center;
91
+ color: var(--text);
92
+ font-weight: 700;
93
+ line-height: 1;
94
+ }
95
+ .sidebar-brand strong { font-size: 18px; }
96
+ .pixel-mark { position: relative; display: inline-block; width: 21px; height: 21px; margin-left: 2px; }
97
+ .pixel-mark i { position: absolute; display: block; width: 6px; height: 6px; background: currentColor; }
98
+ .pixel-mark i:nth-child(1) { top: 0; left: 0; }
99
+ .pixel-mark i:nth-child(2) { top: 0; left: 5px; }
100
+ .pixel-mark i:nth-child(3) { top: 0; left: 10px; }
101
+ .pixel-mark i:nth-child(4) { top: 5px; left: 0; }
102
+ .pixel-mark i:nth-child(5) { top: 5px; left: 10px; }
103
+ .pixel-mark i:nth-child(6) { top: 10px; left: 0; }
104
+ .pixel-mark i:nth-child(7) { top: 10px; left: 5px; }
105
+ .pixel-mark i:nth-child(8) { top: 10px; left: 15px; }
106
+ .pixel-mark i:nth-child(9) { top: 15px; left: 0; }
107
+ .pixel-mark i:nth-child(10) { top: 15px; left: 15px; }
108
+
109
+ .collapse-button, .icon-button, .mobile-sidebar-button {
110
+ display: grid;
111
+ flex: 0 0 auto;
112
+ place-items: center;
113
+ border: 0;
114
+ background: transparent;
115
+ color: var(--muted);
116
+ }
117
+ .collapse-button { width: 30px; height: 30px; border-radius: 6px; }
118
+ .collapse-button:hover, .icon-button:hover, .mobile-sidebar-button:hover { background: var(--sidebar-hover); color: var(--text); }
119
+ .collapse-button svg { width: 18px; height: 18px; }
120
+ .icon-button { width: 28px; height: 28px; border-radius: 6px; }
121
+ .icon-button svg { width: 18px; height: 18px; }
122
+
123
+ .new-session-button {
124
+ display: flex;
125
+ width: calc(100% - 4px);
126
+ height: 38px;
127
+ flex: 0 0 auto;
128
+ align-items: center;
129
+ justify-content: center;
130
+ gap: 7px;
131
+ margin: 8px 2px 10px;
132
+ border: 1px solid var(--border-strong);
133
+ border-radius: 19px;
134
+ background: var(--page);
135
+ color: var(--text);
136
+ font-weight: 550;
137
+ }
138
+ .new-session-button:hover { background: #fafbfc; border-color: #b9c2ca; }
139
+ .new-session-button svg { width: 17px; height: 17px; flex: 0 0 auto; stroke-width: 1.7; }
140
+
141
+ .workspace-heading {
142
+ display: flex;
143
+ min-height: 32px;
144
+ align-items: center;
145
+ justify-content: space-between;
146
+ padding: 0 4px 0 8px;
147
+ color: var(--muted);
148
+ font-size: 11px;
149
+ font-weight: 650;
150
+ text-transform: uppercase;
151
+ }
152
+ .workspace-actions { display: flex; flex: 0 0 auto; }
153
+ .session-search { position: relative; display: block; min-width: 0; flex: 0 1 auto; width: 0; max-width: 0; margin: 0; opacity: 0; overflow: hidden; transform: translateX(10px) scaleX(.72); transform-origin: right center; transition: width 240ms cubic-bezier(.22, 1, .36, 1), max-width 240ms cubic-bezier(.22, 1, .36, 1), margin 200ms ease, opacity 130ms ease, transform 240ms cubic-bezier(.22, 1, .36, 1); pointer-events: none; }
154
+ .workspace-heading.is-searching .workspace-heading-label,
155
+ .workspace-heading.is-searching .workspace-actions { display: none; }
156
+ .workspace-heading.is-searching .session-search { width: 100%; max-width: 100%; margin-right: 4px; opacity: 1; transform: translateX(0) scaleX(1); pointer-events: auto; }
157
+ .session-search input {
158
+ width: 100%;
159
+ height: 32px;
160
+ padding: 0 30px 0 29px;
161
+ border: 1px solid #dfe3e7;
162
+ border-radius: 16px;
163
+ outline: 0;
164
+ background: rgba(255, 255, 255, .48);
165
+ color: var(--text);
166
+ font-size: 12px;
167
+ }
168
+ .session-search input::placeholder { color: #7f8993; }
169
+ .session-search input:hover { border-color: #cfd5db; }
170
+ .session-search input:focus { border-color: #cfd5db; background: var(--page); box-shadow: none; }
171
+ .session-search-icon { position: absolute; top: 9px; left: 9px; z-index: 1; width: 14px; height: 14px; color: #9aa2aa; pointer-events: none; }
172
+ .session-search-clear { position: absolute; top: 4px; right: 4px; display: grid; width: 24px; height: 24px; place-items: center; padding: 0; border: 0; border-radius: 50%; background: transparent; color: #737b83; }
173
+ .session-search-clear .icon { width: 14px; height: 14px; stroke-width: 2; }
174
+ .session-search-clear:hover { background: #e9ecef; color: var(--text); }
175
+
176
+ .workspace-tree {
177
+ min-height: 0;
178
+ flex: 1 1 auto;
179
+ padding: 0 0 12px;
180
+ overflow-x: hidden;
181
+ overflow-y: auto;
182
+ color: var(--muted);
183
+ scrollbar-gutter: auto;
184
+ scrollbar-width: thin;
185
+ scrollbar-color: rgba(125, 132, 138, .45) transparent;
186
+ }
187
+ .workspace-group + .workspace-group { margin-top: 5px; }
188
+ .workspace-button, .session { width: 100%; min-width: 0; border: 0; border-radius: 6px; background: transparent; text-align: left; }
189
+ .workspace-button {
190
+ display: grid;
191
+ height: 34px;
192
+ grid-template-columns: 20px minmax(0, 1fr) 52px;
193
+ gap: 4px;
194
+ align-items: center;
195
+ padding: 0 4px;
196
+ color: #39434d;
197
+ cursor: pointer;
198
+ }
199
+ .workspace-button:hover, .session:hover { background: var(--sidebar-hover); }
200
+ .workspace-button:focus-visible, .session:focus-visible { outline-offset: -2px; }
201
+ .workspace-button strong {
202
+ min-width: 0;
203
+ overflow: hidden;
204
+ font-size: 14px;
205
+ font-weight: 600;
206
+ text-overflow: ellipsis;
207
+ white-space: nowrap;
208
+ }
209
+ .workspace-toggle, .workspace-action {
210
+ display: grid;
211
+ place-items: center;
212
+ padding: 0;
213
+ border: 0;
214
+ border-radius: 4px;
215
+ background: transparent;
216
+ color: var(--muted);
217
+ }
218
+ .workspace-toggle { width: 20px; height: 28px; }
219
+ .workspace-action { width: 24px; height: 28px; }
220
+ .workspace-toggle:hover, .workspace-action:hover { background: rgba(255, 255, 255, .72); color: var(--text); }
221
+ .workspace-action .icon { width: 17px; height: 17px; }
222
+ .workspace-row-actions { display: flex; width: 52px; align-items: center; justify-content: flex-end; gap: 2px; }
223
+ .workspace-chevron { width: 12px; height: 12px; color: var(--subtle); transition: transform 140ms ease; }
224
+ .workspace-group.collapsed .workspace-chevron { transform: rotate(-90deg); }
225
+ .workspace-sessions { margin-top: 1px; }
226
+ .workspace-group.collapsed .workspace-sessions { display: none; }
227
+ .session-row {
228
+ position: relative;
229
+ display: flex;
230
+ min-width: 0;
231
+ align-items: center;
232
+ }
233
+ .session {
234
+ display: grid;
235
+ width: 100%;
236
+ height: 32px;
237
+ grid-template-columns: minmax(0, 1fr) auto;
238
+ gap: 8px;
239
+ align-items: center;
240
+ padding: 0 8px 0 29px;
241
+ color: #5f6973;
242
+ }
243
+ .session.active { background: var(--sidebar-active); color: var(--text); }
244
+ .session-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
245
+ .session-time { color: var(--subtle); font-size: 11.5px; font-variant-numeric: tabular-nums; }
246
+ .session-action {
247
+ position: absolute;
248
+ right: 5px;
249
+ display: grid;
250
+ width: 26px;
251
+ height: 26px;
252
+ place-items: center;
253
+ padding: 0;
254
+ border: 0;
255
+ border-radius: 6px;
256
+ background: var(--sidebar-hover);
257
+ color: var(--muted);
258
+ opacity: 0;
259
+ pointer-events: none;
260
+ transition: opacity 120ms ease;
261
+ }
262
+ .session-action .icon { width: 16px; height: 16px; }
263
+ .session-row:hover .session-action,
264
+ .session-row:focus-within .session-action { opacity: 1; pointer-events: auto; }
265
+ .session-row:hover .session-time,
266
+ .session-row:focus-within .session-time { visibility: hidden; }
267
+ .session-action:hover { background: var(--sidebar-active); color: var(--text); }
268
+ .workspace-sessions .empty, .workspace-tree > .empty { min-height: 32px; padding: 7px 10px 7px 29px; color: var(--subtle); font-size: 12px; }
269
+ .workspace-menu {
270
+ position: fixed;
271
+ z-index: 40;
272
+ width: 178px;
273
+ margin: 0;
274
+ padding: 4px;
275
+ border: 1px solid var(--border);
276
+ border-radius: 6px;
277
+ background: var(--page);
278
+ box-shadow: 0 10px 28px rgba(31, 37, 44, .16);
279
+ }
280
+ .workspace-menu::backdrop { display: none; }
281
+ .workspace-menu button {
282
+ display: block;
283
+ width: 100%;
284
+ height: 32px;
285
+ padding: 0 9px;
286
+ border: 0;
287
+ border-radius: 4px;
288
+ background: transparent;
289
+ text-align: left;
290
+ }
291
+ .workspace-menu button:hover { background: #f1f3f5; }
292
+ .workspace-menu .danger-action { color: var(--error); }
293
+ .workspace-delete-dialog { width: min(380px, calc(100% - 32px)); padding: 0; border: 0; border-radius: 24px; background: var(--page); box-shadow: 0 20px 60px rgba(31, 37, 44, .2); color: var(--text); }
294
+ .workspace-delete-dialog::backdrop { background: rgba(20, 26, 32, .2); }
295
+ .workspace-delete-dialog form { display: grid; gap: 14px; padding: 28px 24px 20px; }
296
+ .workspace-delete-dialog strong { font-size: 16px; font-weight: 650; }
297
+ .workspace-delete-dialog p { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.65; }
298
+ .workspace-delete-dialog .dialog-actions { margin-top: 6px; }
299
+ .workspace-delete-dialog .dialog-actions button { min-width: 64px; height: 36px; border-radius: 18px; }
300
+ .workspace-delete-dialog .dialog-actions .danger-action { border-color: #f0c6c6; color: #e55353; background: transparent; }
301
+ .workspace-delete-dialog .dialog-actions .danger-action:hover { background: #fff3f3; }
302
+ .workspace-rename-dialog {
303
+ width: min(360px, calc(100% - 32px));
304
+ padding: 0;
305
+ border: 1px solid var(--border);
306
+ border-radius: 6px;
307
+ background: var(--page);
308
+ box-shadow: 0 18px 48px rgba(31, 37, 44, .2);
309
+ }
310
+ .workspace-rename-dialog::backdrop { background: rgba(20, 26, 32, .28); }
311
+ .workspace-rename-dialog form { display: grid; gap: 16px; padding: 18px; }
312
+ .workspace-rename-dialog strong { font-size: 14px; }
313
+ .workspace-rename-dialog input { width: 100%; height: 36px; padding: 0 10px; border: 1px solid var(--border-strong); border-radius: 5px; color: var(--text); }
314
+ .dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }
315
+ .dialog-actions button { height: 32px; padding: 0 12px; border: 1px solid var(--border-strong); border-radius: 5px; background: var(--page); }
316
+ .dialog-actions #save-workspace-name { border-color: var(--text); background: var(--text); color: #fff; }
317
+ body.sidebar-collapsed .session-sidebar { align-items: center; padding: 8px; }
318
+ body.sidebar-collapsed .sidebar-brand { position: relative; width: 40px; justify-content: center; padding: 0; }
319
+ body.sidebar-collapsed .brand-word, body.sidebar-collapsed .new-session-button span,
320
+ body.sidebar-collapsed .workspace-heading-label, body.sidebar-collapsed .workspace-tree,
321
+ body.sidebar-collapsed .session-search { display: none; }
322
+ body.sidebar-collapsed .sidebar-brand .collapse-button { position: absolute; inset: 7px 5px auto; z-index: 2; }
323
+ body.sidebar-collapsed .sidebar-brand .collapse-button .icon { display: none; }
324
+ body.sidebar-collapsed .sidebar-brand .collapse-button:hover .icon { display: block; }
325
+ body.sidebar-collapsed .sidebar-brand strong { pointer-events: none; }
326
+ body.sidebar-collapsed .sidebar-brand .pixel-mark { margin: 0; transition: opacity 120ms ease; }
327
+ body.sidebar-collapsed .sidebar-brand:has(.collapse-button:hover) .pixel-mark { opacity: 0; }
328
+ body.sidebar-collapsed .new-session-button { width: 36px; height: 36px; margin: 10px 0 12px; padding: 0; border-color: transparent; background: transparent; }
329
+ body.sidebar-collapsed .new-session-button:hover { background: var(--sidebar-hover); }
330
+ body.sidebar-collapsed .workspace-heading { width: 36px; min-height: 36px; justify-content: center; padding: 0; }
331
+ body.sidebar-collapsed .workspace-actions { flex-direction: column; gap: 4px; }
332
+ body.sidebar-collapsed .icon-button { width: 36px; height: 36px; }
333
+
334
+ .conversation-shell { position: relative; display: grid; min-width: 0; min-height: 0; grid-template-rows: minmax(0, 1fr) auto; background: transparent; }
335
+ .conversation-shell.landing { grid-template-rows: minmax(0, 1fr); }
336
+ .landing .conversation, .landing .composer-dock { grid-area: 1 / 1; }
337
+ .landing .conversation { pointer-events: none; }
338
+ .landing .composer-dock {
339
+ position: absolute;
340
+ top: calc(50% - 81px);
341
+ right: 0;
342
+ left: 0;
343
+ z-index: 1;
344
+ display: flex;
345
+ flex-direction: column;
346
+ align-items: stretch;
347
+ align-self: center;
348
+ width: 100%;
349
+ padding: 0 24px;
350
+ background: transparent;
351
+ }
352
+ .workbench-header {
353
+ min-width: 0;
354
+ align-items: center;
355
+ gap: 12px;
356
+ padding: 0 20px;
357
+ border-bottom: 1px solid var(--border);
358
+ background: rgba(251, 250, 245, .76);
359
+ backdrop-filter: blur(8px);
360
+ -webkit-backdrop-filter: blur(8px);
361
+ }
362
+ .mobile-sidebar-button { display: none; width: 34px; height: 34px; border-radius: 6px; }
363
+ .mobile-sidebar-button svg { width: 19px; height: 19px; }
364
+ .session-header { display: flex; min-width: 0; flex: 1; flex-direction: column; }
365
+ .session-header strong, .session-header small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
366
+ .session-header strong { font-size: 14px; font-weight: 650; }
367
+ .session-header small { color: var(--muted); font-size: 11px; }
368
+ .connection-state { display: inline-flex; align-items: center; gap: 6px; color: var(--healthy); font-size: 11px; }
369
+ .connection-state::before { width: 6px; height: 6px; border-radius: 50%; background: currentColor; content: ""; }
370
+ .connection-state.reconnecting { color: #9a6700; }
371
+
372
+ .conversation { min-height: 0; overflow-y: auto; scroll-behavior: smooth; }
373
+ .landing .conversation { display: grid; place-items: center; }
374
+ .landing-welcome { padding: 0 24px; text-align: center; transform: translateY(-104px); }
375
+ .landing-brand { display: flex; align-items: center; justify-content: center; }
376
+ .landing-brand strong { align-items: flex-end; font-size: 34px; }
377
+ .landing-brand .pixel-mark { width: 36px; height: 36px; margin-left: 3px; }
378
+ .landing-brand .pixel-mark i { width: 9px; height: 9px; }
379
+ .landing-brand .pixel-mark i:nth-child(2) { left: 9px; }
380
+ .landing-brand .pixel-mark i:nth-child(3) { left: 18px; }
381
+ .landing-brand .pixel-mark i:nth-child(4) { top: 9px; }
382
+ .landing-brand .pixel-mark i:nth-child(5) { top: 9px; left: 18px; }
383
+ .landing-brand .pixel-mark i:nth-child(6) { top: 18px; }
384
+ .landing-brand .pixel-mark i:nth-child(7) { top: 18px; left: 9px; }
385
+ .landing-brand .pixel-mark i:nth-child(8) { top: 18px; left: 27px; }
386
+ .landing-brand .pixel-mark i:nth-child(9) { top: 27px; }
387
+ .landing-brand .pixel-mark i:nth-child(10) { top: 27px; left: 27px; }
388
+ .message-row { width: min(760px, calc(100% - 48px)); margin: 0 auto; padding: 18px 0; }
389
+ .message-row.user { display: flex; justify-content: flex-end; padding-top: 26px; }
390
+ .message-row.assistant { width: min(780px, calc(100% - 48px)); }
391
+ .message-row.detail-only { padding: 12px 0; }
392
+ .message-row.detail-only .message-details { margin: 0 auto; }
393
+ .message-content { min-width: 0; max-width: min(78%, 560px); }
394
+ .message-row.assistant .message-content { width: 100%; max-width: none; }
395
+ .message-row.assistant .message-details { width: min(760px, calc(100% - 4px)); margin-right: auto; margin-left: auto; }
396
+ .message-body { color: #252c33; font-size: 14px; line-height: 1.7; overflow-wrap: anywhere; white-space: pre-wrap; }
397
+ .message-row.user .message-body { padding: 10px 14px; border-radius: 18px 18px 4px 18px; background: var(--sidebar-active); color: var(--text); }
398
+ .message-body.markdown { white-space: normal; }
399
+ .message-body.markdown > :first-child { margin-top: 0; }
400
+ .message-body.markdown > :last-child { margin-bottom: 0; }
401
+ .message-body.markdown p { margin: 0 0 12px; }
402
+ .message-body.markdown h1, .message-body.markdown h2, .message-body.markdown h3,
403
+ .message-body.markdown h4, .message-body.markdown h5, .message-body.markdown h6 {
404
+ margin: 18px 0 8px;
405
+ color: var(--text);
406
+ font-weight: 650;
407
+ line-height: 1.3;
408
+ }
409
+ .message-body.markdown h1 { font-size: 1.45em; }
410
+ .message-body.markdown h2 { font-size: 1.3em; }
411
+ .message-body.markdown h3 { font-size: 1.15em; }
412
+ .message-body.markdown ul, .message-body.markdown ol { margin: 0 0 12px; padding-left: 24px; }
413
+ .message-body.markdown li + li { margin-top: 4px; }
414
+ .message-body.markdown blockquote { margin: 0 0 12px; padding: 2px 14px; border-left: 3px solid var(--border-strong); color: var(--muted); }
415
+ .message-body.markdown hr { margin: 18px 0; border: 0; border-top: 1px solid var(--border); }
416
+ .message-body.markdown a { color: var(--accent); text-decoration: underline; text-decoration-color: rgba(40, 103, 178, .35); text-underline-offset: 2px; }
417
+ .message-body.markdown code { padding: 2px 5px; border-radius: 4px; background: #eef1f4; color: #39434d; font: .9em ui-monospace, SFMono-Regular, Menlo, monospace; }
418
+ .message-body.markdown pre { margin: 0 0 14px; overflow-x: auto; padding: 12px 14px; border: 1px solid var(--border); border-radius: 8px; background: #f5f6f7; }
419
+ .message-body.markdown pre code { padding: 0; background: transparent; color: var(--text); font-size: 12px; white-space: pre; }
420
+ .message-body.markdown table { width: 100%; margin: 0 0 14px; border-collapse: collapse; font-size: .95em; }
421
+ .message-body.markdown th, .message-body.markdown td { padding: 7px 9px; border: 1px solid var(--border); text-align: left; vertical-align: top; }
422
+ .message-body.markdown th { background: #f4f5f6; font-weight: 650; }
423
+ .message-body.markdown input[type="checkbox"] { margin-right: 7px; accent-color: var(--accent); }
424
+ .message-details { width: min(760px, calc(100% - 48px)); margin: 4px auto; color: var(--muted); }
425
+ .message-details summary { display: flex; min-height: 34px; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 7px; cursor: pointer; list-style: none; font-size: 13px; }
426
+ .message-details summary::-webkit-details-marker { display: none; }
427
+ .message-details summary:hover { background: rgba(238, 241, 244, .72); }
428
+ .details-mark { width: 8px; height: 8px; flex: 0 0 auto; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); transition: transform 140ms ease; }
429
+ .message-details[open] .details-mark { transform: rotate(45deg); }
430
+ .details-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
431
+ .assistant-detail { margin: 2px 0 4px; }
432
+ .assistant-detail .details-body { max-height: 280px; }
433
+ .details-body { max-height: 360px; margin: 2px 0 0 24px; overflow: auto; padding: 12px 14px; border: 1px solid var(--border); border-radius: 8px; background: rgba(247, 248, 249, .82); }
434
+ .tool-evidence { color: var(--muted); font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; overflow-wrap: anywhere; white-space: pre-wrap; }
435
+
436
+ .composer-dock {
437
+ display: flex;
438
+ flex-direction: column;
439
+ align-items: center;
440
+ gap: 8px;
441
+ padding: 12px 24px 22px;
442
+ background: linear-gradient(to bottom, rgba(250, 247, 237, 0), rgba(250, 247, 237, .94) 18px);
443
+ }
444
+ .composer {
445
+ position: relative;
446
+ display: flex;
447
+ width: min(780px, 100%);
448
+ min-height: 118px;
449
+ flex-direction: column;
450
+ margin: 0 auto;
451
+ padding: 13px 13px 10px;
452
+ border: 1px solid var(--border-strong);
453
+ border-radius: 18px;
454
+ background: var(--page);
455
+ box-shadow: 0 8px 26px rgba(53, 78, 117, .1);
456
+ }
457
+ .composer:focus-within { border-color: #d8cfae; box-shadow: 0 0 0 2px rgba(216, 207, 174, .28), 0 8px 26px rgba(86, 76, 48, .1); }
458
+ .composer.dormant { border-style: dashed; box-shadow: none; cursor: pointer; }
459
+ .composer.dormant:hover { border-color: #8d98a3; }
460
+ .composer.dormant textarea { cursor: pointer; }
461
+ .composer textarea { width: 100%; min-width: 0; min-height: 52px; max-height: 220px; flex: 0 0 auto; padding: 0 2px; resize: none; overflow-y: hidden; border: 0; outline: 0; background: transparent; color: var(--text); font-size: 14px; line-height: 1.55; scrollbar-width: thin; scrollbar-color: rgba(125, 132, 138, .45) transparent; }
462
+ .composer textarea::placeholder { color: var(--subtle); }
463
+ .composer-toolbar { display: flex; min-width: 0; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 7px; }
464
+ .conversation-running { display: flex; width: min(780px, calc(100% - 48px)); margin: 2px auto 18px; align-items: center; gap: 8px; color: var(--muted); font-size: 12px; }
465
+ .runtime-activity { display: flex; width: min(780px, calc(100% - 48px)); flex-wrap: wrap; gap: 6px; margin: 8px auto 14px; padding: 0; list-style: none; }
466
+ .runtime-activity-item { display: inline-flex; min-width: 0; max-width: 320px; align-items: center; gap: 6px; padding: 4px 9px; border: 1px solid var(--border); border-radius: 999px; background: var(--page); color: var(--muted); font-size: 11px; }
467
+ .runtime-activity-item strong { color: var(--text); font-weight: 600; }
468
+ .runtime-activity-item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
469
+ .runtime-activity-item small { color: var(--subtle); }
470
+ .conversation-running-dot { width: 8px; height: 8px; flex: 0 0 auto; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 0 rgba(40, 103, 178, .35); animation: conversation-pulse 1.5s ease-out infinite; }
471
+ @keyframes conversation-pulse { 70% { box-shadow: 0 0 0 6px rgba(40, 103, 178, 0); } 100% { box-shadow: 0 0 0 0 rgba(40, 103, 178, 0); } }
472
+ .model-picker-wrap { position: relative; min-width: 0; max-width: 48%; }
473
+ .model-picker { display: inline-flex; min-width: 150px; max-width: 100%; height: 34px; align-items: center; justify-content: space-between; gap: 8px; margin-right: 0; padding: 0 9px 0 10px; border: 1px solid transparent; border-radius: 8px; outline: 0; background: transparent; color: var(--muted); font-size: 13px; text-align: left; }
474
+ .model-picker:not(:disabled) { cursor: pointer; }
475
+ .model-picker:not(:disabled):hover, .model-picker[aria-expanded="true"] { border-color: var(--border); background: #f7f8f9; color: var(--text); }
476
+ .model-picker:disabled { color: var(--subtle); cursor: default; }
477
+ .model-picker-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
478
+ .model-picker-chevron { width: 14px; height: 14px; flex: 0 0 auto; color: var(--subtle); transition: transform 140ms ease; }
479
+ .model-picker[aria-expanded="true"] .model-picker-chevron { transform: rotate(180deg); }
480
+ .model-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 8; width: 240px; max-height: 300px; overflow-y: auto; padding: 5px; border: 1px solid var(--border); border-radius: 10px; background: var(--page); box-shadow: 0 12px 28px rgba(31, 37, 44, .16); scrollbar-width: thin; scrollbar-color: rgba(125, 132, 138, .45) transparent; }
481
+ .model-menu[hidden] { display: none; }
482
+ .model-menu button { display: flex; width: 100%; min-height: 34px; align-items: center; justify-content: space-between; gap: 10px; padding: 0 9px; border: 0; border-radius: 7px; background: transparent; color: var(--text); font-size: 13px; text-align: left; }
483
+ .model-menu button:hover, .model-menu button[aria-selected="true"] { background: #eef1f4; }
484
+ .model-menu-check { visibility: hidden; color: var(--text); font-size: 15px; }
485
+ .model-menu button[aria-selected="true"] .model-menu-check { visibility: visible; }
486
+ .workspace-tree::-webkit-scrollbar, .model-menu::-webkit-scrollbar, .composer textarea::-webkit-scrollbar { width: 6px; height: 6px; }
487
+ .workspace-tree::-webkit-scrollbar-track, .model-menu::-webkit-scrollbar-track, .composer textarea::-webkit-scrollbar-track { background: transparent; border: 0; }
488
+ .workspace-tree::-webkit-scrollbar-thumb, .model-menu::-webkit-scrollbar-thumb, .composer textarea::-webkit-scrollbar-thumb { border-radius: 999px; background: rgba(125, 132, 138, .45); }
489
+ .workspace-picker-row { display: flex; width: min(780px, 100%); min-width: 0; align-items: center; margin: 0 auto; padding-left: 15px; }
490
+ .workspace-picker { position: relative; display: inline-flex; width: fit-content; max-width: 100%; min-width: 0; height: 36px; align-items: center; justify-content: center; gap: 0; padding: 0 10px 0 0; padding-right: 28px; padding-left: 28px; border: 1px solid transparent; border-radius: 18px; background: transparent; color: var(--text); font-size: 13px; line-height: 1; text-align: center; transition: border-color 140ms ease, background 140ms ease, box-shadow 140ms ease; }
491
+ .workspace-picker:hover, .workspace-picker[aria-expanded="true"] { border-color: #d8cfae; background: var(--sidebar-active); box-shadow: 0 3px 10px rgba(86, 76, 48, .08); }
492
+ .workspace-picker span { min-width: 0; overflow: hidden; text-align: center; text-overflow: ellipsis; white-space: nowrap; }
493
+ .workspace-symbol { position: absolute; left: 8px; width: 15px; height: 15px; }
494
+ .context-chevron { position: absolute; right: 8px; width: 13px; height: 13px; }
495
+ .workspace-picker-row { position: relative; }
496
+ .workspace-picker-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 5; width: 225px; padding: 5px; border: 1px solid var(--border); border-radius: 12px; background: var(--page); box-shadow: 0 12px 28px rgba(31, 37, 44, .16); }
497
+ .workspace-picker-menu[hidden] { display: none; }
498
+ .workspace-picker-menu button { display: flex; width: 100%; height: 38px; align-items: center; gap: 8px; padding: 0 10px; border: 0; border-radius: 8px; background: transparent; color: var(--text); font-size: 12px; text-align: left; }
499
+ .workspace-picker-menu button:hover { background: #f1f3f5; }
500
+ .workspace-picker-menu button[aria-selected="true"] { background: #f4f5f6; }
501
+ .workspace-menu-icon { width: 16px; height: 16px; flex: 0 0 auto; color: var(--muted); }
502
+ .workspace-picker-menu .workspace-menu-check { visibility: hidden; margin-left: auto; font-size: 16px; }
503
+ .workspace-picker-menu button[aria-selected="true"] .workspace-menu-check { visibility: visible; }
504
+ .workspace-picker-menu .workspace-menu-add { margin-top: 4px; border-radius: 8px; color: var(--muted); }
505
+ .send-button { display: grid; width: 34px; height: 34px; flex: 0 0 auto; place-items: center; border: 0; border-radius: 50%; background: var(--warm-accent); color: var(--text); }
506
+ .send-button:hover { background: var(--warm-accent-hover); }
507
+ .send-button:disabled { background: var(--warm-accent-disabled); color: var(--subtle); }
508
+ .send-button svg { width: 17px; height: 17px; stroke-width: 2; }
509
+ .composer-hint { display: none; }
510
+ .composer-hint.error { color: var(--error); }
511
+ .sidebar-scrim { display: none; }
512
+
513
+ @media (max-width: 760px) {
514
+ .app-shell, body.sidebar-collapsed .app-shell { grid-template-columns: 1fr; }
515
+ .session-sidebar {
516
+ position: fixed;
517
+ inset: 0 auto 0 0;
518
+ width: min(86vw, 300px);
519
+ transform: translateX(-100%);
520
+ box-shadow: 12px 0 32px rgba(31, 37, 44, .16);
521
+ transition: transform 180ms ease;
522
+ }
523
+ body.sidebar-open .session-sidebar { transform: translateX(0); }
524
+ body.sidebar-collapsed .session-sidebar { align-items: stretch; padding: 8px 10px; }
525
+ body.sidebar-collapsed .brand-word, body.sidebar-collapsed .workspace-heading-label,
526
+ body.sidebar-collapsed .workspace-tree, body.sidebar-collapsed .session-search,
527
+ body.sidebar-collapsed .new-session-button span { display: initial; }
528
+ body.sidebar-collapsed .sidebar-brand { width: auto; justify-content: space-between; padding: 0 4px 0 6px; }
529
+ body.sidebar-collapsed .sidebar-brand .collapse-button { position: static; }
530
+ body.sidebar-collapsed .sidebar-brand .collapse-button .icon { display: block; }
531
+ body.sidebar-collapsed .new-session-button { width: calc(100% - 4px); margin: 8px 2px 10px; }
532
+ body.sidebar-collapsed .workspace-heading { width: auto; justify-content: space-between; padding: 0 4px 0 8px; }
533
+ .sidebar-scrim { position: fixed; inset: 0; z-index: 10; border: 0; background: rgba(20, 26, 32, .28); }
534
+ body.sidebar-open .sidebar-scrim { display: block; }
535
+ .conversation-shell { grid-template-rows: minmax(0, 1fr) auto; }
536
+ .conversation-shell.landing { grid-template-rows: minmax(0, 1fr); }
537
+ .mobile-sidebar-button { display: grid; }
538
+ .connection-state { font-size: 0; }
539
+ .message-row, .message-details { width: calc(100% - 28px); }
540
+ .message-row.assistant { width: calc(100% - 24px); }
541
+ .conversation-running { width: calc(100% - 24px); }
542
+ .composer-dock { padding: 10px 12px 12px; }
543
+ .landing .composer-dock { top: calc(50% - 74px); padding: 0 12px; }
544
+ .landing-welcome { transform: translateY(-96px); }
545
+ .composer { min-height: 104px; }
546
+ .composer-hint { display: none; }
547
+ }
548
+
549
+ @media (prefers-reduced-motion: reduce) {
550
+ .app-shell, .session-sidebar, .workspace-chevron { transition: none; }
551
+ .conversation { scroll-behavior: auto; }
552
+ }
553
+
554
+ /* The conversation view should start with the actual messages. On narrow
555
+ screens keep only the sidebar trigger as an overlay so navigation remains
556
+ available without restoring the session header. */
557
+ .workbench-header {
558
+ display: none;
559
+ }
560
+
561
+ @media (max-width: 760px) {
562
+ .workbench-header {
563
+ position: absolute;
564
+ inset: 0;
565
+ z-index: 5;
566
+ display: block;
567
+ padding: 0;
568
+ border: 0;
569
+ background: transparent;
570
+ pointer-events: none;
571
+ }
572
+ .workbench-header .session-header,
573
+ .workbench-header .connection-state {
574
+ display: none;
575
+ }
576
+ .workbench-header .mobile-sidebar-button {
577
+ position: fixed;
578
+ top: 12px;
579
+ left: 12px;
580
+ display: grid;
581
+ pointer-events: auto;
582
+ }
583
+ }
584
+
585
+ /* Keep the reference grid visible around the landing composer. */
586
+ .conversation-shell.landing .composer-dock {
587
+ background: transparent;
588
+ }
589
+
590
+ /* An existing session already belongs to its workspace. */
591
+ .conversation-shell:not(.landing) .workspace-picker-row {
592
+ display: none;
593
+ }
594
+
595
+ .conversation-shell:not(.landing) .model-menu {
596
+ top: auto;
597
+ bottom: calc(100% + 6px);
598
+ }
@@ -0,0 +1,34 @@
1
+ import { defineConfig } from "vite";
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ const backendOrigin =
5
+ process.env.OPENPI_WEB_BACKEND?.replace(/\/$/u, "") ||
6
+ "http://127.0.0.1:57107";
7
+
8
+ function backendProxy() {
9
+ return {
10
+ target: backendOrigin,
11
+ changeOrigin: true,
12
+ configure(proxy) {
13
+ proxy.on("proxyReq", (request) => {
14
+ // WebHost validates Origin as well as Host. The browser Origin is the
15
+ // Vite dev origin, so normalize it to the loopback backend target.
16
+ request.setHeader("origin", backendOrigin);
17
+ });
18
+ },
19
+ };
20
+ }
21
+
22
+ export default defineConfig({
23
+ root: fileURLToPath(new URL("./ui/", import.meta.url)),
24
+ server: {
25
+ host: "127.0.0.1",
26
+ port: Number(process.env.OPENPI_WEB_UI_PORT || 5173),
27
+ strictPort: true,
28
+ proxy: {
29
+ "/api": backendProxy(),
30
+ "/events": backendProxy(),
31
+ "/marked.js": backendProxy(),
32
+ },
33
+ },
34
+ });