@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.
Files changed (113) hide show
  1. package/README.md +0 -10
  2. package/dist/artifacts-document.js +2 -3
  3. package/dist/artifacts-presentation.js +2 -3
  4. package/dist/artifacts-spreadsheet.js +2 -2
  5. package/dist/artifacts.d.ts +0 -1
  6. package/dist/artifacts.js +1402 -505
  7. package/dist/artifacts.js.map +1 -1
  8. package/dist/{browser-viewer-AJO7LMVE.js → browser-viewer-2E6R2Y4W.js} +2 -2
  9. package/dist/{chunk-WUISZ3WU.js → chunk-3E2AITTZ.js} +28 -32
  10. package/dist/{chunk-WUISZ3WU.js.map → chunk-3E2AITTZ.js.map} +1 -1
  11. package/dist/{chunk-Y2PRMYLC.js → chunk-DUEBTL3N.js} +3 -4
  12. package/dist/{chunk-Y2PRMYLC.js.map → chunk-DUEBTL3N.js.map} +1 -1
  13. package/dist/{chunk-7UAMYXJY.js → chunk-KNDIXZN6.js} +18 -25
  14. package/dist/{chunk-7UAMYXJY.js.map → chunk-KNDIXZN6.js.map} +1 -1
  15. package/dist/{chunk-XVYRQ4PK.js → chunk-L4OSXQ6H.js} +116 -277
  16. package/dist/chunk-L4OSXQ6H.js.map +1 -0
  17. package/dist/{chunk-7B44WVGH.js → chunk-S5JQ4R22.js} +12 -31
  18. package/dist/chunk-S5JQ4R22.js.map +1 -0
  19. package/dist/{chunk-A4UJB2MM.js → chunk-SEEVCT36.js} +2 -2
  20. package/dist/chunk-UGMKPUEM.js +299 -0
  21. package/dist/chunk-UGMKPUEM.js.map +1 -0
  22. package/dist/{chunk-OQVBTOBJ.js → chunk-USXVQACU.js} +3 -3
  23. package/dist/{chunk-ASCSKOWU.js → chunk-V2ABIVYJ.js} +3 -50
  24. package/dist/chunk-V2ABIVYJ.js.map +1 -0
  25. package/dist/{chunk-UQUR5WG5.js → chunk-X4HIN6TV.js} +157 -66
  26. package/dist/chunk-X4HIN6TV.js.map +1 -0
  27. package/dist/{chunk-QLG6W33X.js → chunk-XEJEOAOV.js} +35 -46
  28. package/dist/chunk-XEJEOAOV.js.map +1 -0
  29. package/dist/components/artifacts/editable-artifact-ui.d.ts +1 -2
  30. package/dist/components/artifacts/index.d.ts +0 -7
  31. package/dist/components/message-timeline.d.ts +1 -7
  32. package/dist/components/open-geni-links.d.ts +0 -16
  33. package/dist/components/session-conversation.d.ts +1 -13
  34. package/dist/{computer-viewer-NB6O6BTV.js → computer-viewer-WMJE6OA3.js} +2 -2
  35. package/dist/connect.js +1 -2
  36. package/dist/connect.js.map +1 -1
  37. package/dist/hooks/latest-question.d.ts +1 -1
  38. package/dist/index.d.ts +2 -3
  39. package/dist/index.js +12 -22
  40. package/dist/index.js.map +1 -1
  41. package/dist/interaction.js +2 -2
  42. package/dist/{latest-question-3HGQUI2E.js → latest-question-QB4EUCW5.js} +2 -2
  43. package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-PFUEZ2U4.js} +5 -37
  44. package/dist/markdown-table-layout-PFUEZ2U4.js.map +1 -0
  45. package/dist/session-ui.d.ts +2 -3
  46. package/dist/session-ui.js +9 -18
  47. package/dist/session-ui.js.map +1 -1
  48. package/dist/session.js +2 -2
  49. package/dist/sites.js +289 -4
  50. package/dist/sites.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/artifacts.ts +0 -37
  53. package/src/components/artifacts/editable-artifact-ui.tsx +1 -3
  54. package/src/components/artifacts/editable-artifact-workbench.tsx +2 -5
  55. package/src/components/artifacts/editable-document.tsx +36 -34
  56. package/src/components/artifacts/editable-presentation.tsx +16 -21
  57. package/src/components/artifacts/index.ts +0 -41
  58. package/src/components/browser-viewer.tsx +20 -59
  59. package/src/components/computer-viewer.tsx +23 -51
  60. package/src/components/markdown-table-layout.ts +5 -49
  61. package/src/components/markdown.tsx +2 -5
  62. package/src/components/message-timeline.tsx +7 -59
  63. package/src/components/open-geni-links.tsx +0 -24
  64. package/src/components/session-conversation.tsx +20 -120
  65. package/src/index.ts +0 -7
  66. package/src/session-ui.ts +0 -7
  67. package/src/timeline/projection.ts +4 -54
  68. package/styles/compiled.css +0 -220
  69. package/dist/chat-interactive-block-VAGABN7J.js +0 -11
  70. package/dist/chunk-5CVXZA3Y.js +0 -292
  71. package/dist/chunk-5CVXZA3Y.js.map +0 -1
  72. package/dist/chunk-7B44WVGH.js.map +0 -1
  73. package/dist/chunk-ASCSKOWU.js.map +0 -1
  74. package/dist/chunk-QLG6W33X.js.map +0 -1
  75. package/dist/chunk-RAIQYITC.js +0 -1772
  76. package/dist/chunk-RAIQYITC.js.map +0 -1
  77. package/dist/chunk-SCOTPE2I.js +0 -589
  78. package/dist/chunk-SCOTPE2I.js.map +0 -1
  79. package/dist/chunk-UQUR5WG5.js.map +0 -1
  80. package/dist/chunk-WEHMPOXK.js +0 -39
  81. package/dist/chunk-WEHMPOXK.js.map +0 -1
  82. package/dist/chunk-XCHWDJ22.js +0 -107
  83. package/dist/chunk-XCHWDJ22.js.map +0 -1
  84. package/dist/chunk-XVYRQ4PK.js.map +0 -1
  85. package/dist/components/artifacts/artifact-chrome.d.ts +0 -120
  86. package/dist/components/artifacts/artifact-sandbox.d.ts +0 -27
  87. package/dist/components/artifacts/chat-interactive-block.d.ts +0 -29
  88. package/dist/components/artifacts/deferred-chat-media.d.ts +0 -15
  89. package/dist/components/artifacts/editable-artifact-view.d.ts +0 -46
  90. package/dist/components/artifacts/latest-task-runner.d.ts +0 -15
  91. package/dist/components/artifacts/session-artifact-viewer.d.ts +0 -39
  92. package/dist/components/artifacts/site-view.d.ts +0 -23
  93. package/dist/components/timeline-pill-geometry.d.ts +0 -6
  94. package/dist/components/timeline-pill-placement.d.ts +0 -18
  95. package/dist/computer-viewer-NB6O6BTV.js.map +0 -1
  96. package/dist/markdown-table-layout-QXSLK4BY.js.map +0 -1
  97. package/dist/timeline-pill-geometry-2G6EK47V.js +0 -73
  98. package/dist/timeline-pill-geometry-2G6EK47V.js.map +0 -1
  99. package/src/components/artifacts/artifact-chrome.tsx +0 -413
  100. package/src/components/artifacts/artifact-sandbox.tsx +0 -185
  101. package/src/components/artifacts/chat-interactive-block.tsx +0 -260
  102. package/src/components/artifacts/deferred-chat-media.tsx +0 -67
  103. package/src/components/artifacts/editable-artifact-view.tsx +0 -187
  104. package/src/components/artifacts/latest-task-runner.ts +0 -49
  105. package/src/components/artifacts/session-artifact-viewer.tsx +0 -376
  106. package/src/components/artifacts/site-view.tsx +0 -116
  107. package/src/components/timeline-pill-geometry.ts +0 -112
  108. package/src/components/timeline-pill-placement.ts +0 -81
  109. /package/dist/{browser-viewer-AJO7LMVE.js.map → browser-viewer-2E6R2Y4W.js.map} +0 -0
  110. /package/dist/{chunk-A4UJB2MM.js.map → chunk-SEEVCT36.js.map} +0 -0
  111. /package/dist/{chunk-OQVBTOBJ.js.map → chunk-USXVQACU.js.map} +0 -0
  112. /package/dist/{chat-interactive-block-VAGABN7J.js.map → computer-viewer-WMJE6OA3.js.map} +0 -0
  113. /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
