@agent-native/core 0.78.0 → 0.78.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 (96) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +15 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +269 -237
  5. package/corpus/core/src/agent/types.ts +22 -0
  6. package/corpus/core/src/client/AgentPanel.tsx +8 -0
  7. package/corpus/core/src/client/AssistantChat.tsx +40 -0
  8. package/corpus/core/src/client/agent-chat.ts +173 -0
  9. package/corpus/core/src/client/composer/TiptapComposer.tsx +316 -60
  10. package/corpus/core/src/client/composer/extensions/MentionReference.tsx +3 -0
  11. package/corpus/core/src/client/composer/types.ts +22 -0
  12. package/corpus/core/src/client/index.ts +5 -0
  13. package/corpus/core/src/server/agent-chat-plugin.ts +10 -0
  14. package/corpus/core/src/server/request-context.ts +8 -0
  15. package/corpus/templates/analytics/server/plugins/agent-chat.ts +13 -0
  16. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +10 -2
  17. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +14 -7
  18. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +12 -0
  19. package/corpus/templates/assets/AGENTS.md +18 -3
  20. package/corpus/templates/assets/actions/_image-model-default.ts +24 -0
  21. package/corpus/templates/assets/actions/dismiss-variant-slots.ts +15 -22
  22. package/corpus/templates/assets/actions/generate-asset.ts +30 -23
  23. package/corpus/templates/assets/actions/generate-image-batch.ts +76 -29
  24. package/corpus/templates/assets/actions/generate-image.ts +46 -30
  25. package/corpus/templates/assets/actions/generate-video.ts +18 -3
  26. package/corpus/templates/assets/actions/list-assets.ts +39 -8
  27. package/corpus/templates/assets/actions/list-libraries.ts +24 -2
  28. package/corpus/templates/assets/actions/navigate.ts +1 -1
  29. package/corpus/templates/assets/actions/open-asset-picker.ts +38 -1
  30. package/corpus/templates/assets/actions/refresh-generation-run.ts +10 -0
  31. package/corpus/templates/assets/actions/save-generated-image.ts +21 -19
  32. package/corpus/templates/assets/actions/variant-slots.ts +131 -28
  33. package/corpus/templates/assets/actions/view-screen.ts +9 -0
  34. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +425 -0
  35. package/corpus/templates/assets/app/components/layout/Header.tsx +3 -1
  36. package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
  37. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -6
  38. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +47 -21
  39. package/corpus/templates/assets/app/i18n-data.ts +1815 -0
  40. package/corpus/templates/assets/app/lib/libraries.ts +6 -0
  41. package/corpus/templates/assets/app/lib/picker-chat-handoff.ts +71 -0
  42. package/corpus/templates/assets/app/routes/_index.tsx +4 -0
  43. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
  44. package/corpus/templates/assets/app/routes/audit.tsx +1 -1
  45. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1557 -2164
  46. package/corpus/templates/assets/app/routes/brand-kits._index.tsx +7 -191
  47. package/corpus/templates/assets/app/routes/brand-kits.tsx +1 -5
  48. package/corpus/templates/assets/app/routes/libraries.tsx +4 -4
  49. package/corpus/templates/assets/app/routes/library.$id.tsx +9 -7
  50. package/corpus/templates/assets/app/routes/library.tsx +1454 -175
  51. package/corpus/templates/assets/server/db/index.ts +1 -1
  52. package/corpus/templates/assets/server/lib/generation.ts +86 -15
  53. package/corpus/templates/assets/server/plugins/agent-chat.ts +196 -0
  54. package/corpus/templates/assets/server/plugins/db.ts +5 -0
  55. package/corpus/templates/assets/shared/api.ts +6 -3
  56. package/dist/agent/production-agent.d.ts.map +1 -1
  57. package/dist/agent/production-agent.js +50 -29
  58. package/dist/agent/production-agent.js.map +1 -1
  59. package/dist/agent/types.d.ts +21 -0
  60. package/dist/agent/types.d.ts.map +1 -1
  61. package/dist/agent/types.js.map +1 -1
  62. package/dist/client/AgentPanel.d.ts +5 -1
  63. package/dist/client/AgentPanel.d.ts.map +1 -1
  64. package/dist/client/AgentPanel.js +2 -2
  65. package/dist/client/AgentPanel.js.map +1 -1
  66. package/dist/client/AssistantChat.d.ts +6 -0
  67. package/dist/client/AssistantChat.d.ts.map +1 -1
  68. package/dist/client/AssistantChat.js +15 -3
  69. package/dist/client/AssistantChat.js.map +1 -1
  70. package/dist/client/agent-chat.d.ts +32 -0
  71. package/dist/client/agent-chat.d.ts.map +1 -1
  72. package/dist/client/agent-chat.js +113 -0
  73. package/dist/client/agent-chat.js.map +1 -1
  74. package/dist/client/composer/TiptapComposer.d.ts +2 -1
  75. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  76. package/dist/client/composer/TiptapComposer.js +228 -56
  77. package/dist/client/composer/TiptapComposer.js.map +1 -1
  78. package/dist/client/composer/extensions/MentionReference.d.ts.map +1 -1
  79. package/dist/client/composer/extensions/MentionReference.js +3 -0
  80. package/dist/client/composer/extensions/MentionReference.js.map +1 -1
  81. package/dist/client/composer/types.d.ts +21 -0
  82. package/dist/client/composer/types.d.ts.map +1 -1
  83. package/dist/client/composer/types.js.map +1 -1
  84. package/dist/client/index.d.ts +1 -1
  85. package/dist/client/index.d.ts.map +1 -1
  86. package/dist/client/index.js +1 -1
  87. package/dist/client/index.js.map +1 -1
  88. package/dist/notifications/routes.d.ts +1 -1
  89. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  90. package/dist/server/agent-chat-plugin.js +5 -0
  91. package/dist/server/agent-chat-plugin.js.map +1 -1
  92. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  93. package/dist/server/request-context.d.ts +8 -0
  94. package/dist/server/request-context.d.ts.map +1 -1
  95. package/dist/server/request-context.js.map +1 -1
  96. package/package.json +1 -1
