@ai-matrx/chat 0.2.5 → 0.2.7
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/CHANGELOG.md +19 -0
- package/dist/agents/addressing/agentAddress.js +1 -1
- package/dist/agents/components/context-items/bodies/ProcessedDocumentTitle.js +3 -2
- package/dist/agents/components/context-items/bodies/WebpageBody.js +6 -1
- package/dist/agents/components/debug/SandboxFileViewer.js +6 -1
- package/dist/agents/components/debug/StreamDebugPanel.js +6 -1
- package/dist/agents/components/inputs/input-components/MediaVariableInput.js +1 -1
- package/dist/agents/components/inputs/resources/attached-documents.js +3 -2
- package/dist/agents/components/inputs/resources/usePasteImageResource.js +2 -2
- package/dist/agents/components/messages-display/TranscriptIntegrityCopyButton.js +6 -2
- package/dist/agents/components/observational-memory/components/MemoryStateInspector.d.ts +0 -12
- package/dist/agents/components/observational-memory/components/MemoryStateInspector.js +10 -2
- package/dist/agents/components/run-controls/AgentExecutionTestModal.js +6 -1
- package/dist/agents/components/shared/AgentDuplicateOutcomeDialog.d.ts +26 -2
- package/dist/agents/components/shared/AgentDuplicateOutcomeDialog.js +50 -4
- package/dist/agents/components/shared/AgentOptionsMenu.js +11 -61
- package/dist/agents/components/shared/StreamProfilerOverlay.js +6 -1
- package/dist/agents/hooks/useAgentDuplicateFlow.d.ts +13 -3
- package/dist/agents/hooks/useAgentDuplicateFlow.js +81 -24
- package/dist/agents/hooks/useWidgetHandle.js +1 -1
- package/dist/agents/prompt-preview/PromptPreviewContent.js +6 -1
- package/dist/agents/redux/agent-definition/cache-bust-middleware.js +1 -1
- package/dist/agents/redux/agent-definition/parse-messages-variables.js +6 -0
- package/dist/agents/redux/agent-shortcuts/thunks.js +1 -1
- package/dist/agents/redux/execution-system/instance-resources/resource-source.js +1 -2
- package/dist/agents/redux/execution-system/thunks/execute-manual-instance.thunk.js +1 -1
- package/dist/agents/redux/execution-system/thunks/launch-conversation.thunk.js +1 -1
- package/dist/agents/redux/execution-system/utils/assert-conversation-id.js +1 -1
- package/dist/agents/redux/execution-system/utils/content-prefilter.js +2 -2
- package/dist/agents/runtime/realtime/launchRealtimeSession.thunk.js +1 -1
- package/dist/agents/surface-catalog-profile.d.ts +1 -1
- package/dist/agents/types/agent-definition.types.d.ts +8 -6
- package/dist/agents/utils/variable-customcomponent.d.ts +2 -2
- package/dist/agents/utils/variable-customcomponent.js +4 -4
- package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
- package/dist/cx-chat/components/user-input/ConversationInput.js +1 -1
- package/dist/cx-conversation/AssistantMessage.js +1 -1
- package/dist/cx-conversation/ConversationInput.js +1 -1
- package/dist/host/builder-door.d.ts +13 -2
- package/dist/host/db-types.d.ts +4 -4
- package/dist/host/defaults/diagnostics.js +1 -1
- package/dist/host/ui-slots.d.ts +0 -20
- package/dist/host/ui-slots.js +0 -26
- package/dist/store/create-chat-store.d.ts +2 -0
- package/dist/store/create-chat-store.js +15 -3
- package/dist/surfaces/runtime/loaded-value-check.js +1 -1
- package/dist/surfaces/runtime/surface-writeback.js +1 -1
- package/dist/surfaces/utils/route-to-surface.js +1 -1
- package/dist/surfaces/utils/surface-display.js +1 -1
- package/dist/tool-call-visualization/result-fields/shape.js +1 -1
- package/dist/ui/file-source.d.ts +3 -17
- package/dist/voice-agent/components/VoiceDebugPanel.js +6 -1
- package/dist/voice-agent/hooks/useRealtimeAgentConfig.js +1 -1
- package/dist/voice-agent/hooks/useXaiVoiceSession.js +2 -2
- package/dist/voice-agent/transport/xaiClient.js +1 -1
- package/dist/window-panels/windows/agents/AgentMemoryWindow.js +7 -5
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.2.7 — 2026-10-06
|
|
4
|
+
### Re-sync before matrx-frontend consumes the published package (P27)
|
|
5
|
+
|
|
6
|
+
- Carries every matrx-frontend `packages/chat` edit made after base `9abc4bc369` (41 files, all
|
|
7
|
+
fast-forward): agent duplicate-any-version flow and one version-copy door, files engine read from
|
|
8
|
+
`@ai-matrx/media` directly (P16f, 8 file slots gone), ALC-16/19 kit clipboard copy, pick-list token,
|
|
9
|
+
`host/db-types.ts`, private-store-has-files suite.
|
|
10
|
+
- The ALC-19 copy call sites imported the app's `@/lib/toast`; they use the package notify port
|
|
11
|
+
(`host/notify`) instead, so the host-tie budget stays 0. `ProcessedDocumentTitle` dispatches the media
|
|
12
|
+
rename thunk through its own signature.
|
|
13
|
+
- Consumer action: none.
|
|
14
|
+
|
|
15
|
+
## 0.2.6 — 2026-10-06
|
|
16
|
+
|
|
17
|
+
- The build no longer bakes `process.env.NODE_ENV` to `"development"` (esbuild's browser-platform default):
|
|
18
|
+
16 modules read the host's value again, so dev-only checks stop running in production and the
|
|
19
|
+
test-mode throw in `surfaces/runtime/loaded-value-check` fires under Jest (P27).
|
|
20
|
+
- Consumer action: none.
|
|
21
|
+
|
|
3
22
|
## 0.2.5 — 2026-10-06
|
|
4
23
|
### Re-sync before matrx-frontend consumes the published package (P27)
|
|
5
24
|
|
|
@@ -37,7 +37,7 @@ function versionIdMessage(address, suppliedId, context) {
|
|
|
37
37
|
function assertAgentIdNotVersion(address, suppliedId, context) {
|
|
38
38
|
if (!address.isVersion || suppliedId === address.agentId) return;
|
|
39
39
|
const message = versionIdMessage(address, suppliedId, context);
|
|
40
|
-
if (
|
|
40
|
+
if (process.env.NODE_ENV !== "production") throw new TypeError(message);
|
|
41
41
|
console.error(message);
|
|
42
42
|
}
|
|
43
43
|
function reportVersionIdRescued(address, suppliedId, context) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { renameFile } from "@ai-matrx/media/files/engine/redux/thunks";
|
|
3
4
|
import { toast } from "../../../../host/notify.js";
|
|
4
5
|
import { useAppDispatch } from "../../../../store/hooks.js";
|
|
5
|
-
import { renameFile } from "../../../../host/ui-slots.js";
|
|
6
6
|
import { useAttachedDocumentDisplayName } from "../../inputs/resources/attached-documents.js";
|
|
7
7
|
import { PdfNamedSurfaceSwitcher } from "../../../../host/ui-slots.js";
|
|
8
8
|
function ProcessedDocumentTitle({
|
|
@@ -20,7 +20,8 @@ function ProcessedDocumentTitle({
|
|
|
20
20
|
const hasSiblings = siblings.length > 1;
|
|
21
21
|
const handleRename = fileId ? async (next) => {
|
|
22
22
|
try {
|
|
23
|
-
|
|
23
|
+
const dispatchFiles = dispatch;
|
|
24
|
+
await dispatchFiles(renameFile({ fileId, newName: next })).unwrap();
|
|
24
25
|
} catch (err) {
|
|
25
26
|
toast.error(
|
|
26
27
|
err instanceof Error ? `Rename failed: ${err.message}` : "Rename failed"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast as copyToast } from "../../../../host/notify.js";
|
|
3
5
|
import { useEffect, useState } from "react";
|
|
4
6
|
import { Check, Copy, ExternalLink } from "lucide-react";
|
|
5
7
|
import {
|
|
@@ -40,13 +42,16 @@ function WebpageBody({ item, setTitle }) {
|
|
|
40
42
|
);
|
|
41
43
|
}
|
|
42
44
|
function WebpageFooter({ item }) {
|
|
45
|
+
const { copyText } = useClipboard({
|
|
46
|
+
notify: (message, kind) => kind === "error" ? copyToast.error(message) : copyToast.success(message)
|
|
47
|
+
});
|
|
43
48
|
const webpage = firstWebpage(item);
|
|
44
49
|
const url = webpage ? webpageUrl(webpage) : null;
|
|
45
50
|
const [copied, setCopied] = useState(false);
|
|
46
51
|
if (!url) return null;
|
|
47
52
|
const copy = async () => {
|
|
48
53
|
try {
|
|
49
|
-
await
|
|
54
|
+
await copyText(url);
|
|
50
55
|
setCopied(true);
|
|
51
56
|
setTimeout(() => setCopied(false), 1500);
|
|
52
57
|
} catch {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast as copyToast } from "../../../host/notify.js";
|
|
3
5
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
4
6
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
7
|
import { FileText, Loader2, RefreshCw, Copy, Check } from "lucide-react";
|
|
@@ -11,6 +13,9 @@ function SandboxFileViewer({
|
|
|
11
13
|
initialPath = "",
|
|
12
14
|
lockPath = false
|
|
13
15
|
}) {
|
|
16
|
+
const { copyText } = useClipboard({
|
|
17
|
+
notify: (message, kind) => kind === "error" ? copyToast.error(message) : copyToast.success(message)
|
|
18
|
+
});
|
|
14
19
|
const [path, setPath] = useState(initialPath);
|
|
15
20
|
const [content, setContent] = useState(null);
|
|
16
21
|
const [loading, setLoading] = useState(false);
|
|
@@ -45,7 +50,7 @@ function SandboxFileViewer({
|
|
|
45
50
|
}, [initialPath, sandboxRowId, load]);
|
|
46
51
|
const handleCopy = async () => {
|
|
47
52
|
if (content == null) return;
|
|
48
|
-
await
|
|
53
|
+
await copyText(content);
|
|
49
54
|
setCopied(true);
|
|
50
55
|
toast.success("File contents copied");
|
|
51
56
|
setTimeout(() => setCopied(false), 1500);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../../../host/notify.js";
|
|
3
5
|
import { Chip, Button, DisclosureHeader } from "@ai-matrx/design-system/controls";
|
|
4
6
|
import { formatDurationMs } from "@ai-matrx/kit/format";
|
|
5
7
|
import React, { useState, useRef, useEffect, useMemo } from "react";
|
|
@@ -49,9 +51,12 @@ function DebugTabScroll({ children }) {
|
|
|
49
51
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-col flex-1 min-h-0 h-full", children: /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1 min-h-0", children }) });
|
|
50
52
|
}
|
|
51
53
|
function useCopy() {
|
|
54
|
+
const { copyText } = useClipboard({
|
|
55
|
+
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
56
|
+
});
|
|
52
57
|
const [copied, setCopied] = useState(null);
|
|
53
58
|
const copy = (text, id) => {
|
|
54
|
-
|
|
59
|
+
copyText(text);
|
|
55
60
|
setCopied(id);
|
|
56
61
|
setTimeout(() => setCopied(null), 1500);
|
|
57
62
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useFileUpload } from "@ai-matrx/media/files/engine/handler/hooks/useFileUpload";
|
|
3
4
|
import { useCallback, useRef, useState } from "react";
|
|
4
5
|
import {
|
|
5
6
|
Image as ImageIcon,
|
|
@@ -20,7 +21,6 @@ import { FileResourceChip } from "../../../../host/ui-slots.js";
|
|
|
20
21
|
import { InlineMediaRef } from "@ai-matrx/media/react";
|
|
21
22
|
import { useFileDocument } from "../../../../host/ui-slots.js";
|
|
22
23
|
import { useMediaResolution } from "@ai-matrx/media/core";
|
|
23
|
-
import { useFileUpload } from "../../../../host/ui-slots.js";
|
|
24
24
|
import { FilesResourcePicker } from "../../../../host/ui-slots.js";
|
|
25
25
|
import { cn } from "@ai-matrx/design-system";
|
|
26
26
|
import { ResourceFamilyPolicyEditor } from "../resources/ResourceFamilyPolicyEditor.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { fileHandler } from "@ai-matrx/media/files/engine";
|
|
3
|
+
import { useFile } from "@ai-matrx/media/files/engine/handler/hooks/useFile";
|
|
2
4
|
import { useEffect, useState } from "react";
|
|
3
|
-
import { resolveFile, useFile } from "../../../../host/ui-slots.js";
|
|
4
5
|
import { normalizeResourceFamilyPolicy } from "./resource-family-policy.js";
|
|
5
6
|
const ATTACHED_DOCUMENT_TOKENS = ["processed_document", "file"];
|
|
6
7
|
function parseAttachedDocumentMetadata(metadata) {
|
|
@@ -78,7 +79,7 @@ function useAttachedDocumentFileNames(fileIds) {
|
|
|
78
79
|
if (!idsKey) return void 0;
|
|
79
80
|
let cancelled = false;
|
|
80
81
|
for (const fileId of idsKey.split(",")) {
|
|
81
|
-
|
|
82
|
+
fileHandler.resolve({ kind: "file_id", fileId }).then((file) => {
|
|
82
83
|
const name = file.meta.fileName?.trim();
|
|
83
84
|
if (cancelled || !name) return;
|
|
84
85
|
setNames((prev) => prev[fileId] === name ? prev : { ...prev, [fileId]: name });
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useFileUpload } from "@ai-matrx/media/files/engine/handler/hooks/useFileUpload";
|
|
3
|
+
import { normalize } from "@ai-matrx/media/files/engine";
|
|
2
4
|
import { useEffect, useRef } from "react";
|
|
3
5
|
import { toast } from "../../../../host/notify.js";
|
|
4
6
|
import { useAppDispatch, useAppStore } from "../../../../store/hooks.js";
|
|
5
|
-
import { useFileUpload } from "../../../../host/ui-slots.js";
|
|
6
7
|
import { composeUploadFolderPath } from "@ai-matrx/media/files";
|
|
7
|
-
import { normalize } from "../../../../host/ui-slots.js";
|
|
8
8
|
import {
|
|
9
9
|
addResource,
|
|
10
10
|
removeResource,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast as copyToast } from "../../../host/notify.js";
|
|
3
5
|
import { useEffect } from "react";
|
|
4
6
|
import { usePathname } from "../../../host/navigation.js";
|
|
5
7
|
import { Stethoscope } from "lucide-react";
|
|
6
8
|
import { useAppSelector, useAppStore } from "../../../store/hooks.js";
|
|
7
9
|
import { selectMessageCount } from "../../redux/execution-system/messages/messages.selectors.js";
|
|
8
10
|
import { selectStreamPhase } from "../../redux/execution-system/selectors/aggregate.selectors.js";
|
|
9
|
-
import { writeClipboard } from "../../../agent-copy/clipboard.js";
|
|
10
11
|
import { toast } from "../../../host/notify.js";
|
|
11
12
|
import { useDebugContext } from "../../../host/prefs-react.js";
|
|
12
13
|
import {
|
|
@@ -25,6 +26,9 @@ function TranscriptIntegrityCopyButtonInner({
|
|
|
25
26
|
surfaceKey,
|
|
26
27
|
effectiveVisibleGroupLimit
|
|
27
28
|
}) {
|
|
29
|
+
const { copyText } = useClipboard({
|
|
30
|
+
notify: (message, kind) => kind === "error" ? copyToast.error(message) : copyToast.success(message)
|
|
31
|
+
});
|
|
28
32
|
const store = useAppStore();
|
|
29
33
|
const pathname = usePathname() ?? "";
|
|
30
34
|
const messageCount = useAppSelector(selectMessageCount(conversationId));
|
|
@@ -64,7 +68,7 @@ function TranscriptIntegrityCopyButtonInner({
|
|
|
64
68
|
effectiveVisibleGroupLimit
|
|
65
69
|
});
|
|
66
70
|
try {
|
|
67
|
-
await
|
|
71
|
+
await copyText(formatTranscriptIntegrityReport(report));
|
|
68
72
|
toast.success(
|
|
69
73
|
report.anomalies.length === 0 ? "Transcript report copied \u2014 no anomalies detected" : `Transcript report copied \u2014 ${report.anomalies.length} anomal${report.anomalies.length === 1 ? "y" : "ies"} flagged`
|
|
70
74
|
);
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MemoryStateInspector
|
|
3
|
-
*
|
|
4
|
-
* Admin-only view of the raw `cx_observational_memory` row for a conversation.
|
|
5
|
-
* Shows the actual `active_observations` text (what's being injected on every
|
|
6
|
-
* turn), plus the buffered observation/reflection state, generation counter,
|
|
7
|
-
* token counts, and metadata.
|
|
8
|
-
*
|
|
9
|
-
* We query Supabase directly with the RLS'd client — non-admin users will
|
|
10
|
-
* see an empty result even if they somehow reach this component, and admins
|
|
11
|
-
* will see the full row.
|
|
12
|
-
*/
|
|
13
1
|
import React from "react";
|
|
14
2
|
interface MemoryStateInspectorProps {
|
|
15
3
|
conversationId: string;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../../../../host/notify.js";
|
|
3
5
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
6
|
import { extractErrorMessage } from "@ai-matrx/data/net";
|
|
5
7
|
import {
|
|
@@ -262,13 +264,16 @@ function BufferedObservationCard({
|
|
|
262
264
|
cycle,
|
|
263
265
|
defaultOpen
|
|
264
266
|
}) {
|
|
267
|
+
const { copyText } = useClipboard({
|
|
268
|
+
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
269
|
+
});
|
|
265
270
|
const [open, setOpen] = useState(defaultOpen);
|
|
266
271
|
const [copied, setCopied] = useState(false);
|
|
267
272
|
const text = cycle.observations ?? "";
|
|
268
273
|
const hasText = text.length > 0;
|
|
269
274
|
const handleCopy = useCallback(() => {
|
|
270
275
|
if (!hasText) return;
|
|
271
|
-
|
|
276
|
+
copyText(text);
|
|
272
277
|
setCopied(true);
|
|
273
278
|
setTimeout(() => setCopied(false), 1500);
|
|
274
279
|
}, [text, hasText]);
|
|
@@ -498,11 +503,14 @@ function TextPayload({
|
|
|
498
503
|
text,
|
|
499
504
|
maxHeightClass = "max-h-64"
|
|
500
505
|
}) {
|
|
506
|
+
const { copyText } = useClipboard({
|
|
507
|
+
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
508
|
+
});
|
|
501
509
|
const [copied, setCopied] = useState(false);
|
|
502
510
|
const hasText = typeof text === "string" && text.length > 0;
|
|
503
511
|
const handleCopy = useCallback(() => {
|
|
504
512
|
if (!text) return;
|
|
505
|
-
|
|
513
|
+
copyText(text);
|
|
506
514
|
setCopied(true);
|
|
507
515
|
setTimeout(() => setCopied(false), 1500);
|
|
508
516
|
}, [text]);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast as copyToast } from "../../../host/notify.js";
|
|
3
5
|
import { useState, useEffect, useCallback } from "react";
|
|
4
6
|
import { kindTextToMarkdown } from "../../../utils/content-ir/surfaces/kind-text-to-markdown.js";
|
|
5
7
|
import { useAppSelector } from "../../../store/hooks.js";
|
|
@@ -43,6 +45,9 @@ function DirectTestMode({
|
|
|
43
45
|
userInput,
|
|
44
46
|
apiEndpointMode
|
|
45
47
|
}) {
|
|
48
|
+
const { copyText } = useClipboard({
|
|
49
|
+
notify: (message, kind) => kind === "error" ? copyToast.error(message) : copyToast.success(message)
|
|
50
|
+
});
|
|
46
51
|
const { launchAgent, close } = useAgentLauncher();
|
|
47
52
|
const [conversationId, setConversationId] = useState(null);
|
|
48
53
|
const [copied, setCopied] = useState(false);
|
|
@@ -88,7 +93,7 @@ function DirectTestMode({
|
|
|
88
93
|
]);
|
|
89
94
|
const handleCopy = useCallback(() => {
|
|
90
95
|
if (responseText) {
|
|
91
|
-
|
|
96
|
+
copyText(kindTextToMarkdown(responseText));
|
|
92
97
|
setCopied(true);
|
|
93
98
|
setTimeout(() => setCopied(false), 2e3);
|
|
94
99
|
}
|
|
@@ -1,4 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
/** "choose" = the person picks which version to copy before anything runs. */
|
|
2
|
+
export type DuplicateOutcomeState = "choose" | "loading" | "success" | "error";
|
|
3
|
+
/** The value of the "copy the agent as it is now" choice. */
|
|
4
|
+
export declare const CURRENT_VERSION_CHOICE = "current";
|
|
5
|
+
export interface DuplicateVersionOption {
|
|
6
|
+
versionId: string;
|
|
7
|
+
versionNumber: number;
|
|
8
|
+
changedAt: string;
|
|
9
|
+
changeNote: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface DuplicateVersionChooser {
|
|
12
|
+
/** The source agent's live version number, shown on the default choice. */
|
|
13
|
+
currentVersion: number | null;
|
|
14
|
+
/** Saved past versions, newest first. */
|
|
15
|
+
versions: DuplicateVersionOption[];
|
|
16
|
+
versionsLoading: boolean;
|
|
17
|
+
/** Set when the version list could not be read — the current version still works. */
|
|
18
|
+
versionsError: string | null;
|
|
19
|
+
/** CURRENT_VERSION_CHOICE or a version id. */
|
|
20
|
+
selected: string;
|
|
21
|
+
onSelectedChange: (value: string) => void;
|
|
22
|
+
onConfirm: () => void;
|
|
23
|
+
}
|
|
2
24
|
export interface AgentDuplicateOutcomeDialogProps {
|
|
3
25
|
open: boolean;
|
|
4
26
|
/** Called when the user dismisses the dialog (X button, escape, or "Stay
|
|
@@ -15,6 +37,8 @@ export interface AgentDuplicateOutcomeDialogProps {
|
|
|
15
37
|
/** Whether the source agent was a builtin/system agent. Tweaks the success
|
|
16
38
|
* copy so admins know the duplicate is also a system agent. */
|
|
17
39
|
asSystem?: boolean;
|
|
40
|
+
/** Required for the "choose" state. */
|
|
41
|
+
chooser?: DuplicateVersionChooser;
|
|
18
42
|
}
|
|
19
43
|
/**
|
|
20
44
|
* Post-duplicate "where would you like to go?" choice surface.
|
|
@@ -30,4 +54,4 @@ export interface AgentDuplicateOutcomeDialogProps {
|
|
|
30
54
|
* Mobile renders as a bottom Drawer (per project mobile rules — never Dialog
|
|
31
55
|
* on mobile); desktop uses a small Dialog (`sm:max-w-md`).
|
|
32
56
|
*/
|
|
33
|
-
export declare function AgentDuplicateOutcomeDialog({ open, onOpenChange, state, newAgentName, newAgentPath, errorMessage, asSystem, }: AgentDuplicateOutcomeDialogProps): import("react").JSX.Element;
|
|
57
|
+
export declare function AgentDuplicateOutcomeDialog({ open, onOpenChange, state, newAgentName, newAgentPath, errorMessage, asSystem, chooser, }: AgentDuplicateOutcomeDialogProps): import("react").JSX.Element;
|
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
} from "@ai-matrx/design-system";
|
|
27
27
|
import { Alert, AlertDescription } from "@ai-matrx/design-system";
|
|
28
28
|
import { Button } from "@ai-matrx/design-system";
|
|
29
|
+
import { Select } from "@ai-matrx/design-system/controls";
|
|
29
30
|
import { useIsMobile } from "@ai-matrx/kit/media-query";
|
|
31
|
+
const CURRENT_VERSION_CHOICE = "current";
|
|
30
32
|
function AgentDuplicateOutcomeDialog({
|
|
31
33
|
open,
|
|
32
34
|
onOpenChange,
|
|
@@ -34,7 +36,8 @@ function AgentDuplicateOutcomeDialog({
|
|
|
34
36
|
newAgentName,
|
|
35
37
|
newAgentPath,
|
|
36
38
|
errorMessage,
|
|
37
|
-
asSystem = false
|
|
39
|
+
asSystem = false,
|
|
40
|
+
chooser
|
|
38
41
|
}) {
|
|
39
42
|
const isMobile = useIsMobile();
|
|
40
43
|
const router = useRouter();
|
|
@@ -52,9 +55,42 @@ function AgentDuplicateOutcomeDialog({
|
|
|
52
55
|
const handleStayHere = () => {
|
|
53
56
|
onOpenChange(false);
|
|
54
57
|
};
|
|
55
|
-
const titleText = state === "loading" ? "Duplicating agent\u2026" : state === "success" ? asSystem ? "System agent duplicated" : "Agent duplicated" : "Duplicate failed";
|
|
56
|
-
const descriptionText = state === "loading" ? "Creating a copy with all messages, variables, settings, and tools." : state === "success" ? newAgentName ? `"${newAgentName}" is ready. Where would you like to go?` : "The copy is ready. Where would you like to go?" : errorMessage ?? "Something went wrong while duplicating.";
|
|
58
|
+
const titleText = state === "choose" ? "Duplicate agent" : state === "loading" ? "Duplicating agent\u2026" : state === "success" ? asSystem ? "System agent duplicated" : "Agent duplicated" : "Duplicate failed";
|
|
59
|
+
const descriptionText = state === "choose" ? "Pick the version to copy into a new agent." : state === "loading" ? "Creating a copy with all messages, variables, settings, and tools." : state === "success" ? newAgentName ? `"${newAgentName}" is ready. Where would you like to go?` : "The copy is ready. Where would you like to go?" : errorMessage ?? "Something went wrong while duplicating.";
|
|
57
60
|
const body = /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
61
|
+
state === "choose" && chooser && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
Select,
|
|
64
|
+
{
|
|
65
|
+
"aria-label": "Version to copy",
|
|
66
|
+
className: "w-full",
|
|
67
|
+
value: chooser.selected,
|
|
68
|
+
onValueChange: chooser.onSelectedChange,
|
|
69
|
+
options: [
|
|
70
|
+
{
|
|
71
|
+
value: CURRENT_VERSION_CHOICE,
|
|
72
|
+
label: chooser.currentVersion != null ? `Current version (v${chooser.currentVersion})` : "Current version"
|
|
73
|
+
},
|
|
74
|
+
...chooser.versions.filter((v) => v.versionNumber !== chooser.currentVersion).map((v) => ({ value: v.versionId, label: versionOptionLabel(v) }))
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
chooser.versionsLoading && /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
79
|
+
/* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }),
|
|
80
|
+
"Loading versions\u2026"
|
|
81
|
+
] }),
|
|
82
|
+
chooser.versionsError && /* @__PURE__ */ jsxs("p", { className: "text-xs text-destructive", children: [
|
|
83
|
+
"Past versions could not be loaded: ",
|
|
84
|
+
chooser.versionsError
|
|
85
|
+
] }),
|
|
86
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
87
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: handleStayHere, children: "Cancel" }),
|
|
88
|
+
/* @__PURE__ */ jsxs(Button, { onClick: chooser.onConfirm, children: [
|
|
89
|
+
/* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" }),
|
|
90
|
+
"Duplicate"
|
|
91
|
+
] })
|
|
92
|
+
] })
|
|
93
|
+
] }),
|
|
58
94
|
state === "loading" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 py-6", children: [
|
|
59
95
|
/* @__PURE__ */ jsx(Loader2, { className: "h-10 w-10 animate-spin text-primary" }),
|
|
60
96
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Please wait\u2026" })
|
|
@@ -137,6 +173,16 @@ function AgentDuplicateOutcomeDialog({
|
|
|
137
173
|
body
|
|
138
174
|
] }) });
|
|
139
175
|
}
|
|
176
|
+
function versionOptionLabel(v) {
|
|
177
|
+
const date = new Date(v.changedAt).toLocaleDateString(void 0, {
|
|
178
|
+
month: "short",
|
|
179
|
+
day: "numeric",
|
|
180
|
+
year: "numeric"
|
|
181
|
+
});
|
|
182
|
+
const note = v.changeNote?.trim();
|
|
183
|
+
return note ? `v${v.versionNumber} \xB7 ${date} \xB7 ${note.length > 40 ? `${note.slice(0, 40)}\u2026` : note}` : `v${v.versionNumber} \xB7 ${date}`;
|
|
184
|
+
}
|
|
140
185
|
export {
|
|
141
|
-
AgentDuplicateOutcomeDialog
|
|
186
|
+
AgentDuplicateOutcomeDialog,
|
|
187
|
+
CURRENT_VERSION_CHOICE
|
|
142
188
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useAppDispatch, useAppSelector } from "../../../store/hooks.js";
|
|
4
4
|
import { applyOrganizationContextHeader } from "../../../host/server/organization-context.js";
|
|
5
|
-
import { getBuilderDoor
|
|
5
|
+
import { getBuilderDoor } from "../../../host/builder-door.js";
|
|
6
6
|
import { invalidateAgentCache } from "../../redux/agent-definition/invalidate-agent-cache.thunk.js";
|
|
7
7
|
import { selectAgentById } from "../../redux/agent-definition/selectors.js";
|
|
8
8
|
import { useOpenAgentSettingsWindow } from "../../../host/window-openers.js";
|
|
@@ -21,7 +21,7 @@ import { useOpenAgentInterfaceVariationsWindow } from "../../../host/window-open
|
|
|
21
21
|
import { useOpenSaveTemplateDialog } from "../../../host/window-openers.js";
|
|
22
22
|
const SAVE_AS_TEMPLATE_LABEL = "Save as template";
|
|
23
23
|
import { useCallback, useState } from "react";
|
|
24
|
-
import {
|
|
24
|
+
import { Link } from "../../../host/navigation.js";
|
|
25
25
|
import {
|
|
26
26
|
MoreHorizontal,
|
|
27
27
|
FileText,
|
|
@@ -66,17 +66,11 @@ import {
|
|
|
66
66
|
} from "@ai-matrx/design-system";
|
|
67
67
|
import { Drawer, DrawerContent, DrawerTitle } from "@ai-matrx/design-system";
|
|
68
68
|
import { MenuTapButton } from "@ai-matrx/tap-target/buttons";
|
|
69
|
-
import {
|
|
70
|
-
AgentDuplicateOutcomeDialog
|
|
71
|
-
} from "./AgentDuplicateOutcomeDialog.js";
|
|
69
|
+
import { useAgentDuplicateFlow } from "../../hooks/useAgentDuplicateFlow.js";
|
|
72
70
|
import { ReferenceCopyMenuItem } from "../../../host/ui-slots.js";
|
|
73
|
-
import {
|
|
74
|
-
isAdminSystemAgentsContext
|
|
75
|
-
} from "./agent-route-context.js";
|
|
76
71
|
import { useAgentLifecycleActions } from "../../lifecycle/useAgentLifecycleActions.js";
|
|
77
|
-
import { getUserMessage } from "@ai-matrx/agents/matrx";
|
|
78
72
|
import { selectIsSuperAdmin } from "../../../host/identity.js";
|
|
79
|
-
import { selectOrganizationId
|
|
73
|
+
import { selectOrganizationId } from "../../../host/org.js";
|
|
80
74
|
import { Button, Tile } from "@ai-matrx/design-system/controls";
|
|
81
75
|
const INTERFACE_VARIATIONS = [
|
|
82
76
|
"Full Modal",
|
|
@@ -182,7 +176,6 @@ function AgentOptionsMenu({
|
|
|
182
176
|
const [isConverting, setIsConverting] = useState(false);
|
|
183
177
|
const [isRefreshingCache, setIsRefreshingCache] = useState(false);
|
|
184
178
|
const dispatch = useAppDispatch();
|
|
185
|
-
const pathname = usePathname();
|
|
186
179
|
const openSettings = useOpenAgentSettingsWindow();
|
|
187
180
|
const openRunHistory = useOpenAgentRunHistoryWindow();
|
|
188
181
|
const openAdvancedEditor = useOpenAgentContentWindow();
|
|
@@ -197,45 +190,13 @@ function AgentOptionsMenu({
|
|
|
197
190
|
const openAdminFindUsages = useOpenAgentAdminFindUsagesWindow();
|
|
198
191
|
const openImport = useOpenAgentImportWindow();
|
|
199
192
|
const openInterfaceVariations = useOpenAgentInterfaceVariationsWindow();
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
);
|
|
205
|
-
const [duplicatedAgentName, setDuplicatedAgentName] = useState("");
|
|
206
|
-
const [duplicateError, setDuplicateError] = useState("");
|
|
207
|
-
const [duplicateAsSystem, setDuplicateAsSystem] = useState(false);
|
|
193
|
+
const duplicateFlow = useAgentDuplicateFlow(agentId, {
|
|
194
|
+
basePath,
|
|
195
|
+
fallbackSuffix: ""
|
|
196
|
+
});
|
|
208
197
|
const agent = useAppSelector((state) => selectAgentById(state, agentId));
|
|
209
198
|
const isBuiltin = agent?.agentType === "builtin";
|
|
210
|
-
const
|
|
211
|
-
const newAgentPath = duplicatedAgentId ? (() => {
|
|
212
|
-
const sourceSegment = `${basePath}/${agentId}`;
|
|
213
|
-
const suffix = pathname && pathname.startsWith(sourceSegment) ? pathname.slice(sourceSegment.length) : "";
|
|
214
|
-
return `${basePath}/${duplicatedAgentId}${suffix}`;
|
|
215
|
-
})() : null;
|
|
216
|
-
const runDuplicate = useCallback(async () => {
|
|
217
|
-
const asSystem = isAdminContext && isBuiltin;
|
|
218
|
-
setDuplicateAsSystem(asSystem);
|
|
219
|
-
setDuplicatedAgentId(null);
|
|
220
|
-
setDuplicatedAgentName(agent?.name ? `Copy of ${agent.name}` : "");
|
|
221
|
-
setDuplicateError("");
|
|
222
|
-
setDuplicateState("loading");
|
|
223
|
-
setDuplicateOpen(true);
|
|
224
|
-
try {
|
|
225
|
-
const newId = await dispatch(
|
|
226
|
-
requireBuilderDoor().duplicateAgent({ agentId, asSystem })
|
|
227
|
-
).unwrap();
|
|
228
|
-
setDuplicatedAgentId(newId);
|
|
229
|
-
setDuplicateState("success");
|
|
230
|
-
} catch (err) {
|
|
231
|
-
if (isOrganizationSelectionCancelled(err)) {
|
|
232
|
-
setDuplicateOpen(false);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
setDuplicateError(getUserMessage(err));
|
|
236
|
-
setDuplicateState("error");
|
|
237
|
-
}
|
|
238
|
-
}, [agent?.name, agentId, dispatch, isAdminContext, isBuiltin]);
|
|
199
|
+
const runDuplicate = duplicateFlow.openChooser;
|
|
239
200
|
const hasBuilderDoor = getBuilderDoor() !== null;
|
|
240
201
|
const managementItems = AGENT_MANAGEMENT_ITEMS.filter(
|
|
241
202
|
(item) => (!isBuiltin || item.label !== "Convert to Template") && (hasBuilderDoor || item.label !== "Duplicate")
|
|
@@ -395,18 +356,7 @@ function AgentOptionsMenu({
|
|
|
395
356
|
setOpen(false);
|
|
396
357
|
};
|
|
397
358
|
const trigger = /* @__PURE__ */ jsx(MenuTapButton, { variant: "transparent" });
|
|
398
|
-
const duplicateDialog =
|
|
399
|
-
AgentDuplicateOutcomeDialog,
|
|
400
|
-
{
|
|
401
|
-
open: duplicateOpen,
|
|
402
|
-
onOpenChange: setDuplicateOpen,
|
|
403
|
-
state: duplicateState,
|
|
404
|
-
newAgentName: duplicatedAgentName,
|
|
405
|
-
newAgentPath,
|
|
406
|
-
errorMessage: duplicateError,
|
|
407
|
-
asSystem: duplicateAsSystem
|
|
408
|
-
}
|
|
409
|
-
);
|
|
359
|
+
const duplicateDialog = duplicateFlow.dialog;
|
|
410
360
|
if (isMobile) {
|
|
411
361
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
412
362
|
/* @__PURE__ */ jsxs(Drawer, { open, onOpenChange: setOpen, children: [
|
|
@@ -518,7 +468,7 @@ function AgentOptionsMenu({
|
|
|
518
468
|
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
519
469
|
/* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-[10px] uppercase tracking-wider text-muted-foreground/60 font-semibold", children: "Manage" }),
|
|
520
470
|
managementItems.map(({ label, icon: Icon, soon }) => {
|
|
521
|
-
const duplicateInFlight =
|
|
471
|
+
const duplicateInFlight = duplicateFlow.isDuplicating;
|
|
522
472
|
const isLoading = label === "Convert to Template" && isConverting || label === "Duplicate" && duplicateInFlight;
|
|
523
473
|
const displayLabel = label === "Convert to Template" && isConverting ? "Saving..." : label === "Duplicate" && duplicateInFlight ? "Duplicating..." : label;
|
|
524
474
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../../../host/notify.js";
|
|
3
5
|
import { useEffect, useState } from "react";
|
|
4
6
|
import { Activity, X, Copy } from "lucide-react";
|
|
5
7
|
import { cn } from "@ai-matrx/design-system";
|
|
6
8
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
7
9
|
function StreamProfilerOverlay() {
|
|
10
|
+
const { copyText } = useClipboard({
|
|
11
|
+
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
12
|
+
});
|
|
8
13
|
const [isOpen, setIsOpen] = useState(false);
|
|
9
14
|
const [reports, setReports] = useState([]);
|
|
10
15
|
useEffect(() => {
|
|
@@ -29,7 +34,7 @@ function StreamProfilerOverlay() {
|
|
|
29
34
|
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(X, {}), "aria-label": "Close", onClick: () => setIsOpen(false) })
|
|
30
35
|
] }),
|
|
31
36
|
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: reports.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center text-slate-500 italic py-8", children: "No stream data collected yet. Trigger a prompt." }) : reports.map((r, i) => /* @__PURE__ */ jsxs("div", { className: "bg-slate-900 border border-slate-800 rounded-lg p-3 space-y-2 relative group", children: [
|
|
32
|
-
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(Copy, {}), onClick: () =>
|
|
37
|
+
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(Copy, {}), onClick: () => copyText(JSON.stringify(r, null, 2)), title: "Copy JSON", "aria-label": "Copy JSON", className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100" }),
|
|
33
38
|
/* @__PURE__ */ jsx("div", { className: "text-blue-400 font-bold mb-2", children: r.Test }),
|
|
34
39
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4 gap-y-1 text-xs", children: [
|
|
35
40
|
/* @__PURE__ */ jsx("div", { className: "text-slate-500", children: "Client Duration:" }),
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
interface UseAgentDuplicateFlowOptions {
|
|
2
2
|
basePath?: string;
|
|
3
|
+
/** Sub-route the copy opens on when the source is not the current page. */
|
|
4
|
+
fallbackSuffix?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface StartDuplicateOptions {
|
|
7
|
+
/** Copy this saved version (definition_version id). Omitted = the current agent. */
|
|
8
|
+
versionId?: string;
|
|
3
9
|
}
|
|
4
10
|
/**
|
|
5
|
-
* Shared duplicate flow for agent surfaces (options menu,
|
|
6
|
-
* floating "create my copy" chip). Returns
|
|
11
|
+
* Shared duplicate flow for agent surfaces (options menu, versions tab,
|
|
12
|
+
* read-only builder save, floating "create my copy" chip). Returns triggers +
|
|
13
|
+
* the one dialog element:
|
|
14
|
+
* - `startDuplicate()` copies right away (current agent, or `{ versionId }`).
|
|
15
|
+
* - `openChooser()` first asks which version to copy, defaulting to current.
|
|
7
16
|
*/
|
|
8
17
|
export declare function useAgentDuplicateFlow(agentId: string, options?: UseAgentDuplicateFlowOptions): {
|
|
9
|
-
readonly startDuplicate: () => Promise<void>;
|
|
18
|
+
readonly startDuplicate: (opts?: StartDuplicateOptions) => Promise<void>;
|
|
19
|
+
readonly openChooser: () => Promise<void>;
|
|
10
20
|
readonly dialog: import("react").JSX.Element;
|
|
11
21
|
readonly isDuplicating: boolean;
|
|
12
22
|
/** False in a host that registered no builder door: nothing offers a duplicate (reported once). */
|