@opengeni/react 0.42.1 → 0.44.2
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/dist/{chunk-SJKT4TKW.js → chunk-23EJ676W.js} +3 -1
- package/dist/chunk-23EJ676W.js.map +1 -0
- package/dist/{chunk-Q2NCKWTK.js → chunk-HFO4ERGQ.js} +51 -4
- package/dist/chunk-HFO4ERGQ.js.map +1 -0
- package/dist/{chunk-4IJCL7YO.js → chunk-LWR4MXSS.js} +4 -1
- package/dist/chunk-LWR4MXSS.js.map +1 -0
- package/dist/{chunk-UWYTCQWW.js → chunk-QQRM3DO3.js} +93 -22
- package/dist/{chunk-UWYTCQWW.js.map → chunk-QQRM3DO3.js.map} +1 -1
- package/dist/{chunk-JALF5FI3.js → chunk-SRFUT2ZU.js} +2 -2
- package/dist/{chunk-WZT5G5OR.js → chunk-U6K24XQD.js} +5 -4
- package/dist/{chunk-WZT5G5OR.js.map → chunk-U6K24XQD.js.map} +1 -1
- package/dist/{chunk-KR2SK5GJ.js → chunk-YNYIAYXQ.js} +427 -94
- package/dist/chunk-YNYIAYXQ.js.map +1 -0
- package/dist/components/chat-composer.d.ts +5 -1
- package/dist/components/composer-transcription-control.d.ts +3 -1
- package/dist/components/composer.d.ts +6 -4
- package/dist/components/session-chrome.d.ts +1 -1
- package/dist/composer.js +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +70 -21
- package/dist/index.js.map +1 -1
- package/dist/model-policy.d.ts +2 -0
- package/dist/model-policy.js +1 -1
- package/dist/realtime/realtime-control.d.ts +29 -1
- package/dist/realtime.d.ts +1 -1
- package/dist/realtime.js +269 -151
- package/dist/realtime.js.map +1 -1
- package/dist/session-ui.js +2 -2
- package/dist/session.js +3 -3
- package/dist/timeline/index.d.ts +1 -1
- package/dist/timeline/parsers.d.ts +13 -8
- package/package.json +2 -2
- package/src/components/chat-composer.tsx +58 -19
- package/src/components/composer-transcription-control.tsx +194 -165
- package/src/components/composer.tsx +78 -14
- package/src/components/model-policy-picker.tsx +7 -2
- package/src/components/session-chrome.tsx +89 -78
- package/src/hooks/use-composer.ts +79 -9
- package/src/index.ts +2 -0
- package/src/model-policy.ts +4 -0
- package/src/realtime/realtime-control.tsx +307 -137
- package/src/realtime.ts +1 -0
- package/src/timeline/index.ts +2 -0
- package/src/timeline/parsers.ts +201 -19
- package/src/timeline/projection.ts +11 -0
- package/src/timeline/tool-renderers.tsx +213 -8
- package/src/timeline/turn-summary.tsx +7 -2
- package/styles/tokens.css +8 -5
- package/dist/chunk-4IJCL7YO.js.map +0 -1
- package/dist/chunk-KR2SK5GJ.js.map +0 -1
- package/dist/chunk-Q2NCKWTK.js.map +0 -1
- package/dist/chunk-SJKT4TKW.js.map +0 -1
- /package/dist/{chunk-JALF5FI3.js.map → chunk-SRFUT2ZU.js.map} +0 -0
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* | --- | --- |
|
|
12
12
|
* | `--og-session-chrome-surface` / `-open` | Dock fill (collapsed / expanded) |
|
|
13
13
|
* | `--og-session-chrome-border` / `-open` | Dock edge |
|
|
14
|
-
* | `--og-session-chrome-highlight` | Sliding chip selection
|
|
14
|
+
* | `--og-session-chrome-highlight` / `-ring` | Sliding chip selection fill + edge |
|
|
15
15
|
* | `--og-session-chrome-shadow` / `-open` | Elevation |
|
|
16
16
|
* | `--og-session-chrome-radius` | Dock corner radius |
|
|
17
17
|
* | `--og-session-chrome-chip-min-height` | Signal chip height |
|
|
@@ -394,7 +394,7 @@ export function SessionChrome({
|
|
|
394
394
|
const chipRefs = useRef<Partial<Record<SessionChromeSignalId, HTMLButtonElement | null>>>({});
|
|
395
395
|
const railRef = useRef<HTMLDivElement | null>(null);
|
|
396
396
|
const panelBodyRef = useRef<HTMLDivElement | null>(null);
|
|
397
|
-
const [pill, setPill] = useState({ left: 0, width: 0, opacity: 0 });
|
|
397
|
+
const [pill, setPill] = useState({ left: 0, top: 0, width: 0, height: 0, opacity: 0 });
|
|
398
398
|
const [panelHeight, setPanelHeight] = useState(0);
|
|
399
399
|
|
|
400
400
|
const signalIds = signals.map((signal) => signal.id).join(",");
|
|
@@ -421,11 +421,15 @@ export function SessionChrome({
|
|
|
421
421
|
}
|
|
422
422
|
const chip = chipRefs.current[active];
|
|
423
423
|
if (!chip) return;
|
|
424
|
+
// Measure against the chip's own box so a wrapped multi-row rail never
|
|
425
|
+
// stretches the highlight into a tall stripe across every signal.
|
|
424
426
|
const railBox = rail.getBoundingClientRect();
|
|
425
427
|
const chipBox = chip.getBoundingClientRect();
|
|
426
428
|
setPill({
|
|
427
429
|
left: chipBox.left - railBox.left,
|
|
430
|
+
top: chipBox.top - railBox.top,
|
|
428
431
|
width: chipBox.width,
|
|
432
|
+
height: chipBox.height,
|
|
429
433
|
opacity: 1,
|
|
430
434
|
});
|
|
431
435
|
};
|
|
@@ -433,6 +437,9 @@ export function SessionChrome({
|
|
|
433
437
|
if (!rail) return;
|
|
434
438
|
const observer = new ResizeObserver(measure);
|
|
435
439
|
observer.observe(rail);
|
|
440
|
+
for (const chip of Object.values(chipRefs.current)) {
|
|
441
|
+
if (chip) observer.observe(chip);
|
|
442
|
+
}
|
|
436
443
|
window.addEventListener("resize", measure);
|
|
437
444
|
return () => {
|
|
438
445
|
observer.disconnect();
|
|
@@ -568,88 +575,92 @@ export function SessionChrome({
|
|
|
568
575
|
}}
|
|
569
576
|
>
|
|
570
577
|
<div
|
|
571
|
-
|
|
572
|
-
className="relative flex flex-wrap items-stretch"
|
|
578
|
+
className="relative"
|
|
573
579
|
style={{
|
|
574
|
-
|
|
575
|
-
|
|
580
|
+
paddingTop: "var(--og-session-chrome-rail-pad)",
|
|
581
|
+
paddingBottom: "var(--og-session-chrome-rail-pad)",
|
|
582
|
+
paddingLeft: "var(--og-session-chrome-rail-pad)",
|
|
583
|
+
paddingRight: "var(--og-session-chrome-rail-pad)",
|
|
576
584
|
}}
|
|
577
585
|
>
|
|
578
|
-
<
|
|
579
|
-
|
|
580
|
-
className="
|
|
586
|
+
<div
|
|
587
|
+
ref={railRef}
|
|
588
|
+
className="relative flex flex-wrap items-center"
|
|
581
589
|
style={{
|
|
582
|
-
|
|
583
|
-
bottom: "var(--og-session-chrome-rail-pad)",
|
|
584
|
-
background: "var(--og-session-chrome-highlight)",
|
|
590
|
+
gap: "var(--og-session-chrome-chip-gap)",
|
|
585
591
|
}}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
"
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
selected
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
592
|
+
>
|
|
593
|
+
<motion.div
|
|
594
|
+
aria-hidden
|
|
595
|
+
className="pointer-events-none absolute left-0 top-0 rounded-og-md"
|
|
596
|
+
style={{
|
|
597
|
+
background: "var(--og-session-chrome-highlight)",
|
|
598
|
+
boxShadow: "inset 0 0 0 1px var(--og-session-chrome-highlight-ring)",
|
|
599
|
+
}}
|
|
600
|
+
initial={false}
|
|
601
|
+
animate={{
|
|
602
|
+
x: pill.left,
|
|
603
|
+
y: pill.top,
|
|
604
|
+
width: pill.width,
|
|
605
|
+
height: pill.height,
|
|
606
|
+
opacity: pill.opacity,
|
|
607
|
+
}}
|
|
608
|
+
transition={{ duration: shellDuration, ease }}
|
|
609
|
+
/>
|
|
610
|
+
{signals.map((signal) => {
|
|
611
|
+
const selected = active === signal.id;
|
|
612
|
+
return (
|
|
613
|
+
<button
|
|
614
|
+
key={signal.id}
|
|
615
|
+
type="button"
|
|
616
|
+
ref={(node) => {
|
|
617
|
+
chipRefs.current[signal.id] = node;
|
|
618
|
+
}}
|
|
619
|
+
aria-expanded={selected}
|
|
620
|
+
aria-controls={panelId}
|
|
621
|
+
aria-label={selected ? `Close ${signal.label}` : undefined}
|
|
622
|
+
data-testid={`session-chrome-${signal.id}`}
|
|
623
|
+
data-og-session-chrome-signal={signal.id}
|
|
624
|
+
onClick={() => setActive(selected ? null : signal.id)}
|
|
625
|
+
className={cn(
|
|
626
|
+
"group relative z-[1] inline-flex min-h-[var(--og-session-chrome-chip-min-height)] max-w-full items-center gap-1 rounded-og-md py-1 text-left text-og-xs outline-none",
|
|
627
|
+
// Coarse pointers keep a 44px target (session-pins acceptance).
|
|
628
|
+
"pointer-coarse:min-h-11",
|
|
629
|
+
"transition-colors duration-150 motion-reduce:transition-none",
|
|
630
|
+
"hover:text-og-fg focus-visible:bg-og-surface-3/50",
|
|
631
|
+
selected ? "text-og-fg" : "text-og-fg-muted",
|
|
632
|
+
)}
|
|
633
|
+
style={{
|
|
634
|
+
paddingInline: "var(--og-session-chrome-chip-pad-x)",
|
|
635
|
+
}}
|
|
636
|
+
>
|
|
637
|
+
<span className={cn("shrink-0", toneClass(signal.tone, selected))}>
|
|
638
|
+
{signal.icon}
|
|
639
|
+
</span>
|
|
640
|
+
<span className="shrink-0 font-medium text-og-fg">{signal.label}</span>
|
|
641
|
+
{signal.detail ? (
|
|
642
|
+
<>
|
|
643
|
+
<span aria-hidden className="shrink-0 text-og-fg-subtle/60">
|
|
644
|
+
·
|
|
645
|
+
</span>
|
|
646
|
+
<span className="min-w-0 max-w-[8.5rem] truncate text-og-fg sm:max-w-[12rem]">
|
|
647
|
+
{signal.detail}
|
|
648
|
+
</span>
|
|
649
|
+
</>
|
|
650
|
+
) : null}
|
|
651
|
+
{selected ? (
|
|
652
|
+
<span
|
|
653
|
+
data-testid="session-chrome-close"
|
|
654
|
+
className="ml-0.5 inline-flex size-4 shrink-0 items-center justify-center rounded-og-sm text-og-fg-subtle transition-colors group-hover:text-og-fg pointer-coarse:size-5"
|
|
655
|
+
aria-hidden
|
|
656
|
+
>
|
|
657
|
+
<XIcon className="size-3" />
|
|
630
658
|
</span>
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
)
|
|
635
|
-
|
|
636
|
-
<AnimatePresence initial={false}>
|
|
637
|
-
{open ? (
|
|
638
|
-
<motion.button
|
|
639
|
-
key="close"
|
|
640
|
-
type="button"
|
|
641
|
-
aria-label="Close session chrome panel"
|
|
642
|
-
onClick={() => setActive(null)}
|
|
643
|
-
initial={reduceMotion ? false : { opacity: 0 }}
|
|
644
|
-
animate={{ opacity: 1 }}
|
|
645
|
-
exit={reduceMotion ? { opacity: 1 } : { opacity: 0 }}
|
|
646
|
-
transition={{ duration: crossfadeDuration, ease }}
|
|
647
|
-
className="relative z-[1] ml-auto inline-flex size-7 shrink-0 items-center justify-center rounded-og-md text-og-fg-subtle outline-none transition-colors hover:bg-og-surface-3/60 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 pointer-coarse:size-11"
|
|
648
|
-
>
|
|
649
|
-
<XIcon className="size-3" />
|
|
650
|
-
</motion.button>
|
|
651
|
-
) : null}
|
|
652
|
-
</AnimatePresence>
|
|
659
|
+
) : null}
|
|
660
|
+
</button>
|
|
661
|
+
);
|
|
662
|
+
})}
|
|
663
|
+
</div>
|
|
653
664
|
</div>
|
|
654
665
|
|
|
655
666
|
<motion.div
|
|
@@ -585,6 +585,22 @@ export function useComposer(
|
|
|
585
585
|
}
|
|
586
586
|
void loadDraft(false);
|
|
587
587
|
}, [durableDrafts, loadDraft, sessionId]);
|
|
588
|
+
// After long background / sleep the in-memory revision is often stale while
|
|
589
|
+
// a prior autosave already advanced the server. Soft-reload on wake so the
|
|
590
|
+
// next keystroke does not OCC against a dead revision.
|
|
591
|
+
useEffect(() => {
|
|
592
|
+
if (!sessionId || !durableDrafts) return;
|
|
593
|
+
const onWake = () => {
|
|
594
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
|
|
595
|
+
void loadDraft(false);
|
|
596
|
+
};
|
|
597
|
+
document.addEventListener("visibilitychange", onWake);
|
|
598
|
+
window.addEventListener("pageshow", onWake);
|
|
599
|
+
return () => {
|
|
600
|
+
document.removeEventListener("visibilitychange", onWake);
|
|
601
|
+
window.removeEventListener("pageshow", onWake);
|
|
602
|
+
};
|
|
603
|
+
}, [durableDrafts, loadDraft, sessionId]);
|
|
588
604
|
useEffect(() => {
|
|
589
605
|
if (!sessionId || !durableDrafts) return;
|
|
590
606
|
return registerSessionReconciler(sessionId, "composer", async () => await loadDraft(false));
|
|
@@ -692,7 +708,16 @@ export function useComposer(
|
|
|
692
708
|
}
|
|
693
709
|
setDraftSaving(true);
|
|
694
710
|
try {
|
|
695
|
-
const saved = await
|
|
711
|
+
const saved = await saveComposerDraftWithStaleRetry({
|
|
712
|
+
client,
|
|
713
|
+
workspaceId,
|
|
714
|
+
sessionId,
|
|
715
|
+
request,
|
|
716
|
+
onAdoptRemote: (remote) => {
|
|
717
|
+
draftRef.current = remote;
|
|
718
|
+
setDraft(remote);
|
|
719
|
+
},
|
|
720
|
+
});
|
|
696
721
|
if (
|
|
697
722
|
targetKeyRef.current !== ownedTargetKey ||
|
|
698
723
|
targetGeneration.current !== ownedGeneration
|
|
@@ -701,8 +726,12 @@ export function useComposer(
|
|
|
701
726
|
}
|
|
702
727
|
draftRef.current = saved;
|
|
703
728
|
setDraft(saved);
|
|
704
|
-
lastSavedSignature.current =
|
|
729
|
+
lastSavedSignature.current = draftSignature({
|
|
730
|
+
...request,
|
|
731
|
+
expectedRevision: saved.revision,
|
|
732
|
+
});
|
|
705
733
|
setDraftConflict(null);
|
|
734
|
+
setError(null);
|
|
706
735
|
success = true;
|
|
707
736
|
} catch (cause) {
|
|
708
737
|
if (
|
|
@@ -1260,11 +1289,13 @@ export function useComposer(
|
|
|
1260
1289
|
}
|
|
1261
1290
|
if (choice === "use_remote") {
|
|
1262
1291
|
applyDraft(remote);
|
|
1292
|
+
setError(null);
|
|
1263
1293
|
return;
|
|
1264
1294
|
}
|
|
1265
1295
|
draftRef.current = remote;
|
|
1266
1296
|
setDraft(remote);
|
|
1267
1297
|
setDraftConflict(null);
|
|
1298
|
+
setError(null);
|
|
1268
1299
|
const payload = currentDraftPayload();
|
|
1269
1300
|
if (payload) await persistPayload({ ...payload, expectedRevision: remote.revision });
|
|
1270
1301
|
},
|
|
@@ -1393,13 +1424,52 @@ function asError(cause: unknown): Error {
|
|
|
1393
1424
|
|
|
1394
1425
|
function isDraftConflictError(error: Error): boolean {
|
|
1395
1426
|
const apiError = error as Partial<OpenGeniApiError>;
|
|
1396
|
-
return
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1427
|
+
if (apiError.status !== 409 || apiError.outcomeUnknown === true) return false;
|
|
1428
|
+
// Production queue OCC returns `DRAFT_CHANGED`. Older/SDK-shaped 409s may
|
|
1429
|
+
// omit code or use the generic conflict labels — all are recoverable OCC.
|
|
1430
|
+
const code = apiError.code;
|
|
1431
|
+
if (
|
|
1432
|
+
code === undefined ||
|
|
1433
|
+
code === "DRAFT_CHANGED" ||
|
|
1434
|
+
code === "conflict" ||
|
|
1435
|
+
code === "idempotency_conflict"
|
|
1436
|
+
) {
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
return /draft changed/i.test(error.message);
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
/**
|
|
1443
|
+
* One OCC retry: adopt the server revision and rewrite the same local content.
|
|
1444
|
+
* Covers the common "tab slept through a successful autosave" case without
|
|
1445
|
+
* stranding the operator on a raw 409 toast.
|
|
1446
|
+
*/
|
|
1447
|
+
async function saveComposerDraftWithStaleRetry(input: {
|
|
1448
|
+
client: {
|
|
1449
|
+
getComposerDraft: (workspaceId: string, sessionId: string) => Promise<ComposerDraft>;
|
|
1450
|
+
saveComposerDraft: (
|
|
1451
|
+
workspaceId: string,
|
|
1452
|
+
sessionId: string,
|
|
1453
|
+
request: SaveComposerDraftRequest,
|
|
1454
|
+
) => Promise<ComposerDraft>;
|
|
1455
|
+
};
|
|
1456
|
+
workspaceId: string;
|
|
1457
|
+
sessionId: string;
|
|
1458
|
+
request: SaveComposerDraftRequest;
|
|
1459
|
+
onAdoptRemote: (remote: ComposerDraft) => void;
|
|
1460
|
+
}): Promise<ComposerDraft> {
|
|
1461
|
+
try {
|
|
1462
|
+
return await input.client.saveComposerDraft(input.workspaceId, input.sessionId, input.request);
|
|
1463
|
+
} catch (cause) {
|
|
1464
|
+
const problem = asError(cause);
|
|
1465
|
+
if (!isDraftConflictError(problem)) throw problem;
|
|
1466
|
+
const remote = await input.client.getComposerDraft(input.workspaceId, input.sessionId);
|
|
1467
|
+
input.onAdoptRemote(remote);
|
|
1468
|
+
return await input.client.saveComposerDraft(input.workspaceId, input.sessionId, {
|
|
1469
|
+
...input.request,
|
|
1470
|
+
expectedRevision: remote.revision,
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1403
1473
|
}
|
|
1404
1474
|
|
|
1405
1475
|
function draftPayload(draft: ComposerDraft): SaveComposerDraftRequest {
|
package/src/index.ts
CHANGED
|
@@ -367,12 +367,14 @@ export type {
|
|
|
367
367
|
// Pure provider-shape parsers (exec banner, V4A diff, secret redaction, …)
|
|
368
368
|
export {
|
|
369
369
|
applyPatchOps,
|
|
370
|
+
applyPatchOpsFromToolItem,
|
|
370
371
|
controlCaret,
|
|
371
372
|
execTruncated,
|
|
372
373
|
isApplyPatch,
|
|
373
374
|
isExecSessionLostBanner,
|
|
374
375
|
looksBinary,
|
|
375
376
|
parseExecBannerSessionId,
|
|
377
|
+
parseFreeformApplyPatch,
|
|
376
378
|
parseToolArgs,
|
|
377
379
|
redactSecrets,
|
|
378
380
|
sandboxCommandExitCode,
|
package/src/model-policy.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type PickerBillingClass = "opengeni_credits" | "codex_subscription" | "by
|
|
|
5
5
|
export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {
|
|
6
6
|
id: string;
|
|
7
7
|
label: string;
|
|
8
|
+
/** Catalog-curated compact label for dense UI; fall back to `label` when absent. */
|
|
9
|
+
shortLabel?: string | undefined;
|
|
8
10
|
billingClass: PickerBillingClass;
|
|
9
11
|
billingClassLabel: string;
|
|
10
12
|
selectable: boolean;
|
|
@@ -171,6 +173,7 @@ export function projectPickerRows(models: WorkspaceModelCatalogModel[]): PickerM
|
|
|
171
173
|
return {
|
|
172
174
|
id: catalog.id,
|
|
173
175
|
label: catalog.label,
|
|
176
|
+
...(catalog.shortLabel ? { shortLabel: catalog.shortLabel } : {}),
|
|
174
177
|
billingClass,
|
|
175
178
|
billingClassLabel: billingClassLabel(billingClass),
|
|
176
179
|
selectable: catalog.availability.selectable,
|
|
@@ -191,6 +194,7 @@ export function projectClientModelRows(models: ClientModel[]): PickerModelRow<Cl
|
|
|
191
194
|
return {
|
|
192
195
|
id: catalog.id,
|
|
193
196
|
label: catalog.label,
|
|
197
|
+
...(catalog.shortLabel ? { shortLabel: catalog.shortLabel } : {}),
|
|
194
198
|
billingClass,
|
|
195
199
|
billingClassLabel: billingClassLabel(billingClass),
|
|
196
200
|
selectable: true,
|