@@ -6,10 +6,12 @@ import { useEditor, EditorContent } from "@tiptap/react";
6
6
  import StarterKit from "@tiptap/starter-kit";
7
7
  import { useState, useRef, useEffect, useCallback, useImperativeHandle, useMemo, } from "react";
8
8
  import { getReasoningEffortOptionsForModel, reasoningEffortLabel, } from "../../shared/reasoning-effort.js";
9
- import { sendToAgentChat } from "../agent-chat.js";
9
+ import { normalizeAgentComposerReference, sendToAgentChat, AGENT_CHAT_INSERT_REFERENCE_EVENT, AGENT_CHAT_INSERT_REFERENCE_MESSAGE_TYPE, } from "../agent-chat.js";
10
10
  import { tryDelegateBuildRequestToBuilder } from "../builder-frame.js";
11
+ import { isTrustedBuilderMessage } from "../builder-frame.js";
11
12
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
12
13
  import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
14
+ import { isTrustedFrameMessage } from "../frame.js";
13
15
  import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
14
16
  import { ComposerPlusMenu } from "./ComposerPlusMenu.js";
15
17
  import { getComposerDraftKey } from "./draft-key.js";
@@ -28,6 +30,93 @@ export function canSubmitComposerContent(options) {
28
30
  (options.hasEditorContent || options.attachmentCount > 0));
29
31
  }
30
32
  const MAX_DOCUMENT_ATTACHMENT_BYTES = 4 * 1024 * 1024;