- // Even a temporary max-content width on the live table changes its height
87
- // during forced layout. Browser scroll anchoring can retain that displacement
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.parentElement!.append(probe);
102
- preferred = copy.getBoundingClientRect().width;
57
+ table.style.width = "max-content";
58
+ preferred = table.getBoundingClientRect().width;
103
59
  } finally {
104
- probe.remove();
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 aria-disabled:cursor-wait aria-disabled:opacity-70 pointer-coarse:min-h-11",
394
+ "inline-flex min-h-7 cursor-pointer items-center disabled:cursor-wait disabled:opacity-70 pointer-coarse:min-h-11",
395
395
  )}
396
- // Not `disabled`: disabling the focused control drops focus to the page,
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, type MarkdownProps } from "./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
- [renderInteractiveBlock, renderMessageText, searchItem?.id, searchTarget],
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
- data-og-pill-placement={questionPillPlacement}
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
- <motion.div
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
- </motion.div>
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
- {jumpPillShown ? (
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 { SESSION_SCOPE_HEADER, type SendMessageInput } from "@opengeni/sdk";
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, viewerLinks, inheritedLinks, defaultLinks) ?? undefined,
189
- [resolveLink, viewerLinks, inheritedLinks, defaultLinks],
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.initialHistoryReady
231
- ? () =>
232
- feed.jumpToLatestQuestion({
233
- onQueuedQuestion: async (turn, navigation) => {
234
- await queue.refresh();
235
- if (
236
- !navigation.isCurrent() ||
237
- scopeRef.current.client !== context.client ||
238
- scopeRef.current.workspaceId !== context.workspaceId
239
- )
240
- return;
241
- setQueueFocusTarget((previous) => ({
242
- turnId: turn.id,
243
- requestId: (previous?.requestId ?? 0) + 1,
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 allowance = parseAllowanceExhaustedRefusal(payload);
1365
- const failureText = allowance
1366
- ? allowanceExhaustedMessage(allowance)
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 (allowance) {
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,