@opengeni/react 0.27.0 → 0.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +32 -0
  2. package/dist/{chunk-OZDLELJQ.js → chunk-OCHBZ2X7.js} +40 -10
  3. package/dist/chunk-OCHBZ2X7.js.map +1 -0
  4. package/dist/{chunk-NBYNFI5T.js → chunk-PMSGE233.js} +5 -3
  5. package/dist/chunk-PMSGE233.js.map +1 -0
  6. package/dist/{chunk-GQN2QIR2.js → chunk-WAA247AB.js} +178 -87
  7. package/dist/chunk-WAA247AB.js.map +1 -0
  8. package/dist/index.d.ts +7 -34
  9. package/dist/index.js +5 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/{queue-surface-implementation-3UW3MIIR.js → queue-surface-implementation-UQ535OKQ.js} +44 -12
  12. package/dist/queue-surface-implementation-UQ535OKQ.js.map +1 -0
  13. package/dist/{session-LsampPc6.d.ts → session-C3tIXLim.d.ts} +1 -1
  14. package/dist/{session-ui-BPOV-tuY.d.ts → session-ui-B_7cfN5u.d.ts} +81 -5
  15. package/dist/session-ui.d.ts +2 -2
  16. package/dist/session-ui.js +4 -2
  17. package/dist/session.d.ts +2 -2
  18. package/dist/session.js +2 -2
  19. package/dist/{use-turn-queue-aDnQvzVz.d.ts → use-turn-queue-Drm_ypue.d.ts} +7 -3
  20. package/package.json +2 -2
  21. package/src/components/message-timeline.tsx +10 -0
  22. package/src/components/queue-surface-implementation.tsx +79 -11
  23. package/src/components/queue-surface.tsx +5 -5
  24. package/src/hooks/use-turn-queue.ts +8 -0
  25. package/src/index.ts +7 -0
  26. package/src/session-ui.ts +9 -0
  27. package/src/timeline/index.ts +11 -2
  28. package/src/timeline/parsers.ts +3 -31
  29. package/src/timeline/projection.ts +53 -9
  30. package/src/timeline/turn-summary.tsx +243 -56
  31. package/src/timeline/types.ts +1 -1
  32. package/dist/chunk-GQN2QIR2.js.map +0 -1
  33. package/dist/chunk-NBYNFI5T.js.map +0 -1
  34. package/dist/chunk-OZDLELJQ.js.map +0 -1
  35. package/dist/queue-surface-implementation-3UW3MIIR.js.map +0 -1
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  buildTimeline,
6
6
  groupTimeline
7
- } from "./chunk-OZDLELJQ.js";
7
+ } from "./chunk-OCHBZ2X7.js";
8
8
  import {
9
9
  formatRelativeTime,
10
10
  stringifyPayload,
@@ -19,7 +19,7 @@ import {
19
19
  import { Loader2Icon } from "lucide-react";
20
20
  import { lazy, Suspense } from "react";
21
21
  import { jsx, jsxs } from "react/jsx-runtime";
22
- var loadQueueSurface = () => import("./queue-surface-implementation-3UW3MIIR.js");
22
+ var loadQueueSurface = () => import("./queue-surface-implementation-UQ535OKQ.js");
23
23
  var QueueSurface = createQueueSurface(loadQueueSurface);
24
24
  function createQueueSurface(loadImplementation) {
25
25
  const LazyQueueSurface = lazy(async () => ({
@@ -27,14 +27,14 @@ function createQueueSurface(loadImplementation) {
27
27
  }));
28
28
  return function QueueSurfaceBoundary(props) {
29
29
  const { queue } = props;
30
- if (queue.queue.length === 0) {
30
+ if (queue.queue.length + queue.pendingInputs.length === 0) {
31
31
  if (!queue.stoppingPreviousAttempt && !queue.error && !queue.mutationError) return null;
32
32
  return /* @__PURE__ */ jsx(EmptyQueueStateSurface, { queue });
33
33
  }
34
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(QueueSurfaceFallback, { count: queue.queue.length }), children: /* @__PURE__ */ jsx(LazyQueueSurface, { ...props }) });
34
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(QueueSurfaceFallback, {}), children: /* @__PURE__ */ jsx(LazyQueueSurface, { ...props }) });
35
35
  };
36
36
  }