33
+ function composerReferenceFromMentionItem(item) {
34
+ return {
35
+ label: item.label,
36
+ icon: item.icon || "file",
37
+ source: item.source,
38
+ refType: item.refType,
39
+ refId: item.refId || null,
40
+ refPath: item.refPath || null,
41
+ slotKey: item.slotKey,
42
+ slotLabel: item.slotLabel,
43
+ metadata: item.metadata,
44
+ clearsSlots: item.clearsSlots,
45
+ relatedReferences: item.relatedReferences,
46
+ };
47
+ }
48
+ function mentionReferenceAttrs(ref) {
49
+ return {
50
+ label: ref.label,
51
+ icon: ref.icon || "file",
52
+ source: ref.source,
53
+ refType: ref.refType,
54
+ refId: ref.refId || null,
55
+ refPath: ref.refPath || null,
56
+ slotKey: ref.slotKey || null,
57
+ slotLabel: ref.slotLabel || null,
58
+ metadata: ref.metadata || null,
59
+ };
60
+ }
61
+ function referenceFromComposerReference(ref) {
62
+ return {
63
+ type: ref.refType === "file"
64
+ ? "file"
65
+ : ref.refType === "agent"
66
+ ? "agent"
67
+ : ref.refType === "custom-agent"
68
+ ? "custom-agent"
69
+ : "mention",
70
+ path: ref.refPath || "",
71
+ name: ref.label,
72
+ source: ref.source || "",
73
+ refType: ref.refType,
74
+ refId: ref.refId || undefined,
75
+ slotKey: ref.slotKey,
76
+ slotLabel: ref.slotLabel,
77
+ metadata: ref.metadata,
78
+ };
79
+ }
80
+ function applySlotReferenceChanges(current, references) {
81
+ let next = current;
82
+ const applyOne = (ref) => {
83
+ for (const related of ref.relatedReferences ?? []) {
84
+ applyOne(related);
85
+ }
86
+ if (!ref.slotKey)
87
+ return;
88
+ const cleared = new Set([ref.slotKey, ...(ref.clearsSlots ?? [])]);
89
+ next = next.filter((existing) => !cleared.has(existing.slotKey ?? ""));
90
+ next = [...next, ref];
91
+ };
92
+ for (const ref of references) {
93
+ applyOne(ref);
94
+ }
95
+ return next;
96
+ }
97
+ function removeSlotReference(current, ref) {
98
+ const removed = new Set([ref.slotKey, ...(ref.clearsSlots ?? [])]);
99
+ return current.filter((existing) => !removed.has(existing.slotKey ?? ""));
100
+ }
101
+ function slotReferenceTitle(ref) {
102
+ return ref.slotLabel ? `${ref.slotLabel}: ${ref.label}` : ref.label;
103
+ }
104
+ function metadataString(metadata, key) {
105
+ const value = metadata?.[key];
106
+ return typeof value === "string" && value.trim() ? value.trim() : null;
107
+ }
108
+ function filterMentionItemsForSlots(items, slotReferences) {
109
+ return items.filter((item) => {
110
+ const requiredSlotKey = metadataString(item.metadata, "requiredSlotKey");
111
+ const requiredRefId = metadataString(item.metadata, "requiredRefId");
112
+ if (!requiredSlotKey || !requiredRefId)
113
+ return true;
114
+ const selected = slotReferences.find((ref) => ref.slotKey === requiredSlotKey);
115
+ if (!selected?.refId)
116
+ return true;
117
+ return selected.refId === requiredRefId;
118
+ });
119
+ }
31
120
  function isDocumentAttachment(value) {
32
121
  if (value.type === "document")
33
122
  return true;
@@ -84,6 +173,21 @@ function uniquifyComposerImageFile(file) {
84
173
  const uniqueName = `${Date.now()}-${Math.random().toString(36).slice(2)}-${file.name}`;
85
174
  return new File([file], uniqueName, { type: file.type });
86
175
  }
176
+ function composerDocumentHasContent(doc) {
177
+ if (doc.textContent.trim().length > 0)
178
+ return true;
179
+ let hasContent = false;
180
+ doc.descendants((node) => {
181
+ if (node.type.name === "mentionReference" ||
182
+ node.type.name === "fileReference" ||
183
+ node.type.name === "skillReference") {
184
+ hasContent = true;
185
+ return false;
186
+ }
187
+ return true;
188
+ });
189
+ return hasContent;
190
+ }
87
191
  export function handleComposerFileDrop(options) {
88
192
  const droppedFiles = Array.from(options.event.dataTransfer?.files ?? []);
89
193
  if (droppedFiles.length === 0)
@@ -479,14 +583,15 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
479
583
  const popoverRef = useRef(null);
480
584
  const composerRuntime = useComposerRuntime();
481
585
  const [editorHasText, setEditorHasText] = useState(false);
586
+ const [slotReferences, setSlotReferences] = useState([]);
482
587
  const composerText = useComposer((state) => state.text);
483
588
  const composerAttachments = useComposer((state) => state.attachments);
484
589
  const canSend = canSubmitComposerContent({
485
- hasEditorContent: editorHasText,
590
+ hasEditorContent: editorHasText || slotReferences.length > 0,
486
591
  attachmentCount: composerAttachments.length,
487
592
  disabled,
488
593
  });
489
- const hasContextItems = contextItems.length > 0;
594
+ const hasContextRows = contextItems.length > 0 || slotReferences.length > 0;
490
595
  const [composerMode, setComposerMode] = useState(null);
491
596
  const composerModeRef = useRef(null);
492
597
  const isMac = typeof navigator !== "undefined" &&
@@ -502,6 +607,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
502
607
  const planModeDisabledRef = useRef(planModeDisabled);
503
608
  planModeDisabledRef.current = planModeDisabled;
504
609
  const { items: mentionItems, isLoading: mentionsLoading } = useMentionSearch(popover?.type === "@" ? popover.query : "", popover?.type === "@");
610
+ const filteredMentionItems = useMemo(() => filterMentionItemsForSlots(mentionItems, slotReferences), [mentionItems, slotReferences]);
505
611
  const { skills, hint, isLoading: skillsLoading, } = useSkills(includeDefaultSlashSkills && popover?.type === "/");
506
612
  const allSlashCommands = useMemo(() => mergeSlashCommands([
507
613
  ...(includeDefaultSlashCommands ? BUILT_IN_COMMANDS : []),
@@ -530,8 +636,8 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
530
636
  s.description?.toLowerCase().includes(q));
531
637
  }, [allSlashSkills, popover]);
532
638
  // Keep refs in sync with state
533
- const mentionItemsRef = useRef(mentionItems);
534
- mentionItemsRef.current = mentionItems;
639
+ const mentionItemsRef = useRef(filteredMentionItems);
640
+ mentionItemsRef.current = filteredMentionItems;
535
641
  const filteredCommandsRef = useRef(filteredCommands);
536
642
  filteredCommandsRef.current = filteredCommands;
537
643
  const filteredSkillsRef = useRef(filteredSkills);
@@ -541,6 +647,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
541
647
  const onTextChangeRef = useRef(onTextChange);
542
648
  onTextChangeRef.current = onTextChange;
543
649
  const initialTextKeyRef = useRef(undefined);
650
+ const seenReferenceInsertIdsRef = useRef(new Set());
544
651
  const closePopover = useCallback(() => {
545
652
  setPopover(null);
546
653
  popoverStateRef.current = null;
@@ -570,7 +677,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
570
677
  if (initialText !== undefined) {
571
678
  ed.commands.setContent(plainTextToDoc(initialText));
572
679
  ed.commands.focus("end");
573
- setEditorHasText(ed.state.doc.textContent.trim().length > 0);
680
+ setEditorHasText(composerDocumentHasContent(ed.state.doc));
574
681
  initialTextKeyRef.current = initialTextKey ?? initialText;
575
682
  }
576
683
  else {
@@ -578,7 +685,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
578
685
  if (saved) {
579
686
  ed.commands.setContent(saved);
580
687
  ed.commands.focus("end");
581
- setEditorHasText(ed.state.doc.textContent.trim().length > 0);
688
+ setEditorHasText(composerDocumentHasContent(ed.state.doc));
582
689
  }
583
690
  }
584
691
  onTextChangeRef.current?.(ed.state.doc.textContent.trim());
@@ -588,26 +695,14 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
588
695
  onUpdate: ({ editor: ed }) => {
589
696
  // Drive the send button's enabled state from the actual editor contents;
590
697
  // the composer runtime is only synced on submit, so its isEmpty lags.
591
- let hasContent = ed.state.doc.textContent.trim().length > 0;
592
- if (!hasContent) {
593
- ed.state.doc.descendants((node) => {
594
- if (node.type.name === "mentionReference" ||
595
- node.type.name === "fileReference" ||
596
- node.type.name === "skillReference") {
597
- hasContent = true;
598
- return false;
599
- }
600
- return true;
601
- });
602
- }
603
- setEditorHasText(hasContent);
698
+ setEditorHasText(composerDocumentHasContent(ed.state.doc));
604
699
  onTextChangeRef.current?.(ed.state.doc.textContent.trim());
605
700
  // Debounce-save draft to localStorage
606
701
  clearTimeout(draftTimerRef.current);
607
702
  draftTimerRef.current = setTimeout(() => {
608
703
  try {
609
704
  const html = ed.getHTML();
610
- const isEmpty = !ed.state.doc.textContent.trim();
705
+ const isEmpty = !composerDocumentHasContent(ed.state.doc);
611
706
  if (isEmpty) {
612
707
  localStorage.removeItem(draftKey);
613
708
  }
@@ -819,6 +914,92 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
819
914
  },
820
915
  },
821
916
  });
917
+ const insertReference = useCallback((ref) => {
918
+ const normalized = normalizeAgentComposerReference(ref);
919
+ const ed = editor;
920
+ if (!normalized || !ed)
921
+ return;
922
+ if (normalized.slotKey) {
923
+ setSlotReferences((current) => applySlotReferenceChanges(current, [normalized]));
924
+ ed.commands.focus("end");
925
+ return;
926
+ }
927
+ if (normalized.relatedReferences?.some((item) => item.slotKey)) {
928
+ setSlotReferences((current) => applySlotReferenceChanges(current, normalized.relatedReferences ?? []));
929
+ }
930
+ ed.chain()
931
+ .focus()
932
+ .insertContent({
933
+ type: "mentionReference",
934
+ attrs: mentionReferenceAttrs(normalized),
935
+ })
936
+ .insertContent(" ")
937
+ .run();
938
+ setEditorHasText(true);
939
+ }, [editor]);
940
+ const insertReferenceIfEmpty = useCallback((payload) => {
941
+ const insertMessageId = typeof payload.insertMessageId === "string"
942
+ ? payload.insertMessageId
943
+ : "";
944
+ if (insertMessageId) {
945
+ if (seenReferenceInsertIdsRef.current.has(insertMessageId))
946
+ return;
947
+ seenReferenceInsertIdsRef.current.add(insertMessageId);
948
+ }
949
+ const ed = editor;
950
+ if (!ed || disabled || composerModeRef.current)
951
+ return;
952
+ const normalized = normalizeAgentComposerReference(payload);
953
+ if (normalized?.slotKey) {
954
+ insertReference(normalized);
955
+ return;
956
+ }
957
+ if (composerAttachments.length > 0)
958
+ return;
959
+ if (composerDocumentHasContent(ed.state.doc))
960
+ return;
961
+ insertReference(payload);
962
+ }, [composerAttachments.length, disabled, editor, insertReference]);
963
+ useEffect(() => {
964
+ if (typeof window === "undefined")
965
+ return;
966
+ const handleEvent = (event) => {
967
+ const payload = event.detail;
968
+ const normalized = normalizeAgentComposerReference(payload);
969
+ if (!normalized)
970
+ return;
971
+ insertReferenceIfEmpty({
972
+ ...normalized,
973
+ insertMessageId: typeof payload?.insertMessageId === "string"
974
+ ? payload.insertMessageId
975
+ : "",
976
+ });
977
+ };
978
+ const handleMessage = (event) => {
979
+ if (!isTrustedFrameMessage(event) && !isTrustedBuilderMessage(event)) {
980
+ return;
981
+ }
982
+ if (event.data?.type !== AGENT_CHAT_INSERT_REFERENCE_MESSAGE_TYPE) {
983
+ return;
984
+ }
985
+ const payload = event.data.data;
986
+ const normalized = normalizeAgentComposerReference(payload);
987
+ if (!normalized)
988
+ return;
989
+ insertReferenceIfEmpty({
990
+ ...normalized,
991
+ insertMessageId: typeof payload?.insertMessageId === "string"
992
+ ? payload.insertMessageId
993
+ : "",
994
+ });
995
+ };
996
+ window.addEventListener(AGENT_CHAT_INSERT_REFERENCE_EVENT, handleEvent);
997
+ window.addEventListener("message", handleMessage);
998
+ return () => {
999
+ window.removeEventListener(AGENT_CHAT_INSERT_REFERENCE_EVENT, handleEvent);
1000
+ window.removeEventListener("message", handleMessage);
1001
+ };
1002
+ }, [insertReferenceIfEmpty]);
822
1003
  useImperativeHandle(focusRef, () => ({
823
1004
  focus() {
824
1005
  editor?.commands.focus("end");
@@ -830,6 +1011,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
830
1011
  editor.commands.focus("end");
831
1012
  const trimmed = editor.state.doc.textContent.trim();
832
1013
  setEditorHasText(trimmed.length > 0);
1014
+ setSlotReferences([]);
833
1015
  composerRuntime.setText(trimmed);
834
1016
  onTextChangeRef.current?.(trimmed);
835
1017
  try {
@@ -842,6 +1024,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
842
1024
  }
843
1025
  catch { }
844
1026
  },
1027
+ insertReference,
845
1028
  }));
846
1029
  const handleSelectMode = useCallback((mode) => {
847
1030
  setComposerMode(mode);
@@ -961,9 +1144,12 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
961
1144
  const extractComposerPayload = useCallback(() => {
962
1145
  const ed = editor;
963
1146
  if (!ed) {
964
- return { text: "", references: [] };
1147
+ return {
1148
+ text: slotReferences.map((ref) => slotReferenceTitle(ref)).join(", "),
1149
+ references: slotReferences.map(referenceFromComposerReference),
1150
+ };
965
1151
  }
966
- const references = [];
1152
+ const references = slotReferences.map(referenceFromComposerReference);
967
1153
  // Build text that preserves @mentions (getText() strips them).
968
1154
  // Walk the document and reconstruct with @name for mention/file/skill nodes.
969
1155
  const textParts = [];
@@ -990,7 +1176,9 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
990
1176
  textParts.push("\n");
991
1177
  }
992
1178
  });
993
- const text = textParts.join("").trim();
1179
+ const rawText = textParts.join("").trim();
1180
+ const text = rawText ||
1181
+ slotReferences.map((ref) => slotReferenceTitle(ref)).join(", ");
994
1182
  ed.state.doc.descendants((node) => {
995
1183
  if (node.type.name === "fileReference") {
996
1184
  // Legacy support
@@ -1016,6 +1204,9 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1016
1204
  source: node.attrs.source,
1017
1205
  refType: node.attrs.refType,
1018
1206
  refId: node.attrs.refId,
1207
+ slotKey: node.attrs.slotKey,
1208
+ slotLabel: node.attrs.slotLabel,
1209
+ metadata: node.attrs.metadata,
1019
1210
  });
1020
1211
  }
1021
1212
  else if (node.type.name === "skillReference") {
@@ -1028,7 +1219,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1028
1219
  }
1029
1220
  });
1030
1221
  return { text, references };
1031
- }, [editor]);
1222
+ }, [editor, slotReferences]);
1032
1223
  const syncComposerState = useCallback(() => {
1033
1224
  const { text, references } = extractComposerPayload();
1034
1225
  const requestMode = execMode === "plan" ? "plan" : execMode === "build" ? "act" : undefined;
@@ -1109,6 +1300,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1109
1300
  cancelActiveVoice();
1110
1301
  ed.commands.clearContent();
1111
1302
  setEditorHasText(false);
1303
+ setSlotReferences([]);
1112
1304
  setComposerMode(null);
1113
1305
  composerModeRef.current = null;
1114
1306
  try {
@@ -1129,6 +1321,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1129
1321
  cancelActiveVoice();
1130
1322
  ed.commands.clearContent();
1131
1323
  setEditorHasText(false);
1324
+ setSlotReferences([]);
1132
1325
  try {
1133
1326
  localStorage.removeItem(draftKey);
1134
1327
  }
@@ -1151,6 +1344,7 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1151
1344
  cancelActiveVoice();
1152
1345
  ed.commands.clearContent();
1153
1346
  setEditorHasText(false);
1347
+ setSlotReferences([]);
1154
1348
  try {
1155
1349
  localStorage.removeItem(draftKey);
1156
1350
  }
@@ -1177,22 +1371,8 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1177
1371
  const currentPos = ed.state.selection.from;
1178
1372
  // startPos is after the trigger char, so -1 to include the @ or /
1179
1373
  const deleteFrom = Math.max(0, pop.startPos - 1);
1180
- ed.chain()
1181
- .focus()
1182
- .deleteRange({ from: deleteFrom, to: currentPos })
1183
- .insertContent({
1184
- type: "mentionReference",
1185
- attrs: {
1186
- label: item.label,
1187
- icon: item.icon || "file",
1188
- source: item.source,
1189
- refType: item.refType,
1190
- refId: item.refId || null,
1191
- refPath: item.refPath || null,
1192
- },
1193
- })
1194
- .insertContent(" ")
1195
- .run();
1374
+ ed.chain().focus().deleteRange({ from: deleteFrom, to: currentPos }).run();
1375
+ insertReference(composerReferenceFromMentionItem(item));
1196
1376
  popoverStateRef.current = null;
1197
1377
  setPopover(null);
1198
1378
  }
@@ -1235,21 +1415,10 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1235
1415
  .chain()
1236
1416
  .focus()
1237
1417
  .deleteRange({ from: deleteFrom, to: currentPos })
1238
- .insertContent({
1239
- type: "mentionReference",
1240
- attrs: {
1241
- label: item.label,
1242
- icon: item.icon || "file",
1243
- source: item.source,
1244
- refType: item.refType,
1245
- refId: item.refId || null,
1246
- refPath: item.refPath || null,
1247
- },
1248
- })
1249
- .insertContent(" ")
1250
1418
  .run();
1419
+ insertReference(composerReferenceFromMentionItem(item));
1251
1420
  closePopover();
1252
- }, [editor, popover, closePopover]);
1421
+ }, [editor, popover, closePopover, insertReference]);
1253
1422
  const handleSelectCommand = useCallback((command) => {
1254
1423
  if (!editor || !popover)
1255
1424
  return;
@@ -1369,7 +1538,10 @@ export function TiptapComposer({ placeholder = "Message agent...", disabled = fa
1369
1538
  setComposerMode(null);
1370
1539
  composerModeRef.current = null;
1371
1540
  editor?.commands.focus("end");
1372
- } }) })), hasContextItems && (_jsx("div", { "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "context-row", className: "agent-composer-context-row flex flex-wrap gap-1.5 px-2.5 pt-2 pb-0", children: contextItems.map((item) => (_jsxs("span", { className: "inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-muted/50 px-2 py-1 text-[11px] font-medium text-foreground", children: [_jsx(IconClipboardList, { className: "h-3 w-3 shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 truncate", children: item.title }), _jsx("button", { type: "button", onClick: () => onRemoveContextItem?.(item.key), "aria-label": `Remove ${item.title} context`, className: "ms-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconX, { className: "h-3 w-3" }) })] }, item.key))) })), _jsx("div", { "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "editor-wrap", className: `agent-composer-editor-wrap ${composerMode || hasContextItems ? "px-2 pt-1 pb-1" : "px-2 pt-2 pb-1"}`, children: _jsx(EditorContent, { editor: editor, "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "editor", className: "agent-composer-editor aui-composer flex-1 min-w-0 [&_.ProseMirror]:outline-none [&_.ProseMirror_p]:m-0 px-0.5" }) }), voiceEnabled && _jsx(VoiceRecordingOverlay, { voice: voice }), _jsxs("div", { "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "toolbar", className: "agent-composer-toolbar flex items-center gap-1 px-2 py-1.5", children: [attachButton ??
1373
- (plusMenuMode === "hidden" ? null : (_jsx(ComposerPlusMenu, { onSelectMode: handleSelectMode, mode: plusMenuMode, onAttachmentError: onAttachmentError }))), toolbarSlot ?? modeControl, _jsx("div", { "data-agent-composer-slot": "toolbar-spacer", className: "flex-1" }), selectedModel && availableModels && onModelChange && (_jsx(ModelSelector, { model: selectedModel, effort: selectedEffort, engines: availableModels, onChange: onModelChange, onEffortChange: onEffortChange, providerConnectStatusEnabled: providerConnectStatusEnabled, onConnectProvider: onConnectProvider, imageModel: imageModelMenu })), execMode && onExecModeChange && (_jsx(ModeSelector, { mode: execMode, onChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason })), actionButton ?? (_jsxs(_Fragment, { children: [extraActionButton, voiceEnabled && (_jsx(VoiceButton, { voice: voice, isMac: isMac, disabled: disabled })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => submitComposer("immediate"), disabled: !canSend, "data-agent-composer-slot": "send-button", className: "agent-composer-send-button shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-primary text-primary-foreground hover:opacity-90 disabled:opacity-30 disabled:cursor-not-allowed", children: _jsx(IconArrowUp, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Send message" })] })] }))] }), _jsx(MentionPopover, { ref: popoverRef, type: popover?.type ?? "@", position: popover?.position ?? null, mentionItems: mentionItems, skills: filteredSkills, commands: filteredCommands, hint: hint, isLoading: popover?.type === "@" ? mentionsLoading : skillsLoading, query: popover?.query ?? "", onSelectMention: handleSelectMention, onSelectSkill: handleSelectSkill, onSelectCommand: handleSelectCommand, onClose: closePopover })] }));
1541
+ } }) })), hasContextRows && (_jsxs("div", { "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "context-row", className: "agent-composer-context-row flex flex-wrap gap-1.5 px-2.5 pt-2 pb-0", children: [slotReferences.map((ref) => (_jsxs("span", { className: "inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1 text-[11px] font-medium text-foreground shadow-sm", children: [_jsx(IconClipboardList, { className: "h-3 w-3 shrink-0 text-muted-foreground" }), ref.slotLabel && (_jsx("span", { className: "shrink-0 text-muted-foreground", children: ref.slotLabel })), _jsx("span", { className: "min-w-0 truncate", children: ref.label }), _jsx("button", { type: "button", onClick: () => {
1542
+ setSlotReferences((current) => removeSlotReference(current, ref));
1543
+ editor?.commands.focus("end");
1544
+ }, "aria-label": `Remove ${slotReferenceTitle(ref)} reference`, className: "ms-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconX, { className: "h-3 w-3" }) })] }, ref.slotKey))), contextItems.map((item) => (_jsxs("span", { className: "inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-muted/50 px-2 py-1 text-[11px] font-medium text-foreground", children: [_jsx(IconClipboardList, { className: "h-3 w-3 shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 truncate", children: item.title }), _jsx("button", { type: "button", onClick: () => onRemoveContextItem?.(item.key), "aria-label": `Remove ${item.title} context`, className: "ms-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconX, { className: "h-3 w-3" }) })] }, item.key)))] })), _jsx("div", { "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "editor-wrap", className: `agent-composer-editor-wrap ${composerMode || hasContextRows ? "px-2 pt-1 pb-1" : "px-2 pt-2 pb-1"}`, children: _jsx(EditorContent, { editor: editor, "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "editor", className: "agent-composer-editor aui-composer flex-1 min-w-0 [&_.ProseMirror]:outline-none [&_.ProseMirror_p]:m-0 px-0.5" }) }), voiceEnabled && _jsx(VoiceRecordingOverlay, { voice: voice }), _jsxs("div", { "data-agent-composer-variant": layoutVariant, "data-agent-composer-slot": "toolbar", className: "agent-composer-toolbar flex items-center gap-1 px-2 py-1.5", children: [attachButton ??
1545
+ (plusMenuMode === "hidden" ? null : (_jsx(ComposerPlusMenu, { onSelectMode: handleSelectMode, mode: plusMenuMode, onAttachmentError: onAttachmentError }))), toolbarSlot ?? modeControl, _jsx("div", { "data-agent-composer-slot": "toolbar-spacer", className: "flex-1" }), selectedModel && availableModels && onModelChange && (_jsx(ModelSelector, { model: selectedModel, effort: selectedEffort, engines: availableModels, onChange: onModelChange, onEffortChange: onEffortChange, providerConnectStatusEnabled: providerConnectStatusEnabled, onConnectProvider: onConnectProvider, imageModel: imageModelMenu })), execMode && onExecModeChange && (_jsx(ModeSelector, { mode: execMode, onChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason })), actionButton ?? (_jsxs(_Fragment, { children: [extraActionButton, voiceEnabled && (_jsx(VoiceButton, { voice: voice, isMac: isMac, disabled: disabled })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => submitComposer("immediate"), disabled: !canSend, "data-agent-composer-slot": "send-button", className: "agent-composer-send-button shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-primary text-primary-foreground hover:opacity-90 disabled:opacity-30 disabled:cursor-not-allowed", children: _jsx(IconArrowUp, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Send message" })] })] }))] }), _jsx(MentionPopover, { ref: popoverRef, type: popover?.type ?? "@", position: popover?.position ?? null, mentionItems: filteredMentionItems, skills: filteredSkills, commands: filteredCommands, hint: hint, isLoading: popover?.type === "@" ? mentionsLoading : skillsLoading, query: popover?.query ?? "", onSelectMention: handleSelectMention, onSelectSkill: handleSelectSkill, onSelectCommand: handleSelectCommand, onClose: closePopover })] }));
1374
1546
  }
1375
1547
  //# sourceMappingURL=TiptapComposer.js.map