@opengeni/react 0.15.0 → 0.20.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.
- package/README.md +110 -10
- package/dist/{chunk-TOJR776I.js → chunk-6BXBGJ3B.js} +45 -369
- package/dist/chunk-6BXBGJ3B.js.map +1 -0
- package/dist/chunk-6UT5OC3P.js +2133 -0
- package/dist/chunk-6UT5OC3P.js.map +1 -0
- package/dist/chunk-EBTT3MYN.js +92 -0
- package/dist/chunk-EBTT3MYN.js.map +1 -0
- package/dist/chunk-HHFA4AFX.js +425 -0
- package/dist/chunk-HHFA4AFX.js.map +1 -0
- package/dist/chunk-LAZ2A743.js +2393 -0
- package/dist/chunk-LAZ2A743.js.map +1 -0
- package/dist/chunk-MRSECXRP.js +563 -0
- package/dist/chunk-MRSECXRP.js.map +1 -0
- package/dist/chunk-TK7G6XLT.js +18 -0
- package/dist/chunk-TK7G6XLT.js.map +1 -0
- package/dist/composer-DvUaa5ki.d.ts +585 -0
- package/dist/composer.d.ts +6 -0
- package/dist/composer.js +56 -0
- package/dist/composer.js.map +1 -0
- package/dist/index.d.ts +1288 -1785
- package/dist/index.js +7015 -8806
- package/dist/index.js.map +1 -1
- package/dist/machines.d.ts +412 -3
- package/dist/machines.js +3 -1
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js +890 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js.map +1 -0
- package/dist/session-Gd4iVPYw.d.ts +425 -0
- package/dist/session-context-DIFFlXKc.d.ts +52 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +50 -0
- package/dist/session.js.map +1 -0
- package/dist/use-composer-BCfm4mzX.d.ts +112 -0
- package/package.json +20 -4
- package/src/approvals.ts +5 -3
- package/src/client.ts +40 -0
- package/src/components/approval-surface.tsx +198 -0
- package/src/components/chat-composer.tsx +94 -993
- package/src/components/code-editor.tsx +104 -26
- package/src/components/command-palette.tsx +8 -2
- package/src/components/composer-transcription-control.tsx +211 -0
- package/src/components/composer.tsx +1536 -0
- package/src/components/desktop-viewer.tsx +3 -3
- package/src/components/enrollment-consent.tsx +2 -2
- package/src/components/file-browser.tsx +367 -29
- package/src/components/human-input-form.tsx +417 -0
- package/src/components/machine-card.tsx +1 -1
- package/src/components/machines-dashboard.tsx +3 -3
- package/src/components/message-timeline.tsx +15 -7
- package/src/components/model-picker.tsx +12 -3
- package/src/components/pierre-diff.tsx +32 -6
- package/src/components/queue-surface-implementation.tsx +1041 -0
- package/src/components/queue-surface-state.tsx +94 -0
- package/src/components/queue-surface.tsx +41 -559
- package/src/components/sandbox-files.tsx +138 -14
- package/src/components/sandbox-terminal.tsx +8 -2
- package/src/components/sandbox-workspace.tsx +339 -119
- package/src/components/workbench-changes.tsx +84 -33
- package/src/components/workspace-dock.tsx +188 -18
- package/src/composer.ts +60 -0
- package/src/hooks/internal.ts +115 -34
- package/src/hooks/use-composer.ts +335 -104
- package/src/hooks/use-human-input.ts +131 -0
- package/src/hooks/use-machine-chip.ts +2 -2
- package/src/hooks/use-machines.ts +27 -11
- package/src/hooks/use-sandbox-files.ts +310 -62
- package/src/hooks/use-sandbox-git.ts +154 -40
- package/src/hooks/use-sandbox-terminal.ts +28 -6
- package/src/hooks/use-session-capabilities.ts +38 -9
- package/src/hooks/use-session-control.ts +37 -8
- package/src/hooks/use-session-events.ts +522 -48
- package/src/hooks/use-slash-commands.ts +38 -38
- package/src/hooks/use-transcription.ts +757 -0
- package/src/hooks/use-turn-queue.ts +134 -66
- package/src/hooks/use-windowed-sections.ts +2 -2
- package/src/hooks/use-workspace-capture.ts +146 -40
- package/src/hooks/use-workspace-edit.ts +50 -14
- package/src/hooks/use-workspace-sessions.ts +3 -0
- package/src/human-input.ts +72 -0
- package/src/index.ts +76 -6
- package/src/lib/noto-sans-arabic-loader.ts +21 -0
- package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
- package/src/lib/use-portal-token-style.ts +51 -0
- package/src/lib/use-unicode-fonts.ts +57 -0
- package/src/provider.tsx +4 -69
- package/src/session-context.ts +135 -0
- package/src/session.ts +79 -0
- package/src/timeline/parsers.ts +35 -0
- package/src/timeline/projection.ts +12 -4
- package/src/timeline/tool-renderers.tsx +41 -0
- package/src/timeline/turn-summary.tsx +2 -2
- package/src/timeline/types.ts +16 -8
- package/styles/index.css +56 -0
- package/styles/index.d.ts +2 -0
- package/styles/tokens.css +11 -6
- package/styles/tokens.d.ts +2 -0
- package/dist/chunk-TOJR776I.js.map +0 -1
- package/dist/machines-BpdwuQcD.d.ts +0 -449
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useEmbeddedSession,
|
|
3
|
+
useSessionEventTrigger
|
|
4
|
+
} from "./chunk-HHFA4AFX.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 draftRef = useRef(null);
|
|
25
|
+
const draftLoadErrorRef = useRef(null);
|
|
26
|
+
const draftReadGeneration = useRef(0);
|
|
27
|
+
const localEditRevision = useRef(0);
|
|
28
|
+
const targetGeneration = useRef(0);
|
|
29
|
+
const lastSavedSignature = useRef(null);
|
|
30
|
+
const saveChain = useRef(Promise.resolve());
|
|
31
|
+
const onSent = options.onSent;
|
|
32
|
+
const onDraftApplied = options.onDraftApplied;
|
|
33
|
+
const sendExtrasRef = useRef(options.sendExtras);
|
|
34
|
+
useLayoutEffect(() => {
|
|
35
|
+
sendExtrasRef.current = options.sendExtras;
|
|
36
|
+
}, [options.sendExtras]);
|
|
37
|
+
const liveExtras = resolveSendExtras(options.sendExtras);
|
|
38
|
+
const liveExtrasVersion = JSON.stringify(liveExtras);
|
|
39
|
+
const targetKeyRef = useRef(targetKey);
|
|
40
|
+
useLayoutEffect(() => {
|
|
41
|
+
if (targetKeyRef.current === targetKey) return;
|
|
42
|
+
targetKeyRef.current = targetKey;
|
|
43
|
+
targetGeneration.current += 1;
|
|
44
|
+
pendingClientEventId.current = null;
|
|
45
|
+
localEditRevision.current = 0;
|
|
46
|
+
draftReadGeneration.current += 1;
|
|
47
|
+
draftRef.current = null;
|
|
48
|
+
draftLoadErrorRef.current = null;
|
|
49
|
+
lastSavedSignature.current = null;
|
|
50
|
+
saveChain.current = Promise.resolve();
|
|
51
|
+
setStateTargetKey(targetKey);
|
|
52
|
+
setValue("");
|
|
53
|
+
setSending(false);
|
|
54
|
+
setPausing(false);
|
|
55
|
+
setResuming(false);
|
|
56
|
+
setError(null);
|
|
57
|
+
setDraft(null);
|
|
58
|
+
setDraftLoading(Boolean(sessionId) && durableDrafts);
|
|
59
|
+
setDraftSaving(false);
|
|
60
|
+
setDraftConflict(null);
|
|
61
|
+
setRestoredResources([]);
|
|
62
|
+
}, [durableDrafts, sessionId, targetKey]);
|
|
63
|
+
const applyDraft = useCallback(
|
|
64
|
+
(next) => {
|
|
65
|
+
if (targetKeyRef.current !== targetKey) return;
|
|
66
|
+
if (!durableDrafts) {
|
|
67
|
+
pendingClientEventId.current = null;
|
|
68
|
+
localEditRevision.current += 1;
|
|
69
|
+
draftRef.current = null;
|
|
70
|
+
lastSavedSignature.current = null;
|
|
71
|
+
setDraft(null);
|
|
72
|
+
setValue(next.text);
|
|
73
|
+
setRestoredResources(next.resources);
|
|
74
|
+
setDraftConflict(null);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
draftRef.current = next;
|
|
78
|
+
lastSavedSignature.current = draftSignature(draftPayload(next));
|
|
79
|
+
localEditRevision.current += 1;
|
|
80
|
+
pendingClientEventId.current = null;
|
|
81
|
+
setDraft(next);
|
|
82
|
+
setValue(next.text);
|
|
83
|
+
setRestoredResources(next.resources);
|
|
84
|
+
setDraftConflict(null);
|
|
85
|
+
onDraftApplied?.(next);
|
|
86
|
+
},
|
|
87
|
+
[durableDrafts, onDraftApplied, targetKey]
|
|
88
|
+
);
|
|
89
|
+
const loadDraft = useCallback(
|
|
90
|
+
async (replaceLocal, rejectOnFailure = false) => {
|
|
91
|
+
if (targetKeyRef.current !== targetKey) return;
|
|
92
|
+
if (!sessionId || !durableDrafts) {
|
|
93
|
+
setDraftLoading(false);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const generation = targetGeneration.current;
|
|
97
|
+
const readTicket = ++draftReadGeneration.current;
|
|
98
|
+
const localAtStart = localEditRevision.current;
|
|
99
|
+
setDraftLoading(true);
|
|
100
|
+
try {
|
|
101
|
+
const fetched = await client.getComposerDraft(workspaceId, sessionId);
|
|
102
|
+
if (generation !== targetGeneration.current || targetKeyRef.current !== targetKey) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const ownsLatestRead = readTicket === draftReadGeneration.current;
|
|
106
|
+
const currentRevision = draftRef.current?.revision ?? -1;
|
|
107
|
+
if ((ownsLatestRead || rejectOnFailure) && fetched.revision >= currentRevision) {
|
|
108
|
+
draftRef.current = fetched;
|
|
109
|
+
setDraft(fetched);
|
|
110
|
+
setDraftConflict(null);
|
|
111
|
+
if (replaceLocal || localAtStart === localEditRevision.current) {
|
|
112
|
+
lastSavedSignature.current = draftSignature(draftPayload(fetched));
|
|
113
|
+
setValue(fetched.text);
|
|
114
|
+
setRestoredResources(fetched.resources);
|
|
115
|
+
onDraftApplied?.(fetched);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const authoritativeRevision = draftRef.current?.revision ?? -1;
|
|
119
|
+
if (rejectOnFailure && authoritativeRevision < fetched.revision) {
|
|
120
|
+
throw new TypeError("Composer draft reconciliation did not commit authoritative state");
|
|
121
|
+
}
|
|
122
|
+
if (ownsLatestRead || rejectOnFailure) {
|
|
123
|
+
const recoveredError = draftLoadErrorRef.current;
|
|
124
|
+
draftLoadErrorRef.current = null;
|
|
125
|
+
if (recoveredError) {
|
|
126
|
+
setError((current) => current === recoveredError ? null : current);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} catch (cause) {
|
|
130
|
+
if (generation === targetGeneration.current && targetKeyRef.current === targetKey && (readTicket === draftReadGeneration.current || rejectOnFailure)) {
|
|
131
|
+
const failure = asError(cause);
|
|
132
|
+
draftLoadErrorRef.current = failure;
|
|
133
|
+
setError(failure);
|
|
134
|
+
}
|
|
135
|
+
if (rejectOnFailure) throw cause;
|
|
136
|
+
} finally {
|
|
137
|
+
if (generation === targetGeneration.current && targetKeyRef.current === targetKey && readTicket === draftReadGeneration.current) {
|
|
138
|
+
setDraftLoading(false);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
[client, durableDrafts, onDraftApplied, sessionId, targetKey, workspaceId]
|
|
143
|
+
);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!sessionId || !durableDrafts) {
|
|
146
|
+
setDraftLoading(false);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
void loadDraft(false);
|
|
150
|
+
}, [durableDrafts, loadDraft, sessionId]);
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (!sessionId || !durableDrafts) return;
|
|
153
|
+
return registerSessionReconciler(sessionId, "composer", async () => await loadDraft(false));
|
|
154
|
+
}, [durableDrafts, loadDraft, registerSessionReconciler, sessionId]);
|
|
155
|
+
useSessionEventTrigger(
|
|
156
|
+
client,
|
|
157
|
+
workspaceId,
|
|
158
|
+
sessionId,
|
|
159
|
+
isComposerDraftEvent,
|
|
160
|
+
() => void loadDraft(false),
|
|
161
|
+
{
|
|
162
|
+
enabled: Boolean(sessionId) && durableDrafts,
|
|
163
|
+
...options.events !== void 0 ? { events: options.events } : {}
|
|
164
|
+
},
|
|
165
|
+
async () => await loadDraft(false, true)
|
|
166
|
+
);
|
|
167
|
+
const currentDraftPayload = useCallback(() => {
|
|
168
|
+
if (targetKeyRef.current !== targetKey) return null;
|
|
169
|
+
const base = draftRef.current;
|
|
170
|
+
if (!base) return null;
|
|
171
|
+
const extras = resolveSendExtras(sendExtrasRef.current);
|
|
172
|
+
return {
|
|
173
|
+
expectedRevision: base.revision,
|
|
174
|
+
text: value,
|
|
175
|
+
resources: mergeResources(restoredResources, extras.resources ?? []),
|
|
176
|
+
tools: extras.tools ?? base.tools,
|
|
177
|
+
model: extras.model ?? base.model,
|
|
178
|
+
reasoningEffort: extras.reasoningEffort ?? base.reasoningEffort
|
|
179
|
+
};
|
|
180
|
+
}, [restoredResources, targetKey, value]);
|
|
181
|
+
const persistPayload = useCallback(
|
|
182
|
+
async (payload) => {
|
|
183
|
+
const ownedTargetKey = targetKey;
|
|
184
|
+
const ownedGeneration = targetGeneration.current;
|
|
185
|
+
if (!sessionId || targetKeyRef.current !== ownedTargetKey) return false;
|
|
186
|
+
let success = false;
|
|
187
|
+
const run = async () => {
|
|
188
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const current = draftRef.current;
|
|
192
|
+
if (!current) return;
|
|
193
|
+
const request = { ...payload, expectedRevision: current.revision };
|
|
194
|
+
const signature = draftSignature(request);
|
|
195
|
+
if (signature === lastSavedSignature.current) {
|
|
196
|
+
success = true;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
setDraftSaving(true);
|
|
200
|
+
try {
|
|
201
|
+
const saved = await client.saveComposerDraft(workspaceId, sessionId, request);
|
|
202
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
draftRef.current = saved;
|
|
206
|
+
setDraft(saved);
|
|
207
|
+
lastSavedSignature.current = signature;
|
|
208
|
+
setDraftConflict(null);
|
|
209
|
+
success = true;
|
|
210
|
+
} catch (cause) {
|
|
211
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
212
|
+
const problem = asError(cause);
|
|
213
|
+
setDraftConflict(problem);
|
|
214
|
+
setError(problem);
|
|
215
|
+
}
|
|
216
|
+
} finally {
|
|
217
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
218
|
+
setDraftSaving(false);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
saveChain.current = saveChain.current.then(run, run);
|
|
223
|
+
await saveChain.current;
|
|
224
|
+
return success;
|
|
225
|
+
},
|
|
226
|
+
[client, sessionId, targetKey, workspaceId]
|
|
227
|
+
);
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (!durableDrafts || !sessionId || draftLoading || sending || !draftRef.current || draftConflict)
|
|
230
|
+
return;
|
|
231
|
+
const payload = currentDraftPayload();
|
|
232
|
+
if (!payload || draftSignature(payload) === lastSavedSignature.current) return;
|
|
233
|
+
const timer = window.setTimeout(() => void persistPayload(payload), 500);
|
|
234
|
+
return () => window.clearTimeout(timer);
|
|
235
|
+
}, [
|
|
236
|
+
currentDraftPayload,
|
|
237
|
+
durableDrafts,
|
|
238
|
+
draftConflict,
|
|
239
|
+
draftLoading,
|
|
240
|
+
liveExtrasVersion,
|
|
241
|
+
persistPayload,
|
|
242
|
+
sending,
|
|
243
|
+
sessionId
|
|
244
|
+
]);
|
|
245
|
+
const dispatch = useCallback(
|
|
246
|
+
async (delivery, explicit) => {
|
|
247
|
+
const ownedTargetKey = targetKey;
|
|
248
|
+
const ownedGeneration = targetGeneration.current;
|
|
249
|
+
const draftAtSend = value;
|
|
250
|
+
const rawText = explicit ?? draftAtSend;
|
|
251
|
+
const hasText = rawText.trim().length > 0;
|
|
252
|
+
const extras = resolveSendExtras(sendExtrasRef.current);
|
|
253
|
+
const hasResources = restoredResources.length > 0 || (extras.resources?.length ?? 0) > 0;
|
|
254
|
+
if (!hasText && !hasResources || !sessionId || sending || targetKeyRef.current !== ownedTargetKey) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
pendingClientEventId.current ??= generateClientEventId();
|
|
258
|
+
setSending(true);
|
|
259
|
+
setError(null);
|
|
260
|
+
try {
|
|
261
|
+
const sendText = hasText ? rawText : FILE_ONLY_MESSAGE_TEXT;
|
|
262
|
+
const currentPayload = currentDraftPayload();
|
|
263
|
+
const payload = currentPayload ? { ...currentPayload, text: sendText } : null;
|
|
264
|
+
if (payload && !await persistPayload(payload)) return false;
|
|
265
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
const input = composeSendInput(sendText, pendingClientEventId.current, extras, {
|
|
269
|
+
...options.effectiveControl?.controlEtag ? { controlEtag: options.effectiveControl.controlEtag } : {},
|
|
270
|
+
...draftRef.current ? { expectedDraftRevision: draftRef.current.revision } : {},
|
|
271
|
+
resources: mergeResources(restoredResources, extras.resources ?? [])
|
|
272
|
+
});
|
|
273
|
+
if (delivery === "steer") {
|
|
274
|
+
await client.steerMessage(workspaceId, sessionId, input);
|
|
275
|
+
} else {
|
|
276
|
+
await client.sendMessage(workspaceId, sessionId, input);
|
|
277
|
+
}
|
|
278
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
pendingClientEventId.current = null;
|
|
282
|
+
const previousDraft = draftRef.current;
|
|
283
|
+
if (previousDraft) {
|
|
284
|
+
const cleared = {
|
|
285
|
+
...previousDraft,
|
|
286
|
+
revision: 0,
|
|
287
|
+
text: "",
|
|
288
|
+
resources: [],
|
|
289
|
+
sourceTurnId: null,
|
|
290
|
+
sourceTurnVersion: null,
|
|
291
|
+
updatedAt: null
|
|
292
|
+
};
|
|
293
|
+
draftRef.current = cleared;
|
|
294
|
+
setDraft(cleared);
|
|
295
|
+
setRestoredResources([]);
|
|
296
|
+
lastSavedSignature.current = draftSignature(draftPayload(cleared));
|
|
297
|
+
}
|
|
298
|
+
if (explicit === void 0) {
|
|
299
|
+
setValue((current) => current === draftAtSend ? "" : current);
|
|
300
|
+
}
|
|
301
|
+
onSent?.(sendText);
|
|
302
|
+
return true;
|
|
303
|
+
} catch (cause) {
|
|
304
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
305
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
} finally {
|
|
309
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
310
|
+
setSending(false);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
[
|
|
315
|
+
client,
|
|
316
|
+
currentDraftPayload,
|
|
317
|
+
onSent,
|
|
318
|
+
options.effectiveControl?.controlEtag,
|
|
319
|
+
persistPayload,
|
|
320
|
+
restoredResources,
|
|
321
|
+
sending,
|
|
322
|
+
sessionId,
|
|
323
|
+
targetKey,
|
|
324
|
+
value,
|
|
325
|
+
workspaceId
|
|
326
|
+
]
|
|
327
|
+
);
|
|
328
|
+
const send = useCallback(async (text) => await dispatch("send", text), [dispatch]);
|
|
329
|
+
const steer = useCallback(async (text) => await dispatch("steer", text), [dispatch]);
|
|
330
|
+
const hasReadyResources = restoredResources.length > 0 || (liveExtras.resources?.length ?? 0) > 0;
|
|
331
|
+
const pause = useCallback(
|
|
332
|
+
async (reason) => {
|
|
333
|
+
const ownedTargetKey = targetKey;
|
|
334
|
+
const ownedGeneration = targetGeneration.current;
|
|
335
|
+
if (!sessionId || pausing || targetKeyRef.current !== ownedTargetKey) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
setPausing(true);
|
|
339
|
+
setError(null);
|
|
340
|
+
try {
|
|
341
|
+
await client.pauseSession(workspaceId, sessionId, {
|
|
342
|
+
...reason !== void 0 ? { reason } : {},
|
|
343
|
+
...options.effectiveControl?.controlEtag ? { expectedControlEtag: options.effectiveControl.controlEtag } : {}
|
|
344
|
+
});
|
|
345
|
+
} catch (cause) {
|
|
346
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
347
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
348
|
+
}
|
|
349
|
+
} finally {
|
|
350
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
351
|
+
setPausing(false);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
[client, workspaceId, sessionId, pausing, options.effectiveControl?.controlEtag, targetKey]
|
|
356
|
+
);
|
|
357
|
+
const resume = useCallback(
|
|
358
|
+
async (reason) => {
|
|
359
|
+
const ownedTargetKey = targetKey;
|
|
360
|
+
const ownedGeneration = targetGeneration.current;
|
|
361
|
+
if (!sessionId || resuming || targetKeyRef.current !== ownedTargetKey) return;
|
|
362
|
+
setResuming(true);
|
|
363
|
+
setError(null);
|
|
364
|
+
try {
|
|
365
|
+
await client.resumeSession(workspaceId, sessionId, {
|
|
366
|
+
...reason !== void 0 ? { reason } : {},
|
|
367
|
+
...options.effectiveControl?.controlEtag ? { expectedControlEtag: options.effectiveControl.controlEtag } : {}
|
|
368
|
+
});
|
|
369
|
+
} catch (cause) {
|
|
370
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
371
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
372
|
+
}
|
|
373
|
+
} finally {
|
|
374
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
375
|
+
setResuming(false);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
[client, workspaceId, sessionId, resuming, options.effectiveControl?.controlEtag, targetKey]
|
|
380
|
+
);
|
|
381
|
+
const resumeScope = useCallback(
|
|
382
|
+
async (option) => {
|
|
383
|
+
const ownedTargetKey = targetKey;
|
|
384
|
+
const ownedGeneration = targetGeneration.current;
|
|
385
|
+
if (!sessionId || resuming || targetKeyRef.current !== ownedTargetKey) return;
|
|
386
|
+
setResuming(true);
|
|
387
|
+
setError(null);
|
|
388
|
+
try {
|
|
389
|
+
if (option.scope === "workspace") {
|
|
390
|
+
if (!client.setWorkspaceInferenceState) {
|
|
391
|
+
throw new Error("Workspace-level resume is not available through this client");
|
|
392
|
+
}
|
|
393
|
+
const workspaceBlocker = options.effectiveControl?.blockers.find(
|
|
394
|
+
(blocker) => blocker.kind === "workspace"
|
|
395
|
+
);
|
|
396
|
+
await client.setWorkspaceInferenceState(workspaceId, {
|
|
397
|
+
action: "resume",
|
|
398
|
+
clientEventId: generateClientEventId(),
|
|
399
|
+
...workspaceBlocker ? { expectedRevision: workspaceBlocker.revision } : {}
|
|
400
|
+
});
|
|
401
|
+
} else if (option.scope === "session" && option.targetId) {
|
|
402
|
+
const target = await client.getQueue(workspaceId, option.targetId);
|
|
403
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
await client.resumeSession(workspaceId, option.targetId, {
|
|
407
|
+
expectedControlEtag: target.effectiveControl.controlEtag
|
|
408
|
+
});
|
|
409
|
+
} else {
|
|
410
|
+
await client.resumeSession(workspaceId, sessionId, {
|
|
411
|
+
...options.effectiveControl?.controlEtag ? { expectedControlEtag: options.effectiveControl.controlEtag } : {}
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
} catch (cause) {
|
|
415
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
416
|
+
setError(asError(cause));
|
|
417
|
+
}
|
|
418
|
+
} finally {
|
|
419
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
420
|
+
setResuming(false);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
[client, options.effectiveControl, resuming, sessionId, targetKey, workspaceId]
|
|
425
|
+
);
|
|
426
|
+
const updateValue = useCallback(
|
|
427
|
+
(next) => {
|
|
428
|
+
if (targetKeyRef.current !== targetKey) return;
|
|
429
|
+
pendingClientEventId.current = null;
|
|
430
|
+
localEditRevision.current += 1;
|
|
431
|
+
setValue(next);
|
|
432
|
+
},
|
|
433
|
+
[targetKey]
|
|
434
|
+
);
|
|
435
|
+
const removeRestoredResource = useCallback(
|
|
436
|
+
(index) => {
|
|
437
|
+
if (targetKeyRef.current !== targetKey) return;
|
|
438
|
+
localEditRevision.current += 1;
|
|
439
|
+
setRestoredResources((current) => current.filter((_, candidate) => candidate !== index));
|
|
440
|
+
},
|
|
441
|
+
[targetKey]
|
|
442
|
+
);
|
|
443
|
+
const resolveDraftConflict = useCallback(
|
|
444
|
+
async (choice) => {
|
|
445
|
+
const ownedTargetKey = targetKey;
|
|
446
|
+
const ownedGeneration = targetGeneration.current;
|
|
447
|
+
if (!sessionId || !durableDrafts || targetKeyRef.current !== ownedTargetKey) return;
|
|
448
|
+
const remote = await client.getComposerDraft(workspaceId, sessionId);
|
|
449
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (choice === "use_remote") {
|
|
453
|
+
applyDraft(remote);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
draftRef.current = remote;
|
|
457
|
+
setDraft(remote);
|
|
458
|
+
setDraftConflict(null);
|
|
459
|
+
const payload = currentDraftPayload();
|
|
460
|
+
if (payload) await persistPayload({ ...payload, expectedRevision: remote.revision });
|
|
461
|
+
},
|
|
462
|
+
[
|
|
463
|
+
applyDraft,
|
|
464
|
+
client,
|
|
465
|
+
currentDraftPayload,
|
|
466
|
+
durableDrafts,
|
|
467
|
+
persistPayload,
|
|
468
|
+
sessionId,
|
|
469
|
+
targetKey,
|
|
470
|
+
workspaceId
|
|
471
|
+
]
|
|
472
|
+
);
|
|
473
|
+
const identityMatches = stateTargetKey === targetKey;
|
|
474
|
+
const reloadDraft = useCallback(async () => await loadDraft(true), [loadDraft]);
|
|
475
|
+
const clearError = useCallback(() => {
|
|
476
|
+
if (targetKeyRef.current !== targetKey) return;
|
|
477
|
+
setError(null);
|
|
478
|
+
setDraftConflict(null);
|
|
479
|
+
}, [targetKey]);
|
|
480
|
+
return {
|
|
481
|
+
value: identityMatches ? value : "",
|
|
482
|
+
setValue: updateValue,
|
|
483
|
+
send,
|
|
484
|
+
steer,
|
|
485
|
+
sending: identityMatches ? sending : false,
|
|
486
|
+
canSend: identityMatches && Boolean(sessionId) && !sending && (value.trim().length > 0 || hasReadyResources),
|
|
487
|
+
pause,
|
|
488
|
+
pausing: identityMatches ? pausing : false,
|
|
489
|
+
resume,
|
|
490
|
+
resumeScope,
|
|
491
|
+
resuming: identityMatches ? resuming : false,
|
|
492
|
+
draft: identityMatches ? draft : null,
|
|
493
|
+
draftRevision: identityMatches ? draft?.revision ?? 0 : 0,
|
|
494
|
+
draftLoading: identityMatches ? draftLoading : Boolean(sessionId) && durableDrafts,
|
|
495
|
+
draftSaving: identityMatches ? draftSaving : false,
|
|
496
|
+
draftConflict: identityMatches ? draftConflict : null,
|
|
497
|
+
draftPersistence: durableDrafts ? "durable" : "disabled",
|
|
498
|
+
applyDraft,
|
|
499
|
+
reloadDraft,
|
|
500
|
+
resolveDraftConflict,
|
|
501
|
+
restoredResources: identityMatches ? restoredResources : [],
|
|
502
|
+
removeRestoredResource,
|
|
503
|
+
error: identityMatches ? error : null,
|
|
504
|
+
clearError
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function isComposerDraftEvent(event) {
|
|
508
|
+
return event.type === "user.message" || event.type === "session.queue.changed";
|
|
509
|
+
}
|
|
510
|
+
var FILE_ONLY_MESSAGE_TEXT = "(see attached files)";
|
|
511
|
+
function resolveSendExtras(extras) {
|
|
512
|
+
return (typeof extras === "function" ? extras() : extras) ?? {};
|
|
513
|
+
}
|
|
514
|
+
function composeSendInput(text, clientEventId, extras, bound = {}) {
|
|
515
|
+
return { ...resolveSendExtras(extras), ...bound, text, clientEventId };
|
|
516
|
+
}
|
|
517
|
+
function shouldSubmitOnKey(event) {
|
|
518
|
+
if (event.key !== "Enter" || event.shiftKey) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
return event.nativeEvent?.isComposing !== true;
|
|
522
|
+
}
|
|
523
|
+
function shouldSteerOnKey(event) {
|
|
524
|
+
return event.metaKey === true || event.ctrlKey === true;
|
|
525
|
+
}
|
|
526
|
+
function generateClientEventId() {
|
|
527
|
+
return globalThis.crypto.randomUUID();
|
|
528
|
+
}
|
|
529
|
+
function asError(cause) {
|
|
530
|
+
return cause instanceof Error ? cause : new Error(String(cause));
|
|
531
|
+
}
|
|
532
|
+
function draftPayload(draft) {
|
|
533
|
+
return {
|
|
534
|
+
expectedRevision: draft.revision,
|
|
535
|
+
text: draft.text,
|
|
536
|
+
resources: draft.resources,
|
|
537
|
+
tools: draft.tools,
|
|
538
|
+
model: draft.model,
|
|
539
|
+
reasoningEffort: draft.reasoningEffort
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function draftSignature(payload) {
|
|
543
|
+
const { expectedRevision: _revision, ...content } = payload;
|
|
544
|
+
return JSON.stringify(content);
|
|
545
|
+
}
|
|
546
|
+
function mergeResources(base, additions) {
|
|
547
|
+
const seen = /* @__PURE__ */ new Set();
|
|
548
|
+
return [...base, ...additions].filter((resource) => {
|
|
549
|
+
const key = JSON.stringify(resource);
|
|
550
|
+
if (seen.has(key)) return false;
|
|
551
|
+
seen.add(key);
|
|
552
|
+
return true;
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export {
|
|
557
|
+
useComposer,
|
|
558
|
+
FILE_ONLY_MESSAGE_TEXT,
|
|
559
|
+
composeSendInput,
|
|
560
|
+
shouldSubmitOnKey,
|
|
561
|
+
shouldSteerOnKey
|
|
562
|
+
};
|
|
563
|
+
//# sourceMappingURL=chunk-MRSECXRP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-composer.ts"],"sourcesContent":["import type {\n ComposerDraft,\n EffectiveControlResumeOption,\n EffectiveSessionControl,\n ResourceRef,\n SaveComposerDraftRequest,\n SendMessageInput,\n SessionEvent,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { useEmbeddedSession, type EmbeddedSessionClientOverride } from \"../session-context\";\nimport { useSessionEventTrigger, type SessionEventFeedOptions } from \"./internal\";\n\nexport type ComposerSendExtras = Omit<SendMessageInput, \"text\" | \"clientEventId\">;\n\nexport type UseComposerOptions = EmbeddedSessionClientOverride &\n SessionEventFeedOptions & {\n /** Called with the accepted text after a successful send. */\n onSent?: ((text: string) => void) | undefined;\n /**\n * Extra message fields (resources, tools, model, reasoningEffort) merged\n * into every send. A function is evaluated at send time so it can read the\n * surrounding UI state (attachment pickers, model selectors, ...).\n */\n sendExtras?: ComposerSendExtras | (() => ComposerSendExtras) | undefined;\n /** Latest server-derived workstream control; bound into Send/Steer OCC. */\n effectiveControl?: EffectiveSessionControl | null | undefined;\n /** Apply durable model/tool/reasoning settings in the host's controlled UI. */\n onDraftApplied?: ((draft: ComposerDraft) => void) | undefined;\n /**\n * Opt out of OpenGeni's remote composer-draft reads and writes. Embedded\n * hosts can use this when their human lane permits messages and controls but\n * deliberately withholds draft mutation because model/tool policy is\n * server-owned. The unsent value remains local React state and send/steer\n * continue to work without a draft revision fence. Queue checkout is hidden\n * because the current queue-edit contract necessarily creates a durable\n * composer draft.\n *\n * @default \"durable\"\n */\n draftPersistence?: \"durable\" | \"disabled\" | undefined;\n };\n\nexport type ComposerState = {\n value: string;\n setValue: (value: string) => void;\n /** Append the draft behind prompts already visible in the queue. */\n send: (text?: string) => Promise<boolean>;\n /** Supersede current direction with the draft. */\n steer: (text?: string) => Promise<boolean>;\n sending: boolean;\n canSend: boolean;\n /** Pause the session without deleting its prompt queue. */\n pause: (reason?: string) => Promise<void>;\n pausing: boolean;\n resume: (reason?: string) => Promise<void>;\n resumeScope: (option: EffectiveControlResumeOption) => Promise<void>;\n resuming: boolean;\n draft: ComposerDraft | null;\n draftRevision: number;\n draftLoading: boolean;\n draftSaving: boolean;\n draftConflict: Error | null;\n /**\n * Whether this controller owns a durable server-side draft. Custom\n * controllers that omit the field retain the historical durable behavior.\n * Queue checkout is unavailable when persistence is disabled because the\n * current queue-edit contract atomically moves the turn into that draft.\n */\n draftPersistence?: \"durable\" | \"disabled\" | undefined;\n /** Apply an atomic queue Edit checkout without a second read. */\n applyDraft: (draft: ComposerDraft) => void;\n reloadDraft: () => Promise<void>;\n resolveDraftConflict: (choice: \"keep_mine\" | \"use_remote\") => Promise<void>;\n restoredResources: ResourceRef[];\n removeRestoredResource: (index: number) => void;\n error: Error | null;\n clearError: () => void;\n};\n\n/**\n * Draft + send + Pause/Resume state for the chat composer — the only\n * human-to-agent input surface. The draft survives a failed send (nothing is\n * more hostile than losing a typed message); each send carries a generated\n * `clientEventId` so retries stay idempotent server-side.\n */\nexport function useComposer(\n sessionId: string | null | undefined,\n options: UseComposerOptions = {},\n): ComposerState {\n const { client, workspaceId, registerSessionReconciler } = useEmbeddedSession(options);\n const durableDrafts = options.draftPersistence !== \"disabled\";\n const targetKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${durableDrafts ? \"durable\" : \"disabled\"}`;\n const [value, setValue] = useState(\"\");\n const [stateTargetKey, setStateTargetKey] = useState(targetKey);\n const [sending, setSending] = useState(false);\n const [pausing, setPausing] = useState(false);\n const [resuming, setResuming] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [draft, setDraft] = useState<ComposerDraft | null>(null);\n const [draftLoading, setDraftLoading] = useState(Boolean(sessionId) && durableDrafts);\n const [draftSaving, setDraftSaving] = useState(false);\n const [draftConflict, setDraftConflict] = useState<Error | null>(null);\n const [restoredResources, setRestoredResources] = useState<ResourceRef[]>([]);\n const pendingClientEventId = useRef<string | null>(null);\n const draftRef = useRef<ComposerDraft | null>(null);\n const draftLoadErrorRef = useRef<Error | null>(null);\n const draftReadGeneration = useRef(0);\n const localEditRevision = useRef(0);\n const targetGeneration = useRef(0);\n const lastSavedSignature = useRef<string | null>(null);\n const saveChain = useRef<Promise<void>>(Promise.resolve());\n const onSent = options.onSent;\n const onDraftApplied = options.onDraftApplied;\n // Read through a ref so a new extras closure (created every render by\n // callers passing inline functions) does not invalidate `send`.\n const sendExtrasRef = useRef(options.sendExtras);\n useLayoutEffect(() => {\n sendExtrasRef.current = options.sendExtras;\n }, [options.sendExtras]);\n const liveExtras = resolveSendExtras(options.sendExtras);\n const liveExtrasVersion = JSON.stringify(liveExtras);\n\n // A composer is bound to one session: switching targets must not leak the\n // previous session's draft, error, or retry idempotency key. Revoke the old\n // target only when the new render commits: a suspended transition must leave\n // the still-visible composer fully interactive.\n const targetKeyRef = useRef(targetKey);\n useLayoutEffect(() => {\n if (targetKeyRef.current === targetKey) return;\n targetKeyRef.current = targetKey;\n targetGeneration.current += 1;\n pendingClientEventId.current = null;\n localEditRevision.current = 0;\n draftReadGeneration.current += 1;\n draftRef.current = null;\n draftLoadErrorRef.current = null;\n lastSavedSignature.current = null;\n // An unresolved save for the old target must neither block nor join the\n // new target's autosave chain. Its own generation fence drops settlement.\n saveChain.current = Promise.resolve();\n setStateTargetKey(targetKey);\n setValue(\"\");\n setSending(false);\n setPausing(false);\n setResuming(false);\n setError(null);\n setDraft(null);\n setDraftLoading(Boolean(sessionId) && durableDrafts);\n setDraftSaving(false);\n setDraftConflict(null);\n setRestoredResources([]);\n }, [durableDrafts, sessionId, targetKey]);\n\n const applyDraft = useCallback(\n (next: ComposerDraft): void => {\n if (targetKeyRef.current !== targetKey) return;\n if (!durableDrafts) {\n pendingClientEventId.current = null;\n localEditRevision.current += 1;\n draftRef.current = null;\n lastSavedSignature.current = null;\n setDraft(null);\n setValue(next.text);\n setRestoredResources(next.resources);\n setDraftConflict(null);\n return;\n }\n draftRef.current = next;\n lastSavedSignature.current = draftSignature(draftPayload(next));\n localEditRevision.current += 1;\n pendingClientEventId.current = null;\n setDraft(next);\n setValue(next.text);\n setRestoredResources(next.resources);\n setDraftConflict(null);\n onDraftApplied?.(next);\n },\n [durableDrafts, onDraftApplied, targetKey],\n );\n\n const loadDraft = useCallback(\n async (replaceLocal: boolean, rejectOnFailure = false): Promise<void> => {\n if (targetKeyRef.current !== targetKey) return;\n if (!sessionId || !durableDrafts) {\n setDraftLoading(false);\n return;\n }\n const generation = targetGeneration.current;\n const readTicket = ++draftReadGeneration.current;\n const localAtStart = localEditRevision.current;\n setDraftLoading(true);\n try {\n const fetched = await client.getComposerDraft(workspaceId, sessionId);\n if (generation !== targetGeneration.current || targetKeyRef.current !== targetKey) {\n return;\n }\n const ownsLatestRead = readTicket === draftReadGeneration.current;\n const currentRevision = draftRef.current?.revision ?? -1;\n if ((ownsLatestRead || rejectOnFailure) && fetched.revision >= currentRevision) {\n draftRef.current = fetched;\n setDraft(fetched);\n setDraftConflict(null);\n if (replaceLocal || localAtStart === localEditRevision.current) {\n lastSavedSignature.current = draftSignature(draftPayload(fetched));\n setValue(fetched.text);\n setRestoredResources(fetched.resources);\n onDraftApplied?.(fetched);\n }\n }\n const authoritativeRevision = draftRef.current?.revision ?? -1;\n if (rejectOnFailure && authoritativeRevision < fetched.revision) {\n throw new TypeError(\"Composer draft reconciliation did not commit authoritative state\");\n }\n if (ownsLatestRead || rejectOnFailure) {\n const recoveredError = draftLoadErrorRef.current;\n draftLoadErrorRef.current = null;\n if (recoveredError) {\n setError((current) => (current === recoveredError ? null : current));\n }\n }\n } catch (cause) {\n if (\n generation === targetGeneration.current &&\n targetKeyRef.current === targetKey &&\n (readTicket === draftReadGeneration.current || rejectOnFailure)\n ) {\n const failure = asError(cause);\n draftLoadErrorRef.current = failure;\n setError(failure);\n }\n if (rejectOnFailure) throw cause;\n } finally {\n if (\n generation === targetGeneration.current &&\n targetKeyRef.current === targetKey &&\n readTicket === draftReadGeneration.current\n ) {\n setDraftLoading(false);\n }\n }\n },\n [client, durableDrafts, onDraftApplied, sessionId, targetKey, workspaceId],\n );\n\n useEffect(() => {\n if (!sessionId || !durableDrafts) {\n setDraftLoading(false);\n return;\n }\n void loadDraft(false);\n }, [durableDrafts, loadDraft, sessionId]);\n useEffect(() => {\n if (!sessionId || !durableDrafts) return;\n return registerSessionReconciler(sessionId, \"composer\", async () => await loadDraft(false));\n }, [durableDrafts, loadDraft, registerSessionReconciler, sessionId]);\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n isComposerDraftEvent,\n () => void loadDraft(false),\n {\n enabled: Boolean(sessionId) && durableDrafts,\n ...(options.events !== undefined ? { events: options.events } : {}),\n },\n async () => await loadDraft(false, true),\n );\n\n const currentDraftPayload = useCallback((): SaveComposerDraftRequest | null => {\n if (targetKeyRef.current !== targetKey) return null;\n const base = draftRef.current;\n if (!base) return null;\n const extras = resolveSendExtras(sendExtrasRef.current);\n return {\n expectedRevision: base.revision,\n text: value,\n resources: mergeResources(restoredResources, extras.resources ?? []),\n tools: extras.tools ?? base.tools,\n model: extras.model ?? base.model,\n reasoningEffort: extras.reasoningEffort ?? base.reasoningEffort,\n };\n }, [restoredResources, targetKey, value]);\n\n const persistPayload = useCallback(\n async (payload: SaveComposerDraftRequest): Promise<boolean> => {\n const ownedTargetKey = targetKey;\n const ownedGeneration = targetGeneration.current;\n if (!sessionId || targetKeyRef.current !== ownedTargetKey) return false;\n let success = false;\n const run = async () => {\n if (\n targetKeyRef.current !== ownedTargetKey ||\n targetGeneration.current !== ownedGeneration\n ) {\n return;\n }\n const current = draftRef.current;\n if (!current) return;\n const request = { ...payload, expectedRevision: current.revision };\n const signature = draftSignature(request);\n if (signature === lastSavedSignature.current) {\n success = true;\n return;\n }\n setDraftSaving(true);\n try {\n const saved = await client.saveComposerDraft(workspaceId, sessionId, request);\n if (\n targetKeyRef.current !== ownedTargetKey ||\n targetGeneration.current !== ownedGeneration\n ) {\n return;\n }\n draftRef.current = saved;\n setDraft(saved);\n lastSavedSignature.current = signature;\n setDraftConflict(null);\n success = true;\n } catch (cause) {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n const problem = asError(cause);\n setDraftConflict(problem);\n setError(problem);\n }\n } finally {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setDraftSaving(false);\n }\n }\n };\n saveChain.current = saveChain.current.then(run, run);\n await saveChain.current;\n return success;\n },\n [client, sessionId, targetKey, workspaceId],\n );\n\n // Private durable autosave. A newer local edit is never replaced by an older\n // response; saves serialize and each reads the latest acknowledged revision.\n useEffect(() => {\n if (\n !durableDrafts ||\n !sessionId ||\n draftLoading ||\n sending ||\n !draftRef.current ||\n draftConflict\n )\n return;\n const payload = currentDraftPayload();\n if (!payload || draftSignature(payload) === lastSavedSignature.current) return;\n const timer = window.setTimeout(() => void persistPayload(payload), 500);\n return () => window.clearTimeout(timer);\n }, [\n currentDraftPayload,\n durableDrafts,\n draftConflict,\n draftLoading,\n liveExtrasVersion,\n persistPayload,\n sending,\n sessionId,\n ]);\n\n const dispatch = useCallback(\n async (delivery: \"send\" | \"steer\", explicit?: string): Promise<boolean> => {\n const ownedTargetKey = targetKey;\n const ownedGeneration = targetGeneration.current;\n const draftAtSend = value;\n const rawText = explicit ?? draftAtSend;\n const hasText = rawText.trim().length > 0;\n // Resolve the extras once: a file-only message (empty text + ≥1 ready\n // resource) is legitimate, so we must not bail on empty text alone.\n const extras = resolveSendExtras(sendExtrasRef.current);\n const hasResources = restoredResources.length > 0 || (extras.resources?.length ?? 0) > 0;\n if (\n (!hasText && !hasResources) ||\n !sessionId ||\n sending ||\n targetKeyRef.current !== ownedTargetKey\n ) {\n return false;\n }\n // Reuse the clientEventId across retries of the same draft so a\n // timeout + resend cannot double-deliver the message.\n pendingClientEventId.current ??= generateClientEventId();\n setSending(true);\n setError(null);\n try {\n // Trimming is only an emptiness check. A non-blank prompt must be sent\n // byte-for-byte as the user wrote it, because expectedDraftRevision\n // binds the submission to that exact durable draft. File-only messages\n // use the same non-blank placeholder in both the saved draft and the\n // submitted prompt so the content fence cannot reject its own client.\n const sendText = hasText ? rawText : FILE_ONLY_MESSAGE_TEXT;\n const currentPayload = currentDraftPayload();\n const payload = currentPayload ? { ...currentPayload, text: sendText } : null;\n if (payload && !(await persistPayload(payload))) return false;\n if (\n targetKeyRef.current !== ownedTargetKey ||\n targetGeneration.current !== ownedGeneration\n ) {\n return false;\n }\n const input = composeSendInput(sendText, pendingClientEventId.current, extras, {\n ...(options.effectiveControl?.controlEtag\n ? { controlEtag: options.effectiveControl.controlEtag }\n : {}),\n ...(draftRef.current ? { expectedDraftRevision: draftRef.current.revision } : {}),\n resources: mergeResources(restoredResources, extras.resources ?? []),\n });\n if (delivery === \"steer\") {\n await client.steerMessage(workspaceId, sessionId, input);\n } else {\n await client.sendMessage(workspaceId, sessionId, input);\n }\n if (\n targetKeyRef.current !== ownedTargetKey ||\n targetGeneration.current !== ownedGeneration\n ) {\n return false;\n }\n pendingClientEventId.current = null;\n const previousDraft = draftRef.current;\n if (previousDraft) {\n const cleared = {\n ...previousDraft,\n revision: 0,\n text: \"\",\n resources: [],\n sourceTurnId: null,\n sourceTurnVersion: null,\n updatedAt: null,\n };\n draftRef.current = cleared;\n setDraft(cleared);\n setRestoredResources([]);\n lastSavedSignature.current = draftSignature(draftPayload(cleared));\n }\n if (explicit === undefined) {\n // Clear only the draft that was sent: edits made while the request\n // was in flight were never delivered and must survive.\n setValue((current) => (current === draftAtSend ? \"\" : current));\n }\n onSent?.(sendText);\n return true;\n } catch (cause) {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n return false;\n } finally {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setSending(false);\n }\n }\n },\n [\n client,\n currentDraftPayload,\n onSent,\n options.effectiveControl?.controlEtag,\n persistPayload,\n restoredResources,\n sending,\n sessionId,\n targetKey,\n value,\n workspaceId,\n ],\n );\n\n const send = useCallback(async (text?: string) => await dispatch(\"send\", text), [dispatch]);\n const steer = useCallback(async (text?: string) => await dispatch(\"steer\", text), [dispatch]);\n\n // A send is possible with non-empty text OR with ≥1 attached resource (a\n // file-only message). Resources ride in `sendExtras`, so we resolve them here\n // — keeping useComposer attachment-agnostic while still lighting up the send\n // affordance the moment a file is ready. ChatComposer additionally gates this\n // on its `attachments.uploading` flag so a message never departs mid-upload.\n const hasReadyResources = restoredResources.length > 0 || (liveExtras.resources?.length ?? 0) > 0;\n\n const pause = useCallback(\n async (reason?: string): Promise<void> => {\n const ownedTargetKey = targetKey;\n const ownedGeneration = targetGeneration.current;\n if (!sessionId || pausing || targetKeyRef.current !== ownedTargetKey) {\n return;\n }\n setPausing(true);\n setError(null);\n try {\n await client.pauseSession(workspaceId, sessionId, {\n ...(reason !== undefined ? { reason } : {}),\n ...(options.effectiveControl?.controlEtag\n ? { expectedControlEtag: options.effectiveControl.controlEtag }\n : {}),\n });\n } catch (cause) {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n } finally {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setPausing(false);\n }\n }\n },\n [client, workspaceId, sessionId, pausing, options.effectiveControl?.controlEtag, targetKey],\n );\n\n const resume = useCallback(\n async (reason?: string): Promise<void> => {\n const ownedTargetKey = targetKey;\n const ownedGeneration = targetGeneration.current;\n if (!sessionId || resuming || targetKeyRef.current !== ownedTargetKey) return;\n setResuming(true);\n setError(null);\n try {\n await client.resumeSession(workspaceId, sessionId, {\n ...(reason !== undefined ? { reason } : {}),\n ...(options.effectiveControl?.controlEtag\n ? { expectedControlEtag: options.effectiveControl.controlEtag }\n : {}),\n });\n } catch (cause) {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n } finally {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setResuming(false);\n }\n }\n },\n [client, workspaceId, sessionId, resuming, options.effectiveControl?.controlEtag, targetKey],\n );\n\n const resumeScope = useCallback(\n async (option: EffectiveControlResumeOption): Promise<void> => {\n const ownedTargetKey = targetKey;\n const ownedGeneration = targetGeneration.current;\n if (!sessionId || resuming || targetKeyRef.current !== ownedTargetKey) return;\n setResuming(true);\n setError(null);\n try {\n if (option.scope === \"workspace\") {\n if (!client.setWorkspaceInferenceState) {\n throw new Error(\"Workspace-level resume is not available through this client\");\n }\n const workspaceBlocker = options.effectiveControl?.blockers.find(\n (blocker) => blocker.kind === \"workspace\",\n );\n await client.setWorkspaceInferenceState(workspaceId, {\n action: \"resume\",\n clientEventId: generateClientEventId(),\n ...(workspaceBlocker ? { expectedRevision: workspaceBlocker.revision } : {}),\n });\n } else if (option.scope === \"session\" && option.targetId) {\n const target = await client.getQueue(workspaceId, option.targetId);\n if (\n targetKeyRef.current !== ownedTargetKey ||\n targetGeneration.current !== ownedGeneration\n ) {\n return;\n }\n await client.resumeSession(workspaceId, option.targetId, {\n expectedControlEtag: target.effectiveControl.controlEtag,\n });\n } else {\n await client.resumeSession(workspaceId, sessionId, {\n ...(options.effectiveControl?.controlEtag\n ? { expectedControlEtag: options.effectiveControl.controlEtag }\n : {}),\n });\n }\n } catch (cause) {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setError(asError(cause));\n }\n } finally {\n if (\n targetKeyRef.current === ownedTargetKey &&\n targetGeneration.current === ownedGeneration\n ) {\n setResuming(false);\n }\n }\n },\n [client, options.effectiveControl, resuming, sessionId, targetKey, workspaceId],\n );\n\n const updateValue = useCallback(\n (next: string) => {\n if (targetKeyRef.current !== targetKey) return;\n pendingClientEventId.current = null;\n localEditRevision.current += 1;\n setValue(next);\n },\n [targetKey],\n );\n\n const removeRestoredResource = useCallback(\n (index: number) => {\n if (targetKeyRef.current !== targetKey) return;\n localEditRevision.current += 1;\n setRestoredResources((current) => current.filter((_, candidate) => candidate !== index));\n },\n [targetKey],\n );\n\n const resolveDraftConflict = useCallback(\n async (choice: \"keep_mine\" | \"use_remote\"): Promise<void> => {\n const ownedTargetKey = targetKey;\n const ownedGeneration = targetGeneration.current;\n if (!sessionId || !durableDrafts || targetKeyRef.current !== ownedTargetKey) return;\n const remote = await client.getComposerDraft(workspaceId, sessionId);\n if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {\n return;\n }\n if (choice === \"use_remote\") {\n applyDraft(remote);\n return;\n }\n draftRef.current = remote;\n setDraft(remote);\n setDraftConflict(null);\n const payload = currentDraftPayload();\n if (payload) await persistPayload({ ...payload, expectedRevision: remote.revision });\n },\n [\n applyDraft,\n client,\n currentDraftPayload,\n durableDrafts,\n persistPayload,\n sessionId,\n targetKey,\n workspaceId,\n ],\n );\n\n const identityMatches = stateTargetKey === targetKey;\n const reloadDraft = useCallback(async () => await loadDraft(true), [loadDraft]);\n const clearError = useCallback(() => {\n if (targetKeyRef.current !== targetKey) return;\n setError(null);\n setDraftConflict(null);\n }, [targetKey]);\n\n return {\n value: identityMatches ? value : \"\",\n setValue: updateValue,\n send,\n steer,\n sending: identityMatches ? sending : false,\n canSend:\n identityMatches &&\n Boolean(sessionId) &&\n !sending &&\n (value.trim().length > 0 || hasReadyResources),\n pause,\n pausing: identityMatches ? pausing : false,\n resume,\n resumeScope,\n resuming: identityMatches ? resuming : false,\n draft: identityMatches ? draft : null,\n draftRevision: identityMatches ? (draft?.revision ?? 0) : 0,\n draftLoading: identityMatches ? draftLoading : Boolean(sessionId) && durableDrafts,\n draftSaving: identityMatches ? draftSaving : false,\n draftConflict: identityMatches ? draftConflict : null,\n draftPersistence: durableDrafts ? \"durable\" : \"disabled\",\n applyDraft,\n reloadDraft,\n resolveDraftConflict,\n restoredResources: identityMatches ? restoredResources : [],\n removeRestoredResource,\n error: identityMatches ? error : null,\n clearError,\n };\n}\n\n/** Events that can atomically replace or clear this subject's durable draft. */\nexport function isComposerDraftEvent(event: Pick<SessionEvent, \"type\">): boolean {\n return event.type === \"user.message\" || event.type === \"session.queue.changed\";\n}\n\n/**\n * Default text for a file-only message (attachment(s) present, no typed draft).\n * Kept non-empty so the wire contract (`text: z.string().min(1)`) and the\n * worker's non-whitespace guard accept it; the attached files still ride in\n * `resources`. Exported for tests.\n */\nexport const FILE_ONLY_MESSAGE_TEXT = \"(see attached files)\";\n\n/** Resolve possibly-deferred extras to a concrete bag (function evaluated now). */\nexport function resolveSendExtras(\n extras: ComposerSendExtras | (() => ComposerSendExtras) | undefined,\n): ComposerSendExtras {\n return (typeof extras === \"function\" ? extras() : extras) ?? {};\n}\n\n/**\n * Merge the draft text + idempotency key with caller-provided extras. The\n * text and clientEventId always win over extras. Exported for tests.\n */\nexport function composeSendInput(\n text: string,\n clientEventId: string,\n extras: ComposerSendExtras | (() => ComposerSendExtras) | undefined,\n bound: Partial<SendMessageInput> = {},\n): SendMessageInput {\n return { ...resolveSendExtras(extras), ...bound, text, clientEventId };\n}\n\n/** Submit on plain Enter; Shift+Enter inserts a newline. Exported for tests. */\nexport function shouldSubmitOnKey(event: {\n key: string;\n shiftKey: boolean;\n metaKey?: boolean;\n ctrlKey?: boolean;\n nativeEvent?: { isComposing?: boolean };\n}): boolean {\n if (event.key !== \"Enter\" || event.shiftKey) {\n return false;\n }\n return event.nativeEvent?.isComposing !== true;\n}\n\n/** Cmd/Ctrl+Enter steers; ordinary Enter appends to the queue. */\nexport function shouldSteerOnKey(event: { metaKey?: boolean; ctrlKey?: boolean }): boolean {\n return event.metaKey === true || event.ctrlKey === true;\n}\n\nfunction generateClientEventId(): string {\n return globalThis.crypto.randomUUID();\n}\n\nfunction asError(cause: unknown): Error {\n return cause instanceof Error ? cause : new Error(String(cause));\n}\n\nfunction draftPayload(draft: ComposerDraft): SaveComposerDraftRequest {\n return {\n expectedRevision: draft.revision,\n text: draft.text,\n resources: draft.resources,\n tools: draft.tools,\n model: draft.model,\n reasoningEffort: draft.reasoningEffort,\n };\n}\n\nfunction draftSignature(payload: SaveComposerDraftRequest): string {\n const { expectedRevision: _revision, ...content } = payload;\n return JSON.stringify(content);\n}\n\nfunction mergeResources(base: ResourceRef[], additions: ResourceRef[]): ResourceRef[] {\n const seen = new Set<string>();\n return [...base, ...additions].filter((resource) => {\n const key = JSON.stringify(resource);\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/lib/cn.ts
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
4
|
+
var twMerge = extendTailwindMerge({
|
|
5
|
+
extend: {
|
|
6
|
+
classGroups: {
|
|
7
|
+
"font-size": [{ text: ["og-xs", "og-sm", "og-base", "og-md"] }]
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
function cn(...inputs) {
|
|
12
|
+
return twMerge(clsx(inputs));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
cn
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=chunk-TK7G6XLT.js.map
|