@vibesdotdev/code-sessions 0.0.0-stage → 1.0.0-rc.1

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 (217) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +165 -2
  3. package/dist/agent/coordinator.agent.descriptor.d.ts +69 -0
  4. package/dist/agent/coordinator.agent.descriptor.d.ts.map +1 -0
  5. package/dist/agent/coordinator.agent.descriptor.js +60 -0
  6. package/dist/agent/coordinator.agent.descriptor.js.map +1 -0
  7. package/dist/agent/coordinator.prompt.descriptor.d.ts +135 -0
  8. package/dist/agent/coordinator.prompt.descriptor.d.ts.map +1 -0
  9. package/dist/agent/coordinator.prompt.descriptor.js +29 -0
  10. package/dist/agent/coordinator.prompt.descriptor.js.map +1 -0
  11. package/dist/agent/coordinator.system-prompt.d.ts +3 -0
  12. package/dist/agent/coordinator.system-prompt.d.ts.map +1 -0
  13. package/dist/agent/coordinator.system-prompt.js +18 -0
  14. package/dist/agent/coordinator.system-prompt.js.map +1 -0
  15. package/dist/agent/developer.agent.descriptor.d.ts +74 -0
  16. package/dist/agent/developer.agent.descriptor.d.ts.map +1 -0
  17. package/dist/agent/developer.agent.descriptor.js +138 -0
  18. package/dist/agent/developer.agent.descriptor.js.map +1 -0
  19. package/dist/agent/developer.prompt.descriptor.d.ts +137 -0
  20. package/dist/agent/developer.prompt.descriptor.d.ts.map +1 -0
  21. package/dist/agent/developer.prompt.descriptor.js +29 -0
  22. package/dist/agent/developer.prompt.descriptor.js.map +1 -0
  23. package/dist/agent/reviewer.agent.descriptor.d.ts +69 -0
  24. package/dist/agent/reviewer.agent.descriptor.d.ts.map +1 -0
  25. package/dist/agent/reviewer.agent.descriptor.js +60 -0
  26. package/dist/agent/reviewer.agent.descriptor.js.map +1 -0
  27. package/dist/agent/reviewer.prompt.descriptor.d.ts +135 -0
  28. package/dist/agent/reviewer.prompt.descriptor.d.ts.map +1 -0
  29. package/dist/agent/reviewer.prompt.descriptor.js +29 -0
  30. package/dist/agent/reviewer.prompt.descriptor.js.map +1 -0
  31. package/dist/agent/reviewer.system-prompt.d.ts +3 -0
  32. package/dist/agent/reviewer.system-prompt.d.ts.map +1 -0
  33. package/dist/agent/reviewer.system-prompt.js +17 -0
  34. package/dist/agent/reviewer.system-prompt.js.map +1 -0
  35. package/dist/agent/system-prompt.d.ts +12 -0
  36. package/dist/agent/system-prompt.d.ts.map +1 -0
  37. package/dist/agent/system-prompt.js +28 -0
  38. package/dist/agent/system-prompt.js.map +1 -0
  39. package/dist/api/code-sessions.api-client.descriptor.d.ts +25 -0
  40. package/dist/api/code-sessions.api-client.descriptor.d.ts.map +1 -0
  41. package/dist/api/code-sessions.api-client.descriptor.js +26 -0
  42. package/dist/api/code-sessions.api-client.descriptor.js.map +1 -0
  43. package/dist/api/code-sessions.api-client.impl.d.ts +156 -0
  44. package/dist/api/code-sessions.api-client.impl.d.ts.map +1 -0
  45. package/dist/api/code-sessions.api-client.impl.js +852 -0
  46. package/dist/api/code-sessions.api-client.impl.js.map +1 -0
  47. package/dist/api/code-sessions.api-client.schemas.d.ts +2 -0
  48. package/dist/api/code-sessions.api-client.schemas.d.ts.map +1 -0
  49. package/dist/api/code-sessions.api-client.schemas.js +2 -0
  50. package/dist/api/code-sessions.api-client.schemas.js.map +1 -0
  51. package/dist/api/code-sessions.api-client.types.d.ts +252 -0
  52. package/dist/api/code-sessions.api-client.types.d.ts.map +1 -0
  53. package/dist/api/code-sessions.api-client.types.js +77 -0
  54. package/dist/api/code-sessions.api-client.types.js.map +1 -0
  55. package/dist/api/index.d.ts +13 -0
  56. package/dist/api/index.d.ts.map +1 -0
  57. package/dist/api/index.js +20 -0
  58. package/dist/api/index.js.map +1 -0
  59. package/dist/capability.d.ts +9 -0
  60. package/dist/capability.d.ts.map +1 -0
  61. package/dist/capability.js +9 -0
  62. package/dist/capability.js.map +1 -0
  63. package/dist/catalog/index.d.ts +2 -0
  64. package/dist/catalog/index.d.ts.map +1 -0
  65. package/dist/catalog/index.js +2 -0
  66. package/dist/catalog/index.js.map +1 -0
  67. package/dist/catalog/session-catalog.d.ts +131 -0
  68. package/dist/catalog/session-catalog.d.ts.map +1 -0
  69. package/dist/catalog/session-catalog.impl.consumer.worker.d.ts +113 -0
  70. package/dist/catalog/session-catalog.impl.consumer.worker.d.ts.map +1 -0
  71. package/dist/catalog/session-catalog.impl.consumer.worker.js +32 -0
  72. package/dist/catalog/session-catalog.impl.consumer.worker.js.map +1 -0
  73. package/dist/catalog/session-catalog.js +126 -0
  74. package/dist/catalog/session-catalog.js.map +1 -0
  75. package/dist/clock/clock.client.d.ts +18 -0
  76. package/dist/clock/clock.client.d.ts.map +1 -0
  77. package/dist/clock/clock.client.js +43 -0
  78. package/dist/clock/clock.client.js.map +1 -0
  79. package/dist/code-sessions.consumer.plugin.d.ts +4 -0
  80. package/dist/code-sessions.consumer.plugin.d.ts.map +1 -0
  81. package/dist/code-sessions.consumer.plugin.js +16 -0
  82. package/dist/code-sessions.consumer.plugin.js.map +1 -0
  83. package/dist/code-sessions.plugin.d.ts +15 -0
  84. package/dist/code-sessions.plugin.d.ts.map +1 -0
  85. package/dist/code-sessions.plugin.js +65 -0
  86. package/dist/code-sessions.plugin.js.map +1 -0
  87. package/dist/config/index.d.ts +9 -0
  88. package/dist/config/index.d.ts.map +1 -0
  89. package/dist/config/index.js +9 -0
  90. package/dist/config/index.js.map +1 -0
  91. package/dist/config/preparation.d.ts +2 -0
  92. package/dist/config/preparation.d.ts.map +1 -0
  93. package/dist/config/preparation.js +2 -0
  94. package/dist/config/preparation.js.map +1 -0
  95. package/dist/docs/code-sessions.docs.descriptor.d.ts +4 -0
  96. package/dist/docs/code-sessions.docs.descriptor.d.ts.map +1 -0
  97. package/dist/docs/code-sessions.docs.descriptor.js +241 -0
  98. package/dist/docs/code-sessions.docs.descriptor.js.map +1 -0
  99. package/dist/index.d.ts +26 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +28 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/pull-request-plan.d.ts +68 -0
  104. package/dist/pull-request-plan.d.ts.map +1 -0
  105. package/dist/pull-request-plan.js +94 -0
  106. package/dist/pull-request-plan.js.map +1 -0
  107. package/dist/schemas/preparation.d.ts +51 -0
  108. package/dist/schemas/preparation.d.ts.map +1 -0
  109. package/dist/schemas/preparation.js +20 -0
  110. package/dist/schemas/preparation.js.map +1 -0
  111. package/dist/schemas/requests.d.ts +411 -0
  112. package/dist/schemas/requests.d.ts.map +1 -0
  113. package/dist/schemas/requests.js +87 -0
  114. package/dist/schemas/requests.js.map +1 -0
  115. package/dist/schemas/responses.d.ts +275 -0
  116. package/dist/schemas/responses.d.ts.map +1 -0
  117. package/dist/schemas/responses.js +90 -0
  118. package/dist/schemas/responses.js.map +1 -0
  119. package/dist/svelte/actions.d.ts +70 -0
  120. package/dist/svelte/actions.d.ts.map +1 -0
  121. package/dist/svelte/actions.js +280 -0
  122. package/dist/svelte/clock.svelte.d.ts +6 -0
  123. package/dist/svelte/clock.svelte.d.ts.map +1 -0
  124. package/dist/svelte/clock.svelte.js +33 -0
  125. package/dist/svelte/components/CreateSessionDialog.svelte +470 -0
  126. package/dist/svelte/components/CreateSessionDialog.svelte.d.ts +57 -0
  127. package/dist/svelte/components/CreateSessionDialog.svelte.d.ts.map +1 -0
  128. package/dist/svelte/components/SessionCard.svelte +216 -0
  129. package/dist/svelte/components/SessionCard.svelte.d.ts +27 -0
  130. package/dist/svelte/components/SessionCard.svelte.d.ts.map +1 -0
  131. package/dist/svelte/components/SessionList.svelte +86 -0
  132. package/dist/svelte/components/SessionList.svelte.d.ts +23 -0
  133. package/dist/svelte/components/SessionList.svelte.d.ts.map +1 -0
  134. package/dist/svelte/components/SessionSeed.svelte +60 -0
  135. package/dist/svelte/components/SessionSeed.svelte.d.ts +9 -0
  136. package/dist/svelte/components/SessionSeed.svelte.d.ts.map +1 -0
  137. package/dist/svelte/components/TranscriptView.svelte +360 -0
  138. package/dist/svelte/components/TranscriptView.svelte.d.ts +26 -0
  139. package/dist/svelte/components/TranscriptView.svelte.d.ts.map +1 -0
  140. package/dist/svelte/create-session-form.d.ts +84 -0
  141. package/dist/svelte/create-session-form.d.ts.map +1 -0
  142. package/dist/svelte/create-session-form.js +107 -0
  143. package/dist/svelte/index.d.ts +20 -0
  144. package/dist/svelte/index.d.ts.map +1 -0
  145. package/dist/svelte/index.js +18 -0
  146. package/dist/svelte/live-session.d.ts +156 -0
  147. package/dist/svelte/live-session.d.ts.map +1 -0
  148. package/dist/svelte/live-session.js +152 -0
  149. package/dist/svelte/loaders.d.ts +74 -0
  150. package/dist/svelte/loaders.d.ts.map +1 -0
  151. package/dist/svelte/loaders.js +124 -0
  152. package/dist/svelte/outcome.d.ts +46 -0
  153. package/dist/svelte/outcome.d.ts.map +1 -0
  154. package/dist/svelte/outcome.js +49 -0
  155. package/dist/svelte/relative-time.d.ts +32 -0
  156. package/dist/svelte/relative-time.d.ts.map +1 -0
  157. package/dist/svelte/relative-time.js +51 -0
  158. package/dist/svelte/session-list-item.d.ts +23 -0
  159. package/dist/svelte/session-list-item.d.ts.map +1 -0
  160. package/dist/svelte/session-list-item.js +1 -0
  161. package/dist/svelte/styles.css +8 -0
  162. package/dist/svelte/transcript-input.d.ts +45 -0
  163. package/dist/svelte/transcript-input.d.ts.map +1 -0
  164. package/dist/svelte/transcript-input.js +188 -0
  165. package/dist/tui/code-sessions-tui.plugin.d.ts +10 -0
  166. package/dist/tui/code-sessions-tui.plugin.d.ts.map +1 -0
  167. package/dist/tui/code-sessions-tui.plugin.js +29 -0
  168. package/dist/tui/code-sessions-tui.plugin.js.map +1 -0
  169. package/dist/tui/code-sessions.tui-app.descriptor.d.ts +7 -0
  170. package/dist/tui/code-sessions.tui-app.descriptor.d.ts.map +1 -0
  171. package/dist/tui/code-sessions.tui-app.descriptor.js +30 -0
  172. package/dist/tui/code-sessions.tui-app.descriptor.js.map +1 -0
  173. package/dist/tui/code-sessions.tui-app.impl.consumer.d.ts +14 -0
  174. package/dist/tui/code-sessions.tui-app.impl.consumer.d.ts.map +1 -0
  175. package/dist/tui/code-sessions.tui-app.impl.consumer.js +453 -0
  176. package/dist/tui/code-sessions.tui-app.impl.consumer.js.map +1 -0
  177. package/dist/tui/code-sessions.tui-client.d.ts +22 -0
  178. package/dist/tui/code-sessions.tui-client.d.ts.map +1 -0
  179. package/dist/tui/code-sessions.tui-client.js +95 -0
  180. package/dist/tui/code-sessions.tui-client.js.map +1 -0
  181. package/dist/tui/code-sessions.tui-open.descriptor.d.ts +7 -0
  182. package/dist/tui/code-sessions.tui-open.descriptor.d.ts.map +1 -0
  183. package/dist/tui/code-sessions.tui-open.descriptor.js +16 -0
  184. package/dist/tui/code-sessions.tui-open.descriptor.js.map +1 -0
  185. package/dist/tui/code-sessions.tui-open.impl.consumer.d.ts +7 -0
  186. package/dist/tui/code-sessions.tui-open.impl.consumer.d.ts.map +1 -0
  187. package/dist/tui/code-sessions.tui-open.impl.consumer.js +11 -0
  188. package/dist/tui/code-sessions.tui-open.impl.consumer.js.map +1 -0
  189. package/dist/tui/code-sessions.tui-state.descriptor.d.ts +16 -0
  190. package/dist/tui/code-sessions.tui-state.descriptor.d.ts.map +1 -0
  191. package/dist/tui/code-sessions.tui-state.descriptor.js +50 -0
  192. package/dist/tui/code-sessions.tui-state.descriptor.js.map +1 -0
  193. package/dist/tui/code-sessions.tui-state.impl.consumer.d.ts +30 -0
  194. package/dist/tui/code-sessions.tui-state.impl.consumer.d.ts.map +1 -0
  195. package/dist/tui/code-sessions.tui-state.impl.consumer.js +533 -0
  196. package/dist/tui/code-sessions.tui-state.impl.consumer.js.map +1 -0
  197. package/dist/tui/code-sessions.tui-state.key.d.ts +322 -0
  198. package/dist/tui/code-sessions.tui-state.key.d.ts.map +1 -0
  199. package/dist/tui/code-sessions.tui-state.key.js +104 -0
  200. package/dist/tui/code-sessions.tui-state.key.js.map +1 -0
  201. package/dist/tui/index.d.ts +9 -0
  202. package/dist/tui/index.d.ts.map +1 -0
  203. package/dist/tui/index.js +8 -0
  204. package/dist/tui/index.js.map +1 -0
  205. package/dist/workspace/index.d.ts +2 -0
  206. package/dist/workspace/index.d.ts.map +1 -0
  207. package/dist/workspace/index.js +2 -0
  208. package/dist/workspace/index.js.map +1 -0
  209. package/dist/workspace/workspace-binding.d.ts +74 -0
  210. package/dist/workspace/workspace-binding.d.ts.map +1 -0
  211. package/dist/workspace/workspace-binding.impl.consumer.worker.d.ts +17 -0
  212. package/dist/workspace/workspace-binding.impl.consumer.worker.d.ts.map +1 -0
  213. package/dist/workspace/workspace-binding.impl.consumer.worker.js +134 -0
  214. package/dist/workspace/workspace-binding.impl.consumer.worker.js.map +1 -0
  215. package/dist/workspace/workspace-binding.js +72 -0
  216. package/dist/workspace/workspace-binding.js.map +1 -0
  217. package/package.json +238 -4
