@opengeni/react 0.15.0 → 0.23.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 (103) hide show
  1. package/README.md +110 -10
  2. package/dist/{chunk-TOJR776I.js → chunk-6XUS5VFM.js} +45 -369
  3. package/dist/chunk-6XUS5VFM.js.map +1 -0
  4. package/dist/chunk-CATATY77.js +2393 -0
  5. package/dist/chunk-CATATY77.js.map +1 -0
  6. package/dist/chunk-I3BJZIG5.js +439 -0
  7. package/dist/chunk-I3BJZIG5.js.map +1 -0
  8. package/dist/chunk-TK7G6XLT.js +18 -0
  9. package/dist/chunk-TK7G6XLT.js.map +1 -0
  10. package/dist/chunk-UAHPKLB6.js +2237 -0
  11. package/dist/chunk-UAHPKLB6.js.map +1 -0
  12. package/dist/chunk-UQS7OQDE.js +591 -0
  13. package/dist/chunk-UQS7OQDE.js.map +1 -0
  14. package/dist/chunk-YDNWMKXO.js +102 -0
  15. package/dist/chunk-YDNWMKXO.js.map +1 -0
  16. package/dist/composer-CVm5uoUI.d.ts +585 -0
  17. package/dist/composer.d.ts +6 -0
  18. package/dist/composer.js +56 -0
  19. package/dist/composer.js.map +1 -0
  20. package/dist/index.d.ts +1288 -1785
  21. package/dist/index.js +7140 -8817
  22. package/dist/index.js.map +1 -1
  23. package/dist/machines.d.ts +412 -3
  24. package/dist/machines.js +3 -1
  25. package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
  26. package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
  27. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
  28. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
  29. package/dist/queue-surface-implementation-3UW3MIIR.js +890 -0
  30. package/dist/queue-surface-implementation-3UW3MIIR.js.map +1 -0
  31. package/dist/session-Db00TRwl.d.ts +446 -0
  32. package/dist/session-context-rgrfElMl.d.ts +58 -0
  33. package/dist/session.d.ts +4 -0
  34. package/dist/session.js +54 -0
  35. package/dist/session.js.map +1 -0
  36. package/dist/use-composer-By1wn3MM.d.ts +99 -0
  37. package/package.json +20 -4
  38. package/src/approvals.ts +5 -3
  39. package/src/client.ts +44 -0
  40. package/src/components/approval-surface.tsx +198 -0
  41. package/src/components/chat-composer.tsx +94 -993
  42. package/src/components/code-editor.tsx +104 -26
  43. package/src/components/command-palette.tsx +8 -2
  44. package/src/components/composer-transcription-control.tsx +211 -0
  45. package/src/components/composer.tsx +1536 -0
  46. package/src/components/desktop-viewer.tsx +3 -3
  47. package/src/components/enrollment-consent.tsx +2 -2
  48. package/src/components/file-browser.tsx +367 -29
  49. package/src/components/human-input-form.tsx +417 -0
  50. package/src/components/machine-card.tsx +1 -1
  51. package/src/components/machines-dashboard.tsx +3 -3
  52. package/src/components/message-timeline.tsx +205 -25
  53. package/src/components/model-picker.tsx +12 -3
  54. package/src/components/pierre-diff.tsx +32 -6
  55. package/src/components/queue-draft-policy.ts +18 -0
  56. package/src/components/queue-surface-implementation.tsx +1037 -0
  57. package/src/components/queue-surface-state.tsx +94 -0
  58. package/src/components/queue-surface.tsx +42 -559
  59. package/src/components/sandbox-files.tsx +138 -14
  60. package/src/components/sandbox-terminal.tsx +8 -2
  61. package/src/components/sandbox-workspace.tsx +339 -119
  62. package/src/components/workbench-changes.tsx +84 -33
  63. package/src/components/workspace-dock.tsx +188 -18
  64. package/src/composer.ts +60 -0
  65. package/src/hooks/internal.ts +115 -34
  66. package/src/hooks/use-composer.ts +385 -104
  67. package/src/hooks/use-human-input.ts +131 -0
  68. package/src/hooks/use-machine-chip.ts +2 -2
  69. package/src/hooks/use-machines.ts +27 -11
  70. package/src/hooks/use-sandbox-files.ts +310 -62
  71. package/src/hooks/use-sandbox-git.ts +154 -40
  72. package/src/hooks/use-sandbox-terminal.ts +28 -6
  73. package/src/hooks/use-session-capabilities.ts +38 -9
  74. package/src/hooks/use-session-control.ts +37 -8
  75. package/src/hooks/use-session-events.ts +522 -48
  76. package/src/hooks/use-session-mcp-approval-policy.ts +161 -0
  77. package/src/hooks/use-slash-commands.ts +38 -38
  78. package/src/hooks/use-transcription.ts +757 -0
  79. package/src/hooks/use-turn-queue.ts +134 -66
  80. package/src/hooks/use-windowed-sections.ts +2 -2
  81. package/src/hooks/use-workspace-capture.ts +146 -40
  82. package/src/hooks/use-workspace-edit.ts +50 -14
  83. package/src/hooks/use-workspace-sessions.ts +3 -0
  84. package/src/human-input.ts +72 -0
  85. package/src/index.ts +85 -6
  86. package/src/lib/noto-sans-arabic-loader.ts +21 -0
  87. package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
  88. package/src/lib/use-portal-token-style.ts +51 -0
  89. package/src/lib/use-unicode-fonts.ts +57 -0
  90. package/src/provider.tsx +4 -69
  91. package/src/session-context.ts +160 -0
  92. package/src/session.ts +89 -0
  93. package/src/timeline/parsers.ts +35 -0
  94. package/src/timeline/projection.ts +12 -4
  95. package/src/timeline/tool-renderers.tsx +41 -0
  96. package/src/timeline/turn-summary.tsx +2 -2
  97. package/src/timeline/types.ts +10 -9
  98. package/styles/index.css +56 -0
  99. package/styles/index.d.ts +2 -0
  100. package/styles/tokens.css +11 -6
  101. package/styles/tokens.d.ts +2 -0
  102. package/dist/chunk-TOJR776I.js.map +0 -1
  103. package/dist/machines-BpdwuQcD.d.ts +0 -449
