@agent-native/core 0.84.25 → 0.84.27

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 (67) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +4 -2
  5. package/corpus/core/src/agent/run-manager.ts +4 -3
  6. package/corpus/core/src/agent/types.ts +9 -2
  7. package/corpus/core/src/client/AssistantChat.tsx +15 -1
  8. package/corpus/core/src/client/FeedbackButton.tsx +2 -2
  9. package/corpus/core/src/client/components/PresenceBar.tsx +7 -2
  10. package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +23 -9
  11. package/corpus/core/src/client/sharing/ShareButton.tsx +22 -3
  12. package/corpus/core/src/client/sharing/ShareDialog.tsx +13 -1
  13. package/corpus/core/src/client/sse-event-processor.ts +49 -20
  14. package/corpus/core/src/sharing/actions/set-resource-visibility.ts +3 -3
  15. package/corpus/templates/design/actions/insert-asset.ts +14 -2
  16. package/corpus/templates/design/actions/insert-design-native-asset.ts +16 -4
  17. package/corpus/templates/design/actions/insert-figma-library-asset.ts +14 -2
  18. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -0
  19. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +54 -10
  20. package/corpus/templates/design/app/components/design/EditPanel.tsx +23 -16
  21. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +7 -2
  22. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +9 -5
  23. package/corpus/templates/design/app/components/layout/Header.tsx +44 -1
  24. package/corpus/templates/design/app/pages/DesignEditor.tsx +267 -105
  25. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +15 -1
  26. package/corpus/templates/design/app/pages/Index.tsx +20 -9
  27. package/corpus/templates/design/app/routes/settings.tsx +1 -1
  28. package/corpus/templates/design/changelog/2026-07-01-design-editor-polish-makes-zoom-assets-sharing-and-agent-handoffs-clearer.md +6 -0
  29. package/corpus/templates/design/changelog/2026-07-01-design-qa-fixes-make-exports-permissions-and-tools-clearer.md +6 -0
  30. package/dist/agent/production-agent.d.ts.map +1 -1
  31. package/dist/agent/production-agent.js +4 -3
  32. package/dist/agent/production-agent.js.map +1 -1
  33. package/dist/agent/run-manager.d.ts.map +1 -1
  34. package/dist/agent/run-manager.js +4 -4
  35. package/dist/agent/run-manager.js.map +1 -1
  36. package/dist/agent/types.d.ts +3 -0
  37. package/dist/agent/types.d.ts.map +1 -1
  38. package/dist/agent/types.js.map +1 -1
  39. package/dist/client/AssistantChat.d.ts.map +1 -1
  40. package/dist/client/AssistantChat.js +16 -1
  41. package/dist/client/AssistantChat.js.map +1 -1
  42. package/dist/client/FeedbackButton.js +2 -2
  43. package/dist/client/FeedbackButton.js.map +1 -1
  44. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  45. package/dist/client/components/PresenceBar.js +6 -2
  46. package/dist/client/components/PresenceBar.js.map +1 -1
  47. package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
  48. package/dist/client/composer/ComposerPlusMenu.js +15 -7
  49. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  50. package/dist/client/sharing/ShareButton.js +15 -2
  51. package/dist/client/sharing/ShareButton.js.map +1 -1
  52. package/dist/client/sharing/ShareDialog.js +16 -1
  53. package/dist/client/sharing/ShareDialog.js.map +1 -1
  54. package/dist/client/sse-event-processor.d.ts.map +1 -1
  55. package/dist/client/sse-event-processor.js +42 -16
  56. package/dist/client/sse-event-processor.js.map +1 -1
  57. package/dist/collab/awareness.d.ts +2 -2
  58. package/dist/collab/awareness.d.ts.map +1 -1
  59. package/dist/collab/routes.d.ts +1 -1
  60. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  61. package/dist/notifications/routes.d.ts +3 -3
  62. package/dist/observability/routes.d.ts +6 -6
  63. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  64. package/dist/server/transcribe-voice.d.ts +1 -1
  65. package/dist/sharing/actions/set-resource-visibility.js +3 -3
  66. package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
  67. package/package.json +1 -1
@@ -66,6 +66,14 @@ function escapeHtml(value: string): string {
66
66
  .replace(/"/g, """);
67
67
  }
68
68
 
