@opengeni/react 7.4.0-canary.36844267522001 → 7.4.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 +0 -10
- package/dist/artifacts-document.js +2 -3
- package/dist/artifacts-presentation.js +2 -3
- package/dist/artifacts-spreadsheet.js +2 -2
- package/dist/artifacts.d.ts +0 -1
- package/dist/artifacts.js +1402 -505
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-AJO7LMVE.js → browser-viewer-2E6R2Y4W.js} +2 -2
- package/dist/{chunk-WUISZ3WU.js → chunk-3E2AITTZ.js} +28 -32
- package/dist/{chunk-WUISZ3WU.js.map → chunk-3E2AITTZ.js.map} +1 -1
- package/dist/{chunk-Y2PRMYLC.js → chunk-DUEBTL3N.js} +3 -4
- package/dist/{chunk-Y2PRMYLC.js.map → chunk-DUEBTL3N.js.map} +1 -1
- package/dist/{chunk-7UAMYXJY.js → chunk-KNDIXZN6.js} +18 -25
- package/dist/{chunk-7UAMYXJY.js.map → chunk-KNDIXZN6.js.map} +1 -1
- package/dist/{chunk-XVYRQ4PK.js → chunk-L4OSXQ6H.js} +116 -277
- package/dist/chunk-L4OSXQ6H.js.map +1 -0
- package/dist/{chunk-7B44WVGH.js → chunk-S5JQ4R22.js} +12 -31
- package/dist/chunk-S5JQ4R22.js.map +1 -0
- package/dist/{chunk-A4UJB2MM.js → chunk-SEEVCT36.js} +2 -2
- package/dist/chunk-UGMKPUEM.js +299 -0
- package/dist/chunk-UGMKPUEM.js.map +1 -0
- package/dist/{chunk-OQVBTOBJ.js → chunk-USXVQACU.js} +3 -3
- package/dist/{chunk-ASCSKOWU.js → chunk-V2ABIVYJ.js} +3 -50
- package/dist/chunk-V2ABIVYJ.js.map +1 -0
- package/dist/{chunk-UQUR5WG5.js → chunk-X4HIN6TV.js} +157 -66
- package/dist/chunk-X4HIN6TV.js.map +1 -0
- package/dist/{chunk-QLG6W33X.js → chunk-XEJEOAOV.js} +35 -46
- package/dist/chunk-XEJEOAOV.js.map +1 -0
- package/dist/components/artifacts/editable-artifact-ui.d.ts +1 -2
- package/dist/components/artifacts/index.d.ts +0 -7
- package/dist/components/message-timeline.d.ts +1 -7
- package/dist/components/open-geni-links.d.ts +0 -16
- package/dist/components/session-conversation.d.ts +1 -13
- package/dist/{computer-viewer-NB6O6BTV.js → computer-viewer-WMJE6OA3.js} +2 -2
- package/dist/connect.js +1 -2
- package/dist/connect.js.map +1 -1
- package/dist/hooks/latest-question.d.ts +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +12 -22
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/{latest-question-3HGQUI2E.js → latest-question-QB4EUCW5.js} +2 -2
- package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-PFUEZ2U4.js} +5 -37
- package/dist/markdown-table-layout-PFUEZ2U4.js.map +1 -0
- package/dist/session-ui.d.ts +2 -3
- package/dist/session-ui.js +9 -18
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +2 -2
- package/dist/sites.js +289 -4
- package/dist/sites.js.map +1 -1
- package/package.json +3 -3
- package/src/artifacts.ts +0 -37
- package/src/components/artifacts/editable-artifact-ui.tsx +1 -3
- package/src/components/artifacts/editable-artifact-workbench.tsx +2 -5
- package/src/components/artifacts/editable-document.tsx +36 -34
- package/src/components/artifacts/editable-presentation.tsx +16 -21
- package/src/components/artifacts/index.ts +0 -41
- package/src/components/browser-viewer.tsx +20 -59
- package/src/components/computer-viewer.tsx +23 -51
- package/src/components/markdown-table-layout.ts +5 -49
- package/src/components/markdown.tsx +2 -5
- package/src/components/message-timeline.tsx +7 -59
- package/src/components/open-geni-links.tsx +0 -24
- package/src/components/session-conversation.tsx +20 -120
- package/src/index.ts +0 -7
- package/src/session-ui.ts +0 -7
- package/src/timeline/projection.ts +4 -54
- package/styles/compiled.css +0 -220
- package/dist/chat-interactive-block-VAGABN7J.js +0 -11
- package/dist/chunk-5CVXZA3Y.js +0 -292
- package/dist/chunk-5CVXZA3Y.js.map +0 -1
- package/dist/chunk-7B44WVGH.js.map +0 -1
- package/dist/chunk-ASCSKOWU.js.map +0 -1
- package/dist/chunk-QLG6W33X.js.map +0 -1
- package/dist/chunk-RAIQYITC.js +0 -1772
- package/dist/chunk-RAIQYITC.js.map +0 -1
- package/dist/chunk-SCOTPE2I.js +0 -589
- package/dist/chunk-SCOTPE2I.js.map +0 -1
- package/dist/chunk-UQUR5WG5.js.map +0 -1
- package/dist/chunk-WEHMPOXK.js +0 -39
- package/dist/chunk-WEHMPOXK.js.map +0 -1
- package/dist/chunk-XCHWDJ22.js +0 -107
- package/dist/chunk-XCHWDJ22.js.map +0 -1
- package/dist/chunk-XVYRQ4PK.js.map +0 -1
- package/dist/components/artifacts/artifact-chrome.d.ts +0 -120
- package/dist/components/artifacts/artifact-sandbox.d.ts +0 -27
- package/dist/components/artifacts/chat-interactive-block.d.ts +0 -29
- package/dist/components/artifacts/deferred-chat-media.d.ts +0 -15
- package/dist/components/artifacts/editable-artifact-view.d.ts +0 -46
- package/dist/components/artifacts/latest-task-runner.d.ts +0 -15
- package/dist/components/artifacts/session-artifact-viewer.d.ts +0 -39
- package/dist/components/artifacts/site-view.d.ts +0 -23
- package/dist/components/timeline-pill-geometry.d.ts +0 -6
- package/dist/components/timeline-pill-placement.d.ts +0 -18
- package/dist/computer-viewer-NB6O6BTV.js.map +0 -1
- package/dist/markdown-table-layout-QXSLK4BY.js.map +0 -1
- package/dist/timeline-pill-geometry-2G6EK47V.js +0 -73
- package/dist/timeline-pill-geometry-2G6EK47V.js.map +0 -1
- package/src/components/artifacts/artifact-chrome.tsx +0 -413
- package/src/components/artifacts/artifact-sandbox.tsx +0 -185
- package/src/components/artifacts/chat-interactive-block.tsx +0 -260
- package/src/components/artifacts/deferred-chat-media.tsx +0 -67
- package/src/components/artifacts/editable-artifact-view.tsx +0 -187
- package/src/components/artifacts/latest-task-runner.ts +0 -49
- package/src/components/artifacts/session-artifact-viewer.tsx +0 -376
- package/src/components/artifacts/site-view.tsx +0 -116
- package/src/components/timeline-pill-geometry.ts +0 -112
- package/src/components/timeline-pill-placement.ts +0 -81
- /package/dist/{browser-viewer-AJO7LMVE.js.map → browser-viewer-2E6R2Y4W.js.map} +0 -0
- /package/dist/{chunk-A4UJB2MM.js.map → chunk-SEEVCT36.js.map} +0 -0
- /package/dist/{chunk-OQVBTOBJ.js.map → chunk-USXVQACU.js.map} +0 -0
- /package/dist/{chat-interactive-block-VAGABN7J.js.map → computer-viewer-WMJE6OA3.js.map} +0 -0
- /package/dist/{latest-question-3HGQUI2E.js.map → latest-question-QB4EUCW5.js.map} +0 -0
|
@@ -16,37 +16,6 @@ export function markdownTableWidth({
|
|
|
16
16
|
return Math.max(columnWidth, Math.min(preferredWidth, available));
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function measurementCopy(source: Node, document: Document): Node {
|
|
20
|
-
if (source.nodeType === Node.ELEMENT_NODE) {
|
|
21
|
-
const element = source as Element;
|
|
22
|
-
// Inert prevents focus, not custom-element construction, media loading or
|
|
23
|
-
// embedded browsing contexts. Do not clone those host-rendered surfaces.
|
|
24
|
-
if (
|
|
25
|
-
element.localName.includes("-") ||
|
|
26
|
-
["iframe", "object", "embed", "video", "audio", "canvas", "script"].includes(
|
|
27
|
-
element.localName,
|
|
28
|
-
)
|
|
29
|
-
) {
|
|
30
|
-
const bounds = element.getBoundingClientRect();
|
|
31
|
-
const placeholder = document.createElement("span");
|
|
32
|
-
placeholder.style.display = "inline-block";
|
|
33
|
-
placeholder.style.width = `${bounds.width}px`;
|
|
34
|
-
placeholder.style.height = `${bounds.height}px`;
|
|
35
|
-
return placeholder;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const copy = document.importNode(source, false);
|
|
39
|
-
if (copy.nodeType === Node.ELEMENT_NODE) {
|
|
40
|
-
const element = copy as Element;
|
|
41
|
-
element.removeAttribute("id");
|
|
42
|
-
for (const attribute of [...element.attributes]) {
|
|
43
|
-
if (attribute.name.startsWith("on")) element.removeAttribute(attribute.name);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
for (const child of source.childNodes) copy.appendChild(measurementCopy(child, document));
|
|
47
|
-
return copy;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
19
|
/** Loaded only for assistant tables. Keep prose and nested scroll owners intact. */
|
|
51
20
|
export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLTableElement) {
|
|
52
21
|
const body = wrapper.parentElement;
|
|
@@ -55,8 +24,6 @@ export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLT
|
|
|
55
24
|
?.closest<HTMLElement>("[data-og-timeline-scroller]");
|
|
56
25
|
if (!body?.classList.contains("og-markdown-body") || !scroller) return;
|
|
57
26
|
|
|
58
|
-
// Build the static copy without a browsing context before inserting its probe.
|
|
59
|
-
const measurementDocument = table.ownerDocument.implementation.createHTMLDocument("");
|
|
60
27
|
let width: number | null = null;
|
|
61
28
|
const reset = () => {
|
|
62
29
|
width = null;
|
|
@@ -83,25 +50,14 @@ export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLT
|
|
|
83
50
|
const right =
|
|
84
51
|
panel.left + scroller.clientLeft + scroller.clientWidth - parseFloat(panelStyle.paddingRight);
|
|
85
52
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
// after the width is restored. Measure a copy without touching live geometry.
|
|
89
|
-
const probe = table.ownerDocument.createElement("div");
|
|
90
|
-
probe.setAttribute("aria-hidden", "true");
|
|
91
|
-
probe.inert = true;
|
|
92
|
-
// A zero-sized, contained box cannot extend the scroller's overflow area,
|
|
93
|
-
// including inside transformed ancestors. Keep the same inherited styling.
|
|
94
|
-
probe.style.cssText =
|
|
95
|
-
"position:absolute;width:0;height:0;overflow:hidden;contain:layout size paint;visibility:hidden;pointer-events:none";
|
|
96
|
-
const copy = measurementCopy(table, measurementDocument) as HTMLTableElement;
|
|
97
|
-
copy.style.width = "max-content";
|
|
98
|
-
probe.append(copy);
|
|
53
|
+
// Measure the original table, preserving its wrapping and TSV copy DOM.
|
|
54
|
+
const previousWidth = table.style.width;
|
|
99
55
|
let preferred: number;
|
|
100
56
|
try {
|
|
101
|
-
table.
|
|
102
|
-
preferred =
|
|
57
|
+
table.style.width = "max-content";
|
|
58
|
+
preferred = table.getBoundingClientRect().width;
|
|
103
59
|
} finally {
|
|
104
|
-
|
|
60
|
+
table.style.width = previousWidth;
|
|
105
61
|
}
|
|
106
62
|
const next = markdownTableWidth({
|
|
107
63
|
columnLeft: column.left,
|
|
@@ -391,15 +391,12 @@ function ActionMarkdownLink({
|
|
|
391
391
|
type="button"
|
|
392
392
|
className={cn(
|
|
393
393
|
MARKDOWN_LINK_CLASS,
|
|
394
|
-
"inline-flex min-h-7 cursor-pointer items-center
|
|
394
|
+
"inline-flex min-h-7 cursor-pointer items-center disabled:cursor-wait disabled:opacity-70 pointer-coarse:min-h-11",
|
|
395
395
|
)}
|
|
396
|
-
|
|
397
|
-
// so a host viewer could not return focus to the link that opened it.
|
|
398
|
-
aria-disabled={state === "loading" || undefined}
|
|
396
|
+
disabled={state === "loading"}
|
|
399
397
|
aria-busy={state === "loading"}
|
|
400
398
|
title={state === "error" ? "Couldn't open this file. Select to retry." : title}
|
|
401
399
|
onClick={() => {
|
|
402
|
-
if (state === "loading") return;
|
|
403
400
|
setState("loading");
|
|
404
401
|
void Promise.resolve()
|
|
405
402
|
.then(onOpen)
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
type OlderHistoryLoadReceipt,
|
|
64
64
|
type OlderHistoryLoader,
|
|
65
65
|
} from "../older-history";
|
|
66
|
-
import { Markdown
|
|
66
|
+
import { Markdown } from "./markdown";
|
|
67
67
|
import { OpenGeniLinkProvider, type OpenGeniLinkResolver } from "./open-geni-links";
|
|
68
68
|
import {
|
|
69
69
|
TimelineBeforeLayout,
|
|
@@ -73,7 +73,6 @@ import {
|
|
|
73
73
|
type TimelineAnchor,
|
|
74
74
|
} from "./timeline-anchor";
|
|
75
75
|
import { useReadingProgress } from "./timeline-reading-progress";
|
|
76
|
-
import { type TimelinePillPlacement, useTimelinePillPlacement } from "./timeline-pill-placement";
|
|
77
76
|
import {
|
|
78
77
|
animateTimelineSettlement,
|
|
79
78
|
captureTimelineSettlement,
|
|
@@ -171,11 +170,6 @@ export type MessageTimelineProps = {
|
|
|
171
170
|
context: { searchTarget: TimelineSearchTarget | null },
|
|
172
171
|
) => ReactNode)
|
|
173
172
|
| undefined;
|
|
174
|
-
/**
|
|
175
|
-
* Render assistant `opengeni-html` / `opengeni-site` fences in the default
|
|
176
|
-
* message renderer (`SessionConversation` supplies the inline Site preview).
|
|
177
|
-
*/
|
|
178
|
-
renderInteractiveBlock?: MarkdownProps["renderInteractiveBlock"];
|
|
179
173
|
/** Drill into a spawned worker session. */
|
|
180
174
|
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
181
175
|
/**
|
|
@@ -317,20 +311,6 @@ const PIN_THRESHOLD_PX = 48;
|
|
|
317
311
|
const JUMP_TO_LATEST_CATCHUP_DEBT_PX = 240;
|
|
318
312
|
/** Breathing room above the question when following stops at an answer. */
|
|
319
313
|
const QUESTION_NAV_MARGIN_PX = 12;
|
|
320
|
-
// Floating pills rest at these spots and slide sideways, in this order, when
|
|
321
|
-
// their resting spot would cover a control (see useTimelinePillPlacement).
|
|
322
|
-
const JUMP_PILL_PLACEMENTS: readonly TimelinePillPlacement[] = ["center", "end", "start"];
|
|
323
|
-
const QUESTION_PILL_PLACEMENTS: readonly TimelinePillPlacement[] = ["end", "center", "start"];
|
|
324
|
-
const JUMP_PILL_PLACEMENT_CLASS: Record<TimelinePillPlacement, string> = {
|
|
325
|
-
center: "inset-x-0 mx-auto",
|
|
326
|
-
end: "right-4 sm:right-6",
|
|
327
|
-
start: "left-4 sm:left-6",
|
|
328
|
-
};
|
|
329
|
-
const QUESTION_PILL_PLACEMENT_CLASS: Record<TimelinePillPlacement, string> = {
|
|
330
|
-
center: "justify-center",
|
|
331
|
-
end: "justify-end",
|
|
332
|
-
start: "justify-start",
|
|
333
|
-
};
|
|
334
314
|
/**
|
|
335
315
|
* Prefetch older history when the top sentinel is this far from the viewport.
|
|
336
316
|
* After a page loads we stay cool until the reader leaves this band (scrolls
|
|
@@ -502,7 +482,6 @@ function cssEscapeAttribute(value: string): string {
|
|
|
502
482
|
*/
|
|
503
483
|
export function MessageTimeline({
|
|
504
484
|
resolveLink,
|
|
505
|
-
renderInteractiveBlock,
|
|
506
485
|
userMessageDisclosureLabels,
|
|
507
486
|
searchTarget,
|
|
508
487
|
events,
|
|
@@ -576,9 +555,6 @@ export function MessageTimeline({
|
|
|
576
555
|
<Markdown
|
|
577
556
|
searchTarget={searchItem?.id === item.id ? searchTarget : null}
|
|
578
557
|
streaming={item.kind === "agent-message" && item.streaming}
|
|
579
|
-
renderInteractiveBlock={
|
|
580
|
-
item.kind === "agent-message" ? renderInteractiveBlock : undefined
|
|
581
|
-
}
|
|
582
558
|
>
|
|
583
559
|
{text}
|
|
584
560
|
</Markdown>
|
|
@@ -591,7 +567,7 @@ export function MessageTimeline({
|
|
|
591
567
|
body
|
|
592
568
|
);
|
|
593
569
|
},
|
|
594
|
-
[
|
|
570
|
+
[renderMessageText, searchItem?.id, searchTarget],
|
|
595
571
|
);
|
|
596
572
|
// Event-window identity is independent of projected rows (partial messages
|
|
597
573
|
// can acquire a different first-delta id when older text arrives).
|
|
@@ -724,21 +700,6 @@ export function MessageTimeline({
|
|
|
724
700
|
const resizeFollowRafRef = useRef<number | null>(null);
|
|
725
701
|
const questionNavFrameRef = useRef<number | null>(null);
|
|
726
702
|
const [questionNav, setQuestionNav] = useState<QuestionNav | null>(null);
|
|
727
|
-
const jumpPillRef = useRef<HTMLButtonElement | null>(null);
|
|
728
|
-
const questionPillRef = useRef<HTMLDivElement | null>(null);
|
|
729
|
-
const jumpPillShown = autoFollow && (!pinned || hasNewer || canSkipTipCatchup);
|
|
730
|
-
const jumpPillPlacement = useTimelinePillPlacement({
|
|
731
|
-
pillRef: jumpPillRef,
|
|
732
|
-
scrollerRef: scrollRef,
|
|
733
|
-
active: jumpPillShown,
|
|
734
|
-
preference: JUMP_PILL_PLACEMENTS,
|
|
735
|
-
});
|
|
736
|
-
const questionPillPlacement = useTimelinePillPlacement({
|
|
737
|
-
pillRef: questionPillRef,
|
|
738
|
-
scrollerRef: scrollRef,
|
|
739
|
-
active: questionNav !== null,
|
|
740
|
-
preference: QUESTION_PILL_PLACEMENTS,
|
|
741
|
-
});
|
|
742
703
|
const [questionTarget, setQuestionTarget] = useState<number | null>(null);
|
|
743
704
|
const [questionPending, setQuestionPending] = useState(false);
|
|
744
705
|
const [questionError, setQuestionError] = useState<string | null>(null);
|
|
@@ -2556,18 +2517,9 @@ export function MessageTimeline({
|
|
|
2556
2517
|
data-og-question-nav=""
|
|
2557
2518
|
// Below the pinned work-header strip (py-1.5 row, 44px on coarse
|
|
2558
2519
|
// pointers), never over it: the header stays a full-width target.
|
|
2559
|
-
|
|
2560
|
-
className={cn(
|
|
2561
|
-
"pointer-events-none absolute inset-x-0 top-11 z-10 flex px-4 sm:px-6 pointer-coarse:top-14",
|
|
2562
|
-
QUESTION_PILL_PLACEMENT_CLASS[questionPillPlacement],
|
|
2563
|
-
)}
|
|
2520
|
+
className="pointer-events-none absolute inset-x-0 top-11 z-10 flex justify-end px-4 sm:px-6 pointer-coarse:top-14"
|
|
2564
2521
|
>
|
|
2565
|
-
<
|
|
2566
|
-
ref={questionPillRef}
|
|
2567
|
-
layout="position"
|
|
2568
|
-
transition={{ duration: 0.18, ease: "easeOut" }}
|
|
2569
|
-
className="pointer-events-auto inline-flex max-w-[calc(50%-0.5rem)] items-center rounded-full border border-og-border bg-og-surface-3/90 text-og-control font-medium text-og-fg shadow-og-md backdrop-blur"
|
|
2570
|
-
>
|
|
2522
|
+
<div className="pointer-events-auto inline-flex max-w-[calc(50%-0.5rem)] items-center rounded-full border border-og-border bg-og-surface-3/90 text-og-control font-medium text-og-fg shadow-og-md backdrop-blur">
|
|
2571
2523
|
<button
|
|
2572
2524
|
type="button"
|
|
2573
2525
|
data-og-jump-to-question=""
|
|
@@ -2585,7 +2537,7 @@ export function MessageTimeline({
|
|
|
2585
2537
|
{questionError}
|
|
2586
2538
|
</span>
|
|
2587
2539
|
)}
|
|
2588
|
-
</
|
|
2540
|
+
</div>
|
|
2589
2541
|
</motion.div>
|
|
2590
2542
|
) : null}
|
|
2591
2543
|
</AnimatePresence>
|
|
@@ -2607,13 +2559,10 @@ export function MessageTimeline({
|
|
|
2607
2559
|
) : null}
|
|
2608
2560
|
</AnimatePresence>
|
|
2609
2561
|
<AnimatePresence>
|
|
2610
|
-
{
|
|
2562
|
+
{((!pinned && autoFollow) || hasNewer || canSkipTipCatchup) && autoFollow ? (
|
|
2611
2563
|
<motion.button
|
|
2612
|
-
ref={jumpPillRef}
|
|
2613
2564
|
type="button"
|
|
2614
2565
|
data-og-jump-to-latest=""
|
|
2615
|
-
data-og-pill-placement={jumpPillPlacement}
|
|
2616
|
-
layout="position"
|
|
2617
2566
|
initial={{ opacity: 0, y: 8 }}
|
|
2618
2567
|
animate={{ opacity: 1, y: 0 }}
|
|
2619
2568
|
exit={{ opacity: 0, y: 8 }}
|
|
@@ -2673,8 +2622,7 @@ export function MessageTimeline({
|
|
|
2673
2622
|
}
|
|
2674
2623
|
}}
|
|
2675
2624
|
className={cn(
|
|
2676
|
-
"absolute bottom-4 w-fit",
|
|
2677
|
-
JUMP_PILL_PLACEMENT_CLASS[jumpPillPlacement],
|
|
2625
|
+
"absolute inset-x-0 bottom-4 mx-auto w-fit",
|
|
2678
2626
|
"inline-flex items-center gap-1.5 rounded-full border border-og-border bg-og-surface-3/90 px-3 py-1.5",
|
|
2679
2627
|
"text-og-control font-medium text-og-fg shadow-og-md backdrop-blur",
|
|
2680
2628
|
"hover:border-og-border-strong",
|
|
@@ -153,27 +153,3 @@ export function sessionLinkResolver(input: {
|
|
|
153
153
|
return null;
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
|
-
|
|
157
|
-
/** An editable artifact or Site to open in a host-mounted viewer. */
|
|
158
|
-
export type OpenGeniViewerTarget = Readonly<{
|
|
159
|
-
kind: "editable-artifact" | "site";
|
|
160
|
-
artifactId: string;
|
|
161
|
-
/** The link text the agent wrote, until the viewer loads the real title. */
|
|
162
|
-
title?: string | undefined;
|
|
163
|
-
}>;
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* "Open in host viewer": resolve agent links to editable artifacts and Sites
|
|
167
|
-
* in this workspace to `open(target)`, for example to mount
|
|
168
|
-
* `SessionArtifactViewer` beside the conversation. Other targets fall through.
|
|
169
|
-
*/
|
|
170
|
-
export function viewerLinkResolver(input: {
|
|
171
|
-
workspaceId: string;
|
|
172
|
-
open: (target: OpenGeniViewerTarget) => void;
|
|
173
|
-
}): OpenGeniLinkResolver {
|
|
174
|
-
return (target) => {
|
|
175
|
-
if (target.kind !== "editable-artifact" && target.kind !== "site") return null;
|
|
176
|
-
if (target.workspaceId !== input.workspaceId) return null;
|
|
177
|
-
return { open: () => input.open({ kind: target.kind, artifactId: target.artifactId }) };
|
|
178
|
-
};
|
|
179
|
-
}
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
lazy,
|
|
4
|
-
Suspense,
|
|
5
|
-
useCallback,
|
|
6
|
-
useEffect,
|
|
7
|
-
useMemo,
|
|
8
|
-
useRef,
|
|
9
|
-
useState,
|
|
10
|
-
type CSSProperties,
|
|
11
|
-
} from "react";
|
|
12
|
-
import type { SiteSnapshotClient } from "./artifacts/chat-interactive-block";
|
|
1
|
+
import type { SendMessageInput } from "@opengeni/sdk";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
|
|
13
3
|
import { useOpenGeni, type ClientOverride } from "../session-context";
|
|
14
4
|
import { useWorkspaceModelCatalog } from "../hooks/use-available-models";
|
|
15
5
|
import { ModelPolicyPicker } from "./model-policy-picker";
|
|
@@ -30,9 +20,7 @@ import {
|
|
|
30
20
|
chainLinkResolvers,
|
|
31
21
|
sessionLinkResolver,
|
|
32
22
|
useOpenGeniLinkResolver,
|
|
33
|
-
viewerLinkResolver,
|
|
34
23
|
type OpenGeniLinkResolver,
|
|
35
|
-
type OpenGeniViewerTarget,
|
|
36
24
|
} from "./open-geni-links";
|
|
37
25
|
import type { UserMessageDisclosureLabels } from "./user-message-body";
|
|
38
26
|
import { conversationTimeline } from "../conversation-timeline";
|
|
@@ -49,18 +37,6 @@ export type SessionConversationProps = ClientOverride & {
|
|
|
49
37
|
* editable artifacts and Sites stay unavailable until the host resolves them.
|
|
50
38
|
*/
|
|
51
39
|
resolveLink?: OpenGeniLinkResolver | undefined;
|
|
52
|
-
/**
|
|
53
|
-
* Open agent links to editable artifacts and Sites in a host viewer, for
|
|
54
|
-
* example `SessionArtifactViewer` mounted beside the conversation. Asked
|
|
55
|
-
* after `resolveLink`.
|
|
56
|
-
*/
|
|
57
|
-
onOpenArtifact?: ((target: OpenGeniViewerTarget) => void) | undefined;
|
|
58
|
-
/**
|
|
59
|
-
* Inline previews for assistant `opengeni-site` / `opengeni-html` fences.
|
|
60
|
-
* Defaults to the OpenGeni preview (Site reads need the proxy's
|
|
61
|
-
* `artifacts` option); `false` shows the fence as code.
|
|
62
|
-
*/
|
|
63
|
-
renderInteractiveBlock?: MessageTimelineProps["renderInteractiveBlock"] | false;
|
|
64
40
|
/** Product-specific tool-call renderers; defaults to the built-in registry. */
|
|
65
41
|
toolRegistry?: MessageTimelineProps["toolRegistry"];
|
|
66
42
|
/**
|
|
@@ -96,8 +72,6 @@ function Conversation({
|
|
|
96
72
|
sessionId,
|
|
97
73
|
renderMessageText,
|
|
98
74
|
resolveLink,
|
|
99
|
-
onOpenArtifact,
|
|
100
|
-
renderInteractiveBlock,
|
|
101
75
|
toolRegistry,
|
|
102
76
|
attachments: attachmentsRequested = true,
|
|
103
77
|
modelPicker,
|
|
@@ -155,11 +129,6 @@ function Conversation({
|
|
|
155
129
|
: {}),
|
|
156
130
|
});
|
|
157
131
|
const region = useRef<HTMLDivElement>(null);
|
|
158
|
-
const defaultInteractiveBlock = useDefaultInteractiveBlock(
|
|
159
|
-
context.client,
|
|
160
|
-
context.workspaceId,
|
|
161
|
-
sessionId,
|
|
162
|
-
);
|
|
163
132
|
const inheritedLinks = useOpenGeniLinkResolver();
|
|
164
133
|
const defaultLinks = useMemo(
|
|
165
134
|
() =>
|
|
@@ -171,22 +140,9 @@ function Conversation({
|
|
|
171
140
|
}),
|
|
172
141
|
[context.client, context.workspaceId, sessionId, config.sandboxFiles],
|
|
173
142
|
);
|
|
174
|
-
const onOpenArtifactRef = useRef(onOpenArtifact);
|
|
175
|
-
onOpenArtifactRef.current = onOpenArtifact;
|
|
176
|
-
const opensArtifacts = onOpenArtifact !== undefined;
|
|
177
|
-
const viewerLinks = useMemo(
|
|
178
|
-
() =>
|
|
179
|
-
opensArtifacts
|
|
180
|
-
? viewerLinkResolver({
|
|
181
|
-
workspaceId: context.workspaceId,
|
|
182
|
-
open: (target) => onOpenArtifactRef.current?.(target),
|
|
183
|
-
})
|
|
184
|
-
: null,
|
|
185
|
-
[context.workspaceId, opensArtifacts],
|
|
186
|
-
);
|
|
187
143
|
const links = useMemo(
|
|
188
|
-
() => chainLinkResolvers(resolveLink,
|
|
189
|
-
[resolveLink,
|
|
144
|
+
() => chainLinkResolvers(resolveLink, inheritedLinks, defaultLinks) ?? undefined,
|
|
145
|
+
[resolveLink, inheritedLinks, defaultLinks],
|
|
190
146
|
);
|
|
191
147
|
const error = detail.error ?? feed.error ?? human.error;
|
|
192
148
|
return (
|
|
@@ -203,11 +159,6 @@ function Conversation({
|
|
|
203
159
|
<MessageTimeline
|
|
204
160
|
renderMessageText={renderMessageText}
|
|
205
161
|
resolveLink={links}
|
|
206
|
-
renderInteractiveBlock={
|
|
207
|
-
renderInteractiveBlock === false
|
|
208
|
-
? undefined
|
|
209
|
-
: (renderInteractiveBlock ?? defaultInteractiveBlock)
|
|
210
|
-
}
|
|
211
162
|
userMessageDisclosureLabels={userMessageDisclosureLabels}
|
|
212
163
|
className="min-h-0 flex-1"
|
|
213
164
|
{...(toolRegistry ? { toolRegistry } : {})}
|
|
@@ -226,25 +177,22 @@ function Conversation({
|
|
|
226
177
|
}}
|
|
227
178
|
loadingOldest={feed.loadingOldest}
|
|
228
179
|
onJumpToLatest={feed.jumpToLatest}
|
|
229
|
-
onJumpToLatestQuestion={
|
|
230
|
-
feed.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
},
|
|
246
|
-
})
|
|
247
|
-
: undefined
|
|
180
|
+
onJumpToLatestQuestion={() =>
|
|
181
|
+
feed.jumpToLatestQuestion({
|
|
182
|
+
onQueuedQuestion: async (turn, navigation) => {
|
|
183
|
+
await queue.refresh();
|
|
184
|
+
if (
|
|
185
|
+
!navigation.isCurrent() ||
|
|
186
|
+
scopeRef.current.client !== context.client ||
|
|
187
|
+
scopeRef.current.workspaceId !== context.workspaceId
|
|
188
|
+
)
|
|
189
|
+
return;
|
|
190
|
+
setQueueFocusTarget((previous) => ({
|
|
191
|
+
turnId: turn.id,
|
|
192
|
+
requestId: (previous?.requestId ?? 0) + 1,
|
|
193
|
+
}));
|
|
194
|
+
},
|
|
195
|
+
})
|
|
248
196
|
}
|
|
249
197
|
onAnnotate={composer.addAnnotation}
|
|
250
198
|
/>
|
|
@@ -327,54 +275,6 @@ function Conversation({
|
|
|
327
275
|
);
|
|
328
276
|
}
|
|
329
277
|
|
|
330
|
-
const LazyChatInteractiveBlock = lazy(() =>
|
|
331
|
-
import("./artifacts/chat-interactive-block").then((module) => ({
|
|
332
|
-
default: module.ChatInteractiveBlock,
|
|
333
|
-
})),
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
type ScopableClient = SiteSnapshotClient & {
|
|
337
|
-
withHeaders?: (headers: Readonly<Record<string, string>>) => SiteSnapshotClient;
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
/** Inline Site/HTML preview reading through this conversation's session scope. */
|
|
341
|
-
function useDefaultInteractiveBlock(
|
|
342
|
-
client: unknown,
|
|
343
|
-
workspaceId: string,
|
|
344
|
-
sessionId: string,
|
|
345
|
-
): NonNullable<MessageTimelineProps["renderInteractiveBlock"]> {
|
|
346
|
-
// Scope lazily: only a rendered preview reads, and some clients (a Site's
|
|
347
|
-
// own client) cannot add headers.
|
|
348
|
-
const scoped = useMemo((): SiteSnapshotClient => {
|
|
349
|
-
let resolved: SiteSnapshotClient | null = null;
|
|
350
|
-
const get = () => {
|
|
351
|
-
const candidate = client as ScopableClient;
|
|
352
|
-
resolved ??=
|
|
353
|
-
typeof candidate.withHeaders === "function"
|
|
354
|
-
? candidate.withHeaders({ [SESSION_SCOPE_HEADER]: sessionId })
|
|
355
|
-
: candidate;
|
|
356
|
-
return resolved;
|
|
357
|
-
};
|
|
358
|
-
return {
|
|
359
|
-
getWorkspaceArtifact: (...args) => get().getWorkspaceArtifact(...args),
|
|
360
|
-
getWorkspaceArtifactHtml: (...args) => get().getWorkspaceArtifactHtml(...args),
|
|
361
|
-
getWorkspaceArtifactContent: (...args) => {
|
|
362
|
-
const target = get();
|
|
363
|
-
if (!target.getWorkspaceArtifactContent) throw new Error("Site version unavailable");
|
|
364
|
-
return target.getWorkspaceArtifactContent(...args);
|
|
365
|
-
},
|
|
366
|
-
};
|
|
367
|
-
}, [client, sessionId]);
|
|
368
|
-
return useCallback(
|
|
369
|
-
(block) => (
|
|
370
|
-
<Suspense fallback={<span role="status">Loading preview…</span>}>
|
|
371
|
-
<LazyChatInteractiveBlock workspaceId={workspaceId} client={scoped} {...block} />
|
|
372
|
-
</Suspense>
|
|
373
|
-
),
|
|
374
|
-
[scoped, workspaceId],
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
278
|
/** Deployment/proxy flags from the client config: uploads, and whether model choice is open. */
|
|
379
279
|
function useClientConfigFlags(client: {
|
|
380
280
|
getClientConfig: () => Promise<{
|
package/src/index.ts
CHANGED
|
@@ -557,20 +557,13 @@ export {
|
|
|
557
557
|
chainLinkResolvers,
|
|
558
558
|
sessionLinkResolver,
|
|
559
559
|
useOpenGeniLinkResolver,
|
|
560
|
-
viewerLinkResolver,
|
|
561
560
|
} from "./components/open-geni-links";
|
|
562
561
|
export type {
|
|
563
562
|
OpenGeniLinkResolution,
|
|
564
563
|
OpenGeniLinkResolver,
|
|
565
564
|
OpenGeniLinkTarget,
|
|
566
|
-
OpenGeniViewerTarget,
|
|
567
565
|
} from "./components/open-geni-links";
|
|
568
566
|
export { parseOpenGeniLink } from "@opengeni/sdk";
|
|
569
|
-
export {
|
|
570
|
-
ArtifactLabelsProvider,
|
|
571
|
-
DEFAULT_ARTIFACT_LABELS,
|
|
572
|
-
type ArtifactLabels,
|
|
573
|
-
} from "./components/artifacts/artifact-chrome";
|
|
574
567
|
export { CopyButton, CopyHoverFrame } from "./components/copy-button";
|
|
575
568
|
export { copyTextToClipboard, tableElementToTsv } from "./lib/clipboard";
|
|
576
569
|
export { SessionStatus, StatusDot, SESSION_STATUS_META } from "./components/session-status";
|
package/src/session-ui.ts
CHANGED
|
@@ -18,20 +18,13 @@ export {
|
|
|
18
18
|
chainLinkResolvers,
|
|
19
19
|
sessionLinkResolver,
|
|
20
20
|
useOpenGeniLinkResolver,
|
|
21
|
-
viewerLinkResolver,
|
|
22
21
|
} from "./components/open-geni-links";
|
|
23
22
|
export type {
|
|
24
23
|
OpenGeniLinkResolution,
|
|
25
24
|
OpenGeniLinkResolver,
|
|
26
25
|
OpenGeniLinkTarget,
|
|
27
|
-
OpenGeniViewerTarget,
|
|
28
26
|
} from "./components/open-geni-links";
|
|
29
27
|
export { parseOpenGeniLink } from "@opengeni/sdk";
|
|
30
|
-
export {
|
|
31
|
-
ArtifactLabelsProvider,
|
|
32
|
-
DEFAULT_ARTIFACT_LABELS,
|
|
33
|
-
type ArtifactLabels,
|
|
34
|
-
} from "./components/artifacts/artifact-chrome";
|
|
35
28
|
export type { TimelineSearchTarget } from "./components/timeline-search";
|
|
36
29
|
export { createOlderHistoryLoadReceipt } from "./older-history";
|
|
37
30
|
export type { OlderHistoryLoader, OlderHistoryLoadReceipt } from "./older-history";
|
|
@@ -9,10 +9,6 @@ import {
|
|
|
9
9
|
type TimelineAnnotation,
|
|
10
10
|
} from "@opengeni/sdk";
|
|
11
11
|
import fleetDecisionItem from "./fleet-decision-projection";
|
|
12
|
-
import {
|
|
13
|
-
allowanceExhaustedMessage,
|
|
14
|
-
parseAllowanceExhaustedRefusal,
|
|
15
|
-
} from "@opengeni/sdk/allowance-refusal";
|
|
16
12
|
import {
|
|
17
13
|
CREDIT_EXHAUSTION_MESSAGE,
|
|
18
14
|
presentFailure,
|
|
@@ -149,31 +145,6 @@ export function buildTimeline(
|
|
|
149
145
|
options: { partialStart?: boolean } = {},
|
|
150
146
|
): TimelineItem[] {
|
|
151
147
|
const items: TimelineItem[] = [];
|
|
152
|
-
const allowanceNotices = new Set<string>();
|
|
153
|
-
const allowanceNotice = (event: SessionEvent, turnId: string | null) => {
|
|
154
|
-
const refusal = parseAllowanceExhaustedRefusal(event.payload);
|
|
155
|
-
if (!refusal) return null;
|
|
156
|
-
const text = allowanceExhaustedMessage(refusal);
|
|
157
|
-
const key = JSON.stringify([
|
|
158
|
-
turnId,
|
|
159
|
-
event.turnAttemptId,
|
|
160
|
-
startupRecoveryRevisionByTurn.get(turnId ?? "") ?? 0,
|
|
161
|
-
refusal.scope,
|
|
162
|
-
refusal.subjectId,
|
|
163
|
-
refusal.resetsAt,
|
|
164
|
-
]);
|
|
165
|
-
if (!allowanceNotices.has(key)) {
|
|
166
|
-
allowanceNotices.add(key);
|
|
167
|
-
items.push({
|
|
168
|
-
kind: "notice",
|
|
169
|
-
id: `${event.id}-allowance`,
|
|
170
|
-
tone: "failed",
|
|
171
|
-
text,
|
|
172
|
-
occurredAt: event.occurredAt,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
return text;
|
|
176
|
-
};
|
|
177
148
|
const prescan = prescanTurnAnchors(events);
|
|
178
149
|
const ordered = orderTimelineEvents(events, prescan);
|
|
179
150
|
// A bounded replay can begin halfway through a message (including inside an
|
|
@@ -1229,11 +1200,6 @@ export function buildTimeline(
|
|
|
1229
1200
|
break;
|
|
1230
1201
|
}
|
|
1231
1202
|
|
|
1232
|
-
case "usage.exhausted": {
|
|
1233
|
-
allowanceNotice(event, turnId);
|
|
1234
|
-
break;
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
1203
|
case "turn.completed": {
|
|
1238
1204
|
// A standalone manual compaction uses the turn ledger for fencing and
|
|
1239
1205
|
// recovery, but it is maintenance rather than a conversational turn.
|
|
@@ -1252,16 +1218,6 @@ export function buildTimeline(
|
|
|
1252
1218
|
// Rendering it as a clean "complete" turn is a lie that leaves the
|
|
1253
1219
|
// session looking healthy while every future turn silently dies, so it
|
|
1254
1220
|
// projects exactly like a failed turn plus an explicit notice.
|
|
1255
|
-
const allowance = parseAllowanceExhaustedRefusal(payload);
|
|
1256
|
-
if (allowance) {
|
|
1257
|
-
finalizeOpen(turnId, "complete", event.occurredAt);
|
|
1258
|
-
takeAgentResponse(turnId);
|
|
1259
|
-
takePendingWaitOutcome(turnId);
|
|
1260
|
-
const text = allowanceExhaustedMessage(allowance);
|
|
1261
|
-
items.push(turnEndItem(event, "failed", text));
|
|
1262
|
-
allowanceNotice(event, turnId);
|
|
1263
|
-
break;
|
|
1264
|
-
}
|
|
1265
1221
|
if (isCreditExhaustionPayload(payload)) {
|
|
1266
1222
|
finalizeOpen(turnId, "complete", event.occurredAt);
|
|
1267
1223
|
takeAgentResponse(turnId);
|
|
@@ -1361,21 +1317,16 @@ export function buildTimeline(
|
|
|
1361
1317
|
// Credit death can hide behind fields `failureMessage` doesn't read
|
|
1362
1318
|
// (detail/segmentLimit), so classify the whole payload before falling
|
|
1363
1319
|
// back to the generic error/message extraction.
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
: isCreditExhaustionPayload(payload)
|
|
1368
|
-
? CREDIT_EXHAUSTION_MESSAGE
|
|
1369
|
-
: failureMessage(payload);
|
|
1320
|
+
const failureText = isCreditExhaustionPayload(payload)
|
|
1321
|
+
? CREDIT_EXHAUSTION_MESSAGE
|
|
1322
|
+
: failureMessage(payload);
|
|
1370
1323
|
// The TURN failed — the in-flight items did not. Chip doctrine: red is
|
|
1371
1324
|
// spent once, on the turn-level outcome. Items caught mid-flight read
|
|
1372
1325
|
// as calm "interrupted" (same as turn.cancelled); an item that itself
|
|
1373
1326
|
// failed keeps its own failed status from its output event.
|
|
1374
1327
|
finalizeOpen(turnId, "cancelled", event.occurredAt);
|
|
1375
1328
|
items.push(turnEndItem(event, "failed", failureText));
|
|
1376
|
-
if (
|
|
1377
|
-
allowanceNotice(event, turnId);
|
|
1378
|
-
} else if (!hadActivity) {
|
|
1329
|
+
if (!hadActivity) {
|
|
1379
1330
|
items.push({
|
|
1380
1331
|
kind: "notice",
|
|
1381
1332
|
id: event.id,
|
|
@@ -1739,7 +1690,6 @@ export function stripOpaqueCitationTokens(text: string): string {
|
|
|
1739
1690
|
|
|
1740
1691
|
/** The turn-end payload shape, as `isCreditExhaustion` wants it. */
|
|
1741
1692
|
function isCreditExhaustionPayload(payload: Record<string, unknown>): boolean {
|
|
1742
|
-
if (payload.code === "allowance_exhausted") return false;
|
|
1743
1693
|
return isCreditExhaustion({
|
|
1744
1694
|
error: typeof payload.error === "string" ? payload.error : null,
|
|
1745
1695
|
detail: typeof payload.detail === "string" ? payload.detail : null,
|