@@ -0,0 +1,591 @@
1
+ import {
2
+ useEmbeddedSession,
3
+ useSessionEventTrigger
4
+ } from "./chunk-I3BJZIG5.js";
5
+
6
+ // src/hooks/use-composer.ts
7
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
8
+ function useComposer(sessionId, options = {}) {
9
+ const { client, workspaceId, registerSessionReconciler } = useEmbeddedSession(options);
10
+ const durableDrafts = options.draftPersistence !== "disabled";
11
+ const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${durableDrafts ? "durable" : "disabled"}`;
12
+ const [value, setValue] = useState("");
13
+ const [stateTargetKey, setStateTargetKey] = useState(targetKey);
14
+ const [sending, setSending] = useState(false);
15
+ const [pausing, setPausing] = useState(false);
16
+ const [resuming, setResuming] = useState(false);
17
+ const [error, setError] = useState(null);
18
+ const [draft, setDraft] = useState(null);
19
+ const [draftLoading, setDraftLoading] = useState(Boolean(sessionId) && durableDrafts);
20
+ const [draftSaving, setDraftSaving] = useState(false);
21
+ const [draftConflict, setDraftConflict] = useState(null);
22
+ const [restoredResources, setRestoredResources] = useState([]);
23
+ const pendingClientEventId = useRef(null);
24
+ const valueRef = useRef("");
25
+ const draftRef = useRef(null);
26
+ const restoredResourcesRef = useRef([]);
27
+ const localEditRevision = useRef(0);
28
+ const targetGeneration = useRef(0);
29
+ const draftReadGeneration = useRef(0);
30
+ const lastSavedSignature = useRef(null);
31
+ const saveChain = useRef(Promise.resolve());
32
+ const onSent = options.onSent;
33
+ const onDraftApplied = options.onDraftApplied;
34
+ const sendExtrasRef = useRef(options.sendExtras);
35
+ sendExtrasRef.current = options.sendExtras;
36
+ const liveExtrasVersion = JSON.stringify(resolveSendExtras(options.sendExtras));
37
+ const targetKeyRef = useRef(targetKey);
38
+ useLayoutEffect(() => {
39
+ if (targetKeyRef.current === targetKey) return;
40
+ targetKeyRef.current = targetKey;
41
+ targetGeneration.current += 1;
42
+ draftReadGeneration.current += 1;
43
+ pendingClientEventId.current = null;
44
+ localEditRevision.current = 0;
45
+ valueRef.current = "";
46
+ draftRef.current = null;
47
+ restoredResourcesRef.current = [];
48
+ lastSavedSignature.current = null;
49
+ saveChain.current = Promise.resolve();
50
+ setStateTargetKey(targetKey);
51
+ setValue("");
52
+ setSending(false);
53
+ setPausing(false);
54
+ setResuming(false);
55
+ setError(null);
56
+ setDraft(null);
57
+ setDraftLoading(Boolean(sessionId) && durableDrafts);
58
+ setDraftSaving(false);
59
+ setDraftConflict(null);
60
+ setRestoredResources([]);
61
+ }, [durableDrafts, sessionId, targetKey]);
62
+ const applyDraft = useCallback(
63
+ (next) => {
64
+ if (targetKeyRef.current !== targetKey) return;
65
+ if (!durableDrafts) {
66
+ pendingClientEventId.current = null;
67
+ localEditRevision.current += 1;
68
+ valueRef.current = next.text;
69
+ restoredResourcesRef.current = next.resources;
70
+ draftRef.current = null;
71
+ lastSavedSignature.current = null;
72
+ setDraft(null);
73
+ setValue(next.text);
74
+ setRestoredResources(next.resources);
75
+ setDraftConflict(null);
76
+ return;
77
+ }
78
+ valueRef.current = next.text;
79
+ draftRef.current = next;
80
+ restoredResourcesRef.current = next.resources;
81
+ lastSavedSignature.current = draftSignature(draftPayload(next));
82
+ localEditRevision.current += 1;
83
+ pendingClientEventId.current = null;
84
+ setDraft(next);
85
+ setValue(next.text);
86
+ setRestoredResources(next.resources);
87
+ setDraftConflict(null);
88
+ onDraftApplied?.(next);
89
+ },
90
+ [durableDrafts, onDraftApplied, targetKey]
91
+ );
92
+ const loadDraft = useCallback(
93
+ async (replaceLocal) => {
94
+ if (targetKeyRef.current !== targetKey) return;
95
+ if (!sessionId || !durableDrafts) {
96
+ setDraftLoading(false);
97
+ return;
98
+ }
99
+ const generation = targetGeneration.current;
100
+ const readTicket = ++draftReadGeneration.current;
101
+ const localAtStart = localEditRevision.current;
102
+ const baseAtStart = draftRef.current;
103
+ const extrasAtStart = resolveSendExtras(sendExtrasRef.current);
104
+ const localSignatureAtStart = baseAtStart ? draftSignature(
105
+ composerDraftPayload(
106
+ baseAtStart,
107
+ valueRef.current,
108
+ restoredResourcesRef.current,
109
+ extrasAtStart
110
+ )
111
+ ) : null;
112
+ const localWasDirtyAtStart = localSignatureAtStart === null ? localAtStart !== 0 : localSignatureAtStart !== lastSavedSignature.current;
113
+ setDraftLoading(true);
114
+ try {
115
+ const fetched = await client.getComposerDraft(workspaceId, sessionId);
116
+ if (generation !== targetGeneration.current || targetKeyRef.current !== targetKey || readTicket !== draftReadGeneration.current) {
117
+ return;
118
+ }
119
+ const currentRevision = draftRef.current?.revision ?? -1;
120
+ if (fetched.revision >= currentRevision) {
121
+ draftRef.current = fetched;
122
+ setDraft(fetched);
123
+ setDraftConflict(null);
124
+ if (replaceLocal || !localWasDirtyAtStart && localAtStart === localEditRevision.current) {
125
+ valueRef.current = fetched.text;
126
+ restoredResourcesRef.current = fetched.resources;
127
+ lastSavedSignature.current = draftSignature(draftPayload(fetched));
128
+ setValue(fetched.text);
129
+ setRestoredResources(fetched.resources);
130
+ onDraftApplied?.(fetched);
131
+ }
132
+ }
133
+ } catch (cause) {
134
+ if (generation === targetGeneration.current && targetKeyRef.current === targetKey && readTicket === draftReadGeneration.current) {
135
+ setError(asError(cause));
136
+ }
137
+ } finally {
138
+ if (generation === targetGeneration.current && targetKeyRef.current === targetKey && readTicket === draftReadGeneration.current) {
139
+ setDraftLoading(false);
140
+ }
141
+ }
142
+ },
143
+ [client, durableDrafts, onDraftApplied, sessionId, targetKey, workspaceId]
144
+ );
145
+ useEffect(() => {
146
+ if (!sessionId || !durableDrafts) {
147
+ setDraftLoading(false);
148
+ return;
149
+ }
150
+ void loadDraft(false);
151
+ }, [durableDrafts, loadDraft, sessionId]);
152
+ useEffect(() => {
153
+ if (!sessionId || !durableDrafts) return;
154
+ return registerSessionReconciler(sessionId, "composer", async () => await loadDraft(false));
155
+ }, [durableDrafts, loadDraft, registerSessionReconciler, sessionId]);
156
+ useSessionEventTrigger(
157
+ client,
158
+ workspaceId,
159
+ sessionId,
160
+ isComposerDraftEvent,
161
+ () => void loadDraft(false),
162
+ {
163
+ enabled: Boolean(sessionId) && durableDrafts,
164
+ ...options.events !== void 0 ? { events: options.events } : {}
165
+ }
166
+ );
167
+ const currentDraftPayload = useCallback(() => {
168
+ if (!durableDrafts || targetKeyRef.current !== targetKey) return null;
169
+ const base = draftRef.current;
170
+ if (!base) return null;
171
+ const extras = resolveSendExtras(sendExtrasRef.current);
172
+ return composerDraftPayload(base, value, restoredResources, extras);
173
+ }, [durableDrafts, restoredResources, targetKey, value]);
174
+ const persistPayload = useCallback(
175
+ async (payload) => {
176
+ const ownedTargetKey = targetKey;
177
+ const ownedGeneration = targetGeneration.current;
178
+ if (!sessionId || !durableDrafts || targetKeyRef.current !== ownedTargetKey) {
179
+ return false;
180
+ }
181
+ let success = false;
182
+ const run = async () => {
183
+ if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
184
+ return;
185
+ }
186
+ const current = draftRef.current;
187
+ if (!current) return;
188
+ const request = { ...payload, expectedRevision: current.revision };
189
+ const signature = draftSignature(request);
190
+ if (signature === lastSavedSignature.current) {
191
+ success = true;
192
+ return;
193
+ }
194
+ setDraftSaving(true);
195
+ try {
196
+ const saved = await client.saveComposerDraft(workspaceId, sessionId, request);
197
+ if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
198
+ return;
199
+ }
200
+ draftRef.current = saved;
201
+ setDraft(saved);
202
+ lastSavedSignature.current = signature;
203
+ setDraftConflict(null);
204
+ success = true;
205
+ } catch (cause) {
206
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
207
+ const problem = asError(cause);
208
+ setDraftConflict(problem);
209
+ setError(problem);
210
+ }
211
+ } finally {
212
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
213
+ setDraftSaving(false);
214
+ }
215
+ }
216
+ };
217
+ saveChain.current = saveChain.current.then(run, run);
218
+ await saveChain.current;
219
+ return success;
220
+ },
221
+ [client, durableDrafts, sessionId, targetKey, workspaceId]
222
+ );
223
+ useEffect(() => {
224
+ if (!durableDrafts || !sessionId || draftLoading || sending || !draftRef.current || draftConflict)
225
+ return;
226
+ const payload = currentDraftPayload();
227
+ if (!payload || draftSignature(payload) === lastSavedSignature.current) return;
228
+ const timer = window.setTimeout(() => void persistPayload(payload), 500);
229
+ return () => window.clearTimeout(timer);
230
+ }, [
231
+ currentDraftPayload,
232
+ durableDrafts,
233
+ draftConflict,
234
+ draftLoading,
235
+ liveExtrasVersion,
236
+ persistPayload,
237
+ sending,
238
+ sessionId
239
+ ]);
240
+ const dispatch = useCallback(
241
+ async (delivery, explicit) => {
242
+ const ownedTargetKey = targetKey;
243
+ const ownedGeneration = targetGeneration.current;
244
+ const draftAtSend = value;
245
+ const rawText = explicit ?? draftAtSend;
246
+ const hasText = rawText.trim().length > 0;
247
+ const extras = resolveSendExtras(sendExtrasRef.current);
248
+ const hasResources = restoredResources.length > 0 || (extras.resources?.length ?? 0) > 0;
249
+ if (!hasText && !hasResources || !sessionId || sending || targetKeyRef.current !== ownedTargetKey) {
250
+ return false;
251
+ }
252
+ pendingClientEventId.current ??= generateClientEventId();
253
+ setSending(true);
254
+ setError(null);
255
+ try {
256
+ const sendText = hasText ? rawText : FILE_ONLY_MESSAGE_TEXT;
257
+ const currentPayload = currentDraftPayload();
258
+ const payload = currentPayload ? { ...currentPayload, text: sendText } : null;
259
+ if (payload && !await persistPayload(payload)) return false;
260
+ if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
261
+ return false;
262
+ }
263
+ const acknowledgedDraft = draftRef.current;
264
+ const sendExtras = acknowledgedDraft?.toolsProvided === true && !Object.prototype.hasOwnProperty.call(extras, "tools") ? { ...extras, tools: acknowledgedDraft.tools } : extras;
265
+ const input = composeSendInput(sendText, pendingClientEventId.current, sendExtras, {
266
+ ...options.effectiveControl?.controlEtag ? { controlEtag: options.effectiveControl.controlEtag } : {},
267
+ ...durableDrafts && draftRef.current ? { expectedDraftRevision: draftRef.current.revision } : {},
268
+ resources: mergeResources(restoredResources, extras.resources ?? [])
269
+ });
270
+ if (delivery === "steer") {
271
+ await client.steerMessage(workspaceId, sessionId, input);
272
+ } else {
273
+ await client.sendMessage(workspaceId, sessionId, input);
274
+ }
275
+ if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
276
+ return false;
277
+ }
278
+ pendingClientEventId.current = null;
279
+ const previousDraft = draftRef.current;
280
+ if (previousDraft) {
281
+ const cleared = {
282
+ ...previousDraft,
283
+ revision: 0,
284
+ text: "",
285
+ resources: [],
286
+ sourceTurnId: null,
287
+ sourceTurnVersion: null,
288
+ updatedAt: null
289
+ };
290
+ draftRef.current = cleared;
291
+ restoredResourcesRef.current = [];
292
+ setDraft(cleared);
293
+ setRestoredResources([]);
294
+ lastSavedSignature.current = draftSignature(draftPayload(cleared));
295
+ }
296
+ if (explicit === void 0) {
297
+ if (valueRef.current === draftAtSend) {
298
+ valueRef.current = "";
299
+ setValue("");
300
+ }
301
+ }
302
+ onSent?.(sendText);
303
+ return true;
304
+ } catch (cause) {
305
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
306
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
307
+ }
308
+ return false;
309
+ } finally {
310
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
311
+ setSending(false);
312
+ }
313
+ }
314
+ },
315
+ [
316
+ client,
317
+ currentDraftPayload,
318
+ durableDrafts,
319
+ onSent,
320
+ options.effectiveControl?.controlEtag,
321
+ persistPayload,
322
+ restoredResources,
323
+ sending,
324
+ sessionId,
325
+ targetKey,
326
+ value,
327
+ workspaceId
328
+ ]
329
+ );
330
+ const send = useCallback(async (text) => await dispatch("send", text), [dispatch]);
331
+ const steer = useCallback(async (text) => await dispatch("steer", text), [dispatch]);
332
+ const hasReadyResources = restoredResources.length > 0 || (resolveSendExtras(sendExtrasRef.current).resources?.length ?? 0) > 0;
333
+ const pause = useCallback(
334
+ async (reason) => {
335
+ const ownedTargetKey = targetKey;
336
+ const ownedGeneration = targetGeneration.current;
337
+ if (!sessionId || pausing || targetKeyRef.current !== ownedTargetKey) {
338
+ return;
339
+ }
340
+ setPausing(true);
341
+ setError(null);
342
+ try {
343
+ await client.pauseSession(workspaceId, sessionId, {
344
+ ...reason !== void 0 ? { reason } : {},
345
+ ...options.effectiveControl?.controlEtag ? { expectedControlEtag: options.effectiveControl.controlEtag } : {}
346
+ });
347
+ } catch (cause) {
348
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
349
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
350
+ }
351
+ } finally {
352
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
353
+ setPausing(false);
354
+ }
355
+ }
356
+ },
357
+ [client, workspaceId, sessionId, pausing, options.effectiveControl?.controlEtag, targetKey]
358
+ );
359
+ const resume = useCallback(
360
+ async (reason) => {
361
+ const ownedTargetKey = targetKey;
362
+ const ownedGeneration = targetGeneration.current;
363
+ if (!sessionId || resuming || targetKeyRef.current !== ownedTargetKey) return;
364
+ setResuming(true);
365
+ setError(null);
366
+ try {
367
+ await client.resumeSession(workspaceId, sessionId, {
368
+ ...reason !== void 0 ? { reason } : {},
369
+ ...options.effectiveControl?.controlEtag ? { expectedControlEtag: options.effectiveControl.controlEtag } : {}
370
+ });
371
+ } catch (cause) {
372
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
373
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
374
+ }
375
+ } finally {
376
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
377
+ setResuming(false);
378
+ }
379
+ }
380
+ },
381
+ [client, workspaceId, sessionId, resuming, options.effectiveControl?.controlEtag, targetKey]
382
+ );
383
+ const resumeScope = useCallback(
384
+ async (option) => {
385
+ const ownedTargetKey = targetKey;
386
+ const ownedGeneration = targetGeneration.current;
387
+ if (!sessionId || resuming || targetKeyRef.current !== ownedTargetKey) return;
388
+ setResuming(true);
389
+ setError(null);
390
+ try {
391
+ if (option.scope === "workspace") {
392
+ const workspaceBlocker = options.effectiveControl?.blockers.find(
393
+ (blocker) => blocker.kind === "workspace"
394
+ );
395
+ if (!client.setWorkspaceInferenceState) {
396
+ throw new Error(
397
+ "@opengeni/react: workspace-scoped resume requires setWorkspaceInferenceState."
398
+ );
399
+ }
400
+ await client.setWorkspaceInferenceState(workspaceId, {
401
+ action: "resume",
402
+ clientEventId: generateClientEventId(),
403
+ ...workspaceBlocker ? { expectedRevision: workspaceBlocker.revision } : {}
404
+ });
405
+ } else if (option.scope === "session" && option.targetId) {
406
+ const target = await client.getQueue(workspaceId, option.targetId);
407
+ if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
408
+ return;
409
+ }
410
+ await client.resumeSession(workspaceId, option.targetId, {
411
+ expectedControlEtag: target.effectiveControl.controlEtag
412
+ });
413
+ } else {
414
+ await client.resumeSession(workspaceId, sessionId, {
415
+ ...options.effectiveControl?.controlEtag ? { expectedControlEtag: options.effectiveControl.controlEtag } : {}
416
+ });
417
+ }
418
+ } catch (cause) {
419
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
420
+ setError(asError(cause));
421
+ }
422
+ } finally {
423
+ if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
424
+ setResuming(false);
425
+ }
426
+ }
427
+ },
428
+ [client, options.effectiveControl, resuming, sessionId, targetKey, workspaceId]
429
+ );
430
+ const updateValue = useCallback(
431
+ (next) => {
432
+ if (targetKeyRef.current !== targetKey) return;
433
+ pendingClientEventId.current = null;
434
+ localEditRevision.current += 1;
435
+ valueRef.current = next;
436
+ setValue(next);
437
+ },
438
+ [targetKey]
439
+ );
440
+ const removeRestoredResource = useCallback(
441
+ (index) => {
442
+ if (targetKeyRef.current !== targetKey) return;
443
+ localEditRevision.current += 1;
444
+ const next = restoredResourcesRef.current.filter((_, candidate) => candidate !== index);
445
+ restoredResourcesRef.current = next;
446
+ setRestoredResources(next);
447
+ },
448
+ [targetKey]
449
+ );
450
+ const hasDraftContent = useCallback(() => {
451
+ const current = draftRef.current;
452
+ const extras = resolveSendExtras(sendExtrasRef.current);
453
+ const toolsProvidedByHost = Object.prototype.hasOwnProperty.call(extras, "tools");
454
+ const tools = toolsProvidedByHost ? extras.tools ?? [] : current?.tools ?? [];
455
+ return valueRef.current.length > 0 || restoredResourcesRef.current.length > 0 || (extras.resources?.length ?? 0) > 0 || tools.length > 0 || current?.sourceTurnId !== null && current?.sourceTurnId !== void 0;
456
+ }, []);
457
+ const resolveDraftConflict = useCallback(
458
+ async (choice) => {
459
+ const ownedTargetKey = targetKey;
460
+ const ownedGeneration = targetGeneration.current;
461
+ if (!sessionId || !durableDrafts || targetKeyRef.current !== ownedTargetKey) return;
462
+ const remote = await client.getComposerDraft(workspaceId, sessionId);
463
+ if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
464
+ return;
465
+ }
466
+ if (choice === "use_remote") {
467
+ applyDraft(remote);
468
+ return;
469
+ }
470
+ draftRef.current = remote;
471
+ setDraft(remote);
472
+ setDraftConflict(null);
473
+ const payload = currentDraftPayload();
474
+ if (payload) await persistPayload({ ...payload, expectedRevision: remote.revision });
475
+ },
476
+ [
477
+ applyDraft,
478
+ client,
479
+ currentDraftPayload,
480
+ durableDrafts,
481
+ persistPayload,
482
+ sessionId,
483
+ targetKey,
484
+ workspaceId
485
+ ]
486
+ );
487
+ const identityMatches = stateTargetKey === targetKey;
488
+ const reloadDraft = useCallback(async () => await loadDraft(true), [loadDraft]);
489
+ const clearError = useCallback(() => {
490
+ if (targetKeyRef.current !== targetKey) return;
491
+ setError(null);
492
+ setDraftConflict(null);
493
+ }, [targetKey]);
494
+ return {
495
+ value: identityMatches ? value : "",
496
+ setValue: updateValue,
497
+ hasDraftContent,
498
+ send,
499
+ steer,
500
+ sending: identityMatches ? sending : false,
501
+ canSend: identityMatches && Boolean(sessionId) && !sending && (value.trim().length > 0 || hasReadyResources),
502
+ pause,
503
+ pausing: identityMatches ? pausing : false,
504
+ resume,
505
+ resumeScope,
506
+ resuming: identityMatches ? resuming : false,
507
+ draft: identityMatches ? draft : null,
508
+ draftRevision: identityMatches ? draft?.revision ?? 0 : 0,
509
+ draftLoading: identityMatches ? draftLoading : Boolean(sessionId) && durableDrafts,
510
+ draftSaving: identityMatches ? draftSaving : false,
511
+ draftConflict: identityMatches ? draftConflict : null,
512
+ draftPersistence: durableDrafts ? "durable" : "disabled",
513
+ applyDraft,
514
+ reloadDraft,
515
+ resolveDraftConflict,
516
+ restoredResources: identityMatches ? restoredResources : [],
517
+ removeRestoredResource,
518
+ error: identityMatches ? error : null,
519
+ clearError
520
+ };
521
+ }
522
+ function isComposerDraftEvent(event) {
523
+ return event.type === "user.message" || event.type === "session.queue.changed";
524
+ }
525
+ var FILE_ONLY_MESSAGE_TEXT = "(see attached files)";
526
+ function resolveSendExtras(extras) {
527
+ return (typeof extras === "function" ? extras() : extras) ?? {};
528
+ }
529
+ function composeSendInput(text, clientEventId, extras, bound = {}) {
530
+ return { ...resolveSendExtras(extras), ...bound, text, clientEventId };
531
+ }
532
+ function shouldSubmitOnKey(event) {
533
+ if (event.key !== "Enter" || event.shiftKey) {
534
+ return false;
535
+ }
536
+ return event.nativeEvent?.isComposing !== true;
537
+ }
538
+ function shouldSteerOnKey(event) {
539
+ return event.metaKey === true || event.ctrlKey === true;
540
+ }
541
+ function generateClientEventId() {
542
+ return globalThis.crypto.randomUUID();
543
+ }
544
+ function asError(cause) {
545
+ return cause instanceof Error ? cause : new Error(String(cause));
546
+ }
547
+ function draftPayload(draft) {
548
+ return {
549
+ expectedRevision: draft.revision,
550
+ text: draft.text,
551
+ resources: draft.resources,
552
+ tools: draft.tools,
553
+ toolsProvided: draft.toolsProvided,
554
+ model: draft.model,
555
+ reasoningEffort: draft.reasoningEffort
556
+ };
557
+ }
558
+ function composerDraftPayload(base, text, restoredResources, extras) {
559
+ const toolsProvidedByHost = Object.prototype.hasOwnProperty.call(extras, "tools");
560
+ return {
561
+ expectedRevision: base.revision,
562
+ text,
563
+ resources: mergeResources(restoredResources, extras.resources ?? []),
564
+ tools: toolsProvidedByHost ? extras.tools ?? [] : base.tools,
565
+ toolsProvided: toolsProvidedByHost ? true : base.toolsProvided,
566
+ model: extras.model ?? base.model,
567
+ reasoningEffort: extras.reasoningEffort ?? base.reasoningEffort
568
+ };
569
+ }
570
+ function draftSignature(payload) {
571
+ const { expectedRevision: _revision, ...content } = payload;
572
+ return JSON.stringify(content);
573
+ }
574
+ function mergeResources(base, additions) {
575
+ const seen = /* @__PURE__ */ new Set();
576
+ return [...base, ...additions].filter((resource) => {
577
+ const key = JSON.stringify(resource);
578
+ if (seen.has(key)) return false;
579
+ seen.add(key);
580
+ return true;
581
+ });
582
+ }
583
+
584
+ export {
585
+ useComposer,
586
+ FILE_ONLY_MESSAGE_TEXT,
587
+ composeSendInput,
588
+ shouldSubmitOnKey,
589
+ shouldSteerOnKey
590
+ };
591
+ //# sourceMappingURL=chunk-UQS7OQDE.js.map