@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +15 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +269 -237
- package/corpus/core/src/agent/types.ts +22 -0
- package/corpus/core/src/client/AgentPanel.tsx +8 -0
- package/corpus/core/src/client/AssistantChat.tsx +40 -0
- package/corpus/core/src/client/agent-chat.ts +173 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +316 -60
- package/corpus/core/src/client/composer/extensions/MentionReference.tsx +3 -0
- package/corpus/core/src/client/composer/types.ts +22 -0
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +10 -0
- package/corpus/core/src/server/request-context.ts +8 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +13 -0
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +10 -2
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +14 -7
- package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +12 -0
- package/corpus/templates/assets/AGENTS.md +18 -3
- package/corpus/templates/assets/actions/_image-model-default.ts +24 -0
- package/corpus/templates/assets/actions/dismiss-variant-slots.ts +15 -22
- package/corpus/templates/assets/actions/generate-asset.ts +30 -23
- package/corpus/templates/assets/actions/generate-image-batch.ts +76 -29
- package/corpus/templates/assets/actions/generate-image.ts +46 -30
- package/corpus/templates/assets/actions/generate-video.ts +18 -3
- package/corpus/templates/assets/actions/list-assets.ts +39 -8
- package/corpus/templates/assets/actions/list-libraries.ts +24 -2
- package/corpus/templates/assets/actions/navigate.ts +1 -1
- package/corpus/templates/assets/actions/open-asset-picker.ts +38 -1
- package/corpus/templates/assets/actions/refresh-generation-run.ts +10 -0
- package/corpus/templates/assets/actions/save-generated-image.ts +21 -19
- package/corpus/templates/assets/actions/variant-slots.ts +131 -28
- package/corpus/templates/assets/actions/view-screen.ts +9 -0
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +425 -0
- package/corpus/templates/assets/app/components/layout/Header.tsx +3 -1
- package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -6
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +47 -21
- package/corpus/templates/assets/app/i18n-data.ts +1815 -0
- package/corpus/templates/assets/app/lib/libraries.ts +6 -0
- package/corpus/templates/assets/app/lib/picker-chat-handoff.ts +71 -0
- package/corpus/templates/assets/app/routes/_index.tsx +4 -0
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
- package/corpus/templates/assets/app/routes/audit.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1557 -2164
- package/corpus/templates/assets/app/routes/brand-kits._index.tsx +7 -191
- package/corpus/templates/assets/app/routes/brand-kits.tsx +1 -5
- package/corpus/templates/assets/app/routes/libraries.tsx +4 -4
- package/corpus/templates/assets/app/routes/library.$id.tsx +9 -7
- package/corpus/templates/assets/app/routes/library.tsx +1454 -175
- package/corpus/templates/assets/server/db/index.ts +1 -1
- package/corpus/templates/assets/server/lib/generation.ts +86 -15
- package/corpus/templates/assets/server/plugins/agent-chat.ts +196 -0
- package/corpus/templates/assets/server/plugins/db.ts +5 -0
- package/corpus/templates/assets/shared/api.ts +6 -3
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +50 -29
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/types.d.ts +21 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +15 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +32 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +113 -0
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +2 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +228 -56
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/extensions/MentionReference.d.ts.map +1 -1
- package/dist/client/composer/extensions/MentionReference.js +3 -0
- package/dist/client/composer/extensions/MentionReference.js.map +1 -1
- package/dist/client/composer/types.d.ts +21 -0
- package/dist/client/composer/types.d.ts.map +1 -1
- package/dist/client/composer/types.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +5 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/request-context.d.ts +8 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/package.json +1 -1
|
@@ -31,8 +31,17 @@ import {
|
|
|
31
31
|
reasoningEffortLabel,
|
|
32
32
|
type ReasoningEffort,
|
|
33
33
|
} from "../../shared/reasoning-effort.js";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
AgentComposerReference,
|
|
36
|
+
normalizeAgentComposerReference,
|
|
37
|
+
sendToAgentChat,
|
|
38
|
+
type AgentChatContextItem,
|
|
39
|
+
AgentComposerReferenceInsertPayload,
|
|
40
|
+
AGENT_CHAT_INSERT_REFERENCE_EVENT,
|
|
41
|
+
AGENT_CHAT_INSERT_REFERENCE_MESSAGE_TYPE,
|
|
42
|
+
} from "../agent-chat.js";
|
|
35
43
|
import { tryDelegateBuildRequestToBuilder } from "../builder-frame.js";
|
|
44
|
+
import { isTrustedBuilderMessage } from "../builder-frame.js";
|
|
36
45
|
import {
|
|
37
46
|
Popover,
|
|
38
47
|
PopoverContent,
|
|
@@ -43,6 +52,7 @@ import {
|
|
|
43
52
|
TooltipContent,
|
|
44
53
|
TooltipTrigger,
|
|
45
54
|
} from "../components/ui/tooltip.js";
|
|
55
|
+
import { isTrustedFrameMessage } from "../frame.js";
|
|
46
56
|
import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
|
|
47
57
|
import { ComposerPlusMenu } from "./ComposerPlusMenu.js";
|
|
48
58
|
import { getComposerDraftKey } from "./draft-key.js";
|
|
@@ -67,10 +77,10 @@ import { useMentionSearch } from "./use-mention-search.js";
|
|
|
67
77
|
import { useSkills } from "./use-skills.js";
|
|
68
78
|
import { useVoiceDictation } from "./useVoiceDictation.js";
|
|
69
79
|
import { VoiceButton, VoiceRecordingOverlay } from "./VoiceButton.js";
|
|
70
|
-
|
|
71
80
|
export interface TiptapComposerHandle {
|
|
72
81
|
focus(): void;
|
|
73
82
|
setText(text: string): void;
|
|
83
|
+
insertReference(ref: AgentComposerReference): void;
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
export type ComposerSubmitIntent = "immediate" | "queued";
|
|
@@ -94,6 +104,120 @@ export function canSubmitComposerContent(options: {
|
|
|
94
104
|
|
|
95
105
|
const MAX_DOCUMENT_ATTACHMENT_BYTES = 4 * 1024 * 1024;
|
|
96
106
|
|
|
107
|
+
function composerReferenceFromMentionItem(
|
|
108
|
+
item: MentionItem,
|
|
109
|
+
): AgentComposerReference {
|
|
110
|
+
return {
|
|
111
|
+
label: item.label,
|
|
112
|
+
icon: item.icon || "file",
|
|
113
|
+
source: item.source,
|
|
114
|
+
refType: item.refType,
|
|
115
|
+
refId: item.refId || null,
|
|
116
|
+
refPath: item.refPath || null,
|
|
117
|
+
slotKey: item.slotKey,
|
|
118
|
+
slotLabel: item.slotLabel,
|
|
119
|
+
metadata: item.metadata,
|
|
120
|
+
clearsSlots: item.clearsSlots,
|
|
121
|
+
relatedReferences: item.relatedReferences,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function mentionReferenceAttrs(ref: AgentComposerReference) {
|
|
126
|
+
return {
|
|
127
|
+
label: ref.label,
|
|
128
|
+
icon: ref.icon || "file",
|
|
129
|
+
source: ref.source,
|
|
130
|
+
refType: ref.refType,
|
|
131
|
+
refId: ref.refId || null,
|
|
132
|
+
refPath: ref.refPath || null,
|
|
133
|
+
slotKey: ref.slotKey || null,
|
|
134
|
+
slotLabel: ref.slotLabel || null,
|
|
135
|
+
metadata: ref.metadata || null,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function referenceFromComposerReference(
|
|
140
|
+
ref: AgentComposerReference,
|
|
141
|
+
): Reference {
|
|
142
|
+
return {
|
|
143
|
+
type:
|
|
144
|
+
ref.refType === "file"
|
|
145
|
+
? "file"
|
|
146
|
+
: ref.refType === "agent"
|
|
147
|
+
? "agent"
|
|
148
|
+
: ref.refType === "custom-agent"
|
|
149
|
+
? "custom-agent"
|
|
150
|
+
: "mention",
|
|
151
|
+
path: ref.refPath || "",
|
|
152
|
+
name: ref.label,
|
|
153
|
+
source: ref.source || "",
|
|
154
|
+
refType: ref.refType,
|
|
155
|
+
refId: ref.refId || undefined,
|
|
156
|
+
slotKey: ref.slotKey,
|
|
157
|
+
slotLabel: ref.slotLabel,
|
|
158
|
+
metadata: ref.metadata,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function applySlotReferenceChanges(
|
|
163
|
+
current: AgentComposerReference[],
|
|
164
|
+
references: AgentComposerReference[],
|
|
165
|
+
): AgentComposerReference[] {
|
|
166
|
+
let next = current;
|
|
167
|
+
|
|
168
|
+
const applyOne = (ref: AgentComposerReference) => {
|
|
169
|
+
for (const related of ref.relatedReferences ?? []) {
|
|
170
|
+
applyOne(related);
|
|
171
|
+
}
|
|
172
|
+
if (!ref.slotKey) return;
|
|
173
|
+
const cleared = new Set([ref.slotKey, ...(ref.clearsSlots ?? [])]);
|
|
174
|
+
next = next.filter((existing) => !cleared.has(existing.slotKey ?? ""));
|
|
175
|
+
next = [...next, ref];
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
for (const ref of references) {
|
|
179
|
+
applyOne(ref);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return next;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function removeSlotReference(
|
|
186
|
+
current: AgentComposerReference[],
|
|
187
|
+
ref: AgentComposerReference,
|
|
188
|
+
): AgentComposerReference[] {
|
|
189
|
+
const removed = new Set([ref.slotKey, ...(ref.clearsSlots ?? [])]);
|
|
190
|
+
return current.filter((existing) => !removed.has(existing.slotKey ?? ""));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function slotReferenceTitle(ref: AgentComposerReference): string {
|
|
194
|
+
return ref.slotLabel ? `${ref.slotLabel}: ${ref.label}` : ref.label;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function metadataString(
|
|
198
|
+
metadata: Record<string, unknown> | undefined,
|
|
199
|
+
key: string,
|
|
200
|
+
): string | null {
|
|
201
|
+
const value = metadata?.[key];
|
|
202
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function filterMentionItemsForSlots(
|
|
206
|
+
items: MentionItem[],
|
|
207
|
+
slotReferences: AgentComposerReference[],
|
|
208
|
+
): MentionItem[] {
|
|
209
|
+
return items.filter((item) => {
|
|
210
|
+
const requiredSlotKey = metadataString(item.metadata, "requiredSlotKey");
|
|
211
|
+
const requiredRefId = metadataString(item.metadata, "requiredRefId");
|
|
212
|
+
if (!requiredSlotKey || !requiredRefId) return true;
|
|
213
|
+
const selected = slotReferences.find(
|
|
214
|
+
(ref) => ref.slotKey === requiredSlotKey,
|
|
215
|
+
);
|
|
216
|
+
if (!selected?.refId) return true;
|
|
217
|
+
return selected.refId === requiredRefId;
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
97
221
|
function isDocumentAttachment(value: Record<string, unknown>): boolean {
|
|
98
222
|
if (value.type === "document") return true;
|
|
99
223
|
const contentType = String(value.contentType ?? "").toLowerCase();
|
|
@@ -165,6 +289,26 @@ function uniquifyComposerImageFile(file: File): File {
|
|
|
165
289
|
return new File([file], uniqueName, { type: file.type });
|
|
166
290
|
}
|
|
167
291
|
|
|
292
|
+
function composerDocumentHasContent(doc: {
|
|
293
|
+
textContent: string;
|
|
294
|
+
descendants: (callback: (node: any) => boolean | void) => void;
|
|
295
|
+
}): boolean {
|
|
296
|
+
if (doc.textContent.trim().length > 0) return true;
|
|
297
|
+
let hasContent = false;
|
|
298
|
+
doc.descendants((node: any) => {
|
|
299
|
+
if (
|
|
300
|
+
node.type.name === "mentionReference" ||
|
|
301
|
+
node.type.name === "fileReference" ||
|
|
302
|
+
node.type.name === "skillReference"
|
|
303
|
+
) {
|
|
304
|
+
hasContent = true;
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
return true;
|
|
308
|
+
});
|
|
309
|
+
return hasContent;
|
|
310
|
+
}
|
|
311
|
+
|
|
168
312
|
export function handleComposerFileDrop(options: {
|
|
169
313
|
event: Pick<DragEvent, "dataTransfer" | "preventDefault" | "stopPropagation">;
|
|
170
314
|
addAttachment: (file: File) => Promise<unknown>;
|
|
@@ -1109,14 +1253,17 @@ export function TiptapComposer({
|
|
|
1109
1253
|
const popoverRef = useRef<MentionPopoverRef>(null);
|
|
1110
1254
|
const composerRuntime = useComposerRuntime();
|
|
1111
1255
|
const [editorHasText, setEditorHasText] = useState(false);
|
|
1256
|
+
const [slotReferences, setSlotReferences] = useState<
|
|
1257
|
+
AgentComposerReference[]
|
|
1258
|
+
>([]);
|
|
1112
1259
|
const composerText = useComposer((state) => state.text);
|
|
1113
1260
|
const composerAttachments = useComposer((state) => state.attachments);
|
|
1114
1261
|
const canSend = canSubmitComposerContent({
|
|
1115
|
-
hasEditorContent: editorHasText,
|
|
1262
|
+
hasEditorContent: editorHasText || slotReferences.length > 0,
|
|
1116
1263
|
attachmentCount: composerAttachments.length,
|
|
1117
1264
|
disabled,
|
|
1118
1265
|
});
|
|
1119
|
-
const
|
|
1266
|
+
const hasContextRows = contextItems.length > 0 || slotReferences.length > 0;
|
|
1120
1267
|
const [composerMode, setComposerMode] = useState<ComposerMode | null>(null);
|
|
1121
1268
|
const composerModeRef = useRef<ComposerMode | null>(null);
|
|
1122
1269
|
const isMac =
|
|
@@ -1138,6 +1285,10 @@ export function TiptapComposer({
|
|
|
1138
1285
|
popover?.type === "@" ? popover.query : "",
|
|
1139
1286
|
popover?.type === "@",
|
|
1140
1287
|
);
|
|
1288
|
+
const filteredMentionItems = useMemo(
|
|
1289
|
+
() => filterMentionItemsForSlots(mentionItems, slotReferences),
|
|
1290
|
+
[mentionItems, slotReferences],
|
|
1291
|
+
);
|
|
1141
1292
|
|
|
1142
1293
|
const {
|
|
1143
1294
|
skills,
|
|
@@ -1186,8 +1337,8 @@ export function TiptapComposer({
|
|
|
1186
1337
|
}, [allSlashSkills, popover]);
|
|
1187
1338
|
|
|
1188
1339
|
// Keep refs in sync with state
|
|
1189
|
-
const mentionItemsRef = useRef(
|
|
1190
|
-
mentionItemsRef.current =
|
|
1340
|
+
const mentionItemsRef = useRef(filteredMentionItems);
|
|
1341
|
+
mentionItemsRef.current = filteredMentionItems;
|
|
1191
1342
|
const filteredCommandsRef = useRef(filteredCommands);
|
|
1192
1343
|
filteredCommandsRef.current = filteredCommands;
|
|
1193
1344
|
const filteredSkillsRef = useRef(filteredSkills);
|
|
@@ -1197,6 +1348,7 @@ export function TiptapComposer({
|
|
|
1197
1348
|
const onTextChangeRef = useRef(onTextChange);
|
|
1198
1349
|
onTextChangeRef.current = onTextChange;
|
|
1199
1350
|
const initialTextKeyRef = useRef<string | number | undefined>(undefined);
|
|
1351
|
+
const seenReferenceInsertIdsRef = useRef<Set<string>>(new Set());
|
|
1200
1352
|
|
|
1201
1353
|
const closePopover = useCallback(() => {
|
|
1202
1354
|
setPopover(null);
|
|
@@ -1229,14 +1381,14 @@ export function TiptapComposer({
|
|
|
1229
1381
|
if (initialText !== undefined) {
|
|
1230
1382
|
ed.commands.setContent(plainTextToDoc(initialText));
|
|
1231
1383
|
ed.commands.focus("end");
|
|
1232
|
-
setEditorHasText(ed.state.doc
|
|
1384
|
+
setEditorHasText(composerDocumentHasContent(ed.state.doc));
|
|
1233
1385
|
initialTextKeyRef.current = initialTextKey ?? initialText;
|
|
1234
1386
|
} else {
|
|
1235
1387
|
const saved = localStorage.getItem(draftKey);
|
|
1236
1388
|
if (saved) {
|
|
1237
1389
|
ed.commands.setContent(saved);
|
|
1238
1390
|
ed.commands.focus("end");
|
|
1239
|
-
setEditorHasText(ed.state.doc
|
|
1391
|
+
setEditorHasText(composerDocumentHasContent(ed.state.doc));
|
|
1240
1392
|
}
|
|
1241
1393
|
}
|
|
1242
1394
|
onTextChangeRef.current?.(ed.state.doc.textContent.trim());
|
|
@@ -1245,21 +1397,7 @@ export function TiptapComposer({
|
|
|
1245
1397
|
onUpdate: ({ editor: ed }) => {
|
|
1246
1398
|
// Drive the send button's enabled state from the actual editor contents;
|
|
1247
1399
|
// the composer runtime is only synced on submit, so its isEmpty lags.
|
|
1248
|
-
|
|
1249
|
-
if (!hasContent) {
|
|
1250
|
-
ed.state.doc.descendants((node: any) => {
|
|
1251
|
-
if (
|
|
1252
|
-
node.type.name === "mentionReference" ||
|
|
1253
|
-
node.type.name === "fileReference" ||
|
|
1254
|
-
node.type.name === "skillReference"
|
|
1255
|
-
) {
|
|
1256
|
-
hasContent = true;
|
|
1257
|
-
return false;
|
|
1258
|
-
}
|
|
1259
|
-
return true;
|
|
1260
|
-
});
|
|
1261
|
-
}
|
|
1262
|
-
setEditorHasText(hasContent);
|
|
1400
|
+
setEditorHasText(composerDocumentHasContent(ed.state.doc));
|
|
1263
1401
|
onTextChangeRef.current?.(ed.state.doc.textContent.trim());
|
|
1264
1402
|
|
|
1265
1403
|
// Debounce-save draft to localStorage
|
|
@@ -1267,7 +1405,7 @@ export function TiptapComposer({
|
|
|
1267
1405
|
draftTimerRef.current = setTimeout(() => {
|
|
1268
1406
|
try {
|
|
1269
1407
|
const html = ed.getHTML();
|
|
1270
|
-
const isEmpty = !ed.state.doc
|
|
1408
|
+
const isEmpty = !composerDocumentHasContent(ed.state.doc);
|
|
1271
1409
|
if (isEmpty) {
|
|
1272
1410
|
localStorage.removeItem(draftKey);
|
|
1273
1411
|
} else {
|
|
@@ -1503,6 +1641,106 @@ export function TiptapComposer({
|
|
|
1503
1641
|
},
|
|
1504
1642
|
});
|
|
1505
1643
|
|
|
1644
|
+
const insertReference = useCallback(
|
|
1645
|
+
(ref: AgentComposerReference) => {
|
|
1646
|
+
const normalized = normalizeAgentComposerReference(ref);
|
|
1647
|
+
const ed = editor;
|
|
1648
|
+
if (!normalized || !ed) return;
|
|
1649
|
+
if (normalized.slotKey) {
|
|
1650
|
+
setSlotReferences((current) =>
|
|
1651
|
+
applySlotReferenceChanges(current, [normalized]),
|
|
1652
|
+
);
|
|
1653
|
+
ed.commands.focus("end");
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
if (normalized.relatedReferences?.some((item) => item.slotKey)) {
|
|
1657
|
+
setSlotReferences((current) =>
|
|
1658
|
+
applySlotReferenceChanges(
|
|
1659
|
+
current,
|
|
1660
|
+
normalized.relatedReferences ?? [],
|
|
1661
|
+
),
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
ed.chain()
|
|
1665
|
+
.focus()
|
|
1666
|
+
.insertContent({
|
|
1667
|
+
type: "mentionReference",
|
|
1668
|
+
attrs: mentionReferenceAttrs(normalized),
|
|
1669
|
+
})
|
|
1670
|
+
.insertContent(" ")
|
|
1671
|
+
.run();
|
|
1672
|
+
setEditorHasText(true);
|
|
1673
|
+
},
|
|
1674
|
+
[editor],
|
|
1675
|
+
);
|
|
1676
|
+
|
|
1677
|
+
const insertReferenceIfEmpty = useCallback(
|
|
1678
|
+
(payload: AgentComposerReferenceInsertPayload) => {
|
|
1679
|
+
const insertMessageId =
|
|
1680
|
+
typeof payload.insertMessageId === "string"
|
|
1681
|
+
? payload.insertMessageId
|
|
1682
|
+
: "";
|
|
1683
|
+
if (insertMessageId) {
|
|
1684
|
+
if (seenReferenceInsertIdsRef.current.has(insertMessageId)) return;
|
|
1685
|
+
seenReferenceInsertIdsRef.current.add(insertMessageId);
|
|
1686
|
+
}
|
|
1687
|
+
const ed = editor;
|
|
1688
|
+
if (!ed || disabled || composerModeRef.current) return;
|
|
1689
|
+
const normalized = normalizeAgentComposerReference(payload);
|
|
1690
|
+
if (normalized?.slotKey) {
|
|
1691
|
+
insertReference(normalized);
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
if (composerAttachments.length > 0) return;
|
|
1695
|
+
if (composerDocumentHasContent(ed.state.doc)) return;
|
|
1696
|
+
insertReference(payload);
|
|
1697
|
+
},
|
|
1698
|
+
[composerAttachments.length, disabled, editor, insertReference],
|
|
1699
|
+
);
|
|
1700
|
+
|
|
1701
|
+
useEffect(() => {
|
|
1702
|
+
if (typeof window === "undefined") return;
|
|
1703
|
+
const handleEvent = (event: Event) => {
|
|
1704
|
+
const payload = (event as CustomEvent).detail;
|
|
1705
|
+
const normalized = normalizeAgentComposerReference(payload);
|
|
1706
|
+
if (!normalized) return;
|
|
1707
|
+
insertReferenceIfEmpty({
|
|
1708
|
+
...normalized,
|
|
1709
|
+
insertMessageId:
|
|
1710
|
+
typeof payload?.insertMessageId === "string"
|
|
1711
|
+
? payload.insertMessageId
|
|
1712
|
+
: "",
|
|
1713
|
+
});
|
|
1714
|
+
};
|
|
1715
|
+
const handleMessage = (event: MessageEvent) => {
|
|
1716
|
+
if (!isTrustedFrameMessage(event) && !isTrustedBuilderMessage(event)) {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
if (event.data?.type !== AGENT_CHAT_INSERT_REFERENCE_MESSAGE_TYPE) {
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
const payload = event.data.data;
|
|
1723
|
+
const normalized = normalizeAgentComposerReference(payload);
|
|
1724
|
+
if (!normalized) return;
|
|
1725
|
+
insertReferenceIfEmpty({
|
|
1726
|
+
...normalized,
|
|
1727
|
+
insertMessageId:
|
|
1728
|
+
typeof payload?.insertMessageId === "string"
|
|
1729
|
+
? payload.insertMessageId
|
|
1730
|
+
: "",
|
|
1731
|
+
});
|
|
1732
|
+
};
|
|
1733
|
+
window.addEventListener(AGENT_CHAT_INSERT_REFERENCE_EVENT, handleEvent);
|
|
1734
|
+
window.addEventListener("message", handleMessage);
|
|
1735
|
+
return () => {
|
|
1736
|
+
window.removeEventListener(
|
|
1737
|
+
AGENT_CHAT_INSERT_REFERENCE_EVENT,
|
|
1738
|
+
handleEvent,
|
|
1739
|
+
);
|
|
1740
|
+
window.removeEventListener("message", handleMessage);
|
|
1741
|
+
};
|
|
1742
|
+
}, [insertReferenceIfEmpty]);
|
|
1743
|
+
|
|
1506
1744
|
useImperativeHandle(focusRef, () => ({
|
|
1507
1745
|
focus() {
|
|
1508
1746
|
editor?.commands.focus("end");
|
|
@@ -1513,6 +1751,7 @@ export function TiptapComposer({
|
|
|
1513
1751
|
editor.commands.focus("end");
|
|
1514
1752
|
const trimmed = editor.state.doc.textContent.trim();
|
|
1515
1753
|
setEditorHasText(trimmed.length > 0);
|
|
1754
|
+
setSlotReferences([]);
|
|
1516
1755
|
composerRuntime.setText(trimmed);
|
|
1517
1756
|
onTextChangeRef.current?.(trimmed);
|
|
1518
1757
|
try {
|
|
@@ -1523,6 +1762,7 @@ export function TiptapComposer({
|
|
|
1523
1762
|
}
|
|
1524
1763
|
} catch {}
|
|
1525
1764
|
},
|
|
1765
|
+
insertReference,
|
|
1526
1766
|
}));
|
|
1527
1767
|
|
|
1528
1768
|
const handleSelectMode = useCallback(
|
|
@@ -1663,10 +1903,15 @@ export function TiptapComposer({
|
|
|
1663
1903
|
const extractComposerPayload = useCallback(() => {
|
|
1664
1904
|
const ed = editor;
|
|
1665
1905
|
if (!ed) {
|
|
1666
|
-
return {
|
|
1906
|
+
return {
|
|
1907
|
+
text: slotReferences.map((ref) => slotReferenceTitle(ref)).join(", "),
|
|
1908
|
+
references: slotReferences.map(referenceFromComposerReference),
|
|
1909
|
+
};
|
|
1667
1910
|
}
|
|
1668
1911
|
|
|
1669
|
-
const references: Reference[] =
|
|
1912
|
+
const references: Reference[] = slotReferences.map(
|
|
1913
|
+
referenceFromComposerReference,
|
|
1914
|
+
);
|
|
1670
1915
|
|
|
1671
1916
|
// Build text that preserves @mentions (getText() strips them).
|
|
1672
1917
|
// Walk the document and reconstruct with @name for mention/file/skill nodes.
|
|
@@ -1691,7 +1936,10 @@ export function TiptapComposer({
|
|
|
1691
1936
|
textParts.push("\n");
|
|
1692
1937
|
}
|
|
1693
1938
|
});
|
|
1694
|
-
const
|
|
1939
|
+
const rawText = textParts.join("").trim();
|
|
1940
|
+
const text =
|
|
1941
|
+
rawText ||
|
|
1942
|
+
slotReferences.map((ref) => slotReferenceTitle(ref)).join(", ");
|
|
1695
1943
|
|
|
1696
1944
|
ed.state.doc.descendants((node: any) => {
|
|
1697
1945
|
if (node.type.name === "fileReference") {
|
|
@@ -1718,6 +1966,9 @@ export function TiptapComposer({
|
|
|
1718
1966
|
source: node.attrs.source,
|
|
1719
1967
|
refType: node.attrs.refType,
|
|
1720
1968
|
refId: node.attrs.refId,
|
|
1969
|
+
slotKey: node.attrs.slotKey,
|
|
1970
|
+
slotLabel: node.attrs.slotLabel,
|
|
1971
|
+
metadata: node.attrs.metadata,
|
|
1721
1972
|
});
|
|
1722
1973
|
} else if (node.type.name === "skillReference") {
|
|
1723
1974
|
references.push({
|
|
@@ -1730,7 +1981,7 @@ export function TiptapComposer({
|
|
|
1730
1981
|
});
|
|
1731
1982
|
|
|
1732
1983
|
return { text, references };
|
|
1733
|
-
}, [editor]);
|
|
1984
|
+
}, [editor, slotReferences]);
|
|
1734
1985
|
|
|
1735
1986
|
const syncComposerState = useCallback(() => {
|
|
1736
1987
|
const { text, references } = extractComposerPayload();
|
|
@@ -1827,6 +2078,7 @@ export function TiptapComposer({
|
|
|
1827
2078
|
cancelActiveVoice();
|
|
1828
2079
|
ed.commands.clearContent();
|
|
1829
2080
|
setEditorHasText(false);
|
|
2081
|
+
setSlotReferences([]);
|
|
1830
2082
|
setComposerMode(null);
|
|
1831
2083
|
composerModeRef.current = null;
|
|
1832
2084
|
try {
|
|
@@ -1849,6 +2101,7 @@ export function TiptapComposer({
|
|
|
1849
2101
|
cancelActiveVoice();
|
|
1850
2102
|
ed.commands.clearContent();
|
|
1851
2103
|
setEditorHasText(false);
|
|
2104
|
+
setSlotReferences([]);
|
|
1852
2105
|
try {
|
|
1853
2106
|
localStorage.removeItem(draftKey);
|
|
1854
2107
|
} catch {}
|
|
@@ -1870,6 +2123,7 @@ export function TiptapComposer({
|
|
|
1870
2123
|
cancelActiveVoice();
|
|
1871
2124
|
ed.commands.clearContent();
|
|
1872
2125
|
setEditorHasText(false);
|
|
2126
|
+
setSlotReferences([]);
|
|
1873
2127
|
try {
|
|
1874
2128
|
localStorage.removeItem(draftKey);
|
|
1875
2129
|
} catch {}
|
|
@@ -1901,22 +2155,8 @@ export function TiptapComposer({
|
|
|
1901
2155
|
const currentPos = ed.state.selection.from;
|
|
1902
2156
|
// startPos is after the trigger char, so -1 to include the @ or /
|
|
1903
2157
|
const deleteFrom = Math.max(0, pop.startPos - 1);
|
|
1904
|
-
ed.chain()
|
|
1905
|
-
|
|
1906
|
-
.deleteRange({ from: deleteFrom, to: currentPos })
|
|
1907
|
-
.insertContent({
|
|
1908
|
-
type: "mentionReference",
|
|
1909
|
-
attrs: {
|
|
1910
|
-
label: item.label,
|
|
1911
|
-
icon: item.icon || "file",
|
|
1912
|
-
source: item.source,
|
|
1913
|
-
refType: item.refType,
|
|
1914
|
-
refId: item.refId || null,
|
|
1915
|
-
refPath: item.refPath || null,
|
|
1916
|
-
},
|
|
1917
|
-
})
|
|
1918
|
-
.insertContent(" ")
|
|
1919
|
-
.run();
|
|
2158
|
+
ed.chain().focus().deleteRange({ from: deleteFrom, to: currentPos }).run();
|
|
2159
|
+
insertReference(composerReferenceFromMentionItem(item));
|
|
1920
2160
|
popoverStateRef.current = null;
|
|
1921
2161
|
setPopover(null);
|
|
1922
2162
|
}
|
|
@@ -1968,22 +2208,11 @@ export function TiptapComposer({
|
|
|
1968
2208
|
.chain()
|
|
1969
2209
|
.focus()
|
|
1970
2210
|
.deleteRange({ from: deleteFrom, to: currentPos })
|
|
1971
|
-
.insertContent({
|
|
1972
|
-
type: "mentionReference",
|
|
1973
|
-
attrs: {
|
|
1974
|
-
label: item.label,
|
|
1975
|
-
icon: item.icon || "file",
|
|
1976
|
-
source: item.source,
|
|
1977
|
-
refType: item.refType,
|
|
1978
|
-
refId: item.refId || null,
|
|
1979
|
-
refPath: item.refPath || null,
|
|
1980
|
-
},
|
|
1981
|
-
})
|
|
1982
|
-
.insertContent(" ")
|
|
1983
2211
|
.run();
|
|
2212
|
+
insertReference(composerReferenceFromMentionItem(item));
|
|
1984
2213
|
closePopover();
|
|
1985
2214
|
},
|
|
1986
|
-
[editor, popover, closePopover],
|
|
2215
|
+
[editor, popover, closePopover, insertReference],
|
|
1987
2216
|
);
|
|
1988
2217
|
|
|
1989
2218
|
const handleSelectCommand = useCallback(
|
|
@@ -2131,12 +2360,39 @@ export function TiptapComposer({
|
|
|
2131
2360
|
/>
|
|
2132
2361
|
</div>
|
|
2133
2362
|
)}
|
|
2134
|
-
{
|
|
2363
|
+
{hasContextRows && (
|
|
2135
2364
|
<div
|
|
2136
2365
|
data-agent-composer-variant={layoutVariant}
|
|
2137
2366
|
data-agent-composer-slot="context-row"
|
|
2138
2367
|
className="agent-composer-context-row flex flex-wrap gap-1.5 px-2.5 pt-2 pb-0"
|
|
2139
2368
|
>
|
|
2369
|
+
{slotReferences.map((ref) => (
|
|
2370
|
+
<span
|
|
2371
|
+
key={ref.slotKey}
|
|
2372
|
+
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"
|
|
2373
|
+
>
|
|
2374
|
+
<IconClipboardList className="h-3 w-3 shrink-0 text-muted-foreground" />
|
|
2375
|
+
{ref.slotLabel && (
|
|
2376
|
+
<span className="shrink-0 text-muted-foreground">
|
|
2377
|
+
{ref.slotLabel}
|
|
2378
|
+
</span>
|
|
2379
|
+
)}
|
|
2380
|
+
<span className="min-w-0 truncate">{ref.label}</span>
|
|
2381
|
+
<button
|
|
2382
|
+
type="button"
|
|
2383
|
+
onClick={() => {
|
|
2384
|
+
setSlotReferences((current) =>
|
|
2385
|
+
removeSlotReference(current, ref),
|
|
2386
|
+
);
|
|
2387
|
+
editor?.commands.focus("end");
|
|
2388
|
+
}}
|
|
2389
|
+
aria-label={`Remove ${slotReferenceTitle(ref)} reference`}
|
|
2390
|
+
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"
|
|
2391
|
+
>
|
|
2392
|
+
<IconX className="h-3 w-3" />
|
|
2393
|
+
</button>
|
|
2394
|
+
</span>
|
|
2395
|
+
))}
|
|
2140
2396
|
{contextItems.map((item) => (
|
|
2141
2397
|
<span
|
|
2142
2398
|
key={item.key}
|
|
@@ -2160,7 +2416,7 @@ export function TiptapComposer({
|
|
|
2160
2416
|
data-agent-composer-variant={layoutVariant}
|
|
2161
2417
|
data-agent-composer-slot="editor-wrap"
|
|
2162
2418
|
className={`agent-composer-editor-wrap ${
|
|
2163
|
-
composerMode ||
|
|
2419
|
+
composerMode || hasContextRows ? "px-2 pt-1 pb-1" : "px-2 pt-2 pb-1"
|
|
2164
2420
|
}`}
|
|
2165
2421
|
>
|
|
2166
2422
|
<EditorContent
|
|
@@ -2233,7 +2489,7 @@ export function TiptapComposer({
|
|
|
2233
2489
|
ref={popoverRef}
|
|
2234
2490
|
type={popover?.type ?? "@"}
|
|
2235
2491
|
position={popover?.position ?? null}
|
|
2236
|
-
mentionItems={
|
|
2492
|
+
mentionItems={filteredMentionItems}
|
|
2237
2493
|
skills={filteredSkills}
|
|
2238
2494
|
commands={filteredCommands}
|
|
2239
2495
|
hint={hint}
|
|
@@ -22,6 +22,11 @@ export interface MentionItem {
|
|
|
22
22
|
refPath?: string;
|
|
23
23
|
refId?: string;
|
|
24
24
|
section?: string;
|
|
25
|
+
slotKey?: string;
|
|
26
|
+
slotLabel?: string;
|
|
27
|
+
metadata?: Record<string, unknown>;
|
|
28
|
+
clearsSlots?: string[];
|
|
29
|
+
relatedReferences?: MentionReferenceInsert[];
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
export interface Reference {
|
|
@@ -31,6 +36,23 @@ export interface Reference {
|
|
|
31
36
|
source: string;
|
|
32
37
|
refType?: string;
|
|
33
38
|
refId?: string;
|
|
39
|
+
slotKey?: string;
|
|
40
|
+
slotLabel?: string;
|
|
41
|
+
metadata?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface MentionReferenceInsert {
|
|
45
|
+
label: string;
|
|
46
|
+
icon?: string;
|
|
47
|
+
source?: string;
|
|
48
|
+
refType: string;
|
|
49
|
+
refId?: string | null;
|
|
50
|
+
refPath?: string | null;
|
|
51
|
+
slotKey?: string;
|
|
52
|
+
slotLabel?: string;
|
|
53
|
+
metadata?: Record<string, unknown>;
|
|
54
|
+
clearsSlots?: string[];
|
|
55
|
+
relatedReferences?: MentionReferenceInsert[];
|
|
34
56
|
}
|
|
35
57
|
|
|
36
58
|
export interface SlashCommand {
|
|
@@ -11,7 +11,9 @@ export {
|
|
|
11
11
|
appendAgentChatContextToMessage,
|
|
12
12
|
clearAgentChatContext,
|
|
13
13
|
formatAgentChatContextItemsForPrompt,
|
|
14
|
+
insertAgentComposerReference,
|
|
14
15
|
listAgentChatContext,
|
|
16
|
+
normalizeAgentComposerReference,
|
|
15
17
|
refreshAgentChatContext,
|
|
16
18
|
removeAgentChatContextItem,
|
|
17
19
|
sendToAgentChat,
|
|
@@ -27,6 +29,9 @@ export {
|
|
|
27
29
|
type AgentChatContextSetOptions,
|
|
28
30
|
type AgentChatContextState,
|
|
29
31
|
type AgentChatMessage,
|
|
32
|
+
type AgentComposerReference,
|
|
33
|
+
type AgentComposerReferenceInsertOptions,
|
|
34
|
+
type AgentComposerReferenceInsertPayload,
|
|
30
35
|
} from "./agent-chat.js";
|
|
31
36
|
export {
|
|
32
37
|
saveAgentEngineApiKey,
|
|
@@ -6660,6 +6660,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6660
6660
|
refPath?: string;
|
|
6661
6661
|
refId?: string;
|
|
6662
6662
|
section?: string;
|
|
6663
|
+
slotKey?: string;
|
|
6664
|
+
slotLabel?: string;
|
|
6665
|
+
metadata?: Record<string, unknown>;
|
|
6666
|
+
clearsSlots?: string[];
|
|
6667
|
+
relatedReferences?: unknown[];
|
|
6663
6668
|
}
|
|
6664
6669
|
|
|
6665
6670
|
const matchesQuery = (item: MentionItemResponse) =>
|
|
@@ -6796,6 +6801,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6796
6801
|
refPath: item.refPath,
|
|
6797
6802
|
refId: item.refId,
|
|
6798
6803
|
section: provider.label,
|
|
6804
|
+
slotKey: item.slotKey,
|
|
6805
|
+
slotLabel: item.slotLabel,
|
|
6806
|
+
metadata: item.metadata,
|
|
6807
|
+
clearsSlots: item.clearsSlots,
|
|
6808
|
+
relatedReferences: item.relatedReferences,
|
|
6799
6809
|
})),
|
|
6800
6810
|
);
|
|
6801
6811
|
} catch (e) {
|
|
@@ -51,6 +51,14 @@ export interface RequestRunContext {
|
|
|
51
51
|
engine?: import("../agent/engine/types.js").AgentEngine;
|
|
52
52
|
/** Model name for this run (set by onEngineResolved). */
|
|
53
53
|
model?: string;
|
|
54
|
+
/**
|
|
55
|
+
* True when this run is executing inside the durable background-function
|
|
56
|
+
* worker (the `_process-run` self-dispatch), not the synchronous foreground
|
|
57
|
+
* request. Template `extraContext` / system-prompt builders can read this to
|
|
58
|
+
* skip heavy, hang-prone enrichment (large data-dictionary DB reads, etc.)
|
|
59
|
+
* in the worker so it reliably claims its run within the setup budget.
|
|
60
|
+
*/
|
|
61
|
+
isBackgroundWorker?: boolean;
|
|
54
62
|
/** Tool calls made so far in the current agent loop. */
|
|
55
63
|
toolCalls?: Array<{ name: string; input: unknown }>;
|
|
56
64
|
/** Tool results returned so far in the current agent loop. */
|