69
+ function createInsertedNodeId(prefix: string): string {
70
+ const random =
71
+ typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
72
+ ? crypto.randomUUID().replace(/-/g, "").slice(0, 12)
73
+ : Math.random().toString(36).slice(2, 14);
74
+ return `inserted-${prefix}-${random}`;
75
+ }
76
+
69
77
  function insertBeforeClosingTag(
70
78
  html: string,
71
79
  closingTag: "main" | "body",
@@ -83,6 +91,7 @@ function optionalDataAttribute(name: string, value: string | undefined) {
83
91
  function appendFigmaAssetMarkup(
84
92
  html: string,
85
93
  args: z.infer<typeof schemaInput>,
94
+ nodeId: string,
86
95
  ): string {
87
96
  const label = args.name?.trim() || "Figma library asset";
88
97
  const description = args.description?.trim();
@@ -90,7 +99,7 @@ function appendFigmaAssetMarkup(
90
99
  ? `<a href="${escapeHtml(args.sourceUrl)}" target="_blank" rel="noreferrer" class="text-slate-500 underline decoration-slate-300 underline-offset-2 hover:text-slate-800">Open in Figma</a>`
91
100
  : "";
92
101
  const snippet = `
93
- <section class="mx-auto my-8 max-w-5xl px-4" data-agent-native-asset-source="figma" data-agent-native-figma-asset data-figma-file-key="${escapeHtml(args.fileKey)}"${optionalDataAttribute("data-figma-node-id", args.nodeId)}${optionalDataAttribute("data-figma-component-key", args.componentKey)} data-figma-asset-kind="${escapeHtml(args.kind)}">
102
+ <section class="mx-auto my-8 max-w-5xl px-4" data-agent-native-asset-source="figma" data-agent-native-figma-asset data-agent-native-node-id="${escapeHtml(nodeId)}" data-agent-native-layer-name="${escapeHtml(label)}" data-figma-file-key="${escapeHtml(args.fileKey)}"${optionalDataAttribute("data-figma-node-id", args.nodeId)}${optionalDataAttribute("data-figma-component-key", args.componentKey)} data-figma-asset-kind="${escapeHtml(args.kind)}">
94
103
  <figure class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
95
104
  <img src="${escapeHtml(args.renderUrl)}" alt="${escapeHtml(label)}" class="w-full rounded-t-2xl object-contain" />
96
105
  <figcaption class="flex flex-wrap items-center gap-x-3 gap-y-1 px-4 py-3 text-sm text-slate-600">
@@ -201,7 +210,8 @@ export default defineAction({
201
210
  // Collab read is best-effort; fall back to stored content.
202
211
  }
203
212
 
204
- const content = appendFigmaAssetMarkup(base, args);
213
+ const insertedNodeId = createInsertedNodeId("figma");
214
+ const content = appendFigmaAssetMarkup(base, args, insertedNodeId);
205
215
  const now = new Date().toISOString();
206
216
  await db
207
217
  .update(schema.designFiles)
@@ -223,6 +233,8 @@ export default defineAction({
223
233
  fileId: file.id,
224
234
  filename: file.filename,
225
235
  inserted: true,
236
+ insertedNodeId,
237
+ insertedSelector: `[data-agent-native-node-id="${insertedNodeId}"]`,
226
238
  source: "figma",
227
239
  fileKey: args.fileKey,
228
240
  nodeId: args.nodeId ?? null,
@@ -1775,6 +1775,11 @@ export function DesignCanvas({
1775
1775
  // explicit viewport width (e.g. 390 / 768 / 1280 side-by-side breakpoints).
1776
1776
  const resolvedWidth =
1777
1777
  previewWidthPx != null ? `${previewWidthPx}px` : iframeWidth;
1778
+ const focusScrollSurface = useCallback(() => {
1779
+ const surface = scrollContainerRef.current;
1780
+ if (!surface || document.activeElement === surface) return;
1781
+ surface.focus({ preventScroll: true });
1782
+ }, []);
1778
1783
 
1779
1784
  // Wrap the iframe in a positioned container so DrawOverlay /
1780
1785
  // CanvasCommentPins can absolutely-position themselves on top of the
@@ -1901,6 +1906,9 @@ export function DesignCanvas({
1901
1906
  return (
1902
1907
  <div
1903
1908
  ref={scrollContainerRef}
1909
+ tabIndex={-1}
1910
+ onPointerEnter={focusScrollSurface}
1911
+ onMouseEnter={focusScrollSurface}
1904
1912
  className="relative h-full w-full overflow-hidden"
1905
1913
  >
1906
1914
  {iframeElement}
@@ -1915,6 +1923,9 @@ export function DesignCanvas({
1915
1923
  return (
1916
1924
  <div
1917
1925
  ref={scrollContainerRef}
1926
+ tabIndex={-1}
1927
+ onPointerEnter={focusScrollSurface}
1928
+ onMouseEnter={focusScrollSurface}
1918
1929
  className="relative h-full w-full overflow-hidden"
1919
1930
  style={{
1920
1931
  width: embeddedFrame.displayWidth,
@@ -1945,6 +1956,9 @@ export function DesignCanvas({
1945
1956
  return (
1946
1957
  <div
1947
1958
  ref={scrollContainerRef}
1959
+ tabIndex={-1}
1960
+ onPointerEnter={focusScrollSurface}
1961
+ onMouseEnter={focusScrollSurface}
1948
1962
  className="relative flex-1 h-full overflow-auto"
1949
1963
  >
1950
1964
  {/* Canvas area. "none" mode fills the canvas (responsive preview);
@@ -108,6 +108,12 @@ export interface DesignExtensionSlotContext extends Record<string, unknown> {
108
108
  content: string,
109
109
  updatedAt?: string,
110
110
  ) => void;
111
+ onAssetInserted?: (selection: {
112
+ fileId?: string;
113
+ nodeId?: string;
114
+ selector?: string;
115
+ title?: string;
116
+ }) => void;
111
117
  }
112
118
 
113
119
  interface DesignExtensionsPanelProps {
@@ -301,6 +307,7 @@ interface FirstPartyRowProps {
301
307
 
302
308
  function FirstPartyExtRow({
303
309
  label,
310
+ description,
304
311
  icon,
305
312
  badge,
306
313
  isOpen,
@@ -321,14 +328,14 @@ function FirstPartyExtRow({
321
328
  <span className="block truncate text-sm font-medium leading-tight text-foreground">
322
329
  {label}
323
330
  </span>
324
- <span className="mt-0.5 block truncate text-xs leading-none text-muted-foreground">
325
- <span className="truncate">Built-in</span>
331
+ <span className="mt-0.5 line-clamp-1 text-xs leading-snug text-muted-foreground">
332
+ {description}
326
333
  </span>
327
334
  </span>
328
335
  {badge}
329
336
  </button>
330
337
  {isOpen && (
331
- <div className="mb-2 ml-[3.75rem] overflow-hidden rounded-md border border-border/70 bg-background/70">
338
+ <div className="mb-2 mt-1 overflow-hidden rounded-md border border-border/70 bg-background/70">
332
339
  {children}
333
340
  </div>
334
341
  )}
@@ -348,6 +355,12 @@ function ToolFilterMenu<T extends string>({
348
355
  onChange: (value: T) => void;
349
356
  }) {
350
357
  const selected = options.find((option) => option.value === value);
358
+ const triggerLabel =
359
+ value === "all"
360
+ ? label === "Category"
361
+ ? "All categories"
362
+ : `All ${label.toLowerCase()}s`
363
+ : (selected?.label ?? label);
351
364
  return (
352
365
  <DropdownMenu>
353
366
  <DropdownMenuTrigger asChild>
@@ -358,7 +371,7 @@ function ToolFilterMenu<T extends string>({
358
371
  className="h-8 cursor-pointer gap-1.5 rounded-md bg-transparent px-2.5 text-sm font-medium"
359
372
  >
360
373
  <IconAdjustmentsHorizontal className="size-4 text-muted-foreground" />
361
- <span>{selected?.label ?? label}</span>
374
+ <span>{triggerLabel}</span>
362
375
  <IconChevronDown className="size-3.5 text-muted-foreground" />
363
376
  </Button>
364
377
  </DropdownMenuTrigger>
@@ -463,6 +476,25 @@ export function AssetLibraryPanel({ context }: AssetLibraryPanelProps) {
463
476
  : undefined,
464
477
  { enabled: Boolean(figmaRequest) },
465
478
  );
479
+ const notifyAssetInserted = useCallback(
480
+ (result: unknown, title: string) => {
481
+ if (!result || typeof result !== "object") return;
482
+ const row = result as Record<string, unknown>;
483
+ context.onAssetInserted?.({
484
+ fileId: typeof row.fileId === "string" ? row.fileId : undefined,
485
+ nodeId:
486
+ typeof row.insertedNodeId === "string"
487
+ ? row.insertedNodeId
488
+ : undefined,
489
+ selector:
490
+ typeof row.insertedSelector === "string"
491
+ ? row.insertedSelector
492
+ : undefined,
493
+ title,
494
+ });
495
+ },
496
+ [context],
497
+ );
466
498
 
467
499
  const handleReady = useCallback(
468
500
  (_payload: unknown, _event: MessageEvent, ref: EmbeddedAppRef) => {
@@ -508,7 +540,8 @@ export function AssetLibraryPanel({ context }: AssetLibraryPanelProps) {
508
540
  fileId: context.activeFileId || undefined,
509
541
  },
510
542
  {
511
- onSuccess: () => {
543
+ onSuccess: (result) => {
544
+ notifyAssetInserted(result, title ?? altText ?? "Asset");
512
545
  toast.success("Asset inserted into design.");
513
546
  },
514
547
  onError: () => {
@@ -517,7 +550,7 @@ export function AssetLibraryPanel({ context }: AssetLibraryPanelProps) {
517
550
  },
518
551
  );
519
552
  },
520
- [context.designId, context.activeFileId, insertAsset],
553
+ [context.designId, context.activeFileId, insertAsset, notifyAssetInserted],
521
554
  );
522
555
 
523
556
  const handleInsertNativeAsset = (asset: DesignNativeAsset) => {
@@ -528,7 +561,8 @@ export function AssetLibraryPanel({ context }: AssetLibraryPanelProps) {
528
561
  fileId: context.activeFileId || undefined,
529
562
  },
530
563
  {
531
- onSuccess: () => {
564
+ onSuccess: (result) => {
565
+ notifyAssetInserted(result, asset.title);
532
566
  toast.success(`${asset.title} inserted into design.`);
533
567
  },
534
568
  onError: (error) => {
@@ -566,7 +600,8 @@ export function AssetLibraryPanel({ context }: AssetLibraryPanelProps) {
566
600
  fileId: context.activeFileId || undefined,
567
601
  },
568
602
  {
569
- onSuccess: () => {
603
+ onSuccess: (result) => {
604
+ notifyAssetInserted(result, asset.name);
570
605
  toast.success("Figma asset inserted into design.");
571
606
  },
572
607
  onError: (error) => {
@@ -1267,6 +1302,11 @@ export function DesignExtensionsPanel({
1267
1302
  visibleFirstPartyRows.length > 0 ||
1268
1303
  visibleInstalls.length > 0 ||
1269
1304
  visibleInstallable.length > 0;
1305
+ const showPluginsEmptyState =
1306
+ !hasAnyVisibleTool &&
1307
+ categoryFilter === "plugins" &&
1308
+ !normalizedSearch &&
1309
+ sourceFilter !== "built-in";
1270
1310
 
1271
1311
  return (
1272
1312
  <div className={cn("flex min-h-0 flex-1 flex-col", className)}>
@@ -1412,10 +1452,14 @@ export function DesignExtensionsPanel({
1412
1452
  <IconSearch className="size-4 text-muted-foreground" />
1413
1453
  </div>
1414
1454
  <p className="text-sm font-medium text-foreground">
1415
- No tools found
1455
+ {showPluginsEmptyState
1456
+ ? "No plugins installed"
1457
+ : "No tools found"}
1416
1458
  </p>
1417
1459
  <p className="mx-auto mt-1 max-w-52 text-xs leading-5 text-muted-foreground">
1418
- Try another search or clear the filters.
1460
+ {showPluginsEmptyState
1461
+ ? "Create a plugin or clear the Category filter to browse built-in tools."
1462
+ : "Try another search or clear the filters."}
1419
1463
  </p>
1420
1464
  </div>
1421
1465
  ) : null}
@@ -3189,22 +3189,29 @@ function TypographyDetailsPopover({
3189
3189
 
3190
3190
  return (
3191
3191
  <Popover open={open} onOpenChange={setOpen}>
3192
- <PopoverTrigger asChild>
3193
- <Button
3194
- type="button"
3195
- variant="ghost"
3196
- size="icon"
3197
- aria-label={"Typography details" /* i18n-ignore design action */}
3198
- aria-pressed={open}
3199
- className={cn(
3200
- "h-6 min-w-6 cursor-pointer rounded-md text-muted-foreground hover:bg-[var(--design-editor-panel-raised-bg)] hover:text-foreground",
3201
- open &&
3202
- "bg-[var(--design-editor-accent-color)]/20 text-[var(--design-editor-accent-color)] hover:text-[var(--design-editor-accent-color)]",
3203
- )}
3204
- >
3205
- <IconLayoutSettings className="size-3.5" />
3206
- </Button>
3207
- </PopoverTrigger>
3192
+ <Tooltip>
3193
+ <TooltipTrigger asChild>
3194
+ <PopoverTrigger asChild>
3195
+ <Button
3196
+ type="button"
3197
+ variant="ghost"
3198
+ size="icon"
3199
+ aria-label={"Typography details" /* i18n-ignore design action */}
3200
+ aria-pressed={open}
3201
+ className={cn(
3202
+ "h-6 min-w-6 cursor-pointer rounded-md text-muted-foreground hover:bg-[var(--design-editor-panel-raised-bg)] hover:text-foreground",
3203
+ open &&
3204
+ "bg-[var(--design-editor-accent-color)]/20 text-[var(--design-editor-accent-color)] hover:text-[var(--design-editor-accent-color)]",
3205
+ )}
3206
+ >
3207
+ <IconLayoutSettings className="size-3.5" />
3208
+ </Button>
3209
+ </PopoverTrigger>
3210
+ </TooltipTrigger>
3211
+ <TooltipContent>
3212
+ {"Typography details" /* i18n-ignore design action */}
3213
+ </TooltipContent>
3214
+ </Tooltip>
3208
3215
  <PopoverContent
3209
3216
  side="left"
3210
3217
  align="end"
@@ -4825,7 +4825,11 @@ export function MultiScreenCanvas({
4825
4825
  className="relative h-full w-full select-none overflow-hidden outline-none"
4826
4826
  onMouseDownCapture={handleMouseDown}
4827
4827
  onMouseMove={handleMouseMove}
4828
- style={{ cursor: surfaceCursor, touchAction: "none" }}
4828
+ style={{
4829
+ cursor: surfaceCursor,
4830
+ overscrollBehavior: "none",
4831
+ touchAction: "none",
4832
+ }}
4829
4833
  >
4830
4834
  {showPixelGrid ? (
4831
4835
  <div
@@ -5769,6 +5773,7 @@ const Screen = memo(function Screen({
5769
5773
  onMouseEnter={() => updateDirectHover(true)}
5770
5774
  onMouseLeave={() => updateDirectHover(false)}
5771
5775
  style={{
5776
+ width: labelInfoMaxWidth,
5772
5777
  maxWidth: labelInfoMaxWidth,
5773
5778
  transform: `translateY(-50%) scale(${chromeScale})`,
5774
5779
  transformOrigin: "left center",
@@ -5784,7 +5789,7 @@ const Screen = memo(function Screen({
5784
5789
  <span
5785
5790
  data-frame-title
5786
5791
  className={cn(
5787
- "min-w-0 truncate !text-[11px] font-medium",
5792
+ "min-w-0 flex-1 truncate !text-[11px] font-medium",
5788
5793
  emphasized
5789
5794
  ? "text-[var(--design-editor-accent-color)]"
5790
5795
  : activeOrEmphasized
@@ -63,7 +63,8 @@ export function InspectorAiActions({
63
63
  routeSourceFile,
64
64
  designId,
65
65
  };
66
- const disabled = !canEdit || !request.trim();
66
+ const copyDisabled = !request.trim();
67
+ const askDisabled = !canEdit || copyDisabled;
67
68
 
68
69
  return (
69
70
  <Collapsible open={open} onOpenChange={setOpen} className="w-full">
@@ -97,9 +98,12 @@ export function InspectorAiActions({
97
98
  : t("designEditor.localSourceEdit.describeChange")
98
99
  }
99
100
  className="min-h-[64px] resize-none text-xs"
100
- disabled={!canEdit}
101
101
  onKeyDown={(e) => {
102
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && !disabled) {
102
+ if (
103
+ e.key === "Enter" &&
104
+ (e.metaKey || e.ctrlKey) &&
105
+ !askDisabled
106
+ ) {
103
107
  e.preventDefault();
104
108
  void sendEdit(args);
105
109
  setRequest("");
@@ -112,7 +116,7 @@ export function InspectorAiActions({
112
116
  size="sm"
113
117
  variant="default"
114
118
  className="flex-1 gap-1.5 text-xs"
115
- disabled={disabled}
119
+ disabled={askDisabled}
116
120
  onClick={() => {
117
121
  void sendEdit(args);
118
122
  setRequest("");
@@ -126,7 +130,7 @@ export function InspectorAiActions({
126
130
  size="sm"
127
131
  variant="outline"
128
132
  className="gap-1.5 text-xs"
129
- disabled={disabled}
133
+ disabled={copyDisabled}
130
134
  onClick={() => {
131
135
  void copyPrompt(args);
132
136
  }}
@@ -1,6 +1,7 @@
1
1
  import { useActionQuery, useT } from "@agent-native/core/client";
2
2
  import { AgentToggleButton } from "@agent-native/core/client";
3
3
  import { RunsTray } from "@agent-native/core/client/progress";
4
+ import { useCallback } from "react";
4
5
  import { useLocation } from "react-router";
5
6
 
6
7
  import { useHeaderTitle, useHeaderActions } from "./HeaderActions";
@@ -12,6 +13,11 @@ const pageTitleKeys: Record<string, string> = {
12
13
  "/settings": "navigation.settings",
13
14
  };
14
15
 
16
+ type HeaderAgentRun = {
17
+ title?: string;
18
+ metadata?: Record<string, unknown> | null;
19
+ };
20
+
15
21
  function DesignTitle({ id }: { id: string }) {
16
22
  const { data } = useActionQuery<{ title?: string }>("get-design", { id });
17
23
  const title = data?.title ?? "Design";
@@ -42,6 +48,43 @@ function ResolvedTitle() {
42
48
  export function Header() {
43
49
  const title = useHeaderTitle();
44
50
  const actions = useHeaderActions();
51
+ const openRunThread = useCallback(
52
+ (threadId: string, run?: HeaderAgentRun) => {
53
+ window.dispatchEvent(
54
+ new CustomEvent("agent-panel:set-mode", {
55
+ detail: { mode: "chat" },
56
+ }),
57
+ );
58
+ window.dispatchEvent(new CustomEvent("agent-panel:open"));
59
+ const metadata = run?.metadata ?? {};
60
+ const parentThreadId =
61
+ typeof metadata.parentThreadId === "string"
62
+ ? metadata.parentThreadId.trim()
63
+ : "";
64
+ const isAgentTeam =
65
+ metadata.kind === "agent-team" || metadata.source === "agent-teams";
66
+ if (isAgentTeam && parentThreadId && parentThreadId !== threadId) {
67
+ window.dispatchEvent(
68
+ new CustomEvent("agent-task-open", {
69
+ detail: {
70
+ threadId,
71
+ parentThreadId,
72
+ description:
73
+ typeof metadata.description === "string"
74
+ ? metadata.description
75
+ : run?.title || "",
76
+ name: typeof metadata.name === "string" ? metadata.name : "",
77
+ },
78
+ }),
79
+ );
80
+ return;
81
+ }
82
+ window.dispatchEvent(
83
+ new CustomEvent("agent-chat:open-thread", { detail: { threadId } }),
84
+ );
85
+ },
86
+ [],
87
+ );
45
88
 
46
89
  return (
47
90
  <header className="flex h-12 items-center gap-3 border-b border-border bg-background px-4 lg:px-6 shrink-0">
@@ -50,7 +93,7 @@ export function Header() {
50
93
  </div>
51
94
  <div className="flex items-center gap-2 shrink-0">
52
95
  {actions}
53
- <RunsTray pollMs={1500} />
96
+ <RunsTray pollMs={1500} onOpenThread={openRunThread} />
54
97
  <AgentToggleButton />
55
98
  </div>
56
99
  </header>