@ai-matrx/chat 0.2.10 → 0.3.3
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 +30 -0
- package/dist/agent-copy/clipboard.d.ts +2 -4
- package/dist/agent-copy/clipboard.js +1 -14
- package/dist/agents/components/context-items/bodies/WebpageBody.js +1 -1
- package/dist/agents/components/context-preview/AttachedContextSection.js +9 -8
- package/dist/agents/components/context-preview/ContextCompareView.js +13 -13
- package/dist/agents/components/context-preview/ContextPreviewPanel.js +13 -13
- package/dist/agents/components/debug/SandboxFileViewer.js +1 -1
- package/dist/agents/components/debug/StreamDebugPanel.js +2 -2
- package/dist/agents/components/messages-display/TranscriptIntegrityCopyButton.js +1 -1
- package/dist/agents/components/observational-memory/components/MemoryStateInspector.js +4 -4
- package/dist/agents/components/run-controls/AgentExecutionTestModal.js +2 -2
- package/dist/agents/components/run-controls/PayloadTab.js +2 -1
- package/dist/agents/components/run-controls/panels/BackendTargetPanel.js +2 -1
- package/dist/agents/components/shared/AgentSaveStatus.js +22 -1
- package/dist/agents/hooks/useAgentAutoSave.js +22 -5
- package/dist/agents/prompt-preview/PromptPreviewContent.js +1 -1
- package/dist/agents/redux/agent-definition/slice.js +1 -0
- package/dist/agents/redux/agent-settings/agentSettingsSlice.d.ts +3 -0
- package/dist/agents/redux/agent-settings/agentSettingsSlice.js +12 -2
- package/dist/agents/redux/agent-settings/types.d.ts +7 -0
- package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateCore.js +2 -1
- package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateList.js +2 -1
- package/dist/agents/redux/execution-system/messages/messages.selectors.d.ts +10 -0
- package/dist/agents/redux/execution-system/messages/messages.selectors.js +31 -1
- package/dist/agents/redux/execution-system/thunks/process-stream.js +17 -0
- package/dist/agents/redux/execution-system/thunks/terminal-request-failure.d.ts +27 -0
- package/dist/agents/redux/execution-system/thunks/terminal-request-failure.js +56 -0
- package/dist/cx-chat/components/messages/AssistantMessage.js +1 -1
- package/dist/cx-chat/components/messages/UserMessage.js +2 -1
- package/dist/cx-conversation/AssistantMessage.js +1 -1
- package/dist/cx-conversation/UserMessage.js +2 -1
- package/dist/host/db-types.d.ts +15 -12
- package/dist/host/ui-slots.d.ts +2 -4
- package/dist/host/ui-slots.js +10 -5
- package/dist/public-chat/components/PublicMessageOptionsMenu.js +7 -7
- package/dist/surfaces/services/surface-config.service.d.ts +3 -1
- package/dist/surfaces/services/surface-config.service.js +33 -17
- package/dist/tool-call-visualization/components/ToolTabBodies.d.ts +0 -4
- package/dist/tool-call-visualization/components/ToolTabBodies.js +2 -21
- package/dist/voice-agent/components/VoiceDebugPanel.js +1 -1
- package/dist/window-panels/windows/agents/ObservationalMemoryWindow.js +2 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.3.3 — 2026-10-07
|
|
4
|
+
### Re-sync before matrx-frontend consumes the published package (P27)
|
|
5
|
+
|
|
6
|
+
- Carries every
|
|
7
|
+
matrx-frontend `packages/chat` edit after `7f630d97d9`: a server-ended failed request is a failed turn
|
|
8
|
+
(`thunks/terminal-request-failure`, process-stream, messages selectors), agent autosave recovery.
|
|
9
|
+
Its suite needs the app's content-ir host, so it lives app-side with the other host suites.
|
|
10
|
+
- Consumer action: none.
|
|
11
|
+
|
|
12
|
+
## 0.3.2
|
|
13
|
+
|
|
14
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
15
|
+
`git diff npm/chat/v0.2.10..npm/chat/v0.3.2 -- apps/shared/chat`).
|
|
16
|
+
No source changes intended and no consumer action required.
|
|
17
|
+
|
|
18
|
+
## 0.3.1 — 2026-10-07
|
|
19
|
+
### Re-sync before matrx-frontend consumes the published package (P27)
|
|
20
|
+
|
|
21
|
+
- Carries every matrx-frontend `packages/chat` edit after `636e7ef2dd` (context-preview copy, agent-settings
|
|
22
|
+
`offeringId`, surface-config service, db-types) and `surfaces/lib/ui-surfaces-agent-writes` +
|
|
23
|
+
`ui-surfaces-scope` (+ suite), which earlier re-syncs missed; the app imports both.
|
|
24
|
+
- Consumer action: none.
|
|
25
|
+
|
|
26
|
+
## 0.3.0 — 2026-10-07
|
|
27
|
+
|
|
28
|
+
ALC-19: the chat clipboard slot is content-shaped.
|
|
29
|
+
|
|
30
|
+
- Host UI slot `copyToClipboard` (`host/ui-slots`) is renamed `copyContent(content, options)`; its stand-in copies a string as text (anything else as JSON) through `@ai-matrx/kit/clipboard` and calls `options.onError` on failure. `agent-copy/clipboard` no longer exports the failure-swallowing `writeClipboard` (only `approxTokens`).
|
|
31
|
+
- Consumer action: register `copyContent` (not `copyToClipboard`) in `registerChatUi`; import `copyContent` from `@ai-matrx/chat/host/ui-slots`; replace any `writeClipboard` import with `@ai-matrx/kit/clipboard` `copyText`.
|
|
32
|
+
|
|
3
33
|
## 0.2.10 — 2026-10-07
|
|
4
34
|
### Re-sync before matrx-frontend consumes the published package (P27)
|
|
5
35
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared
|
|
3
|
-
*
|
|
4
|
-
* never re-roll a clipboard write or a token estimate at a callsite.
|
|
2
|
+
* Shared payload-size helper for the agent-copy primitives. Clipboard writes go through
|
|
3
|
+
* `@ai-matrx/kit/clipboard`; never re-roll a token estimate at a callsite.
|
|
5
4
|
*/
|
|
6
|
-
export declare function writeClipboard(text: string): Promise<void>;
|
|
7
5
|
/** Rough token estimate (chars / 4) — same convention as the Groomer window. */
|
|
8
6
|
export declare function approxTokens(text: string): number;
|
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
async function writeClipboard(text) {
|
|
2
|
-
try {
|
|
3
|
-
await navigator.clipboard.writeText(text);
|
|
4
|
-
} catch {
|
|
5
|
-
const textarea = document.createElement("textarea");
|
|
6
|
-
textarea.value = text;
|
|
7
|
-
document.body.appendChild(textarea);
|
|
8
|
-
textarea.select();
|
|
9
|
-
document.execCommand("copy");
|
|
10
|
-
document.body.removeChild(textarea);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
1
|
function approxTokens(text) {
|
|
14
2
|
return Math.ceil(text.length / 4);
|
|
15
3
|
}
|
|
16
4
|
export {
|
|
17
|
-
approxTokens
|
|
18
|
-
writeClipboard
|
|
5
|
+
approxTokens
|
|
19
6
|
};
|
|
@@ -32,7 +32,7 @@ import { setConversationDocumentEnabledThunk } from "../../redux/execution-syste
|
|
|
32
32
|
import { setScratchpadGateThunk } from "../../redux/execution-system/instance-working-document/scratchpad.thunks.js";
|
|
33
33
|
import { useActiveContextLayerItems } from "../context-items/useActiveContextLayerItems.js";
|
|
34
34
|
import { docKindForContextKey } from "../../utils/workingDocumentContext.js";
|
|
35
|
-
import {
|
|
35
|
+
import { CopyButtons } from "../../../host/ui-slots.js";
|
|
36
36
|
import { cn } from "@ai-matrx/design-system";
|
|
37
37
|
import { contextEntryLabel } from "../context-policies-display/contextEntryLabel.js";
|
|
38
38
|
const COPY_REVEAL = "opacity-0 transition-opacity pointer-coarse:opacity-100";
|
|
@@ -196,11 +196,12 @@ function AttachedContextSection({
|
|
|
196
196
|
k
|
|
197
197
|
)) }),
|
|
198
198
|
/* @__PURE__ */ jsx(
|
|
199
|
-
|
|
199
|
+
CopyButtons,
|
|
200
200
|
{
|
|
201
|
-
|
|
201
|
+
label: "Copy",
|
|
202
|
+
json: variables,
|
|
202
203
|
size: "xs",
|
|
203
|
-
className: cn(COPY_REVEAL, "group-hover/vars:opacity-100")
|
|
204
|
+
className: cn("absolute top-1 right-1 z-10", COPY_REVEAL, "group-hover/vars:opacity-100")
|
|
204
205
|
}
|
|
205
206
|
)
|
|
206
207
|
] }),
|
|
@@ -285,12 +286,12 @@ function AttachedContextSection({
|
|
|
285
286
|
/* @__PURE__ */ jsx("span", { className: "italic text-muted-foreground", children: "(empty \u2014 sent, but has no content yet)" })
|
|
286
287
|
) }),
|
|
287
288
|
/* @__PURE__ */ jsx(
|
|
288
|
-
|
|
289
|
+
CopyButtons,
|
|
289
290
|
{
|
|
290
|
-
|
|
291
|
-
|
|
291
|
+
label: "Copy",
|
|
292
|
+
human: item.preview,
|
|
292
293
|
size: "xs",
|
|
293
|
-
className: cn(COPY_REVEAL, "group-hover/entry:opacity-100")
|
|
294
|
+
className: cn("absolute top-1 right-1 z-10", COPY_REVEAL, "group-hover/entry:opacity-100")
|
|
294
295
|
}
|
|
295
296
|
)
|
|
296
297
|
] }, item.key);
|
|
@@ -9,7 +9,7 @@ import { Skeleton } from "@ai-matrx/design-system";
|
|
|
9
9
|
import { Badge, Button, Chip } from "@ai-matrx/design-system/controls";
|
|
10
10
|
import { Textarea } from "@ai-matrx/design-system";
|
|
11
11
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/tabs.js";
|
|
12
|
-
import {
|
|
12
|
+
import { CopyButtons } from "../../../host/ui-slots.js";
|
|
13
13
|
import { RichContent } from "../../../host/ui-slots.js";
|
|
14
14
|
import { SystemItemsLine } from "./SystemItemsLine.js";
|
|
15
15
|
import { useAppDispatch, useAppSelector } from "../../../store/hooks.js";
|
|
@@ -109,12 +109,12 @@ function Block({
|
|
|
109
109
|
) : block ? /* @__PURE__ */ jsxs("div", { className: "group/block relative mt-1.5", children: [
|
|
110
110
|
/* @__PURE__ */ jsx("pre", { "data-kind-source": "explicit", className: "whitespace-pre-wrap break-words rounded-md border border-border bg-muted/40 px-2.5 py-2 font-mono text-[11px] leading-relaxed text-foreground", children: block }),
|
|
111
111
|
/* @__PURE__ */ jsx(
|
|
112
|
-
|
|
112
|
+
CopyButtons,
|
|
113
113
|
{
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
label: "Copy",
|
|
115
|
+
human: block,
|
|
116
116
|
size: "sm",
|
|
117
|
-
className: "opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
117
|
+
className: "absolute top-1 right-1 z-10 opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
118
118
|
}
|
|
119
119
|
)
|
|
120
120
|
] }) : /* @__PURE__ */ jsx("div", { className: "mt-1.5 rounded-md border border-border bg-muted/30 px-2.5 py-2 text-xs italic text-muted-foreground", children: "No block \u2014 this side delivers no context for this selection." }),
|
|
@@ -141,12 +141,12 @@ function FedBlock({ label, text, slot }) {
|
|
|
141
141
|
text ? /* @__PURE__ */ jsxs("div", { className: "group/block relative mt-1", children: [
|
|
142
142
|
/* @__PURE__ */ jsx("pre", { "data-kind-source": "explicit", className: "max-h-[28rem] overflow-auto whitespace-pre-wrap break-words rounded-md border border-border bg-muted/40 px-2.5 py-2 font-mono text-[11px] leading-relaxed text-foreground", children: text }),
|
|
143
143
|
/* @__PURE__ */ jsx(
|
|
144
|
-
|
|
144
|
+
CopyButtons,
|
|
145
145
|
{
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
label: "Copy",
|
|
147
|
+
human: text,
|
|
148
148
|
size: "sm",
|
|
149
|
-
className: "opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
149
|
+
className: "absolute top-1 right-1 z-10 opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
150
150
|
}
|
|
151
151
|
)
|
|
152
152
|
] }) : /* @__PURE__ */ jsxs("p", { className: "mt-1 rounded-md border border-border bg-muted/30 px-2.5 py-2 text-xs italic text-muted-foreground", children: [
|
|
@@ -278,12 +278,12 @@ function JsonBlock({ label, value, slot }) {
|
|
|
278
278
|
/* @__PURE__ */ jsxs("div", { className: "group/block relative mt-1", children: [
|
|
279
279
|
/* @__PURE__ */ jsx("pre", { "data-kind-source": "explicit", className: "overflow-auto whitespace-pre-wrap break-all rounded-md border border-border bg-muted/40 px-2.5 py-2 font-mono text-[11px] leading-relaxed text-foreground", children: text }),
|
|
280
280
|
/* @__PURE__ */ jsx(
|
|
281
|
-
|
|
281
|
+
CopyButtons,
|
|
282
282
|
{
|
|
283
|
-
|
|
284
|
-
|
|
283
|
+
label: "Copy",
|
|
284
|
+
human: text,
|
|
285
285
|
size: "sm",
|
|
286
|
-
className: "opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
286
|
+
className: "absolute top-1 right-1 z-10 opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
287
287
|
}
|
|
288
288
|
)
|
|
289
289
|
] })
|
|
@@ -5,7 +5,7 @@ import { AlertTriangle, Braces, FileCode2, RefreshCw } from "lucide-react";
|
|
|
5
5
|
import { cn } from "@ai-matrx/design-system";
|
|
6
6
|
import { Skeleton } from "@ai-matrx/design-system";
|
|
7
7
|
import { Badge, Button, Chip, SegmentedControl } from "@ai-matrx/design-system/controls";
|
|
8
|
-
import {
|
|
8
|
+
import { CopyButtons } from "../../../host/ui-slots.js";
|
|
9
9
|
import { useAppSelector } from "../../../store/hooks.js";
|
|
10
10
|
import { SurfaceRuntimeProvider } from "../../../surfaces/runtime/SurfaceRuntimeContext.js";
|
|
11
11
|
import {
|
|
@@ -68,12 +68,12 @@ function VariableGroup({
|
|
|
68
68
|
] }),
|
|
69
69
|
/* @__PURE__ */ jsx("div", { className: "mt-1 whitespace-pre-wrap break-words rounded bg-muted/40 px-2 py-1 font-mono text-[11px] leading-relaxed text-foreground/90", children: f.value || /* @__PURE__ */ jsx("span", { className: "italic text-muted-foreground", children: "(empty)" }) }),
|
|
70
70
|
/* @__PURE__ */ jsx(
|
|
71
|
-
|
|
71
|
+
CopyButtons,
|
|
72
72
|
{
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
label: "Copy",
|
|
74
|
+
human: f.value,
|
|
75
75
|
size: "xs",
|
|
76
|
-
className: "opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/var:opacity-100"
|
|
76
|
+
className: "absolute top-1 right-1 z-10 opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/var:opacity-100"
|
|
77
77
|
}
|
|
78
78
|
)
|
|
79
79
|
] }, key);
|
|
@@ -201,12 +201,12 @@ function ResolvedView({
|
|
|
201
201
|
className: "relative h-7 w-7 shrink-0",
|
|
202
202
|
title: "Copy everything the agent receives",
|
|
203
203
|
children: /* @__PURE__ */ jsx(
|
|
204
|
-
|
|
204
|
+
CopyButtons,
|
|
205
205
|
{
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
label: "Copy all for AI",
|
|
207
|
+
human: copyAllText,
|
|
208
208
|
size: "sm",
|
|
209
|
-
|
|
209
|
+
className: "absolute top-1 right-1 z-10"
|
|
210
210
|
}
|
|
211
211
|
)
|
|
212
212
|
}
|
|
@@ -286,12 +286,12 @@ function ResolvedBody({
|
|
|
286
286
|
block ? /* @__PURE__ */ jsxs("div", { className: "group/block relative mt-1.5", children: [
|
|
287
287
|
/* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap break-words rounded-md border border-primary/20 bg-muted/40 px-2.5 py-2 font-mono text-[11px] leading-relaxed text-foreground", children: block }),
|
|
288
288
|
/* @__PURE__ */ jsx(
|
|
289
|
-
|
|
289
|
+
CopyButtons,
|
|
290
290
|
{
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
label: "Copy",
|
|
292
|
+
human: block,
|
|
293
293
|
size: "sm",
|
|
294
|
-
className: "opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
294
|
+
className: "absolute top-1 right-1 z-10 opacity-0 transition-opacity pointer-coarse:opacity-100 group-hover/block:opacity-100"
|
|
295
295
|
}
|
|
296
296
|
)
|
|
297
297
|
] }) : /* @__PURE__ */ jsx("div", { className: "mt-1.5 rounded-md border border-border bg-muted/30 px-2.5 py-2 text-xs italic text-muted-foreground", children: "No block would be injected \u2014 no organization or scopes active." }),
|
|
@@ -50,7 +50,7 @@ function SandboxFileViewer({
|
|
|
50
50
|
}, [initialPath, sandboxRowId, load]);
|
|
51
51
|
const handleCopy = async () => {
|
|
52
52
|
if (content == null) return;
|
|
53
|
-
await copyText(content);
|
|
53
|
+
if (!await copyText(content)) return;
|
|
54
54
|
setCopied(true);
|
|
55
55
|
toast.success("File contents copied");
|
|
56
56
|
setTimeout(() => setCopied(false), 1500);
|
|
@@ -55,8 +55,8 @@ function useCopy() {
|
|
|
55
55
|
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
56
56
|
});
|
|
57
57
|
const [copied, setCopied] = useState(null);
|
|
58
|
-
const copy = (text, id) => {
|
|
59
|
-
copyText(text);
|
|
58
|
+
const copy = async (text, id) => {
|
|
59
|
+
if (!await copyText(text)) return;
|
|
60
60
|
setCopied(id);
|
|
61
61
|
setTimeout(() => setCopied(null), 1500);
|
|
62
62
|
};
|
|
@@ -68,7 +68,7 @@ function TranscriptIntegrityCopyButtonInner({
|
|
|
68
68
|
effectiveVisibleGroupLimit
|
|
69
69
|
});
|
|
70
70
|
try {
|
|
71
|
-
await copyText(formatTranscriptIntegrityReport(report));
|
|
71
|
+
if (!await copyText(formatTranscriptIntegrityReport(report))) return;
|
|
72
72
|
toast.success(
|
|
73
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`
|
|
74
74
|
);
|
|
@@ -271,9 +271,9 @@ function BufferedObservationCard({
|
|
|
271
271
|
const [copied, setCopied] = useState(false);
|
|
272
272
|
const text = cycle.observations ?? "";
|
|
273
273
|
const hasText = text.length > 0;
|
|
274
|
-
const handleCopy = useCallback(() => {
|
|
274
|
+
const handleCopy = useCallback(async () => {
|
|
275
275
|
if (!hasText) return;
|
|
276
|
-
copyText(text);
|
|
276
|
+
if (!await copyText(text)) return;
|
|
277
277
|
setCopied(true);
|
|
278
278
|
setTimeout(() => setCopied(false), 1500);
|
|
279
279
|
}, [text, hasText]);
|
|
@@ -508,9 +508,9 @@ function TextPayload({
|
|
|
508
508
|
});
|
|
509
509
|
const [copied, setCopied] = useState(false);
|
|
510
510
|
const hasText = typeof text === "string" && text.length > 0;
|
|
511
|
-
const handleCopy = useCallback(() => {
|
|
511
|
+
const handleCopy = useCallback(async () => {
|
|
512
512
|
if (!text) return;
|
|
513
|
-
copyText(text);
|
|
513
|
+
if (!await copyText(text)) return;
|
|
514
514
|
setCopied(true);
|
|
515
515
|
setTimeout(() => setCopied(false), 1500);
|
|
516
516
|
}, [text]);
|
|
@@ -91,9 +91,9 @@ function DirectTestMode({
|
|
|
91
91
|
close,
|
|
92
92
|
conversationId
|
|
93
93
|
]);
|
|
94
|
-
const handleCopy = useCallback(() => {
|
|
94
|
+
const handleCopy = useCallback(async () => {
|
|
95
95
|
if (responseText) {
|
|
96
|
-
copyText(kindTextToMarkdown(responseText));
|
|
96
|
+
if (!await copyText(kindTextToMarkdown(responseText))) return;
|
|
97
97
|
setCopied(true);
|
|
98
98
|
setTimeout(() => setCopied(false), 2e3);
|
|
99
99
|
}
|
|
@@ -168,7 +168,8 @@ function PayloadTab({ conversationId }) {
|
|
|
168
168
|
if (!request) return;
|
|
169
169
|
try {
|
|
170
170
|
const text = JSON.stringify(request, null, 2);
|
|
171
|
-
void copyText(text).then(() => {
|
|
171
|
+
void copyText(text).then((copied2) => {
|
|
172
|
+
if (!copied2) return;
|
|
172
173
|
setCopied(true);
|
|
173
174
|
window.setTimeout(() => setCopied(false), 1500);
|
|
174
175
|
});
|
|
@@ -26,7 +26,8 @@ function CopyableValue({
|
|
|
26
26
|
});
|
|
27
27
|
const [copied, setCopied] = useState(false);
|
|
28
28
|
const handleCopy = () => {
|
|
29
|
-
void copyText(value).then(() => {
|
|
29
|
+
void copyText(value).then((copied2) => {
|
|
30
|
+
if (!copied2) return;
|
|
30
31
|
setCopied(true);
|
|
31
32
|
setTimeout(() => setCopied(false), 1200);
|
|
32
33
|
});
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Badge, Button } from "@ai-matrx/design-system/controls";
|
|
4
4
|
import { useState } from "react";
|
|
5
|
-
import { Save, Loader2, AlertTriangle, Eye } from "lucide-react";
|
|
5
|
+
import { Save, Loader2, AlertTriangle, Eye, Undo2 } from "lucide-react";
|
|
6
|
+
import { useAppDispatch } from "../../../store/hooks.js";
|
|
7
|
+
import { resetAllAgentFields } from "../../redux/agent-definition/slice.js";
|
|
6
8
|
import { useChatCanvasTab } from "../../../host/canvas.js";
|
|
7
9
|
import { AGENT_UNSAVED_CHANGES_KIND } from "../../../host/canvas-tabs.js";
|
|
8
10
|
import { cn } from "@ai-matrx/design-system";
|
|
@@ -41,7 +43,9 @@ function AgentSaveStatus({
|
|
|
41
43
|
reachBadge,
|
|
42
44
|
available
|
|
43
45
|
} = useAgentSaveAction(agentId, { editModeOverride });
|
|
46
|
+
const dispatch = useAppDispatch();
|
|
44
47
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
48
|
+
const [confirmDiscardOpen, setConfirmDiscardOpen] = useState(false);
|
|
45
49
|
const diffTab = useChatCanvasTab({ kind: AGENT_UNSAVED_CHANGES_KIND, key: agentId });
|
|
46
50
|
const handleSelectModel = () => {
|
|
47
51
|
setShowModelWarning(false);
|
|
@@ -67,6 +71,7 @@ function AgentSaveStatus({
|
|
|
67
71
|
isEditMode && isDirty && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
68
72
|
/* @__PURE__ */ jsx(Badge, { tone: "warning", children: isNewRoute ? "Not saved" : "Unsaved" }),
|
|
69
73
|
/* @__PURE__ */ jsx(ErrorAlchemyMenu, {}),
|
|
74
|
+
!isNewRoute && /* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(Undo2, {}), onClick: () => setConfirmDiscardOpen(true), disabled: isLoading, title: "Discard unsaved changes", "aria-label": "Discard unsaved changes" }),
|
|
70
75
|
!isNewRoute && /* @__PURE__ */ jsx(Button, { variant: "quiet", pressed: diffTab.isVisible, icon: /* @__PURE__ */ jsx(Eye, {}), onClick: () => diffTab.toggle({ title: "Unsaved changes", data: { agentId } }), title: "View unsaved changes", "aria-label": "View unsaved changes" })
|
|
71
76
|
] }),
|
|
72
77
|
isEditMode && /* @__PURE__ */ jsx(Button, { variant: "quiet", icon: isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }) : /* @__PURE__ */ jsx(Save, {}), onClick: handleSave, disabled: !canSave, title: isReadOnlySave ? "View only \u2014 create your copy to save changes" : isNewRoute ? "Save new agent" : isDirty ? "Save changes" : "No unsaved changes", "aria-label": isReadOnlySave ? "View only \u2014 create your copy to save changes" : isNewRoute ? "Save new agent" : isDirty ? "Save changes" : "No unsaved changes" }),
|
|
@@ -90,6 +95,22 @@ function AgentSaveStatus({
|
|
|
90
95
|
/* @__PURE__ */ jsx(AlertDialogAction, { onClick: handleSelectModel, children: "Select a Model" })
|
|
91
96
|
] })
|
|
92
97
|
] }) }),
|
|
98
|
+
/* @__PURE__ */ jsx(AlertDialog, { open: confirmDiscardOpen, onOpenChange: setConfirmDiscardOpen, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
99
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
100
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: "Discard unsaved changes?" }),
|
|
101
|
+
/* @__PURE__ */ jsx(AlertDialogDescription, { children: "The agent goes back to its last saved version." })
|
|
102
|
+
] }),
|
|
103
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
|
|
104
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { children: "Keep editing" }),
|
|
105
|
+
/* @__PURE__ */ jsx(
|
|
106
|
+
AlertDialogAction,
|
|
107
|
+
{
|
|
108
|
+
onClick: () => dispatch(resetAllAgentFields({ id: agentId })),
|
|
109
|
+
children: "Discard"
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
] })
|
|
113
|
+
] }) }),
|
|
93
114
|
/* @__PURE__ */ jsx(
|
|
94
115
|
AgentSettingsModal,
|
|
95
116
|
{
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
selectAgentIsReadOnly,
|
|
9
9
|
selectAgentAccessResolved
|
|
10
10
|
} from "../redux/agent-definition/selectors.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
resetAllAgentFields,
|
|
13
|
+
setAgentField
|
|
14
|
+
} from "../redux/agent-definition/slice.js";
|
|
12
15
|
import { readField } from "@ai-matrx/agents/field-flags";
|
|
13
16
|
import { toast } from "../../host/notify.js";
|
|
14
17
|
const STORAGE_PREFIX = "agent-autosave:";
|
|
@@ -65,7 +68,16 @@ function useAgentAutoSave(agentId) {
|
|
|
65
68
|
if (restored.length > 0) {
|
|
66
69
|
restoredRef.current = true;
|
|
67
70
|
toast.info(
|
|
68
|
-
`Restored unsaved changes to ${record.name || "this agent"} from this browser (${restored.join(", ")}). Save to keep them, or
|
|
71
|
+
`Restored unsaved changes to ${record.name || "this agent"} from this browser (${restored.join(", ")}). Save to keep them, or discard them.`,
|
|
72
|
+
{
|
|
73
|
+
id: `agent-autosave-restored:${agentId}`,
|
|
74
|
+
duration: 2e4,
|
|
75
|
+
action: {
|
|
76
|
+
label: "Discard",
|
|
77
|
+
// Back to the saved agent; the clean-state effect drops the backup.
|
|
78
|
+
onClick: () => dispatch(resetAllAgentFields({ id: agentId }))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
69
81
|
);
|
|
70
82
|
}
|
|
71
83
|
}, [agentId, record, fetchStatus, dispatch]);
|
|
@@ -78,9 +90,14 @@ function useAgentAutoSave(agentId) {
|
|
|
78
90
|
try {
|
|
79
91
|
const snapshot = { _dirty: true };
|
|
80
92
|
if (record._dirtyFields) {
|
|
81
|
-
|
|
82
|
-
record._dirtyFields
|
|
83
|
-
)
|
|
93
|
+
const fields = new Set(
|
|
94
|
+
Object.keys(record._dirtyFields)
|
|
95
|
+
);
|
|
96
|
+
if (fields.has("modelId") || fields.has("settings")) {
|
|
97
|
+
fields.add("modelId");
|
|
98
|
+
fields.add("settings");
|
|
99
|
+
}
|
|
100
|
+
for (const field of fields) {
|
|
84
101
|
snapshot[field] = readField(record, field);
|
|
85
102
|
}
|
|
86
103
|
}
|
|
@@ -44,7 +44,7 @@ function PromptPreviewContent({
|
|
|
44
44
|
}, [conversationId, store, nonce]);
|
|
45
45
|
const copy = async (label, text) => {
|
|
46
46
|
try {
|
|
47
|
-
await copyText(text);
|
|
47
|
+
if (!await copyText(text)) return;
|
|
48
48
|
toast.success(`${label} copied`);
|
|
49
49
|
} catch {
|
|
50
50
|
toast.error("Couldn't copy");
|
|
@@ -182,6 +182,7 @@ function pushUndoEntry(record, field, previousValue) {
|
|
|
182
182
|
function applyFieldEdit(record, field, value) {
|
|
183
183
|
const previousValue = record[field];
|
|
184
184
|
const wasDirty = hasField(record._dirtyFields, field);
|
|
185
|
+
if (!wasDirty && isEqual(value, previousValue)) return;
|
|
185
186
|
if (!wasDirty) {
|
|
186
187
|
record._fieldHistory[field] = previousValue;
|
|
187
188
|
}
|
|
@@ -579,6 +579,7 @@ export declare const requestModelSwitch: import("@reduxjs/toolkit").AsyncThunk<{
|
|
|
579
579
|
newModelControls: import("./types.js").NormalizedControls;
|
|
580
580
|
mode: ResolutionMode;
|
|
581
581
|
customActions: {};
|
|
582
|
+
offeringId?: string | undefined;
|
|
582
583
|
};
|
|
583
584
|
newDefaults: Partial<{
|
|
584
585
|
model?: string | undefined;
|
|
@@ -831,6 +832,8 @@ export declare const requestModelSwitch: import("@reduxjs/toolkit").AsyncThunk<{
|
|
|
831
832
|
}, {
|
|
832
833
|
agentId: string;
|
|
833
834
|
newModelId: string;
|
|
835
|
+
/** The new model's class picked in the same gesture (applied on confirm). */
|
|
836
|
+
offeringId?: string;
|
|
834
837
|
}, import("@reduxjs/toolkit").AsyncThunkConfig>;
|
|
835
838
|
/**
|
|
836
839
|
* Save agent settings to the database.
|
|
@@ -154,7 +154,11 @@ const loadAgentSettingsDirect = createAsyncThunk(
|
|
|
154
154
|
);
|
|
155
155
|
const requestModelSwitch = createAsyncThunk(
|
|
156
156
|
"agentSettings/requestModelSwitch",
|
|
157
|
-
async ({
|
|
157
|
+
async ({
|
|
158
|
+
agentId,
|
|
159
|
+
newModelId,
|
|
160
|
+
offeringId
|
|
161
|
+
}, { getState, rejectWithValue }) => {
|
|
158
162
|
const state = getState();
|
|
159
163
|
const newModel = selectModelById(state, newModelId);
|
|
160
164
|
if (!newModel) {
|
|
@@ -200,7 +204,8 @@ const requestModelSwitch = createAsyncThunk(
|
|
|
200
204
|
// new model does not carry natively (settings-translation K7). Reset
|
|
201
205
|
// or remove is the person's explicit choice in the dialog.
|
|
202
206
|
mode: "keep_all",
|
|
203
|
-
customActions: {}
|
|
207
|
+
customActions: {},
|
|
208
|
+
...offeringId ? { offeringId } : {}
|
|
204
209
|
},
|
|
205
210
|
newDefaults
|
|
206
211
|
};
|
|
@@ -433,6 +438,11 @@ const agentSettingsSlice = createSlice({
|
|
|
433
438
|
...resolved,
|
|
434
439
|
model: pending.newModelId
|
|
435
440
|
};
|
|
441
|
+
if (pending.offeringId) {
|
|
442
|
+
finalSettings.offering_id = pending.offeringId;
|
|
443
|
+
} else {
|
|
444
|
+
delete finalSettings.offering_id;
|
|
445
|
+
}
|
|
436
446
|
if (entry.context === "builder") {
|
|
437
447
|
entry.defaults = finalSettings;
|
|
438
448
|
} else {
|
|
@@ -67,6 +67,13 @@ export interface PendingModelSwitch {
|
|
|
67
67
|
mode: ResolutionMode;
|
|
68
68
|
/** Per-conflict override when mode === 'custom' */
|
|
69
69
|
customActions: Partial<Record<keyof AgentSettings, ConflictAction>>;
|
|
70
|
+
/**
|
|
71
|
+
* The CLASS (offering) picked with the new model, applied on confirm.
|
|
72
|
+
* Absent = no class picked → the old model's pin is dropped on confirm (a
|
|
73
|
+
* class belongs to exactly one model). Never written before confirm, so the
|
|
74
|
+
* entry never pairs the old model with the new model's class.
|
|
75
|
+
*/
|
|
76
|
+
offeringId?: string;
|
|
70
77
|
}
|
|
71
78
|
export interface AgentVariable {
|
|
72
79
|
name: string;
|
package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateCore.js
CHANGED
|
@@ -25,7 +25,8 @@ function useCopyText(text) {
|
|
|
25
25
|
});
|
|
26
26
|
const [copied, setCopied] = useState(false);
|
|
27
27
|
const copy = () => {
|
|
28
|
-
copyText(text).then(() => {
|
|
28
|
+
copyText(text).then((copied2) => {
|
|
29
|
+
if (!copied2) return;
|
|
29
30
|
setCopied(true);
|
|
30
31
|
setTimeout(() => setCopied(false), 1500);
|
|
31
32
|
});
|
package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateList.js
CHANGED
|
@@ -26,7 +26,8 @@ function useCopyText(text) {
|
|
|
26
26
|
});
|
|
27
27
|
const [copied, setCopied] = useState(false);
|
|
28
28
|
const copy = () => {
|
|
29
|
-
copyText(text).then(() => {
|
|
29
|
+
copyText(text).then((copied2) => {
|
|
30
|
+
if (!copied2) return;
|
|
30
31
|
setCopied(true);
|
|
31
32
|
setTimeout(() => setCopied(false), 1500);
|
|
32
33
|
});
|
|
@@ -169,6 +169,16 @@ export declare function extractInspectableText(record: MessageRecord | undefined
|
|
|
169
169
|
* CONVERSATION_FAILURE_AND_RETRY_FE_GUIDE.md.
|
|
170
170
|
*/
|
|
171
171
|
export declare function isFailedRecord(record: MessageRecord | undefined): boolean;
|
|
172
|
+
/**
|
|
173
|
+
* The provider stop reason a reloaded turn was saved with, when it is NOT a
|
|
174
|
+
* normal finish — or null. A turn the provider stopped early can be saved as
|
|
175
|
+
* an ordinary `active` message with its partial text and only
|
|
176
|
+
* `metadata.finish_reason` telling the truth (a recitation stop, 2026-10-06);
|
|
177
|
+
* that row reloaded with no error at all.
|
|
178
|
+
*/
|
|
179
|
+
export declare function abnormalFinishReason(record: MessageRecord | undefined): string | null;
|
|
180
|
+
/** The sentence a reloaded early-stopped turn shows. */
|
|
181
|
+
export declare function earlyStopMessage(reason: string): string;
|
|
172
182
|
/**
|
|
173
183
|
* The persisted error text for a failed turn. Prefers the structured top-level
|
|
174
184
|
* `error.message`; falls back to the legacy `metadata.error` string, then to
|
|
@@ -282,7 +282,33 @@ function isFailedRecord(record) {
|
|
|
282
282
|
if (record.status === "failed") return true;
|
|
283
283
|
if (record.error) return true;
|
|
284
284
|
const md = record.metadata;
|
|
285
|
-
|
|
285
|
+
if (!!md && typeof md === "object" && !Array.isArray(md) && md.failed === true) {
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
return abnormalFinishReason(record) !== null;
|
|
289
|
+
}
|
|
290
|
+
const NORMAL_FINISH_REASONS = /* @__PURE__ */ new Set([
|
|
291
|
+
"stop",
|
|
292
|
+
"end_turn",
|
|
293
|
+
"stop_sequence",
|
|
294
|
+
"tool_use",
|
|
295
|
+
"tool_calls",
|
|
296
|
+
"function_call",
|
|
297
|
+
"pause_turn",
|
|
298
|
+
"completed",
|
|
299
|
+
"success"
|
|
300
|
+
]);
|
|
301
|
+
function abnormalFinishReason(record) {
|
|
302
|
+
if (!record || record.role !== "assistant") return null;
|
|
303
|
+
const md = record.metadata;
|
|
304
|
+
if (!md || typeof md !== "object" || Array.isArray(md)) return null;
|
|
305
|
+
const reason = md.finish_reason;
|
|
306
|
+
if (typeof reason !== "string" || reason.length === 0) return null;
|
|
307
|
+
const normalized = reason.toLowerCase().replace(/^finishreason\./, "");
|
|
308
|
+
return NORMAL_FINISH_REASONS.has(normalized) ? null : normalized;
|
|
309
|
+
}
|
|
310
|
+
function earlyStopMessage(reason) {
|
|
311
|
+
return reason === "max_tokens" || reason === "length" ? "The response hit its length limit before it finished." : "The response was stopped early by the model provider, so it may be incomplete.";
|
|
286
312
|
}
|
|
287
313
|
function extractRecordError(record) {
|
|
288
314
|
if (!record) return void 0;
|
|
@@ -294,6 +320,8 @@ function extractRecordError(record) {
|
|
|
294
320
|
const err = md.error;
|
|
295
321
|
if (typeof err === "string" && err.length > 0) return err;
|
|
296
322
|
}
|
|
323
|
+
const stopped = abnormalFinishReason(record);
|
|
324
|
+
if (stopped) return earlyStopMessage(stopped);
|
|
297
325
|
const flat = extractFlatText(record);
|
|
298
326
|
return flat.length > 0 ? flat : void 0;
|
|
299
327
|
}
|
|
@@ -545,6 +573,8 @@ const selectMessagesInterleavedRuns = (conversationId, messageIds) => {
|
|
|
545
573
|
};
|
|
546
574
|
export {
|
|
547
575
|
EMPTY_CONVERSATION_MESSAGES,
|
|
576
|
+
abnormalFinishReason,
|
|
577
|
+
earlyStopMessage,
|
|
548
578
|
extractAnswerDocumentText,
|
|
549
579
|
extractContentBlocks,
|
|
550
580
|
extractFlatText,
|
|
@@ -69,6 +69,10 @@ import {
|
|
|
69
69
|
appendCitation
|
|
70
70
|
} from "../active-requests/active-requests.slice.js";
|
|
71
71
|
import { parseNormalizedCitation } from "../messages/message-citations.js";
|
|
72
|
+
import {
|
|
73
|
+
terminalFailureFromCompletion,
|
|
74
|
+
terminalFailureFromRecordUpdate
|
|
75
|
+
} from "./terminal-request-failure.js";
|
|
72
76
|
import { confirmServerSync } from "../conversations/conversations.slice.js";
|
|
73
77
|
import { receivedFsChange } from "../../../../host/ui-slots.js";
|
|
74
78
|
import {
|
|
@@ -430,6 +434,17 @@ async function processStream({
|
|
|
430
434
|
terminalTransportController.abort("post-terminal-grace");
|
|
431
435
|
}, POST_TERMINAL_GRACE_MS);
|
|
432
436
|
};
|
|
437
|
+
const failRequestFromTerminalStatus = (error) => {
|
|
438
|
+
if (!error) return;
|
|
439
|
+
if (getState().activeRequests.byRequestId[requestId]?.status === "error") {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
console.warn(
|
|
443
|
+
`[stream:${requestId.slice(0, 8)}] user request ended FAILED without an error event (${error.error_type}) \u2014 surfacing it as a failed turn.`
|
|
444
|
+
);
|
|
445
|
+
dispatch(setRequestStatus({ requestId, status: "error", error }));
|
|
446
|
+
dispatch(setInstanceStatus({ conversationId, status: "error" }));
|
|
447
|
+
};
|
|
433
448
|
let lastTransportSeq = getState().activeRequests.byRequestId[requestId]?.lastTransportSeq ?? 0;
|
|
434
449
|
let transportStreamId = getState().activeRequests.byRequestId[requestId]?.transportStreamId ?? null;
|
|
435
450
|
let retainedEntry;
|
|
@@ -635,6 +650,7 @@ async function processStream({
|
|
|
635
650
|
}
|
|
636
651
|
if (d.operation === "user_request") {
|
|
637
652
|
dispatch(setCompletion({ requestId, data: d }));
|
|
653
|
+
failRequestFromTerminalStatus(terminalFailureFromCompletion(d));
|
|
638
654
|
completionStats = result;
|
|
639
655
|
const totals = completionStats.total_usage?.total;
|
|
640
656
|
if (totals) {
|
|
@@ -1664,6 +1680,7 @@ async function processStream({
|
|
|
1664
1680
|
refreshUserMessageWithoutBody(d.record_id);
|
|
1665
1681
|
}
|
|
1666
1682
|
} else if (d.table === "user_request") {
|
|
1683
|
+
failRequestFromTerminalStatus(terminalFailureFromRecordUpdate(d));
|
|
1667
1684
|
dispatch(
|
|
1668
1685
|
patchUserRequest({
|
|
1669
1686
|
id: d.record_id,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A request that ENDED FAILED without an `error` event.
|
|
3
|
+
*
|
|
4
|
+
* The server has three ways to say a user request failed, and only one of
|
|
5
|
+
* them is the `error` event: a terminal failure it persisted on purpose (a
|
|
6
|
+
* provider safety / recitation stop, a model refusal, any finish-reason
|
|
7
|
+
* failure) is announced as a `user_request` completion with
|
|
8
|
+
* `status: "failed"` and/or a `record_update` moving the `user_request` row
|
|
9
|
+
* to `failed`, then a normal `end`. Before this module the stream treated
|
|
10
|
+
* both as bookkeeping, the `end` handler marked the request `complete`, and
|
|
11
|
+
* the turn showed NO error — while its half-written content (a flashcard set
|
|
12
|
+
* cut off by a recitation stop, 2026-10-06) sat there looking finished.
|
|
13
|
+
*
|
|
14
|
+
* Pure: reads the event payload, returns the `ErrorPayload` the request
|
|
15
|
+
* should carry, or null when the event is not a terminal failure. It never
|
|
16
|
+
* matches on the message TEXT (the server is free to reword it) — only on
|
|
17
|
+
* the operation/table, the status, and the structured fields.
|
|
18
|
+
*/
|
|
19
|
+
import type { CompletionPayload, ErrorPayload, RecordUpdatePayload } from "@ai-matrx/agents/generated/stream-events";
|
|
20
|
+
/** error_type when the server sent no structured one. */
|
|
21
|
+
export declare const TERMINAL_FAILURE_ERROR_TYPE = "request_failed";
|
|
22
|
+
/** Shown when the server said "failed" and nothing more. */
|
|
23
|
+
export declare const TERMINAL_FAILURE_FALLBACK_MESSAGE = "The response stopped before it finished.";
|
|
24
|
+
/** A `user_request` completion whose status is not success. */
|
|
25
|
+
export declare function terminalFailureFromCompletion(d: CompletionPayload): ErrorPayload | null;
|
|
26
|
+
/** A `record_update` moving the `user_request` row to `failed`. */
|
|
27
|
+
export declare function terminalFailureFromRecordUpdate(d: RecordUpdatePayload): ErrorPayload | null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const TERMINAL_FAILURE_ERROR_TYPE = "request_failed";
|
|
2
|
+
const TERMINAL_FAILURE_FALLBACK_MESSAGE = "The response stopped before it finished.";
|
|
3
|
+
function asRecord(value) {
|
|
4
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
5
|
+
}
|
|
6
|
+
function readStructuredError(raw) {
|
|
7
|
+
if (typeof raw === "string") {
|
|
8
|
+
const trimmed = raw.trim();
|
|
9
|
+
if (trimmed.startsWith("{")) {
|
|
10
|
+
try {
|
|
11
|
+
return readStructuredError(JSON.parse(trimmed));
|
|
12
|
+
} catch {
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return { errorType: null, message: trimmed || null };
|
|
16
|
+
}
|
|
17
|
+
const obj = asRecord(raw);
|
|
18
|
+
if (!obj) return { errorType: null, message: null };
|
|
19
|
+
const errorType = typeof obj.error_type === "string" ? obj.error_type : typeof obj.type === "string" ? obj.type : null;
|
|
20
|
+
const message = typeof obj.user_message === "string" && obj.user_message ? obj.user_message : typeof obj.message === "string" && obj.message ? obj.message : null;
|
|
21
|
+
return { errorType, message };
|
|
22
|
+
}
|
|
23
|
+
function buildPayload(source, fields, finishReason) {
|
|
24
|
+
const structured = readStructuredError(fields?.error);
|
|
25
|
+
const errorType = structured.errorType ?? (typeof fields?.error_type === "string" ? fields.error_type : null) ?? TERMINAL_FAILURE_ERROR_TYPE;
|
|
26
|
+
const message = structured.message ?? TERMINAL_FAILURE_FALLBACK_MESSAGE;
|
|
27
|
+
return {
|
|
28
|
+
error_type: errorType,
|
|
29
|
+
message,
|
|
30
|
+
user_message: message,
|
|
31
|
+
details: {
|
|
32
|
+
source,
|
|
33
|
+
...finishReason ? { finish_reason: finishReason } : {},
|
|
34
|
+
...typeof fields?.reason === "string" ? { reason: fields.reason } : {}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function terminalFailureFromCompletion(d) {
|
|
39
|
+
if (d.operation !== "user_request" || d.status !== "failed") return null;
|
|
40
|
+
const result = asRecord(d.result);
|
|
41
|
+
const metadata = asRecord(result?.metadata);
|
|
42
|
+
const finishReason = typeof result?.finish_reason === "string" ? result.finish_reason : null;
|
|
43
|
+
return buildPayload("completion", metadata ?? result, finishReason);
|
|
44
|
+
}
|
|
45
|
+
function terminalFailureFromRecordUpdate(d) {
|
|
46
|
+
if (d.table !== "user_request" || d.status !== "failed") return null;
|
|
47
|
+
const metadata = asRecord(d.metadata);
|
|
48
|
+
const finishReason = typeof metadata?.finish_reason === "string" ? metadata.finish_reason : null;
|
|
49
|
+
return buildPayload("record_update", metadata, finishReason);
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
TERMINAL_FAILURE_ERROR_TYPE,
|
|
53
|
+
TERMINAL_FAILURE_FALLBACK_MESSAGE,
|
|
54
|
+
terminalFailureFromCompletion,
|
|
55
|
+
terminalFailureFromRecordUpdate
|
|
56
|
+
};
|
|
@@ -185,7 +185,7 @@ function AssistantMessage({
|
|
|
185
185
|
variant: "quiet",
|
|
186
186
|
onClick: async () => {
|
|
187
187
|
if (!audioUrl) return;
|
|
188
|
-
await copyText(audioUrl);
|
|
188
|
+
if (!await copyText(audioUrl)) return;
|
|
189
189
|
setIsAudioLinkCopied(true);
|
|
190
190
|
setTimeout(() => setIsAudioLinkCopied(false), 2e3);
|
|
191
191
|
},
|
|
@@ -135,7 +135,8 @@ function UserMessage({
|
|
|
135
135
|
}, [isEditing]);
|
|
136
136
|
const handleCopy = (e) => {
|
|
137
137
|
e.stopPropagation();
|
|
138
|
-
copyText(message.content).then(() => {
|
|
138
|
+
copyText(message.content).then((copied) => {
|
|
139
|
+
if (!copied) return;
|
|
139
140
|
setIsCopied(true);
|
|
140
141
|
setTimeout(() => setIsCopied(false), 2e3);
|
|
141
142
|
});
|
|
@@ -185,7 +185,7 @@ function AssistantMessage({
|
|
|
185
185
|
variant: "quiet",
|
|
186
186
|
onClick: async () => {
|
|
187
187
|
if (!audioUrl) return;
|
|
188
|
-
await copyText(audioUrl);
|
|
188
|
+
if (!await copyText(audioUrl)) return;
|
|
189
189
|
setIsAudioLinkCopied(true);
|
|
190
190
|
setTimeout(() => setIsAudioLinkCopied(false), 2e3);
|
|
191
191
|
},
|
|
@@ -135,7 +135,8 @@ function UserMessage({
|
|
|
135
135
|
}, [isEditing]);
|
|
136
136
|
const handleCopy = (e) => {
|
|
137
137
|
e.stopPropagation();
|
|
138
|
-
copyText(message.content).then(() => {
|
|
138
|
+
copyText(message.content).then((copied) => {
|
|
139
|
+
if (!copied) return;
|
|
139
140
|
setIsCopied(true);
|
|
140
141
|
setTimeout(() => setIsCopied(false), 2e3);
|
|
141
142
|
});
|
package/dist/host/db-types.d.ts
CHANGED
|
@@ -18887,6 +18887,7 @@ export type ChatDatabase = {
|
|
|
18887
18887
|
container_side: string;
|
|
18888
18888
|
conveys_max: Database["public"]["Enums"]["permission_level"];
|
|
18889
18889
|
created_at: string;
|
|
18890
|
+
id: string;
|
|
18890
18891
|
is_active: boolean;
|
|
18891
18892
|
label: string | null;
|
|
18892
18893
|
notes: string | null;
|
|
@@ -18900,6 +18901,7 @@ export type ChatDatabase = {
|
|
|
18900
18901
|
container_side?: string;
|
|
18901
18902
|
conveys_max?: Database["public"]["Enums"]["permission_level"];
|
|
18902
18903
|
created_at?: string;
|
|
18904
|
+
id?: string;
|
|
18903
18905
|
is_active?: boolean;
|
|
18904
18906
|
label?: string | null;
|
|
18905
18907
|
notes?: string | null;
|
|
@@ -18913,6 +18915,7 @@ export type ChatDatabase = {
|
|
|
18913
18915
|
container_side?: string;
|
|
18914
18916
|
conveys_max?: Database["public"]["Enums"]["permission_level"];
|
|
18915
18917
|
created_at?: string;
|
|
18918
|
+
id?: string;
|
|
18916
18919
|
is_active?: boolean;
|
|
18917
18920
|
label?: string | null;
|
|
18918
18921
|
notes?: string | null;
|
|
@@ -38345,6 +38348,18 @@ export type ChatDatabase = {
|
|
|
38345
38348
|
isSetofReturn: true;
|
|
38346
38349
|
};
|
|
38347
38350
|
};
|
|
38351
|
+
get_pick_list_for_selection: {
|
|
38352
|
+
Args: {
|
|
38353
|
+
p_list_id: string;
|
|
38354
|
+
};
|
|
38355
|
+
Returns: Json;
|
|
38356
|
+
};
|
|
38357
|
+
get_pick_lists_summary: {
|
|
38358
|
+
Args: {
|
|
38359
|
+
p_user_id: string;
|
|
38360
|
+
};
|
|
38361
|
+
Returns: Json;
|
|
38362
|
+
};
|
|
38348
38363
|
get_project_members_with_users: {
|
|
38349
38364
|
Args: {
|
|
38350
38365
|
p_project_id: string;
|
|
@@ -38532,12 +38547,6 @@ export type ChatDatabase = {
|
|
|
38532
38547
|
};
|
|
38533
38548
|
Returns: Json;
|
|
38534
38549
|
};
|
|
38535
|
-
get_pick_list_for_selection: {
|
|
38536
|
-
Args: {
|
|
38537
|
-
p_list_id: string;
|
|
38538
|
-
};
|
|
38539
|
-
Returns: Json;
|
|
38540
|
-
};
|
|
38541
38550
|
get_tables_and_columns: {
|
|
38542
38551
|
Args: never;
|
|
38543
38552
|
Returns: {
|
|
@@ -38695,12 +38704,6 @@ export type ChatDatabase = {
|
|
|
38695
38704
|
};
|
|
38696
38705
|
Returns: Json;
|
|
38697
38706
|
};
|
|
38698
|
-
get_pick_lists_summary: {
|
|
38699
|
-
Args: {
|
|
38700
|
-
p_user_id: string;
|
|
38701
|
-
};
|
|
38702
|
-
Returns: Json;
|
|
38703
|
-
};
|
|
38704
38707
|
get_user_messages: {
|
|
38705
38708
|
Args: {
|
|
38706
38709
|
p_feedback_id: string;
|
package/dist/host/ui-slots.d.ts
CHANGED
|
@@ -163,7 +163,7 @@ export interface ChatUiSlots {
|
|
|
163
163
|
SystemInstructionModal: AnyComponent;
|
|
164
164
|
confirm: AnyFn;
|
|
165
165
|
copyRichContent: AnyFn;
|
|
166
|
-
|
|
166
|
+
copyContent: AnyFn;
|
|
167
167
|
useTablesEverywhere: AnyFn;
|
|
168
168
|
useTextareaFormatting: AnyFn;
|
|
169
169
|
useClipboardPaste: AnyFn;
|
|
@@ -354,7 +354,6 @@ export interface ChatUiSlots {
|
|
|
354
354
|
AccessGate: AnyComponent;
|
|
355
355
|
ReferenceCopyMenuItem: AnyComponent;
|
|
356
356
|
ReferenceCopyButton: AnyComponent;
|
|
357
|
-
InlineCopyButton: AnyComponent;
|
|
358
357
|
MandateNotesPanel: AnyComponent;
|
|
359
358
|
SurfaceBoundAgentsList: SlotComponent<AgentsListProps>;
|
|
360
359
|
ProposedDirectivesZone: AnyComponent;
|
|
@@ -508,7 +507,7 @@ export declare const WebpageSnapshotView: AnyComponent;
|
|
|
508
507
|
export declare function hostFn<K extends keyof ChatUiSlots>(name: K, fallback?: AnyFn): ChatUiSlots[K];
|
|
509
508
|
export declare const confirm: AnyFn;
|
|
510
509
|
export declare const copyRichContent: AnyFn;
|
|
511
|
-
export declare const
|
|
510
|
+
export declare const copyContent: AnyFn;
|
|
512
511
|
export declare const useTablesEverywhere: AnyFn;
|
|
513
512
|
export declare const useTextareaFormatting: AnyFn;
|
|
514
513
|
export declare const useClipboardPaste: AnyFn;
|
|
@@ -620,7 +619,6 @@ export declare const AnswerTextPreview: AnyComponent;
|
|
|
620
619
|
export declare const AccessGate: AnyComponent;
|
|
621
620
|
export declare const ReferenceCopyMenuItem: AnyComponent;
|
|
622
621
|
export declare const ReferenceCopyButton: AnyComponent;
|
|
623
|
-
export declare const InlineCopyButton: AnyComponent;
|
|
624
622
|
export declare const MandateNotesPanel: AnyComponent;
|
|
625
623
|
export declare const SurfaceBoundAgentsList: SlotComponent<AgentsListProps>;
|
|
626
624
|
export declare const ProposedDirectivesZone: AnyComponent;
|
package/dist/host/ui-slots.js
CHANGED
|
@@ -69,7 +69,14 @@ const failingThunk = (name) => () => () => {
|
|
|
69
69
|
const STAND_INS = {
|
|
70
70
|
confirm: async () => false,
|
|
71
71
|
copyRichContent: async () => false,
|
|
72
|
-
|
|
72
|
+
// Content-shaped like the host's `copyContent(content, options)`: a string copies as text, anything
|
|
73
|
+
// else as JSON. A failed copy is reported through `onError` and returns false, never swallowed.
|
|
74
|
+
copyContent: async (content, options) => {
|
|
75
|
+
const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
76
|
+
const landed = await copyText(text ?? "");
|
|
77
|
+
if (!landed) options?.onError?.(new Error("Failed to copy"));
|
|
78
|
+
return landed;
|
|
79
|
+
},
|
|
73
80
|
useTablesEverywhere: () => NONE,
|
|
74
81
|
useCenterControlFit: () => true,
|
|
75
82
|
connectorDefinitionFromMcp: () => null,
|
|
@@ -228,7 +235,7 @@ function hostFn(name, fallback) {
|
|
|
228
235
|
}
|
|
229
236
|
const confirm = slotFn("confirm");
|
|
230
237
|
const copyRichContent = slotFn("copyRichContent");
|
|
231
|
-
const
|
|
238
|
+
const copyContent = slotFn("copyContent");
|
|
232
239
|
const useTablesEverywhere = slotFn("useTablesEverywhere");
|
|
233
240
|
const useTextareaFormatting = slotFn("useTextareaFormatting", () => void 0);
|
|
234
241
|
const useClipboardPaste = slotFn("useClipboardPaste", () => void 0);
|
|
@@ -339,7 +346,6 @@ const AnswerTextPreview = slotComponent("AnswerTextPreview", unregisteredWidget(
|
|
|
339
346
|
const AccessGate = slotComponent("AccessGate", unregisteredWidget("AccessGate"));
|
|
340
347
|
const ReferenceCopyMenuItem = slotComponent("ReferenceCopyMenuItem", unregisteredWidget("ReferenceCopyMenuItem"));
|
|
341
348
|
const ReferenceCopyButton = slotComponent("ReferenceCopyButton", unregisteredWidget("ReferenceCopyButton"));
|
|
342
|
-
const InlineCopyButton = slotComponent("InlineCopyButton", unregisteredWidget("InlineCopyButton"));
|
|
343
349
|
const MandateNotesPanel = slotComponent("MandateNotesPanel", unregisteredWidget("MandateNotesPanel"));
|
|
344
350
|
const SurfaceBoundAgentsList = slotComponent("SurfaceBoundAgentsList", unregisteredWidget("SurfaceBoundAgentsList"));
|
|
345
351
|
const ProposedDirectivesZone = slotComponent("ProposedDirectivesZone", unregisteredWidget("ProposedDirectivesZone"));
|
|
@@ -548,7 +554,6 @@ export {
|
|
|
548
554
|
IconButton,
|
|
549
555
|
InPlaceEditor,
|
|
550
556
|
InfoHint,
|
|
551
|
-
InlineCopyButton,
|
|
552
557
|
InputCapabilitiesEditor,
|
|
553
558
|
ItemMenu,
|
|
554
559
|
ItemRow,
|
|
@@ -628,8 +633,8 @@ export {
|
|
|
628
633
|
confirm,
|
|
629
634
|
connectorDefinitionFromMcp,
|
|
630
635
|
convertMarkdownToHtml,
|
|
636
|
+
copyContent,
|
|
631
637
|
copyRichContent,
|
|
632
|
-
copyToClipboard,
|
|
633
638
|
createHtmlPage,
|
|
634
639
|
createSandboxFilesystemAdapter,
|
|
635
640
|
currentCostUnit,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
Share2
|
|
18
18
|
} from "lucide-react";
|
|
19
19
|
import { getMarkdownStylesheet } from "@ai-matrx/print/markdown";
|
|
20
|
-
import { AdvancedMenu,
|
|
20
|
+
import { AdvancedMenu, copyContent } from "../../host/ui-slots.js";
|
|
21
21
|
import { EmailInputDialog } from "../../host/ui-slots.js";
|
|
22
22
|
import { AuthGateDialog } from "../../host/ui-slots.js";
|
|
23
23
|
import { notesCreate } from "../../host/ui-slots.js";
|
|
@@ -125,7 +125,7 @@ const PublicMessageOptionsMenu = ({
|
|
|
125
125
|
onClose();
|
|
126
126
|
};
|
|
127
127
|
const handleCopyPlain = async () => {
|
|
128
|
-
await
|
|
128
|
+
await copyContent(content, {
|
|
129
129
|
onSuccess: () => {
|
|
130
130
|
},
|
|
131
131
|
onError: (error) => {
|
|
@@ -134,7 +134,7 @@ const PublicMessageOptionsMenu = ({
|
|
|
134
134
|
});
|
|
135
135
|
};
|
|
136
136
|
const handleCopyGoogleDocs = async () => {
|
|
137
|
-
await
|
|
137
|
+
await copyContent(content, {
|
|
138
138
|
isMarkdown: true,
|
|
139
139
|
formatForGoogleDocs: true,
|
|
140
140
|
onSuccess: () => {
|
|
@@ -147,7 +147,7 @@ const PublicMessageOptionsMenu = ({
|
|
|
147
147
|
});
|
|
148
148
|
};
|
|
149
149
|
const handleCopyWithThinking = async () => {
|
|
150
|
-
await
|
|
150
|
+
await copyContent(content, {
|
|
151
151
|
isMarkdown: true,
|
|
152
152
|
includeThinking: true,
|
|
153
153
|
onSuccess: () => {
|
|
@@ -167,7 +167,7 @@ const PublicMessageOptionsMenu = ({
|
|
|
167
167
|
toast.info("HTML preview not available here");
|
|
168
168
|
return;
|
|
169
169
|
}
|
|
170
|
-
await
|
|
170
|
+
await copyContent(content, {
|
|
171
171
|
isMarkdown: true,
|
|
172
172
|
formatForWordPress: true,
|
|
173
173
|
showHtmlPreview: true,
|
|
@@ -210,7 +210,7 @@ const PublicMessageOptionsMenu = ({
|
|
|
210
210
|
}
|
|
211
211
|
};
|
|
212
212
|
const handleCopyCompleteHTML = async () => {
|
|
213
|
-
await
|
|
213
|
+
await copyContent(content, {
|
|
214
214
|
isMarkdown: true,
|
|
215
215
|
formatForWordPress: true,
|
|
216
216
|
showHtmlPreview: true,
|
|
@@ -229,7 +229,7 @@ const PublicMessageOptionsMenu = ({
|
|
|
229
229
|
${filteredHtml}
|
|
230
230
|
</body>
|
|
231
231
|
</html>`;
|
|
232
|
-
await
|
|
232
|
+
await copyContent(completeHTML, {
|
|
233
233
|
onSuccess: () => {
|
|
234
234
|
},
|
|
235
235
|
onError: (error) => {
|
|
@@ -115,7 +115,9 @@ export declare function setRoleSelection(args: {
|
|
|
115
115
|
scope: PrefScopeInput;
|
|
116
116
|
}): Promise<void>;
|
|
117
117
|
/** Move one agent choice / roster item to Trash (restorable; re-selecting revives it). */
|
|
118
|
-
export declare function deleteRolePref(prefId: string
|
|
118
|
+
export declare function deleteRolePref(prefId: string, opts?: {
|
|
119
|
+
platformTier?: boolean;
|
|
120
|
+
}): Promise<void>;
|
|
119
121
|
export declare function addRosterItem(args: {
|
|
120
122
|
surfaceName: string;
|
|
121
123
|
roleName: string;
|
|
@@ -279,6 +279,9 @@ function matchScope(q, scope, tierOrganization) {
|
|
|
279
279
|
}
|
|
280
280
|
return q.is("user_id", null).is("scope_id", null).eq("organization_id", tierOrganization);
|
|
281
281
|
}
|
|
282
|
+
function withPlatformRoleAdminLane(request, on) {
|
|
283
|
+
return on ? request.setHeader("x-matrx-admin-lane", "1") : request;
|
|
284
|
+
}
|
|
282
285
|
async function setRoleSelection(args) {
|
|
283
286
|
const {
|
|
284
287
|
surfaceName,
|
|
@@ -291,33 +294,46 @@ async function setRoleSelection(args) {
|
|
|
291
294
|
const client = sb();
|
|
292
295
|
let q = client.schema("ui").from("ui_surface_agent_pref").select("id").eq("surface_name", surfaceName).eq("role_name", roleName).eq("kind", "selection").eq("position", position);
|
|
293
296
|
q = matchScope(q, scope, await tierOrganizationFor(scope));
|
|
294
|
-
const
|
|
297
|
+
const platformLane = scope.global === true;
|
|
298
|
+
const { data: existing, error: findErr } = await withPlatformRoleAdminLane(
|
|
299
|
+
q,
|
|
300
|
+
platformLane
|
|
301
|
+
).maybeSingle();
|
|
295
302
|
if (findErr) throw findErr;
|
|
296
303
|
if (existing) {
|
|
297
304
|
await writeOne(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
305
|
+
withPlatformRoleAdminLane(
|
|
306
|
+
client.schema("ui").from("ui_surface_agent_pref").update({
|
|
307
|
+
agent_id: agentId,
|
|
308
|
+
deleted_at: null,
|
|
309
|
+
...settings ? { settings } : {}
|
|
310
|
+
}).eq("id", existing.id).select("id"),
|
|
311
|
+
platformLane
|
|
312
|
+
),
|
|
303
313
|
{ action: "save", noun: "agent choice" }
|
|
304
314
|
);
|
|
305
315
|
return;
|
|
306
316
|
}
|
|
307
|
-
const { error } = await
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
const { error } = await withPlatformRoleAdminLane(
|
|
318
|
+
client.schema("ui").from("ui_surface_agent_pref").insert({
|
|
319
|
+
surface_name: surfaceName,
|
|
320
|
+
role_name: roleName,
|
|
321
|
+
agent_id: agentId,
|
|
322
|
+
kind: "selection",
|
|
323
|
+
position,
|
|
324
|
+
settings: settings ?? {},
|
|
325
|
+
...await scopeInsertColumns(scope)
|
|
326
|
+
}),
|
|
327
|
+
platformLane
|
|
328
|
+
);
|
|
316
329
|
if (error) throw error;
|
|
317
330
|
}
|
|
318
|
-
async function deleteRolePref(prefId) {
|
|
331
|
+
async function deleteRolePref(prefId, opts = {}) {
|
|
319
332
|
await writeOne(
|
|
320
|
-
|
|
333
|
+
withPlatformRoleAdminLane(
|
|
334
|
+
sb().schema("ui").from("ui_surface_agent_pref").update({ deleted_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", prefId).is("deleted_at", null).select("id"),
|
|
335
|
+
opts.platformTier === true
|
|
336
|
+
),
|
|
321
337
|
{ action: "remove", noun: "agent choice" }
|
|
322
338
|
);
|
|
323
339
|
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ToolLifecycleEntry } from "../../agents/types/request.types.js";
|
|
3
3
|
import type { ToolOverlayTabSpec, ToolRendererProps } from "../types.js";
|
|
4
|
-
export declare const CopyButton: React.FC<{
|
|
5
|
-
text: string;
|
|
6
|
-
className?: string;
|
|
7
|
-
}>;
|
|
8
4
|
export declare const InputView: React.FC<{
|
|
9
5
|
entry: ToolLifecycleEntry;
|
|
10
6
|
}>;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
-
import { toast } from "../../host/notify.js";
|
|
5
3
|
import { resultToHuman } from "../utils/human-copy.js";
|
|
6
|
-
import React
|
|
4
|
+
import React from "react";
|
|
7
5
|
import { useToolConversationId } from "./ToolConversationContext.js";
|
|
8
|
-
import {
|
|
6
|
+
import { CircleAlert, FileCode2, Route, Settings2 } from "lucide-react";
|
|
9
7
|
import { Badge } from "@ai-matrx/design-system";
|
|
10
8
|
import { CopyButtons } from "../../host/ui-slots.js";
|
|
11
9
|
import { JsonInspector } from "../../host/ui-slots.js";
|
|
@@ -21,7 +19,6 @@ import {
|
|
|
21
19
|
toolEntryBundleToHuman
|
|
22
20
|
} from "../utils/toolEntryBundle.js";
|
|
23
21
|
import { ErrorAlchemyMenu } from "../../host/ui-slots.js";
|
|
24
|
-
import { Button } from "@ai-matrx/design-system/controls";
|
|
25
22
|
function buildAgentInput(entry, description, data) {
|
|
26
23
|
return {
|
|
27
24
|
kind: "tool-result",
|
|
@@ -31,21 +28,6 @@ function buildAgentInput(entry, description, data) {
|
|
|
31
28
|
attributes: { tool: entry.toolName, status: entry.status }
|
|
32
29
|
};
|
|
33
30
|
}
|
|
34
|
-
const CopyButton = ({
|
|
35
|
-
text,
|
|
36
|
-
className
|
|
37
|
-
}) => {
|
|
38
|
-
const { copyText } = useClipboard({
|
|
39
|
-
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
40
|
-
});
|
|
41
|
-
const [copied, setCopied] = useState(false);
|
|
42
|
-
const handleCopy = async () => {
|
|
43
|
-
await copyText(text);
|
|
44
|
-
setCopied(true);
|
|
45
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
46
|
-
};
|
|
47
|
-
return /* @__PURE__ */ jsx(Button, { variant: "quiet", icon: copied ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(Copy, {}), glyphTone: copied ? "success" : void 0, onClick: handleCopy, title: copied ? "Copied!" : "Copy to clipboard", className, children: copied ? "Copied" : "Copy" });
|
|
48
|
-
};
|
|
49
31
|
const InputView = ({
|
|
50
32
|
entry
|
|
51
33
|
}) => {
|
|
@@ -221,7 +203,6 @@ const CustomOverlayBody = ({ entry, Component, conversationId: conversationIdPro
|
|
|
221
203
|
) }) });
|
|
222
204
|
};
|
|
223
205
|
export {
|
|
224
|
-
CopyButton,
|
|
225
206
|
CustomOverlayBody,
|
|
226
207
|
EntryResultsBody,
|
|
227
208
|
ErrorView,
|
|
@@ -43,7 +43,8 @@ function useCopyText(text) {
|
|
|
43
43
|
});
|
|
44
44
|
const [copied, setCopied] = useState(false);
|
|
45
45
|
const copy = useCallback(() => {
|
|
46
|
-
copyText(text).then(() => {
|
|
46
|
+
copyText(text).then((copied2) => {
|
|
47
|
+
if (!copied2) return;
|
|
47
48
|
setCopied(true);
|
|
48
49
|
setTimeout(() => setCopied(false), 1500);
|
|
49
50
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "THE one AI Matrx person-to-agent chat system: the full chat (running agents, conversations, tool calls, streaming, context, surfaces, voice) as per-module subpaths @ai-matrx/chat/<domain>/<module> over a host contract (/host, /host/react) whose only required value is a db; plus the kernel compatibility entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) until P28.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -202,23 +202,23 @@
|
|
|
202
202
|
"uuid": "^14.0.2",
|
|
203
203
|
"zod": "^4.6.5",
|
|
204
204
|
"@ai-matrx/agents": "latest",
|
|
205
|
-
"@ai-matrx/alchemy": "latest",
|
|
206
|
-
"@ai-matrx/associations": "latest",
|
|
207
205
|
"@ai-matrx/browser-audio": "latest",
|
|
206
|
+
"@ai-matrx/associations": "latest",
|
|
207
|
+
"@ai-matrx/alchemy": "latest",
|
|
208
208
|
"@ai-matrx/canvas": "latest",
|
|
209
209
|
"@ai-matrx/content-ir": "latest",
|
|
210
|
+
"@ai-matrx/content-ir-react": "latest",
|
|
210
211
|
"@ai-matrx/data": "latest",
|
|
211
|
-
"@ai-matrx/design-system": "latest",
|
|
212
212
|
"@ai-matrx/diff": "latest",
|
|
213
|
+
"@ai-matrx/design-system": "latest",
|
|
213
214
|
"@ai-matrx/icons": "latest",
|
|
214
|
-
"@ai-matrx/content-ir-react": "latest",
|
|
215
215
|
"@ai-matrx/media": "latest",
|
|
216
|
+
"@ai-matrx/kit": "latest",
|
|
216
217
|
"@ai-matrx/print": "latest",
|
|
217
218
|
"@ai-matrx/realtime": "latest",
|
|
218
|
-
"@ai-matrx/records": "latest",
|
|
219
219
|
"@ai-matrx/records-ui": "latest",
|
|
220
220
|
"@ai-matrx/tap-target": "latest",
|
|
221
|
-
"@ai-matrx/
|
|
221
|
+
"@ai-matrx/records": "latest"
|
|
222
222
|
},
|
|
223
223
|
"devDependencies": {
|
|
224
224
|
"@arethetypeswrong/cli": "^0.18.5",
|