@@ -0,0 +1,470 @@
1
+ <script lang="ts" module>
2
+ import type { CodeSessionCreateInput, CodeSessionSeed } from '@vibesdotdev/code-sessions';
3
+
4
+ export type HostTargetOption = 'local' | 'fleet' | 'cloud';
5
+
6
+ export interface FleetHostOption {
7
+ hostId: string;
8
+ name: string;
9
+ online: boolean;
10
+ }
11
+
12
+ export interface CreateSessionDialogProps {
13
+ open?: boolean;
14
+ /**
15
+ * Task this session is being started for, when the user got here from one.
16
+ *
17
+ * Reuses the platform's existing `CodeSessionSeed` contract rather than a
18
+ * web-only shape: the work-items TUI already creates sessions with
19
+ * `{ source: 'work-item', title, ref }`, the platform threads it into the
20
+ * sandbox as `VIBES_SEED_*`, and the session record persists it verbatim.
21
+ * A second convention here would mean a session started from the web could
22
+ * not be traced back the way one started from the TUI can.
23
+ */
24
+ seed?: CodeSessionSeed;
25
+ /** Called when the user submits the form; parent handles the actual create call. */
26
+ oncreate?: (input: CodeSessionCreateInput) => void | Promise<void>;
27
+ /** Called when the dialog is closed. */
28
+ onclose?: () => void;
29
+ /** Repository options to populate the picker. */
30
+ repositories?: Array<{
31
+ id: string;
32
+ name: string;
33
+ defaultBranch?: string | null;
34
+ description?: string | null;
35
+ group?: string;
36
+ }>;
37
+ /** Whether the create action is in progress. */
38
+ creating?: boolean;
39
+ /** Configuration-only embeddings may continue to a separate dispatch step. */
40
+ submitLabel?: string;
41
+ /** Error from a failed create attempt. */
42
+ createError?: string | null;
43
+ /** Optional URL for creating a repository in the owning forge surface. */
44
+ repositoryCreateHref?: string;
45
+ repositoryCreateLabel?: string;
46
+ /** Hide scratch mode when the caller requires a repository-backed outcome. */
47
+ allowEphemeral?: boolean;
48
+ /** Let the user explicitly request repository provisioning at the caller's submit boundary. */
49
+ allowRepositoryCreation?: boolean;
50
+ /** Execution targets the embedding surface can actually provision. */
51
+ allowedHostTargets?: HostTargetOption[];
52
+ /**
53
+ * Fleet machines available for code execution (labeled sandbox:code-execution).
54
+ * When empty the "Fleet host" radio is disabled and an onboarding hint is shown.
55
+ */
56
+ fleetHosts?: FleetHostOption[];
57
+ }
58
+ </script>
59
+
60
+ <script lang="ts">
61
+ import * as Dialog from '@vibesdotdev/kit/components/ui/dialog';
62
+ import { Button } from '@vibesdotdev/kit/components/ui/button';
63
+ import { Input } from '@vibesdotdev/kit/components/ui/input';
64
+ import { Label } from '@vibesdotdev/kit/components/ui/label';
65
+ import { Select, SelectContent, SelectItem, SelectTrigger } from '@vibesdotdev/kit/components';
66
+ import { Icon } from '@vibesdotdev/kit/components';
67
+ import { SearchSelect } from '@vibesdotdev/kit/svelte';
68
+ import {
69
+ buildCreateSessionInput,
70
+ canSubmitCreateSession,
71
+ describeCreateSessionBlocker,
72
+ isSelectableFleetHost,
73
+ resolveDefaultHostTarget,
74
+ resolveDefaultFleetHostId,
75
+ resolveSelectableHostTargets
76
+ } from '../create-session-form.js';
77
+
78
+ let {
79
+ open = false,
80
+ seed = undefined,
81
+ oncreate,
82
+ onclose,
83
+ repositories = [],
84
+ creating = false,
85
+ submitLabel = 'Start session',
86
+ createError = null,
87
+ repositoryCreateHref = undefined,
88
+ repositoryCreateLabel = 'Create git.vibes.dev repository',
89
+ allowEphemeral = true,
90
+ allowRepositoryCreation = false,
91
+ allowedHostTargets = ['cloud', 'local', 'fleet'],
92
+ fleetHosts = []
93
+ }: CreateSessionDialogProps = $props();
94
+
95
+ // --- Form state ---
96
+ let selectedHostTarget: HostTargetOption = $state('cloud');
97
+ let selectedRepositoryId: string = $state('');
98
+ let checkoutRef: string = $state('');
99
+ let ephemeral: boolean = $state(false);
100
+ let createRepositoryIfMissing: boolean = $state(false);
101
+ const defaultFleetHostId = $derived(resolveDefaultFleetHostId(fleetHosts));
102
+ const hasOnlineFleetHosts = $derived(defaultFleetHostId.length > 0);
103
+ let selectedFleetHostId: string = $state('');
104
+
105
+ const selectedRepo = $derived(repositories.find((r) => r.id === selectedRepositoryId));
106
+ const selectedFleetHost = $derived(fleetHosts.find((h) => h.hostId === selectedFleetHostId));
107
+ const selectableHostTargets = $derived(resolveSelectableHostTargets(allowedHostTargets, fleetHosts));
108
+ const defaultHostTarget = $derived(resolveDefaultHostTarget(selectableHostTargets));
109
+ const canStartSession = $derived(
110
+ canSubmitCreateSession({
111
+ ephemeral,
112
+ selectedRepositoryId,
113
+ createRepositoryIfMissing,
114
+ selectedHostTarget,
115
+ allowedHostTargets: selectableHostTargets,
116
+ selectedFleetHostId
117
+ })
118
+ );
119
+ // Why the submit button is disabled. Never null while `canStartSession` is
120
+ // false — the two are pinned to agree by test.
121
+ const submitBlocker = $derived(
122
+ describeCreateSessionBlocker({
123
+ ephemeral,
124
+ selectedRepositoryId,
125
+ createRepositoryIfMissing,
126
+ selectedHostTarget,
127
+ allowedHostTargets: selectableHostTargets,
128
+ selectedFleetHostId,
129
+ repositoryCount: repositories.length,
130
+ repositoryCreationAvailable: allowRepositoryCreation
131
+ })
132
+ );
133
+
134
+ function handleOpenChange(value: boolean) {
135
+ if (!value) onclose?.();
136
+ }
137
+
138
+ function resetForm() {
139
+ if (defaultHostTarget) selectedHostTarget = defaultHostTarget;
140
+ selectedRepositoryId = '';
141
+ checkoutRef = '';
142
+ ephemeral = false;
143
+ createRepositoryIfMissing = false;
144
+ selectedFleetHostId = defaultFleetHostId;
145
+ }
146
+
147
+ $effect(() => {
148
+ if (!allowEphemeral && ephemeral) ephemeral = false;
149
+ });
150
+
151
+ $effect(() => {
152
+ if (!allowRepositoryCreation || repositories.length > 0) createRepositoryIfMissing = false;
153
+ });
154
+
155
+ $effect(() => {
156
+ if (!isSelectableFleetHost(fleetHosts, selectedFleetHostId)) {
157
+ selectedFleetHostId = defaultFleetHostId;
158
+ }
159
+ });
160
+
161
+ $effect(() => {
162
+ if (!allowedHostTargets.includes(selectedHostTarget)) {
163
+ if (defaultHostTarget) selectedHostTarget = defaultHostTarget;
164
+ }
165
+ });
166
+
167
+ $effect(() => {
168
+ if (selectedHostTarget === 'fleet' && !hasOnlineFleetHosts) {
169
+ if (defaultHostTarget) selectedHostTarget = defaultHostTarget;
170
+ }
171
+ });
172
+
173
+ async function handleSubmit(e: SubmitEvent) {
174
+ e.preventDefault();
175
+ if (!canStartSession) return;
176
+
177
+ let hostTarget: CodeSessionCreateInput['hostTarget'];
178
+ if (selectedHostTarget === 'cloud') {
179
+ hostTarget = { kind: 'cloud' };
180
+ } else if (selectedHostTarget === 'local') {
181
+ hostTarget = { kind: 'host', hostId: 'local' };
182
+ } else {
183
+ // fleet — use the real hostId the user selected
184
+ hostTarget = { kind: 'host', hostId: selectedFleetHostId };
185
+ }
186
+
187
+ // Built by the pure helper so the contract — including that the seed
188
+ // survives the trip — is pinned by a test rather than by this component.
189
+ const input: CodeSessionCreateInput = buildCreateSessionInput({
190
+ hostTarget,
191
+ ephemeral,
192
+ selectedRepositoryId,
193
+ createRepositoryIfMissing,
194
+ checkoutRef,
195
+ seed
196
+ });
197
+
198
+ await oncreate?.(input);
199
+ if (!createError) {
200
+ resetForm();
201
+ }
202
+ }
203
+
204
+ const hostOptions: Array<{ value: HostTargetOption; label: string; description: string }> = [
205
+ {
206
+ value: 'cloud',
207
+ label: 'Cloud sandbox',
208
+ description: 'Runs in a managed cloud environment. Snapshot execution — re-runnable commands.'
209
+ },
210
+ {
211
+ value: 'local',
212
+ label: 'Local machine',
213
+ description: 'Runs on your local machine. Full interactive terminal.'
214
+ },
215
+ {
216
+ value: 'fleet',
217
+ label: 'Connected machine',
218
+ description: 'Runs on a paired fleet machine. Full interactive terminal.'
219
+ }
220
+ ];
221
+ const visibleHostOptions = $derived(
222
+ hostOptions.filter((option) => allowedHostTargets.includes(option.value))
223
+ );
224
+ </script>
225
+
226
+ <!--
227
+ CreateSessionDialog — host-target picker with repository + ref fields.
228
+ No internal jargon. Destructive actions confirm with consequences stated.
229
+ -->
230
+ <Dialog.Root {open} onOpenChange={handleOpenChange}>
231
+ <Dialog.Content class="sm:max-w-lg">
232
+ <Dialog.Header>
233
+ <Dialog.Title>New code session</Dialog.Title>
234
+ <Dialog.Description>
235
+ Choose where to run your session and which repository to clone.
236
+ </Dialog.Description>
237
+ </Dialog.Header>
238
+
239
+ {#if seed}
240
+ <!--
241
+ Shown, not hidden: the person needs to see which task this session will
242
+ be started for before they commit a sandbox to it.
243
+ -->
244
+ <div class="rounded-lg border bg-muted/40 p-3">
245
+ <p class="text-xs text-muted-foreground">Working on</p>
246
+ <p class="mt-1 font-medium leading-snug">{seed.title}</p>
247
+ {#if seed.body}
248
+ <p class="mt-1 text-sm text-muted-foreground line-clamp-3">{seed.body}</p>
249
+ {/if}
250
+ </div>
251
+ {/if}
252
+
253
+ <form onsubmit={handleSubmit} class="space-y-5 pt-2">
254
+ <!-- Host target picker -->
255
+ <!--
256
+ A real radio group. These three radios previously sat in a bare
257
+ <div> under a <Label> that labelled nothing, so assistive tech
258
+ announced three orphaned controls with no group name.
259
+ -->
260
+ <div class="space-y-2" role="radiogroup" aria-labelledby="host-target-label">
261
+ <Label id="host-target-label">Where to run</Label>
262
+ <div class="grid gap-2">
263
+ {#each visibleHostOptions as opt}
264
+ {@const isFleet = opt.value === 'fleet'}
265
+ {@const fleetDisabled = isFleet && !hasOnlineFleetHosts}
266
+ <label
267
+ class={[
268
+ 'flex items-start gap-3 rounded-lg border p-3 transition-colors',
269
+ fleetDisabled
270
+ ? 'cursor-not-allowed opacity-60'
271
+ : 'cursor-pointer hover:bg-muted/40',
272
+ selectedHostTarget === opt.value ? 'border-primary bg-primary/5' : ''
273
+ ].join(' ')}
274
+ >
275
+ <input
276
+ type="radio"
277
+ name="hostTarget"
278
+ value={opt.value}
279
+ bind:group={selectedHostTarget}
280
+ disabled={fleetDisabled}
281
+ class="mt-0.5 accent-primary"
282
+ />
283
+ <div class="flex-1 min-w-0">
284
+ <p class="font-medium leading-none">{opt.label}</p>
285
+ <p class="mt-1 text-sm text-muted-foreground">{opt.description}</p>
286
+ {#if isFleet && fleetHosts.length === 0}
287
+ <p class="mt-2 text-xs text-muted-foreground">
288
+ No machines are set up for code execution. Add one from Machines, then run:
289
+ <code class="font-mono">vibes fleet daemon --labels sandbox:code-execution</code
290
+ >
291
+ </p>
292
+ {:else if isFleet && !hasOnlineFleetHosts}
293
+ <p class="mt-2 text-xs text-muted-foreground">
294
+ No code-execution machines are online. Start a paired machine, then run:
295
+ <code class="font-mono">vibes fleet daemon --labels sandbox:code-execution</code
296
+ >
297
+ </p>
298
+ {/if}
299
+ {#if isFleet && selectedHostTarget === 'fleet' && fleetHosts.length > 0}
300
+ <!-- Machine picker shown inline when fleet is selected -->
301
+ <div class="mt-3">
302
+ <Select type="single" bind:value={selectedFleetHostId}>
303
+ <SelectTrigger class="w-full">
304
+ <!-- Same defect as the repository picker: `SelectValue` only
305
+ knows the hostId, so the trigger kept reading as empty
306
+ after a real choice. Fixed here too rather than left for
307
+ the next person to rediscover on a machine that has
308
+ fleet hosts to select. -->
309
+ {selectedFleetHost?.name ?? 'Choose a machine…'}
310
+ </SelectTrigger>
311
+ <SelectContent>
312
+ {#each fleetHosts as host}
313
+ <SelectItem value={host.hostId} disabled={!host.online}>
314
+ {host.name}
315
+ {#if !host.online}
316
+ <span class="ml-1 text-xs text-muted-foreground">(Offline)</span>
317
+ {/if}
318
+ </SelectItem>
319
+ {/each}
320
+ </SelectContent>
321
+ </Select>
322
+ </div>
323
+ {/if}
324
+ </div>
325
+ </label>
326
+ {/each}
327
+ </div>
328
+ </div>
329
+
330
+ {#if allowEphemeral}
331
+ <!-- Ephemeral toggle -->
332
+ <div class="flex items-center gap-3">
333
+ <input
334
+ id="ephemeral-toggle"
335
+ type="checkbox"
336
+ bind:checked={ephemeral}
337
+ class="accent-primary"
338
+ />
339
+ <Label for="ephemeral-toggle" class="cursor-pointer">
340
+ Ephemeral workspace
341
+ <span class="ml-1 text-xs text-muted-foreground">(no repository — scratch space)</span>
342
+ </Label>
343
+ </div>
344
+ {/if}
345
+
346
+ {#if !ephemeral}
347
+ <!-- Repository selector -->
348
+ <div class="space-y-2">
349
+ <Label for="repo-select">Repository</Label>
350
+ {#if repositories.length > 0}
351
+ <SearchSelect
352
+ id="repo-select"
353
+ label="Repository"
354
+ options={repositories.map((repo) => ({
355
+ value: repo.id,
356
+ label: repo.name,
357
+ description: repo.description,
358
+ group: repo.group
359
+ }))}
360
+ bind:value={selectedRepositoryId}
361
+ placeholder="Select a repository…"
362
+ searchPlaceholder="Search repositories…"
363
+ emptyMessage="No repositories found"
364
+ disabled={creating}
365
+ />
366
+ {:else}
367
+ <div class="rounded-md border border-dashed border-border px-3 py-3 text-sm">
368
+ <p class="font-medium text-foreground">No repositories available</p>
369
+ {#if allowRepositoryCreation}
370
+ <label class="mt-2 flex cursor-pointer items-start gap-2">
371
+ <input
372
+ type="checkbox"
373
+ bind:checked={createRepositoryIfMissing}
374
+ class="mt-0.5 accent-primary"
375
+ />
376
+ <span>
377
+ Create a repository when this assignment starts
378
+ <span class="mt-1 block text-xs text-muted-foreground">
379
+ Your project visibility and repository plan still apply.
380
+ </span>
381
+ </span>
382
+ </label>
383
+ {:else}
384
+ <p class="mt-1 text-xs text-muted-foreground">
385
+ Use an ephemeral workspace for scratch work, or create a git.vibes.dev repository
386
+ before starting a repository-backed session.
387
+ </p>
388
+ {/if}
389
+ {#if repositoryCreateHref}
390
+ <Button
391
+ href={repositoryCreateHref}
392
+ size="sm"
393
+ variant="outline"
394
+ class="mt-3 h-8 gap-1 text-xs"
395
+ >
396
+ <Icon icon="lucide:external-link" class="size-3.5" aria-hidden="true" />
397
+ {repositoryCreateLabel}
398
+ </Button>
399
+ {/if}
400
+ </div>
401
+ {/if}
402
+ </div>
403
+
404
+ <!-- Branch/ref applies only to a repository that already exists. -->
405
+ {#if repositories.length > 0}
406
+ <div class="space-y-2">
407
+ <Label for="ref-input">
408
+ Branch or commit
409
+ <span class="ml-1 text-xs text-muted-foreground">(optional)</span>
410
+ </Label>
411
+ <Input
412
+ id="ref-input"
413
+ type="text"
414
+ placeholder={selectedRepo?.defaultBranch ?? 'main'}
415
+ bind:value={checkoutRef}
416
+ />
417
+ </div>
418
+ {/if}
419
+ {/if}
420
+
421
+ {#if createError}
422
+ <!-- role="alert": a failed create used to render silently to assistive tech. -->
423
+ <div
424
+ role="alert"
425
+ class="flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive"
426
+ >
427
+ <Icon icon="lucide:circle-x" class="mt-0.5 size-4 shrink-0" aria-hidden="true" />
428
+ {createError}
429
+ </div>
430
+ {/if}
431
+
432
+ {#if submitBlocker && !creating}
433
+ <!--
434
+ Say WHY "Start session" is unavailable. A disabled button with no
435
+ explanation is a dead end, and the worst case — an empty
436
+ repository list — is exactly when the user most needs to be told
437
+ where to go instead.
438
+ -->
439
+ <p
440
+ id="create-session-blocker"
441
+ class="flex items-start gap-2 text-sm text-muted-foreground"
442
+ role="status"
443
+ aria-live="polite"
444
+ >
445
+ <Icon icon="lucide:info" class="mt-0.5 size-4 shrink-0" aria-hidden="true" />
446
+ {submitBlocker.message}
447
+ </p>
448
+ {/if}
449
+
450
+ <Dialog.Footer>
451
+ <Button type="button" variant="outline" onclick={onclose} disabled={creating}>
452
+ Cancel
453
+ </Button>
454
+ <Button
455
+ type="submit"
456
+ disabled={creating || !canStartSession}
457
+ aria-describedby={submitBlocker && !creating ? 'create-session-blocker' : undefined}
458
+ >
459
+ {#if creating}
460
+ <Icon icon="lucide:loader-2" class="mr-2 size-4 animate-spin" aria-hidden="true" />
461
+ Starting…
462
+ {:else}
463
+ <Icon icon="lucide:play" class="mr-2 size-4" aria-hidden="true" />
464
+ {submitLabel}
465
+ {/if}
466
+ </Button>
467
+ </Dialog.Footer>
468
+ </form>
469
+ </Dialog.Content>
470
+ </Dialog.Root>
@@ -0,0 +1,57 @@
1
+ import type { CodeSessionCreateInput, CodeSessionSeed } from '@vibesdotdev/code-sessions';
2
+ export type HostTargetOption = 'local' | 'fleet' | 'cloud';
3
+ export interface FleetHostOption {
4
+ hostId: string;
5
+ name: string;
6
+ online: boolean;
7
+ }
8
+ export interface CreateSessionDialogProps {
9
+ open?: boolean;
10
+ /**
11
+ * Task this session is being started for, when the user got here from one.
12
+ *
13
+ * Reuses the platform's existing `CodeSessionSeed` contract rather than a
14
+ * web-only shape: the work-items TUI already creates sessions with
15
+ * `{ source: 'work-item', title, ref }`, the platform threads it into the
16
+ * sandbox as `VIBES_SEED_*`, and the session record persists it verbatim.
17
+ * A second convention here would mean a session started from the web could
18
+ * not be traced back the way one started from the TUI can.
19
+ */
20
+ seed?: CodeSessionSeed;
21
+ /** Called when the user submits the form; parent handles the actual create call. */
22
+ oncreate?: (input: CodeSessionCreateInput) => void | Promise<void>;
23
+ /** Called when the dialog is closed. */
24
+ onclose?: () => void;
25
+ /** Repository options to populate the picker. */
26
+ repositories?: Array<{
27
+ id: string;
28
+ name: string;
29
+ defaultBranch?: string | null;
30
+ description?: string | null;
31
+ group?: string;
32
+ }>;
33
+ /** Whether the create action is in progress. */
34
+ creating?: boolean;
35
+ /** Configuration-only embeddings may continue to a separate dispatch step. */
36
+ submitLabel?: string;
37
+ /** Error from a failed create attempt. */
38
+ createError?: string | null;
39
+ /** Optional URL for creating a repository in the owning forge surface. */
40
+ repositoryCreateHref?: string;
41
+ repositoryCreateLabel?: string;
42
+ /** Hide scratch mode when the caller requires a repository-backed outcome. */
43
+ allowEphemeral?: boolean;
44
+ /** Let the user explicitly request repository provisioning at the caller's submit boundary. */
45
+ allowRepositoryCreation?: boolean;
46
+ /** Execution targets the embedding surface can actually provision. */
47
+ allowedHostTargets?: HostTargetOption[];
48
+ /**
49
+ * Fleet machines available for code execution (labeled sandbox:code-execution).
50
+ * When empty the "Fleet host" radio is disabled and an onboarding hint is shown.
51
+ */
52
+ fleetHosts?: FleetHostOption[];
53
+ }
54
+ declare const CreateSessionDialog: import("svelte").Component<CreateSessionDialogProps, {}, "">;
55
+ type CreateSessionDialog = ReturnType<typeof CreateSessionDialog>;
56
+ export default CreateSessionDialog;
57
+ //# sourceMappingURL=CreateSessionDialog.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateSessionDialog.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/CreateSessionDialog.svelte.ts"],"names":[],"mappings":"AAGC,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE1F,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,iDAAiD;IACjD,YAAY,CAAC,EAAE,KAAK,CAAC;QACpB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0EAA0E;IAC1E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+FAA+F;IAC/F,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;CAC/B;AAqWF,QAAA,MAAM,mBAAmB,8DAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}