@opengeni/react 5.0.5-canary.3 → 5.2.0-canary.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 +13 -1
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +1391 -2
- package/dist/artifacts.js.map +1 -1
- package/dist/chunk-2TKI3WEG.js +73 -0
- package/dist/chunk-2TKI3WEG.js.map +1 -0
- package/dist/{chunk-V3WX3TQG.js → chunk-45XEUVTK.js} +2 -2
- package/dist/{chunk-M2HNWZYR.js → chunk-IHQII5WO.js} +69 -18
- package/dist/chunk-IHQII5WO.js.map +1 -0
- package/dist/{chunk-HUMIRRYI.js → chunk-IYCR4343.js} +19 -19
- package/dist/{chunk-HUMIRRYI.js.map → chunk-IYCR4343.js.map} +1 -1
- package/dist/{chunk-6J56T3MG.js → chunk-NYESIADM.js} +14 -5
- package/dist/chunk-NYESIADM.js.map +1 -0
- package/dist/{chunk-7S2KWUJD.js → chunk-QXJCNVSD.js} +2283 -2215
- package/dist/chunk-QXJCNVSD.js.map +1 -0
- package/dist/{chunk-BV5A4OTE.js → chunk-T5FRXMC3.js} +21 -2
- package/dist/{chunk-BV5A4OTE.js.map → chunk-T5FRXMC3.js.map} +1 -1
- package/dist/{chunk-BRFMGY4L.js → chunk-UGMKPUEM.js} +28 -3
- package/dist/chunk-UGMKPUEM.js.map +1 -0
- package/dist/components/artifacts/inline-html-document.d.ts +2 -0
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +2 -0
- package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
- package/dist/components/markdown.d.ts +13 -1
- package/dist/components/message-timeline.d.ts +4 -1
- package/dist/components/session-conversation.d.ts +3 -0
- package/dist/components/user-message-body.d.ts +10 -1
- package/dist/composer.js +2 -2
- package/dist/{computer-viewer-7TEZC6Y2.js → computer-viewer-AA4JH2AP.js} +2 -2
- package/dist/connect.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +9 -6
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +1 -1
- package/dist/{platform-activity-row-27LKAWZD.js → platform-activity-row-UW4UVVM4.js} +6 -3
- package/dist/platform-activity-row-UW4UVVM4.js.map +1 -0
- package/dist/session-ui.d.ts +1 -1
- package/dist/session-ui.js +6 -5
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +2 -2
- package/dist/sites-ui.d.ts +2 -1
- package/dist/sites.js +12 -4
- package/dist/sites.js.map +1 -1
- package/dist/timeline/activity-rail.d.ts +1 -0
- package/dist/timeline/platform-activity-row.d.ts +1 -1
- package/dist/timeline/retained-image.d.ts +16 -0
- package/dist/timeline/rolling-activity.d.ts +9 -0
- package/dist/timeline/shared.d.ts +4 -1
- package/dist/timeline/turn-summary.d.ts +4 -1
- package/package.json +4 -4
- package/src/artifacts.ts +2 -0
- package/src/components/artifacts/inline-html-document.ts +24 -0
- package/src/components/artifacts/published-html-artifact-frame.tsx +32 -2
- package/src/components/artifacts/visualization-assets/base.css +1015 -0
- package/src/components/artifacts/visualization-assets/helpers.html +349 -0
- package/src/components/artifacts/visualization-assets.gen.ts +3 -0
- package/src/components/copy-button.tsx +7 -2
- package/src/components/markdown.tsx +111 -34
- package/src/components/message-timeline.tsx +61 -4
- package/src/components/session-conversation.tsx +5 -0
- package/src/components/user-message-body.tsx +20 -2
- package/src/hooks/use-composer.ts +15 -2
- package/src/hooks/use-computer-session.ts +36 -1
- package/src/index.ts +10 -2
- package/src/session-ui.ts +4 -1
- package/src/sites-ui.tsx +23 -6
- package/src/timeline/activity-rail.tsx +20 -3
- package/src/timeline/platform-activity-row.tsx +5 -2
- package/src/timeline/retained-image.ts +91 -0
- package/src/timeline/rolling-activity.tsx +83 -0
- package/src/timeline/shared.tsx +33 -12
- package/src/timeline/tool-renderers.tsx +7 -91
- package/src/timeline/turn-summary.tsx +21 -15
- package/styles/compiled.css +122 -0
- package/styles/index.css +49 -0
- package/dist/chunk-6J56T3MG.js.map +0 -1
- package/dist/chunk-7S2KWUJD.js.map +0 -1
- package/dist/chunk-BRFMGY4L.js.map +0 -1
- package/dist/chunk-M2HNWZYR.js.map +0 -1
- package/dist/platform-activity-row-27LKAWZD.js.map +0 -1
- /package/dist/{chunk-V3WX3TQG.js.map → chunk-45XEUVTK.js.map} +0 -0
- /package/dist/{computer-viewer-7TEZC6Y2.js.map → computer-viewer-AA4JH2AP.js.map} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RollingActivity } from "../timeline/rolling-activity";
|
|
1
2
|
import { GenieLoadingOptionsContext, type GenieLoadingOptions } from "../timeline/genie-loading";
|
|
2
3
|
import { ChildSessionLink } from "./child-session-link";
|
|
3
4
|
import { useStartupDetails } from "../timeline/startup-preference";
|
|
@@ -62,6 +63,7 @@ import {
|
|
|
62
63
|
UserMessageBody,
|
|
63
64
|
UserMessageDisclosureProvider,
|
|
64
65
|
type UserMessageDisclosureContextValue,
|
|
66
|
+
type UserMessageDisclosureLabels,
|
|
65
67
|
} from "./user-message-body";
|
|
66
68
|
import {
|
|
67
69
|
createTipFollowState,
|
|
@@ -128,6 +130,8 @@ const TimelineAnnotationSelection = lazy(() => import("./timeline-annotation-sel
|
|
|
128
130
|
const TimelineAnnotationMarkers = lazy(() => import("./timeline-annotation-markers"));
|
|
129
131
|
|
|
130
132
|
export type MessageTimelineProps = {
|
|
133
|
+
/** Localized user-message disclosure actions, including custom UserMessageBody renderers. */
|
|
134
|
+
userMessageDisclosureLabels?: UserMessageDisclosureLabels | undefined;
|
|
131
135
|
/** Raw session events (projected internally) … */
|
|
132
136
|
events?: SessionEvent[] | undefined;
|
|
133
137
|
/** … or pre-projected items (e.g. from `useSessionEvents().timeline`). */
|
|
@@ -404,6 +408,7 @@ function cssEscapeAttribute(value: string): string {
|
|
|
404
408
|
* with a "jump to latest" affordance when the reader scrolls back.
|
|
405
409
|
*/
|
|
406
410
|
export function MessageTimeline({
|
|
411
|
+
userMessageDisclosureLabels,
|
|
407
412
|
events,
|
|
408
413
|
items,
|
|
409
414
|
status: _status,
|
|
@@ -919,8 +924,16 @@ export function MessageTimeline({
|
|
|
919
924
|
() => ({
|
|
920
925
|
expandedByMessageId: userMessageDisclosureMemoryRef.current,
|
|
921
926
|
beginChange: beginUserMessageDisclosureChange,
|
|
927
|
+
labels: {
|
|
928
|
+
showMore: userMessageDisclosureLabels?.showMore,
|
|
929
|
+
showLess: userMessageDisclosureLabels?.showLess,
|
|
930
|
+
},
|
|
922
931
|
}),
|
|
923
|
-
[
|
|
932
|
+
[
|
|
933
|
+
beginUserMessageDisclosureChange,
|
|
934
|
+
userMessageDisclosureLabels?.showMore,
|
|
935
|
+
userMessageDisclosureLabels?.showLess,
|
|
936
|
+
],
|
|
924
937
|
);
|
|
925
938
|
const timelineGroupEntryContext = useMemo<TimelineGroupEntryContext>(
|
|
926
939
|
() => ({
|
|
@@ -2514,6 +2527,12 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2514
2527
|
const startupDetails = useStartupDetails();
|
|
2515
2528
|
const enter = useEntranceAnimation();
|
|
2516
2529
|
const settleChrome = useTurnSettleOpen();
|
|
2530
|
+
const previousSingleActivity = useRef<ActivityItem | undefined>(undefined);
|
|
2531
|
+
useLayoutEffect(() => {
|
|
2532
|
+
const work =
|
|
2533
|
+
group.kind === "activity" ? group.items.filter((item) => item.kind !== "startup-phase") : [];
|
|
2534
|
+
previousSingleActivity.current = work.length === 1 ? work[0] : undefined;
|
|
2535
|
+
}, [group]);
|
|
2517
2536
|
const foldMemory = useFoldMemory();
|
|
2518
2537
|
// Settled (or live-fold) activity clusters get a chip. Inside an expanded
|
|
2519
2538
|
// turn that is the second layer — quiet nested chips under the outer turn
|
|
@@ -2540,7 +2559,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2540
2559
|
);
|
|
2541
2560
|
}
|
|
2542
2561
|
const settleFold =
|
|
2543
|
-
|
|
2562
|
+
!turnSummary?.rolling &&
|
|
2563
|
+
(group.kind === "turn" ? !!(enter && !insideTurn && !turnDefaultOpen) : liveActivitySettle);
|
|
2544
2564
|
switch (group.kind) {
|
|
2545
2565
|
case "activity":
|
|
2546
2566
|
// Preparation is one quiet surface, not a fold with eight technical steps.
|
|
@@ -2566,6 +2586,24 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2566
2586
|
/>
|
|
2567
2587
|
);
|
|
2568
2588
|
}
|
|
2589
|
+
if (
|
|
2590
|
+
turnSummary?.rolling &&
|
|
2591
|
+
!startupDetails &&
|
|
2592
|
+
group.items.filter((item) => item.kind !== "startup-phase").length === 1
|
|
2593
|
+
) {
|
|
2594
|
+
return (
|
|
2595
|
+
<ActivityRail
|
|
2596
|
+
items={group.items}
|
|
2597
|
+
startupActive={false}
|
|
2598
|
+
bare
|
|
2599
|
+
toolRegistry={toolRegistry}
|
|
2600
|
+
onOpenSession={onOpenSession}
|
|
2601
|
+
onMemoryClick={onMemoryClick}
|
|
2602
|
+
loadRetainedScreenshot={loadRetainedScreenshot}
|
|
2603
|
+
loadRetainedArtifact={loadRetainedArtifact}
|
|
2604
|
+
/>
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2569
2607
|
if (insideTurn) {
|
|
2570
2608
|
// Nested chips whenever the parent has ≥2 clusters. During outer settle
|
|
2571
2609
|
// chrome they stay force-open so structure is visible and height stays
|
|
@@ -2577,7 +2615,13 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2577
2615
|
// settled closed pre-wrap was showing as a CHIP, so mounting it closed
|
|
2578
2616
|
// is both the stable height and the honest state — force-opening it
|
|
2579
2617
|
// was the "already-collapsed cluster auto-expands at the end" reopen.
|
|
2580
|
-
const
|
|
2618
|
+
const visibleActivity = group.items.filter((item) => item.kind !== "startup-phase");
|
|
2619
|
+
const singleThought =
|
|
2620
|
+
!startupDetails &&
|
|
2621
|
+
visibleActivity.length === 1 &&
|
|
2622
|
+
visibleActivity[0]?.kind === "reasoning";
|
|
2623
|
+
const useNestedChip =
|
|
2624
|
+
nestClusterChips && activityShouldFold && !containsGeneratedImage && !singleThought;
|
|
2581
2625
|
if (!useNestedChip) {
|
|
2582
2626
|
return (
|
|
2583
2627
|
<ActivityRail
|
|
@@ -2622,7 +2666,20 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2622
2666
|
items={group.items}
|
|
2623
2667
|
outcome={group.outcome}
|
|
2624
2668
|
failureText={group.failureText}
|
|
2625
|
-
defaultOpen={
|
|
2669
|
+
defaultOpen={
|
|
2670
|
+
group.outcome === "failed" || (!turnSummary?.rolling && !activityShouldFold)
|
|
2671
|
+
? true
|
|
2672
|
+
: undefined
|
|
2673
|
+
}
|
|
2674
|
+
liveHeader={
|
|
2675
|
+
turnSummary?.rolling && !group.outcome && !foldLiveCluster ? (
|
|
2676
|
+
<RollingActivity
|
|
2677
|
+
items={group.items}
|
|
2678
|
+
toolRegistry={toolRegistry}
|
|
2679
|
+
previousItem={previousSingleActivity.current}
|
|
2680
|
+
/>
|
|
2681
|
+
) : undefined
|
|
2682
|
+
}
|
|
2626
2683
|
foldKey={group.id}
|
|
2627
2684
|
facets={turnSummary?.facets}
|
|
2628
2685
|
settleFold={settleFold}
|
|
@@ -11,11 +11,14 @@ import { ChatComposer, type ChatComposerProps } from "./chat-composer";
|
|
|
11
11
|
import { QueueSurface } from "./queue-surface";
|
|
12
12
|
import { HumanInputSurface, type HumanInputSurfaceProps } from "./human-input-surface";
|
|
13
13
|
import { MessageTimeline } from "./message-timeline";
|
|
14
|
+
import type { UserMessageDisclosureLabels } from "./user-message-body";
|
|
14
15
|
import { conversationTimeline } from "../conversation-timeline";
|
|
15
16
|
import { cn } from "../lib/cn";
|
|
16
17
|
|
|
17
18
|
export type SessionConversationProps = ClientOverride & {
|
|
18
19
|
sessionId: string;
|
|
20
|
+
/** Localized actions for already-sent user-message disclosure. */
|
|
21
|
+
userMessageDisclosureLabels?: UserMessageDisclosureLabels | undefined;
|
|
19
22
|
loadSkillReview?: HumanInputSurfaceProps["loadSkillReview"];
|
|
20
23
|
className?: string;
|
|
21
24
|
/** Defaults to filling the host. The host owns available height. */
|
|
@@ -32,6 +35,7 @@ export function SessionConversation(props: SessionConversationProps) {
|
|
|
32
35
|
|
|
33
36
|
function Conversation({
|
|
34
37
|
sessionId,
|
|
38
|
+
userMessageDisclosureLabels,
|
|
35
39
|
loadSkillReview,
|
|
36
40
|
client,
|
|
37
41
|
workspaceId,
|
|
@@ -71,6 +75,7 @@ function Conversation({
|
|
|
71
75
|
>
|
|
72
76
|
{error && <p role="alert">{error.message}</p>}
|
|
73
77
|
<MessageTimeline
|
|
78
|
+
userMessageDisclosureLabels={userMessageDisclosureLabels}
|
|
74
79
|
className="min-h-0 flex-1"
|
|
75
80
|
items={conversationTimeline(feed.timeline, queue, composer)}
|
|
76
81
|
status={status}
|
|
@@ -190,7 +190,15 @@ function observeSharedResize(element: Element, callback: () => void): (() => voi
|
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
export type UserMessageDisclosureLabels = {
|
|
194
|
+
/** Collapsed disclosure action. Defaults to "Show more". */
|
|
195
|
+
showMore?: string | undefined;
|
|
196
|
+
/** Expanded disclosure action. Defaults to "Show less". */
|
|
197
|
+
showLess?: string | undefined;
|
|
198
|
+
};
|
|
199
|
+
|
|
193
200
|
export type UserMessageDisclosureContextValue = {
|
|
201
|
+
labels?: UserMessageDisclosureLabels | undefined;
|
|
194
202
|
expandedByMessageId: Map<string, boolean>;
|
|
195
203
|
beginChange: (
|
|
196
204
|
messageBody: HTMLElement,
|
|
@@ -235,6 +243,8 @@ export type UserMessageBodyProps = {
|
|
|
235
243
|
text: string;
|
|
236
244
|
children: ReactNode;
|
|
237
245
|
className?: string | undefined;
|
|
246
|
+
/** Per-field overrides for timeline labels, then the English defaults. */
|
|
247
|
+
disclosureLabels?: UserMessageDisclosureLabels | undefined;
|
|
238
248
|
};
|
|
239
249
|
|
|
240
250
|
/**
|
|
@@ -247,7 +257,13 @@ export type UserMessageBodyProps = {
|
|
|
247
257
|
* remembers expansion per durable message id and preserves the reader's scroll
|
|
248
258
|
* anchor when height changes.
|
|
249
259
|
*/
|
|
250
|
-
export function UserMessageBody({
|
|
260
|
+
export function UserMessageBody({
|
|
261
|
+
messageId,
|
|
262
|
+
text,
|
|
263
|
+
children,
|
|
264
|
+
className,
|
|
265
|
+
disclosureLabels,
|
|
266
|
+
}: UserMessageBodyProps) {
|
|
251
267
|
const disclosure = useContext(UserMessageDisclosureContext);
|
|
252
268
|
const [expanded, setExpanded] = useState(
|
|
253
269
|
() => disclosure?.expandedByMessageId.get(messageId) ?? false,
|
|
@@ -454,7 +470,9 @@ export function UserMessageBody({ messageId, text, children, className }: UserMe
|
|
|
454
470
|
className="mt-1.5 inline-flex min-h-7 items-center rounded-og-sm px-1.5 text-og-xs font-medium text-og-fg-muted outline-hidden transition-colors hover:bg-og-surface-3/60 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/45 pointer-coarse:min-h-11"
|
|
455
471
|
onClick={(event) => toggle(event.currentTarget)}
|
|
456
472
|
>
|
|
457
|
-
{expanded
|
|
473
|
+
{expanded
|
|
474
|
+
? (disclosureLabels?.showLess ?? disclosure?.labels?.showLess ?? "Show less")
|
|
475
|
+
: (disclosureLabels?.showMore ?? disclosure?.labels?.showMore ?? "Show more")}
|
|
458
476
|
</button>
|
|
459
477
|
</div>
|
|
460
478
|
);
|
|
@@ -677,6 +677,8 @@ export function useComposer(
|
|
|
677
677
|
const [acceptedControl, setAcceptedControl] = useState<EffectiveSessionControl | null>(null);
|
|
678
678
|
const [resuming, setResuming] = useState(false);
|
|
679
679
|
const [error, setError] = useState<Error | null>(null);
|
|
680
|
+
// Background draft reads recover independently of Send/Steer/control failures.
|
|
681
|
+
const [draftReadError, setDraftReadError] = useState<Error | null>(null);
|
|
680
682
|
const [draft, setDraft] = useState<ComposerDraft | null>(null);
|
|
681
683
|
const [draftLoading, setDraftLoading] = useState(Boolean(sessionId) && durableDrafts);
|
|
682
684
|
const [draftSaving, setDraftSaving] = useState(false);
|
|
@@ -794,6 +796,7 @@ export function useComposer(
|
|
|
794
796
|
setAcceptedControl(null);
|
|
795
797
|
setResuming(false);
|
|
796
798
|
setError(null);
|
|
799
|
+
setDraftReadError(null);
|
|
797
800
|
setDraft(null);
|
|
798
801
|
setDraftLoading(Boolean(sessionId) && durableDrafts);
|
|
799
802
|
setDraftSaving(false);
|
|
@@ -920,6 +923,7 @@ export function useComposer(
|
|
|
920
923
|
if (targetKeyRef.current !== targetKey) return;
|
|
921
924
|
if (!sessionId || !durableDrafts) {
|
|
922
925
|
setDraftLoading(false);
|
|
926
|
+
setDraftReadError(null);
|
|
923
927
|
return;
|
|
924
928
|
}
|
|
925
929
|
let retry = draftReadRetryRef.current;
|
|
@@ -993,6 +997,8 @@ export function useComposer(
|
|
|
993
997
|
if (retry.timer !== null) clearTimeout(retry.timer);
|
|
994
998
|
retry.failures = 0;
|
|
995
999
|
retry.timer = null;
|
|
1000
|
+
// Recovery also counts when the server returns the same draft revision.
|
|
1001
|
+
setDraftReadError(null);
|
|
996
1002
|
const currentRevision = draftRef.current?.revision ?? -1;
|
|
997
1003
|
// Reconnect and client-generation effects can legitimately ask for the
|
|
998
1004
|
// draft again. A same-revision response is not new authority: publishing
|
|
@@ -1045,12 +1051,18 @@ export function useComposer(
|
|
|
1045
1051
|
}
|
|
1046
1052
|
} catch (cause) {
|
|
1047
1053
|
if (
|
|
1054
|
+
!requestAbort.signal.aborted &&
|
|
1048
1055
|
generation === targetGeneration.current &&
|
|
1049
1056
|
targetKeyRef.current === targetKey &&
|
|
1050
1057
|
readTicket === draftReadGeneration.current
|
|
1051
1058
|
) {
|
|
1052
|
-
|
|
1059
|
+
const problem = asError(cause);
|
|
1060
|
+
const timedOut = problem.name === "TimeoutError";
|
|
1061
|
+
setDraftReadError(
|
|
1062
|
+
timedOut ? new Error("Draft sync timed out. Retrying…", { cause }) : problem,
|
|
1063
|
+
);
|
|
1053
1064
|
if (
|
|
1065
|
+
timedOut ||
|
|
1054
1066
|
cause instanceof TypeError ||
|
|
1055
1067
|
(cause && typeof cause === "object" && "retryable" in cause && cause.retryable === true)
|
|
1056
1068
|
) {
|
|
@@ -2639,6 +2651,7 @@ export function useComposer(
|
|
|
2639
2651
|
const clearError = useCallback(() => {
|
|
2640
2652
|
if (targetKeyRef.current !== targetKey) return;
|
|
2641
2653
|
setError(null);
|
|
2654
|
+
setDraftReadError(null);
|
|
2642
2655
|
setDraftConflict(null);
|
|
2643
2656
|
}, [targetKey]);
|
|
2644
2657
|
// `valueRef` is the synchronous composer authority. React state exists to
|
|
@@ -2706,7 +2719,7 @@ export function useComposer(
|
|
|
2706
2719
|
resolveDraftConflict,
|
|
2707
2720
|
restoredResources: identityMatches ? restoredResources : [],
|
|
2708
2721
|
removeRestoredResource,
|
|
2709
|
-
error: identityMatches ? error : null,
|
|
2722
|
+
error: identityMatches ? (error ?? draftReadError) : null,
|
|
2710
2723
|
clearError,
|
|
2711
2724
|
};
|
|
2712
2725
|
}
|
|
@@ -117,6 +117,33 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
117
117
|
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
118
118
|
const targets = sortComputerTargets(targetResponse.targets);
|
|
119
119
|
const selected = chooseTarget(targets, selectedTargetIdRef.current, session.platform);
|
|
120
|
+
// Discovery is useful independently of semantic observation. Publish it
|
|
121
|
+
// now so a slow/unresponsive application cannot block the frame stream
|
|
122
|
+
// or prevent the person from choosing a different window or screen.
|
|
123
|
+
const previous = observationRef.current.observation;
|
|
124
|
+
const retainedObservation =
|
|
125
|
+
selected &&
|
|
126
|
+
previous?.target.id === selected.id &&
|
|
127
|
+
previous.target.controllerGeneration === selected.controllerGeneration &&
|
|
128
|
+
previous.target.targetGeneration === selected.targetGeneration
|
|
129
|
+
? previous
|
|
130
|
+
: null;
|
|
131
|
+
selectedTargetIdRef.current = selected?.id ?? null;
|
|
132
|
+
targetsRef.current = { computerSessionId, targets };
|
|
133
|
+
observationRef.current = { computerSessionId, observation: retainedObservation };
|
|
134
|
+
setState((current) =>
|
|
135
|
+
current.computerSessionId === computerSessionId
|
|
136
|
+
? {
|
|
137
|
+
...current,
|
|
138
|
+
session,
|
|
139
|
+
targets,
|
|
140
|
+
selectedTargetId: selected?.id ?? null,
|
|
141
|
+
observation: retainedObservation,
|
|
142
|
+
loading: false,
|
|
143
|
+
error: null,
|
|
144
|
+
}
|
|
145
|
+
: current,
|
|
146
|
+
);
|
|
120
147
|
const observation = selected
|
|
121
148
|
? await client.observeComputerTarget(workspaceId, computerSessionId, selected.id, {
|
|
122
149
|
signal: controller.signal,
|
|
@@ -303,7 +330,15 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
303
330
|
? (targetsRef.current.targets.find((candidate) => candidate.id === frame.targetId) ??
|
|
304
331
|
(currentObservation?.target.id === frame.targetId ? currentObservation.target : null))
|
|
305
332
|
: null;
|
|
306
|
-
const
|
|
333
|
+
const inputTarget =
|
|
334
|
+
(action.type === "keyboard" || action.type === "clipboard") &&
|
|
335
|
+
targetsRef.current.computerSessionId === computerSessionId
|
|
336
|
+
? targetsRef.current.targets.find(
|
|
337
|
+
(candidate) => candidate.id === selectedTargetIdRef.current,
|
|
338
|
+
)
|
|
339
|
+
: null;
|
|
340
|
+
const target =
|
|
341
|
+
frameTarget ?? focusTarget ?? currentObservation?.target ?? inputTarget ?? null;
|
|
307
342
|
if (!target) throw new Error("The desktop target is not ready for input.");
|
|
308
343
|
if (frame && frame.targetId !== selectedTargetIdRef.current) {
|
|
309
344
|
throw new Error("The displayed desktop frame is no longer selected.");
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { retainedImageId } from "./components/markdown";
|
|
1
2
|
// oxlint-disable-next-line typescript/triple-slash-reference -- package consumers must load the ambient type for this optional untyped peer without emitting a runtime import.
|
|
2
3
|
/// <reference path="./types/external.d.ts" />
|
|
3
4
|
|
|
@@ -511,13 +512,20 @@ export { GeneratedVideoPlayer } from "./components/generated-video-player";
|
|
|
511
512
|
export type { GeneratedVideoPlayerProps } from "./components/generated-video-player";
|
|
512
513
|
export type { MessageTimelineProps } from "./components/message-timeline";
|
|
513
514
|
export { UserMessageBody, userMessageLikelyNeedsDisclosure } from "./components/user-message-body";
|
|
514
|
-
export type {
|
|
515
|
+
export type {
|
|
516
|
+
UserMessageBodyProps,
|
|
517
|
+
UserMessageDisclosureLabels,
|
|
518
|
+
} from "./components/user-message-body";
|
|
515
519
|
export {
|
|
516
520
|
Markdown,
|
|
517
521
|
sandboxFileLocationFromHref,
|
|
518
522
|
sandboxFilePathFromHref,
|
|
519
523
|
} from "./components/markdown";
|
|
520
|
-
export type {
|
|
524
|
+
export type {
|
|
525
|
+
MarkdownProps,
|
|
526
|
+
MarkdownInteractiveBlock,
|
|
527
|
+
SandboxFileLocation,
|
|
528
|
+
} from "./components/markdown";
|
|
521
529
|
export { CopyButton, CopyHoverFrame } from "./components/copy-button";
|
|
522
530
|
export { copyTextToClipboard, tableElementToTsv } from "./lib/clipboard";
|
|
523
531
|
export { SessionStatus, StatusDot, SESSION_STATUS_META } from "./components/session-status";
|
package/src/session-ui.ts
CHANGED
|
@@ -16,7 +16,10 @@ export type { MessageTimelineProps } from "./components/message-timeline";
|
|
|
16
16
|
export { createOlderHistoryLoadReceipt } from "./older-history";
|
|
17
17
|
export type { OlderHistoryLoader, OlderHistoryLoadReceipt } from "./older-history";
|
|
18
18
|
export { UserMessageBody, userMessageLikelyNeedsDisclosure } from "./components/user-message-body";
|
|
19
|
-
export type {
|
|
19
|
+
export type {
|
|
20
|
+
UserMessageBodyProps,
|
|
21
|
+
UserMessageDisclosureLabels,
|
|
22
|
+
} from "./components/user-message-body";
|
|
20
23
|
export { BUILT_IN_TURN_SUMMARY_FACET_IDS } from "./timeline/turn-summary";
|
|
21
24
|
export type {
|
|
22
25
|
BuiltInTurnSummaryFacetId,
|
package/src/sites-ui.tsx
CHANGED
|
@@ -29,10 +29,11 @@ type SiteScope = { client: SiteClient; workspaceId: string; className?: string }
|
|
|
29
29
|
/** Shared native/embedded read boundary: pin content to the observed version,
|
|
30
30
|
* and never accept a response belonging to another workspace or Site. */
|
|
31
31
|
export async function loadSiteSnapshot(
|
|
32
|
-
client: Pick<SiteClient, "getWorkspaceArtifact" | "getWorkspaceArtifactHtml"
|
|
32
|
+
client: Pick<SiteClient, "getWorkspaceArtifact" | "getWorkspaceArtifactHtml"> &
|
|
33
|
+
Partial<Pick<OpenGeniClient, "getWorkspaceArtifactContent">>,
|
|
33
34
|
workspaceId: string,
|
|
34
35
|
siteId: string,
|
|
35
|
-
options: { signal?: AbortSignal; includeArchivedContent?: boolean } = {},
|
|
36
|
+
options: { signal?: AbortSignal; includeArchivedContent?: boolean; versionId?: string } = {},
|
|
36
37
|
): Promise<{
|
|
37
38
|
detail: WorkspaceArtifactDetailResponse;
|
|
38
39
|
content: SiteDisplayContent | null;
|
|
@@ -42,19 +43,35 @@ export async function loadSiteSnapshot(
|
|
|
42
43
|
options.signal?.throwIfAborted();
|
|
43
44
|
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
44
45
|
throw new Error("Site scope mismatch");
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const selectedVersion = options.versionId
|
|
47
|
+
? detail.versions.find((version) => version.id === options.versionId)
|
|
48
|
+
: detail.artifact.currentVersion;
|
|
49
|
+
const versionId = options.versionId ?? selectedVersion?.id;
|
|
50
|
+
let content: SiteDisplayContent | null =
|
|
51
|
+
selectedVersion &&
|
|
52
|
+
versionId &&
|
|
53
|
+
(detail.artifact.status === "active" || options.includeArchivedContent)
|
|
48
54
|
? {
|
|
49
55
|
artifactId: siteId,
|
|
50
56
|
versionId,
|
|
51
|
-
requestedTools:
|
|
57
|
+
requestedTools: selectedVersion!.requestedTools,
|
|
52
58
|
html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {
|
|
53
59
|
...requestOptions,
|
|
54
60
|
versionId,
|
|
55
61
|
}),
|
|
56
62
|
}
|
|
57
63
|
: null;
|
|
64
|
+
if (
|
|
65
|
+
versionId &&
|
|
66
|
+
!selectedVersion &&
|
|
67
|
+
(detail.artifact.status === "active" || options.includeArchivedContent)
|
|
68
|
+
) {
|
|
69
|
+
if (!client.getWorkspaceArtifactContent) throw new Error("Site version unavailable");
|
|
70
|
+
content = await client.getWorkspaceArtifactContent(workspaceId, siteId, {
|
|
71
|
+
...requestOptions,
|
|
72
|
+
versionId,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
58
75
|
options.signal?.throwIfAborted();
|
|
59
76
|
if (content && (content.artifactId !== siteId || content.versionId !== versionId))
|
|
60
77
|
throw new Error("Site content mismatch");
|
|
@@ -2,7 +2,7 @@ import { AnimatePresence, motion, useReducedMotion } from "motion/react";
|
|
|
2
2
|
import { GenieLoading } from "./genie-loading";
|
|
3
3
|
import { useStartupDetails } from "./startup-preference";
|
|
4
4
|
import { ArrowRightIcon, BotIcon, BrainCircuitIcon } from "lucide-react";
|
|
5
|
-
import { lazy, Suspense, useLayoutEffect, useRef, useState } from "react";
|
|
5
|
+
import { lazy, Suspense, useContext, useLayoutEffect, useRef, useState } from "react";
|
|
6
6
|
import { jsx as rowJsx, jsxs as rowJsxs } from "react/jsx-runtime";
|
|
7
7
|
import { Markdown } from "../components/markdown";
|
|
8
8
|
import { cn } from "../lib/cn";
|
|
@@ -11,7 +11,13 @@ import { defaultToolRegistry } from "./tool-renderers";
|
|
|
11
11
|
import { useEntranceAnimation, useEntranceAnimationLive } from "./entrance";
|
|
12
12
|
import type { RetainedArtifactLoader, RetainedScreenshotLoader, ToolRegistry } from "./registry";
|
|
13
13
|
import { useSeenActivityIds } from "./seen-activity-ids";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
BodyNote,
|
|
16
|
+
PayloadBlock,
|
|
17
|
+
ActivityDisclosure,
|
|
18
|
+
CompactActivityContext,
|
|
19
|
+
ToolCallTruncationProvider,
|
|
20
|
+
} from "./shared";
|
|
15
21
|
import { toolDisplayName } from "./tool-display-name";
|
|
16
22
|
import type { ActivityItem, MemoryItem, WorkerItem } from "./types";
|
|
17
23
|
|
|
@@ -214,7 +220,7 @@ function assertNever(item: never): never {
|
|
|
214
220
|
throw new Error(`ActivityRail: unhandled activity item ${JSON.stringify(item)}`);
|
|
215
221
|
}
|
|
216
222
|
|
|
217
|
-
function renderActivity(
|
|
223
|
+
export function renderActivity(
|
|
218
224
|
item: ActivityItem,
|
|
219
225
|
toolRegistry: ToolRegistry,
|
|
220
226
|
onOpenSession: ((sessionId: string) => void) | undefined,
|
|
@@ -380,6 +386,7 @@ function WorkerRow({
|
|
|
380
386
|
item: WorkerItem;
|
|
381
387
|
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
382
388
|
}) {
|
|
389
|
+
const compact = useContext(CompactActivityContext);
|
|
383
390
|
const running = item.status === "running";
|
|
384
391
|
const failed = item.status === "failed";
|
|
385
392
|
const cancelled = item.status === "cancelled";
|
|
@@ -399,6 +406,16 @@ function WorkerRow({
|
|
|
399
406
|
: cancelled
|
|
400
407
|
? "Worker interrupted"
|
|
401
408
|
: "Worker messaged";
|
|
409
|
+
if (compact) {
|
|
410
|
+
return (
|
|
411
|
+
<ActivityDisclosure
|
|
412
|
+
icon={<BotIcon className="size-3.5" />}
|
|
413
|
+
title={title}
|
|
414
|
+
preview={item.prompt}
|
|
415
|
+
running={running}
|
|
416
|
+
/>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
402
419
|
// A worker is a first-class actor but still a STEP on the rail — a borderless
|
|
403
420
|
// row (no card), aligned to its sibling tool rows: the chevron column is an
|
|
404
421
|
// empty spacer (a worker doesn't expand), then the bot glyph, then the title
|
|
@@ -10,7 +10,6 @@ export default function PlatformActivityRow({
|
|
|
10
10
|
t: displayName,
|
|
11
11
|
b: BotIcon,
|
|
12
12
|
m: Markdown,
|
|
13
|
-
r: truncate,
|
|
14
13
|
j,
|
|
15
14
|
s,
|
|
16
15
|
}: {
|
|
@@ -35,7 +34,11 @@ export default function PlatformActivityRow({
|
|
|
35
34
|
children: "Thought",
|
|
36
35
|
}),
|
|
37
36
|
running: item.streaming,
|
|
38
|
-
|
|
37
|
+
compactPreview: item.text.replace(/\*\*|__|`/g, "").replace(/\s+/g, " "),
|
|
38
|
+
preview: j("span", {
|
|
39
|
+
className: "og-reasoning-preview",
|
|
40
|
+
children: j(Markdown, { streaming: item.streaming, children: item.text }),
|
|
41
|
+
}),
|
|
39
42
|
children: j("div", {
|
|
40
43
|
className: "text-og-base leading-6 text-og-fg-muted [&_strong]:text-og-fg-muted",
|
|
41
44
|
children: j(Markdown, { streaming: item.streaming, children: item.text }),
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import type { RetainedArtifactReference } from "@opengeni/sdk";
|
|
3
|
+
import type { ToolRendererProps } from "./registry";
|
|
4
|
+
type RetainedImageState =
|
|
5
|
+
| { kind: "loading" }
|
|
6
|
+
| { kind: "ready"; url: string }
|
|
7
|
+
| { kind: "unavailable"; label: string }
|
|
8
|
+
| { kind: "error"; message: string };
|
|
9
|
+
|
|
10
|
+
export function useRetainedImageObjectUrl(
|
|
11
|
+
artifact: RetainedArtifactReference,
|
|
12
|
+
load: ToolRendererProps["loadRetainedArtifact"],
|
|
13
|
+
): RetainedImageState {
|
|
14
|
+
const [state, setState] = useState<RetainedImageState>({ kind: "loading" });
|
|
15
|
+
// Function outputs are commonly serialized JSON. Parsing them creates a new
|
|
16
|
+
// object on every render, so depend on the immutable wire value rather than
|
|
17
|
+
// object identity; otherwise the loader can refetch after its own setState.
|
|
18
|
+
const artifactValue = retainedArtifactValue(artifact);
|
|
19
|
+
const stableArtifactRef = useRef({ value: artifactValue, artifact });
|
|
20
|
+
if (stableArtifactRef.current.value !== artifactValue) {
|
|
21
|
+
stableArtifactRef.current = { value: artifactValue, artifact };
|
|
22
|
+
}
|
|
23
|
+
const stableArtifact = stableArtifactRef.current.artifact;
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!load) {
|
|
26
|
+
setState({ kind: "unavailable", label: "retrieval is not configured" });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
let objectUrl: string | null = null;
|
|
31
|
+
setState({ kind: "loading" });
|
|
32
|
+
void load(stableArtifact, controller.signal)
|
|
33
|
+
.then((source) => {
|
|
34
|
+
if (controller.signal.aborted) return;
|
|
35
|
+
if (!source) {
|
|
36
|
+
setState({ kind: "unavailable", label: "bytes are unavailable" });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!(source instanceof Uint8Array)) {
|
|
40
|
+
if (!source.url.trim()) {
|
|
41
|
+
setState({ kind: "unavailable", label: "URL is unavailable" });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
setState({ kind: "ready", url: source.url });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
objectUrl = URL.createObjectURL(
|
|
48
|
+
new Blob([source as unknown as BlobPart], { type: stableArtifact.contentType }),
|
|
49
|
+
);
|
|
50
|
+
setState({ kind: "ready", url: objectUrl });
|
|
51
|
+
})
|
|
52
|
+
.catch((error: unknown) => {
|
|
53
|
+
if (controller.signal.aborted) return;
|
|
54
|
+
const status =
|
|
55
|
+
error && typeof error === "object" && "status" in error
|
|
56
|
+
? Number((error as { status?: unknown }).status)
|
|
57
|
+
: null;
|
|
58
|
+
setState(
|
|
59
|
+
status === 404
|
|
60
|
+
? { kind: "unavailable", label: "deleted" }
|
|
61
|
+
: status === 410
|
|
62
|
+
? { kind: "unavailable", label: "expired or unavailable" }
|
|
63
|
+
: { kind: "error", message: "retrieval failed" },
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
return () => {
|
|
67
|
+
controller.abort();
|
|
68
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
69
|
+
};
|
|
70
|
+
}, [stableArtifact, load]);
|
|
71
|
+
return state;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function retainedArtifactValue(artifact: RetainedArtifactReference): string {
|
|
75
|
+
return [
|
|
76
|
+
artifact.artifactId,
|
|
77
|
+
artifact.kind,
|
|
78
|
+
artifact.contentType,
|
|
79
|
+
artifact.originalBytes,
|
|
80
|
+
artifact.sha256,
|
|
81
|
+
artifact.retainedAt,
|
|
82
|
+
artifact.dimensions?.width ?? "",
|
|
83
|
+
artifact.dimensions?.height ?? "",
|
|
84
|
+
artifact.retention.policy,
|
|
85
|
+
artifact.retention.expiresAt ?? "",
|
|
86
|
+
artifact.retrieval.method,
|
|
87
|
+
artifact.retrieval.path,
|
|
88
|
+
artifact.retrieval.acceptRanges,
|
|
89
|
+
artifact.retrieval.maxRangeBytes,
|
|
90
|
+
].join("\0");
|
|
91
|
+
}
|