37
- function QueueSurfaceFallback({ count }) {
37
+ function QueueSurfaceFallback() {
38
38
  return /* @__PURE__ */ jsx(
39
39
  "div",
40
40
  {
@@ -50,11 +50,7 @@ function QueueSurfaceFallback({ count }) {
50
50
  className: "size-3.5 shrink-0 animate-spin motion-reduce:animate-none"
51
51
  }
52
52
  ),
53
- "Loading ",
54
- count,
55
- " queued prompt",
56
- count === 1 ? "" : "s",
57
- "\u2026"
53
+ "Loading inputs\u2026"
58
54
  ] }) })
59
55
  }
60
56
  );
@@ -408,6 +404,7 @@ function createToolRegistry(baseEntries, baseFallback, options = {}) {
408
404
  }
409
405
 
410
406
  // src/timeline/parsers.ts
407
+ import { normalizeMcpOutput } from "@opengeni/sdk";
411
408
  function sandboxCommandExitCode(out) {
412
409
  const match = String(out ?? "").match(/Process exited with code (-?\d+)/);
413
410
  return match ? Number(match[1]) : null;
@@ -583,28 +580,8 @@ function tailPeek(text) {
583
580
  return lines[lines.length - 1] ?? "";
584
581
  }
585
582
  function unwrapMcpOutput(output) {
586
- if (output && typeof output === "object" && output.type === "text" && typeof output.text === "string") {
587
- const record = output;
588
- return { text: record.text, isError: Boolean(record.isError) };
589
- }
590
- if (output && typeof output === "object" && "content" in output) {
591
- const record = output;
592
- const isError = Boolean(record.isError);
593
- if (Array.isArray(record.content)) {
594
- const textPart = record.content.find(
595
- (part) => !!part && typeof part === "object" && part.type === "text"
596
- );
597
- return {
598
- text: textPart ? String(textPart.text) : JSON.stringify(output),
599
- isError
600
- };
601
- }
602
- return { text: JSON.stringify(output), isError };
603
- }
604
- return {
605
- text: typeof output === "string" ? output : output == null ? "" : JSON.stringify(output),
606
- isError: false
607
- };
583
+ const normalized = normalizeMcpOutput(output);
584
+ return { text: normalized.text, isError: normalized.isError };
608
585
  }
609
586
  function screenshotDataUrl(out) {
610
587
  if (typeof out === "string") {
@@ -2388,9 +2365,17 @@ function WorkerRow({
2388
2365
 
2389
2366
  // src/timeline/turn-summary.tsx
2390
2367
  import { CheckIcon, ChevronRightIcon as ChevronRightIcon2, CircleSlashIcon, TriangleAlertIcon } from "lucide-react";
2391
- import { useState as useState7 } from "react";
2368
+ import { Component, useMemo as useMemo2, useState as useState7 } from "react";
2392
2369
  import { Collapsible as Collapsible2 } from "radix-ui";
2393
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2370
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2371
+ var BUILT_IN_TURN_SUMMARY_FACET_IDS = [
2372
+ "steps",
2373
+ "files",
2374
+ "commands",
2375
+ "screenshots",
2376
+ "memories",
2377
+ "duration"
2378
+ ];
2394
2379
  function TurnSummary({
2395
2380
  items,
2396
2381
  outcome,
@@ -2398,12 +2383,31 @@ function TurnSummary({
2398
2383
  durationMs,
2399
2384
  defaultOpen,
2400
2385
  bare,
2386
+ facets: facetConfiguration,
2401
2387
  children
2402
2388
  }) {
2403
2389
  const forcedDefaultOpen = useForcedDefaultOpen();
2404
2390
  const [open, setOpen] = useState7(defaultOpen ?? forcedDefaultOpen ?? false);
2405
2391
  const enter = useEntranceAnimation();
2406
- const facets = summarizeTurn(items, durationMs);
2392
+ const context = useMemo2(
2393
+ () => createTurnSummaryContext(items, outcome, failureText, durationMs),
2394
+ [items, outcome, failureText, durationMs]
2395
+ );
2396
+ const facetDefinitions = useMemo2(
2397
+ () => resolveTurnSummaryFacets(facetConfiguration),
2398
+ [facetConfiguration]
2399
+ );
2400
+ const facets = useMemo2(
2401
+ () => facetDefinitions.flatMap((facet) => {
2402
+ try {
2403
+ const result = facet.summarize(context);
2404
+ return result && hasFacetContent(result.content) ? [{ facet, result }] : [];
2405
+ } catch {
2406
+ return [];
2407
+ }
2408
+ }),
2409
+ [context, facetDefinitions]
2410
+ );
2407
2411
  return /* @__PURE__ */ jsxs8(
2408
2412
  Collapsible2.Root,
2409
2413
  {
@@ -2445,7 +2449,13 @@ function TurnSummary({
2445
2449
  }
2446
2450
  ),
2447
2451
  /* @__PURE__ */ jsxs8("span", { className: cn("min-w-0 flex-1 truncate", bare ? "text-og-sm" : "text-og-fg-muted"), children: [
2448
- facets,
2452
+ facets.map(({ facet, result }, index) => /* @__PURE__ */ jsx10(FacetRenderBoundary, { children: /* @__PURE__ */ jsxs8(Fragment4, { children: [
2453
+ index > 0 ? " \xB7 " : null,
2454
+ /* @__PURE__ */ jsxs8("span", { "aria-label": result.ariaLabel, title: result.title, children: [
2455
+ result.icon ? /* @__PURE__ */ jsx10("span", { "aria-hidden": true, className: "mr-1 inline-flex align-[-0.125em]", children: result.icon }) : null,
2456
+ result.content
2457
+ ] })
2458
+ ] }) }, facet.id)),
2449
2459
  outcome === "failed" && failureText ? /* @__PURE__ */ jsxs8("span", { className: "text-og-status-failed", children: [
2450
2460
  " \xB7 ",
2451
2461
  failureText
@@ -2472,56 +2482,130 @@ function TurnSummary({
2472
2482
  }
2473
2483
  );
2474
2484
  }
2475
- function summarizeTurn(items, durationMs) {
2476
- let files = 0;
2477
- let commands = 0;
2478
- let screenshots = 0;
2479
- let memoriesSaved = 0;
2480
- let memoriesUpdated = 0;
2481
- for (const item of items) {
2482
- if (item.kind === "memory") {
2483
- if (item.variant === "corrected") {
2484
- memoriesUpdated += 1;
2485
- } else {
2486
- memoriesSaved += 1;
2485
+ function createTurnSummaryContext(items, outcome, failureText, durationMs) {
2486
+ const itemSnapshot = Object.freeze([...items]);
2487
+ const toolCalls = Object.freeze(
2488
+ itemSnapshot.filter((item) => item.kind === "tool-call")
2489
+ );
2490
+ const settled = itemSnapshot.every((item) => {
2491
+ if (item.kind === "reasoning") {
2492
+ return !item.streaming;
2493
+ }
2494
+ if (item.kind === "tool-call" || item.kind === "worker" || item.kind === "sandbox") {
2495
+ return item.status !== "running";
2496
+ }
2497
+ return true;
2498
+ });
2499
+ return Object.freeze({
2500
+ items: itemSnapshot,
2501
+ toolCalls,
2502
+ outcome,
2503
+ failureText,
2504
+ durationMs,
2505
+ settled
2506
+ });
2507
+ }
2508
+ var BUILT_IN_TURN_SUMMARY_FACETS = Object.freeze([
2509
+ {
2510
+ id: "steps",
2511
+ summarize: ({ items }) => ({
2512
+ content: `${items.length} ${items.length === 1 ? "step" : "steps"}`
2513
+ })
2514
+ },
2515
+ {
2516
+ id: "files",
2517
+ summarize: ({ toolCalls }) => {
2518
+ let files = 0;
2519
+ for (const item of toolCalls) {
2520
+ if (isApplyPatch(item)) {
2521
+ files += applyPatchOps(item.raw).length;
2522
+ }
2487
2523
  }
2488
- continue;
2524
+ return files ? { content: `${files} ${files === 1 ? "file" : "files"} edited` } : null;
2489
2525
  }
2490
- if (item.kind !== "tool-call") {
2491
- continue;
2526
+ },
2527
+ {
2528
+ id: "commands",
2529
+ summarize: ({ toolCalls }) => {
2530
+ const commands = toolCalls.filter((item) => item.name === "exec_command").length;
2531
+ return commands ? { content: `${commands} ${commands === 1 ? "command" : "commands"}` } : null;
2532
+ }
2533
+ },
2534
+ {
2535
+ id: "screenshots",
2536
+ summarize: ({ toolCalls }) => {
2537
+ let screenshots = 0;
2538
+ for (const item of toolCalls) {
2539
+ if ((rawTypeOf(item) === "computer_call" || item.name === "computer_call" || item.name === "computer_screenshot") && (screenshotDataUrl(item.output) !== null || mediaPreviewFact(item.output) !== null)) {
2540
+ screenshots += 1;
2541
+ }
2542
+ }
2543
+ return screenshots ? {
2544
+ content: `${screenshots} ${screenshots === 1 ? "screenshot" : "screenshots"}`
2545
+ } : null;
2492
2546
  }
2493
- if (isApplyPatch(item)) {
2494
- files += applyPatchOps(item.raw).length;
2495
- } else if (item.name === "exec_command") {
2496
- commands += 1;
2497
- } else if (rawTypeOf(item) === "computer_call" || item.name === "computer_call" || item.name === "computer_screenshot") {
2498
- if (screenshotDataUrl(item.output) !== null || mediaPreviewFact(item.output) !== null) {
2499
- screenshots += 1;
2547
+ },
2548
+ {
2549
+ id: "memories",
2550
+ summarize: ({ items }) => {
2551
+ let saved = 0;
2552
+ let updated = 0;
2553
+ for (const item of items) {
2554
+ if (item.kind !== "memory") {
2555
+ continue;
2556
+ }
2557
+ if (item.variant === "corrected") {
2558
+ updated += 1;
2559
+ } else {
2560
+ saved += 1;
2561
+ }
2562
+ }
2563
+ const parts = [];
2564
+ if (saved) {
2565
+ parts.push(`${saved} ${saved === 1 ? "memory" : "memories"} saved`);
2500
2566
  }
2567
+ if (updated) {
2568
+ parts.push(`${updated} ${updated === 1 ? "memory" : "memories"} updated`);
2569
+ }
2570
+ return parts.length > 0 ? { content: parts.join(" \xB7 ") } : null;
2501
2571
  }
2572
+ },
2573
+ {
2574
+ id: "duration",
2575
+ summarize: ({ durationMs }) => {
2576
+ const duration = formatDurationFacet(durationMs);
2577
+ return duration ? { content: duration } : null;
2578
+ }
2579
+ }
2580
+ ]);
2581
+ function resolveTurnSummaryFacets(configuration) {
2582
+ const requested = configuration?.replace ?? [
2583
+ ...BUILT_IN_TURN_SUMMARY_FACETS.filter(
2584
+ (facet) => !configuration?.remove?.includes(facet.id)
2585
+ ),
2586
+ ...configuration?.add ?? []
2587
+ ];
2588
+ const seen = /* @__PURE__ */ new Set();
2589
+ return requested.filter((facet) => {
2590
+ if (!facet.id || seen.has(facet.id)) {
2591
+ return false;
2592
+ }
2593
+ seen.add(facet.id);
2594
+ return true;
2595
+ });
2596
+ }
2597
+ function hasFacetContent(content) {
2598
+ return content !== null && content !== void 0 && content !== false && content !== "";
2599
+ }
2600
+ var FacetRenderBoundary = class extends Component {
2601
+ state = { failed: false };
2602
+ static getDerivedStateFromError() {
2603
+ return { failed: true };
2502
2604
  }
2503
- const parts = [`${items.length} ${items.length === 1 ? "step" : "steps"}`];
2504
- if (files) {
2505
- parts.push(`${files} ${files === 1 ? "file" : "files"} edited`);
2506
- }
2507
- if (commands) {
2508
- parts.push(`${commands} ${commands === 1 ? "command" : "commands"}`);
2509
- }
2510
- if (screenshots) {
2511
- parts.push(`${screenshots} ${screenshots === 1 ? "screenshot" : "screenshots"}`);
2512
- }
2513
- if (memoriesSaved) {
2514
- parts.push(`${memoriesSaved} ${memoriesSaved === 1 ? "memory" : "memories"} saved`);
2515
- }
2516
- if (memoriesUpdated) {
2517
- parts.push(`${memoriesUpdated} ${memoriesUpdated === 1 ? "memory" : "memories"} updated`);
2518
- }
2519
- const duration = formatDurationFacet(durationMs);
2520
- if (duration) {
2521
- parts.push(duration);
2605
+ render() {
2606
+ return this.state.failed ? null : this.props.children;
2522
2607
  }
2523
- return parts.join(" \xB7 ");
2524
- }
2608
+ };
2525
2609
  function formatDurationFacet(durationMs) {
2526
2610
  if (durationMs === void 0 || !Number.isFinite(durationMs) || durationMs < 1e3) {
2527
2611
  return null;
@@ -2796,11 +2880,11 @@ import {
2796
2880
  useCallback as useCallback2,
2797
2881
  useEffect as useEffect4,
2798
2882
  useLayoutEffect,
2799
- useMemo as useMemo2,
2883
+ useMemo as useMemo3,
2800
2884
  useRef as useRef3,
2801
2885
  useState as useState8
2802
2886
  } from "react";
2803
- import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
2887
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
2804
2888
  var INITIAL_MOUNTED_GROUPS = 1;
2805
2889
  function MessageTimeline({
2806
2890
  events,
@@ -2812,6 +2896,7 @@ function MessageTimeline({
2812
2896
  onReconnect,
2813
2897
  resolveProviderLogo,
2814
2898
  toolRegistry = defaultToolRegistry,
2899
+ turnSummary,
2815
2900
  autoFollow = true,
2816
2901
  hasOlder = false,
2817
2902
  loadingOlder = false,
@@ -2819,8 +2904,8 @@ function MessageTimeline({
2819
2904
  emptyState,
2820
2905
  className
2821
2906
  }) {
2822
- const resolvedItems = useMemo2(() => items ?? buildTimeline(events ?? []), [items, events]);
2823
- const allGroups = useMemo2(() => groupTimeline(resolvedItems), [resolvedItems]);
2907
+ const resolvedItems = useMemo3(() => items ?? buildTimeline(events ?? []), [items, events]);
2908
+ const allGroups = useMemo3(() => groupTimeline(resolvedItems), [resolvedItems]);
2824
2909
  const keyedGroups = useStableTimelineGroupKeys(allGroups);
2825
2910
  const { mountedGroups: groups, mountingOlderGroups } = useProgressivelyMountedGroups(keyedGroups);
2826
2911
  const firstGroupKey = allGroups[0] ? timelineGroupKey(allGroups[0]) : null;
@@ -3009,6 +3094,7 @@ function MessageTimeline({
3009
3094
  onReconnect,
3010
3095
  resolveProviderLogo,
3011
3096
  toolRegistry,
3097
+ turnSummary,
3012
3098
  foldLiveCluster: isAgentProgress(groups[index + 1]?.group)
3013
3099
  }
3014
3100
  ) }, key)),
@@ -3048,7 +3134,7 @@ function MessageTimeline({
3048
3134
  }
3049
3135
  function useStableTimelineGroupKeys(allGroups) {
3050
3136
  const previousRef = useRef3([]);
3051
- const keyedGroups = useMemo2(() => {
3137
+ const keyedGroups = useMemo3(() => {
3052
3138
  const previousByItemId = /* @__PURE__ */ new Map();
3053
3139
  for (const previous of previousRef.current) {
3054
3140
  for (const itemId of timelineGroupItemIds(previous.group)) {
@@ -3084,7 +3170,7 @@ function useStableTimelineGroupKeys(allGroups) {
3084
3170
  }
3085
3171
  function useProgressivelyMountedGroups(allGroups) {
3086
3172
  const previousGroupKeysRef = useRef3([]);
3087
- const groupKeys = useMemo2(() => {
3173
+ const groupKeys = useMemo3(() => {
3088
3174
  const nextKeys = allGroups.map((group) => group.key);
3089
3175
  const previousKeys = previousGroupKeysRef.current;
3090
3176
  return nextKeys.length === previousKeys.length && nextKeys.every((key, index) => key === previousKeys[index]) ? previousKeys : nextKeys;
@@ -3151,6 +3237,7 @@ var TimelineGroupView = memo2(function TimelineGroupView2({
3151
3237
  onReconnect,
3152
3238
  resolveProviderLogo,
3153
3239
  toolRegistry,
3240
+ turnSummary,
3154
3241
  insideTurn = false,
3155
3242
  foldLiveCluster = false
3156
3243
  }) {
@@ -3164,6 +3251,7 @@ var TimelineGroupView = memo2(function TimelineGroupView2({
3164
3251
  failureText: insideTurn ? void 0 : group.failureText,
3165
3252
  defaultOpen: !insideTurn && group.outcome === "failed" ? true : void 0,
3166
3253
  bare: insideTurn,
3254
+ facets: turnSummary?.facets,
3167
3255
  children: /* @__PURE__ */ jsx13(
3168
3256
  ActivityRail,
3169
3257
  {
@@ -3201,6 +3289,7 @@ var TimelineGroupView = memo2(function TimelineGroupView2({
3201
3289
  onReconnect,
3202
3290
  resolveProviderLogo,
3203
3291
  toolRegistry,
3292
+ turnSummary,
3204
3293
  insideTurn: true
3205
3294
  },
3206
3295
  timelineGroupKey(child)
@@ -3214,6 +3303,7 @@ var TimelineGroupView = memo2(function TimelineGroupView2({
3214
3303
  durationMs: durationBetween(group.startedAt, group.endedAt),
3215
3304
  defaultOpen: !insideTurn && group.outcome === "failed" ? true : void 0,
3216
3305
  bare: insideTurn,
3306
+ facets: turnSummary?.facets,
3217
3307
  children: insideTurn ? (
3218
3308
  // A nested turn is already on an ancestor rail — its body just stacks
3219
3309
  // flush (the bare-node body already indents it), so no second rule.
@@ -3347,7 +3437,7 @@ function AgentMessageRow({
3347
3437
  "aria-hidden": true
3348
3438
  }
3349
3439
  ) : null;
3350
- return /* @__PURE__ */ jsx13("div", { className: cn(enter && "animate-og-enter", "min-w-0 text-og-md leading-7 text-og-fg"), children: renderMessageText ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
3440
+ return /* @__PURE__ */ jsx13("div", { className: cn(enter && "animate-og-enter", "min-w-0 text-og-md leading-7 text-og-fg"), children: renderMessageText ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
3351
3441
  renderMessageText(item.text, item),
3352
3442
  caret
3353
3443
  ] }) : (
@@ -3756,6 +3846,7 @@ export {
3756
3846
  defaultToolRegistry,
3757
3847
  createDefaultToolRegistry,
3758
3848
  ActivityRail,
3849
+ BUILT_IN_TURN_SUMMARY_FACET_IDS,
3759
3850
  TurnSummary,
3760
3851
  Markdown,
3761
3852
  SESSION_STATUS_META,
@@ -3764,4 +3855,4 @@ export {
3764
3855
  MessageTimeline,
3765
3856
  TimelineRow
3766
3857
  };
3767
- //# sourceMappingURL=chunk-GQN2QIR2.js.map
3858
+ //# sourceMappingURL=chunk-WAA247AB.js.map