@ai-matrx/chat 0.3.27 → 0.3.30
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 +20 -0
- package/dist/agents/components/chat/begin-fresh-chat.js +2 -2
- package/dist/agents/components/chat/sandbox-insight/SandboxActivityFeed.d.ts +0 -10
- package/dist/agents/components/chat/sandbox-insight/SandboxActivityFeed.js +2 -3
- package/dist/agents/components/context-policies-display/contextPolicyPreview.js +2 -1
- package/dist/agents/components/context-preview/AttachedContextSection.js +3 -2
- package/dist/agents/components/context-preview/ContextCompareView.js +2 -2
- package/dist/agents/components/inputs/smart-input/ComposerDraftNotice.js +2 -1
- package/dist/agents/components/inputs/smart-input/ConversationContextRail.js +0 -9
- package/dist/agents/components/observational-memory/components/MemoryStateInspector.js +3 -2
- package/dist/agents/components/run-controls/panels/ModelContextPanel.js +4 -4
- package/dist/agents/components/working-document/DocumentLinkPicker.d.ts +0 -8
- package/dist/agents/components/working-document/DocumentLinkPicker.js +2 -8
- package/dist/agents/hooks/useAgentLauncher.js +4 -3
- package/dist/agents/redux/execution-system/context-rules/request-context.js +5 -1
- package/dist/agents/utils/table-variable.js +2 -2
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +4 -4
- package/dist/cx-chat/hooks/useInstanceBootstrap.js +3 -1
- package/dist/host/db-types.d.ts +47 -1
- package/dist/host/defaults/webpage-snapshot-view.js +2 -1
- package/dist/host/ui-slots.d.ts +0 -13
- package/dist/host/ui-slots.js +2 -1
- package/dist/surfaces/utils/surface-check-ledger.js +2 -7
- package/dist/tool-call-visualization/registry/registry.js +2 -1
- package/dist/tool-call-visualization/renderers/ctx/CtxItemCard.d.ts +0 -15
- package/dist/tool-call-visualization/renderers/ctx/CtxItemCard.js +2 -5
- package/dist/tool-call-visualization/renderers/research/ResearchOverlay.d.ts +0 -15
- package/dist/tool-call-visualization/renderers/research/ResearchOverlay.js +2 -1
- package/dist/tool-call-visualization/renderers/scrape/ScrapeInline.d.ts +0 -30
- package/dist/tool-call-visualization/renderers/scrape/ScrapeInline.js +2 -1
- package/dist/tool-call-visualization/renderers/scrape/ScrapeOverlay.d.ts +0 -11
- package/dist/tool-call-visualization/renderers/scrape/ScrapeOverlay.js +3 -2
- package/dist/tool-call-visualization/result-fields/KeyValueGrid.d.ts +0 -33
- package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +2 -7
- package/dist/ui/mandates-storage/mandateStorage.d.ts +19 -0
- package/dist/ui/mandates-storage/shortcutStorage.d.ts +38 -0
- package/dist/ui/permission-levels.d.ts +8 -2
- package/dist/ui/permission-levels.js +12 -1
- package/package.json +8 -8
- package/dist/agents/components/shared/AgentDuplicateOutcomeDialog.d.ts +0 -57
- package/dist/agents/components/shared/AgentDuplicateOutcomeDialog.js +0 -188
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.3.30
|
|
4
|
+
|
|
5
|
+
The composer rail's Scratch pill no longer dead-ends: with no scratchpad attached it used to toast "Nothing to show in the canvas yet" and open nothing. It now opens the scratchpad canvas tab, whose body hydrates the active scratchpad and creates one when the pool is empty.
|
|
6
|
+
|
|
7
|
+
Consumer action: none.
|
|
8
|
+
|
|
9
|
+
## 0.3.29
|
|
10
|
+
|
|
11
|
+
Never reached npm: the tag commit failed CI typecheck on a content-ir-react error fixed right after. These changes ship in 0.3.30.
|
|
12
|
+
|
|
13
|
+
Package-twins collapse onto `@ai-matrx/kit`: character counts across context, memory, scrape and tool-call views use `formatCount` (compact where the view was compact); sandbox activity and context-compare durations use `formatDurationMs` compact; the document link picker and surface-check age use `formatRelativeTime` ("5h ago" / "5h" where they said "today"); reserved-conversation, table-variable and canvas conversation ids use `isUuidShape`. The `isUuidValue` slot's DEFAULT now matches the registered host rule (trimmed RFC 9562); hosts still override it.
|
|
14
|
+
|
|
15
|
+
Consumer action: none.
|
|
16
|
+
|
|
17
|
+
## 0.3.28
|
|
18
|
+
|
|
19
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
20
|
+
`git diff npm/chat/v0.3.27..npm/chat/v0.3.28 -- apps/shared/chat`).
|
|
21
|
+
No source changes intended and no consumer action required.
|
|
22
|
+
|
|
3
23
|
## 0.3.27
|
|
4
24
|
|
|
5
25
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { isUuidShape } from "@ai-matrx/kit/uuid";
|
|
2
3
|
import { clearFocus } from "../../redux/execution-system/conversation-focus/conversation-focus.slice.js";
|
|
3
4
|
import { resolveMandateAsking } from "../../../mandates/resolve-asking.js";
|
|
4
5
|
import { isOrganizationSelectionCancelled } from "../../../host/org.js";
|
|
@@ -38,9 +39,8 @@ function getFreshChatHref(activeAgentId, defaultAgentId) {
|
|
|
38
39
|
}
|
|
39
40
|
return `/chat/a/${encodeURIComponent(activeAgentId)}`;
|
|
40
41
|
}
|
|
41
|
-
const RESERVED_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
42
42
|
function reservedConversationIdOf(param) {
|
|
43
|
-
return param &&
|
|
43
|
+
return param && isUuidShape(param) ? param.toLowerCase() : null;
|
|
44
44
|
}
|
|
45
45
|
function getReservedFreshChatHref(activeAgentId, defaultAgentId, conversationId) {
|
|
46
46
|
return `${getFreshChatHref(activeAgentId, defaultAgentId)}?c=${conversationId}`;
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SandboxActivityFeed — "what has the agent actually done inside the box?"
|
|
3
|
-
*
|
|
4
|
-
* One line per sandbox tool call, oldest → newest: the tool, its subject (the
|
|
5
|
-
* command, or the path), how it ended (exit code / failed / still running),
|
|
6
|
-
* how long it took, and the output on expand. It is the transcript's sandbox
|
|
7
|
-
* work, extracted and put in one place — reading the SAME tool calls the
|
|
8
|
-
* thread renders from (`sandbox-activity.ts` merges the live lifecycle map
|
|
9
|
-
* with the persisted `cx_tool_call` rows), never a second data path.
|
|
10
|
-
*/
|
|
11
1
|
import React from "react";
|
|
12
2
|
export declare function SandboxActivityFeed({ conversationId, }: {
|
|
13
3
|
conversationId: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatDurationMs } from "@ai-matrx/kit/format";
|
|
3
4
|
import React from "react";
|
|
4
5
|
import { AlertTriangle, ChevronRight, Loader2, Terminal } from "lucide-react";
|
|
5
6
|
import { cn, ErrorBox } from "@ai-matrx/design-system";
|
|
@@ -13,9 +14,7 @@ import { DURABLE_VFS_BADGE_TEXT } from "../../../../tool-call-visualization/rend
|
|
|
13
14
|
import { ErrorAlchemyMenu } from "../../../../host/ui-slots.js";
|
|
14
15
|
import { getToolDisplayName } from "../../../../tool-call-visualization/registry/registry.js";
|
|
15
16
|
function formatDuration(ms) {
|
|
16
|
-
|
|
17
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
18
|
-
return `${(ms / 1e3).toFixed(ms < 1e4 ? 1 : 0)}s`;
|
|
17
|
+
return ms === null ? null : formatDurationMs(ms, { style: "compact" });
|
|
19
18
|
}
|
|
20
19
|
const ActivityRow = ({ row }) => {
|
|
21
20
|
const [open, setOpen] = React.useState(false);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
1
2
|
import {
|
|
2
3
|
previewKnownContext,
|
|
3
4
|
resolveContextEntryValue
|
|
@@ -6,7 +7,7 @@ import { unwrapRichContextValue } from "./contextValueUtils.js";
|
|
|
6
7
|
const PREVIEW_MAX = 32;
|
|
7
8
|
const SIZE_HINT_RE = /^\d[\d,]*\s*chars$/i;
|
|
8
9
|
function formatCharCount(n) {
|
|
9
|
-
return `~${n
|
|
10
|
+
return `~${formatCount(n)} chars`;
|
|
10
11
|
}
|
|
11
12
|
function extractCharCount(value) {
|
|
12
13
|
if (value === void 0 || value === null) return null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import { Badge, Chip, Button } from "@ai-matrx/design-system/controls";
|
|
4
5
|
import { useMemo } from "react";
|
|
5
6
|
import {
|
|
@@ -272,7 +273,7 @@ function AttachedContextSection({
|
|
|
272
273
|
),
|
|
273
274
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-medium text-foreground", children: item.title }),
|
|
274
275
|
/* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[10px] tabular-nums text-muted-foreground", children: [
|
|
275
|
-
item.chars
|
|
276
|
+
formatCount(item.chars),
|
|
276
277
|
" chars"
|
|
277
278
|
] }),
|
|
278
279
|
item.onRemove && /* @__PURE__ */ jsx(RemoveX, { label: item.title, onRemove: item.onRemove })
|
|
@@ -306,7 +307,7 @@ function AttachedContextSection({
|
|
|
306
307
|
] }),
|
|
307
308
|
" ",
|
|
308
309
|
"\xB7 ~",
|
|
309
|
-
totalChars
|
|
310
|
+
formatCount(totalChars),
|
|
310
311
|
" characters of content"
|
|
311
312
|
] })
|
|
312
313
|
] });
|
|
@@ -21,7 +21,7 @@ import { usePageCaptureContribution } from "../../../host/ui-slots.js";
|
|
|
21
21
|
import { useContextPreview } from "./useContextPreview.js";
|
|
22
22
|
import { ErrorAlchemyMenu } from "../../../host/ui-slots.js";
|
|
23
23
|
import { AGENT_ICON } from "@ai-matrx/icons/domain";
|
|
24
|
-
import { formatCount } from "@ai-matrx/kit/format";
|
|
24
|
+
import { formatCount, formatDurationMs } from "@ai-matrx/kit/format";
|
|
25
25
|
import { getUserId } from "../../../host/identity.js";
|
|
26
26
|
import { humanizeIdentifier } from "@ai-matrx/kit/text-case";
|
|
27
27
|
import { useAgentName } from "../../identity/agent-identity.js";
|
|
@@ -525,7 +525,7 @@ function AnswerBoth({
|
|
|
525
525
|
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-primary", children: p === "old" ? "Current system" : "Record store" }),
|
|
526
526
|
/* @__PURE__ */ jsxs("span", { className: "text-[10px] tabular-nums text-muted-foreground", children: [
|
|
527
527
|
a?.model && !/^[0-9a-f-]{36}$/i.test(a.model) ? a.model : "",
|
|
528
|
-
typeof a?.duration_ms === "number" ? ` ${(a.duration_ms
|
|
528
|
+
typeof a?.duration_ms === "number" ? ` ${formatDurationMs(a.duration_ms, { style: "compact" })}` : "",
|
|
529
529
|
typeof a?.input_tokens === "number" ? ` \xB7 ${formatCount(a.input_tokens)} tokens in` : ""
|
|
530
530
|
] })
|
|
531
531
|
] }),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import { RotateCcw, TriangleAlert } from "lucide-react";
|
|
4
5
|
import { useComposerDraftRestore } from "../../../redux/execution-system/instance-user-input/useComposerDraftRestore.js";
|
|
5
6
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
@@ -15,7 +16,7 @@ function ComposerDraftNotice({
|
|
|
15
16
|
/* @__PURE__ */ jsx(RotateCcw, { className: "w-3 h-3 shrink-0" }),
|
|
16
17
|
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
17
18
|
"We put your unsent draft back (",
|
|
18
|
-
restoredChars
|
|
19
|
+
formatCount(restoredChars),
|
|
19
20
|
" ",
|
|
20
21
|
"characters)."
|
|
21
22
|
] }),
|
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
useOpenScratchpadPanel,
|
|
37
37
|
useOpenWorkingDocumentPanel
|
|
38
38
|
} from "../../../../host/window-openers.js";
|
|
39
|
-
import { reportCanvasOpenDrop } from "../../../../host/ui-slots.js";
|
|
40
39
|
import { selectCloudBrowserRunLive } from "../../../../host/ui-slots.js";
|
|
41
40
|
import {
|
|
42
41
|
selectInstanceContextEntries,
|
|
@@ -182,14 +181,6 @@ function ConversationContextRail({
|
|
|
182
181
|
contextTab.toggle(key);
|
|
183
182
|
};
|
|
184
183
|
const toggleDocInCanvas = (kind) => {
|
|
185
|
-
if (kind === "scratch" && !activeScratchId) {
|
|
186
|
-
reportCanvasOpenDrop({
|
|
187
|
-
reason: "nothing-to-show",
|
|
188
|
-
requested: scratchTitle?.trim() || "Scratchpad",
|
|
189
|
-
detail: "no scratchpad is attached to this conversation yet"
|
|
190
|
-
});
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
184
|
const tabId = kind === "scratch" ? scratchpadTabId() : conversationDocumentsTabId(conversationId);
|
|
194
185
|
if (canvasOpen && currentCanvasSourceId === tabId) {
|
|
195
186
|
canvas.hide();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
5
|
import { toast } from "../../../../host/notify.js";
|
|
5
6
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
@@ -335,7 +336,7 @@ function BufferedObservationCard({
|
|
|
335
336
|
"t"
|
|
336
337
|
] }),
|
|
337
338
|
hasText && /* @__PURE__ */ jsxs("span", { children: [
|
|
338
|
-
/* @__PURE__ */ jsx("span", { className: "text-foreground/70 font-mono", children: text.length
|
|
339
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground/70 font-mono", children: formatCount(text.length) }),
|
|
339
340
|
" ",
|
|
340
341
|
"chars"
|
|
341
342
|
] })
|
|
@@ -523,7 +524,7 @@ function TextPayload({
|
|
|
523
524
|
] }),
|
|
524
525
|
hasText && text && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
525
526
|
/* @__PURE__ */ jsxs("span", { className: "text-[10px] font-mono text-muted-foreground shrink-0", children: [
|
|
526
|
-
text.length
|
|
527
|
+
formatCount(text.length),
|
|
527
528
|
" chars"
|
|
528
529
|
] }),
|
|
529
530
|
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: copied ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(Copy, {}), glyphTone: copied ? "success" : void 0, onClick: handleCopy, title: "Copy", "aria-label": "Copy", className: "shrink-0" })
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { formatDurationSeconds } from "@ai-matrx/kit/format";
|
|
3
|
+
import { formatDurationSeconds, formatCount } from "@ai-matrx/kit/format";
|
|
4
4
|
import { useEffect, useMemo, useState } from "react";
|
|
5
5
|
import { useAppDispatch, useAppSelector } from "../../../../store/hooks.js";
|
|
6
6
|
import { useOrganizationRequired } from "../../../../host/ui-slots.js";
|
|
@@ -178,7 +178,7 @@ function TrimSummaryView({ trim }) {
|
|
|
178
178
|
valueClassName: skipped ? "text-amber-500" : rewrittenCount > 0 ? "text-emerald-500" : "text-muted-foreground"
|
|
179
179
|
}
|
|
180
180
|
),
|
|
181
|
-
rewrittenCount > 0 && /* @__PURE__ */ jsx(StatRow, { label: "Freed", value: `${freed
|
|
181
|
+
rewrittenCount > 0 && /* @__PURE__ */ jsx(StatRow, { label: "Freed", value: `${formatCount(freed)} chars` }),
|
|
182
182
|
rewrittenBlocks.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-[10px] font-mono text-muted-foreground space-y-0.5 mt-1", children: [
|
|
183
183
|
rewrittenBlocks.slice(0, 8).map((b) => /* @__PURE__ */ jsxs("div", { className: "truncate", children: [
|
|
184
184
|
"pos ",
|
|
@@ -189,9 +189,9 @@ function TrimSummaryView({ trim }) {
|
|
|
189
189
|
b.tier,
|
|
190
190
|
") \xB7",
|
|
191
191
|
" ",
|
|
192
|
-
b.before_chars
|
|
192
|
+
formatCount(b.before_chars),
|
|
193
193
|
" \u2192 ",
|
|
194
|
-
b.after_chars
|
|
194
|
+
formatCount(b.after_chars)
|
|
195
195
|
] }, `${b.message_position}:${b.call_id}`)),
|
|
196
196
|
rewrittenBlocks.length > 8 && /* @__PURE__ */ jsxs("div", { className: "italic", children: [
|
|
197
197
|
"\u2026and ",
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DocumentLinkPicker — pick an existing document (working / scratch) of the
|
|
3
|
-
* current user to LINK this conversation to. Powers cross-conversation sharing:
|
|
4
|
-
* both conversations then point at the same `content.document` row, so
|
|
5
|
-
* edits round-trip to all of them.
|
|
6
|
-
*
|
|
7
|
-
* Desktop: Popover. Mobile: BottomSheet (nested pickers must not use Popover).
|
|
8
|
-
*/
|
|
9
1
|
import React from "react";
|
|
10
2
|
import { type WorkingDocumentKind } from "../../redux/execution-system/instance-working-document/cx-working-document.service.js";
|
|
11
3
|
interface DocumentLinkPickerProps {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatRelativeTime } from "@ai-matrx/kit/format";
|
|
3
4
|
import React, { useCallback, useMemo, useState } from "react";
|
|
4
5
|
import { FileText, Loader2, NotebookPen, Search } from "lucide-react";
|
|
5
6
|
import { Input, Tabs, Tile } from "@ai-matrx/design-system/controls";
|
|
@@ -23,14 +24,7 @@ function previewOf(content) {
|
|
|
23
24
|
return trimmed.length > 80 ? `${trimmed.slice(0, 80)}\u2026` : trimmed;
|
|
24
25
|
}
|
|
25
26
|
function formatWhen(iso) {
|
|
26
|
-
|
|
27
|
-
if (Number.isNaN(then)) return "";
|
|
28
|
-
const diff = Date.now() - then;
|
|
29
|
-
const day = 864e5;
|
|
30
|
-
if (diff < day) return "today";
|
|
31
|
-
if (diff < 2 * day) return "yesterday";
|
|
32
|
-
if (diff < 7 * day) return `${Math.floor(diff / day)}d ago`;
|
|
33
|
-
return new Date(iso).toLocaleDateString();
|
|
27
|
+
return formatRelativeTime(iso, { fallback: "" });
|
|
34
28
|
}
|
|
35
29
|
const SCOPES = [
|
|
36
30
|
{ value: "mine", label: "Mine" },
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
} from "../../ui/projectCreateAiDebug.js";
|
|
23
23
|
import { toast } from "../../host/notify.js";
|
|
24
24
|
import { isNotAuthenticatedError } from "../../host/identity.js";
|
|
25
|
+
import { isOrganizationSelectionCancelled } from "../../host/org.js";
|
|
26
|
+
import { launchFailureMessage } from "../redux/execution-system/utils/launch-failure-message.js";
|
|
25
27
|
function isConversationInvocation(options) {
|
|
26
28
|
return "identity" in options && "engine" in options && "routing" in options && "origin" in options;
|
|
27
29
|
}
|
|
@@ -289,6 +291,7 @@ function useAgentLauncher(agentId, optionsInput) {
|
|
|
289
291
|
return;
|
|
290
292
|
}
|
|
291
293
|
if (isNotAuthenticatedError(err)) return;
|
|
294
|
+
if (isOrganizationSelectionCancelled(err)) return;
|
|
292
295
|
if (isProjectCreateFlow(sourceFeature, agentId)) {
|
|
293
296
|
warnProjectCreateAi(
|
|
294
297
|
"useAgentLauncher \u2192 launchAgentExecution FAILED",
|
|
@@ -303,9 +306,7 @@ function useAgentLauncher(agentId, optionsInput) {
|
|
|
303
306
|
} else {
|
|
304
307
|
console.error("Failed to create agent conversation:", err);
|
|
305
308
|
}
|
|
306
|
-
toast.error(
|
|
307
|
-
err instanceof Error && err.message ? err.message : "Failed to start the agent conversation."
|
|
308
|
-
);
|
|
309
|
+
toast.error(launchFailureMessage(err, "Failed to start the agent conversation."));
|
|
309
310
|
});
|
|
310
311
|
return () => {
|
|
311
312
|
launch.wanted = false;
|
|
@@ -371,7 +371,11 @@ const selectDisplayContextRows = (conversationId, mandateKillSwitch = false) =>
|
|
|
371
371
|
}
|
|
372
372
|
const actual = toContextReceipt(receipt);
|
|
373
373
|
const shown = new Set(rows.map((row) => row.key));
|
|
374
|
-
const
|
|
374
|
+
const systemRows = expected ? compareReceipt(expected.rows, actual).systemRows : actual.rows.filter((row) => row.origin !== "client");
|
|
375
|
+
const unsent = [
|
|
376
|
+
...systemRows,
|
|
377
|
+
...actual.rows.filter((row) => isServerAuthoritativeKey(row.key) && !systemRows.includes(row))
|
|
378
|
+
];
|
|
375
379
|
const cap = selectContextInlineCap(state, conversationId);
|
|
376
380
|
const primarySurface = resolveClientSurface(state, conversationId) ?? null;
|
|
377
381
|
const serverAdded = systemRowsToResolved(unsent).filter((row) => !shown.has(row.key)).map((row) => ({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { isUuidShape } from "@ai-matrx/kit/uuid";
|
|
2
2
|
function isTableVariableType(type) {
|
|
3
3
|
return type === "table" || type === "tables";
|
|
4
4
|
}
|
|
@@ -7,7 +7,7 @@ function tableVariableTypeOf(customComponent) {
|
|
|
7
7
|
return typeof type === "string" && isTableVariableType(type) ? type : null;
|
|
8
8
|
}
|
|
9
9
|
function isTableId(value) {
|
|
10
|
-
return typeof value === "string" &&
|
|
10
|
+
return typeof value === "string" && isUuidShape(value.trim());
|
|
11
11
|
}
|
|
12
12
|
function readTableReference(value) {
|
|
13
13
|
const out = [];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { isUuidShape } from "@ai-matrx/kit/uuid";
|
|
2
3
|
import { useEffect, useRef, useState } from "react";
|
|
3
4
|
import { useAgentLauncher } from "../../agents/hooks/useAgentLauncher.js";
|
|
4
5
|
import { DEFAULT_NEW_CHAT_MANDATE_KEY } from "../../agents/components/chat/chat-quick-actions.config.js";
|
|
@@ -15,10 +16,9 @@ function initialRequest(start) {
|
|
|
15
16
|
return { kind: "open", conversationId: start.conversationId, agentId: start.agentId ?? null, nonce: 0 };
|
|
16
17
|
return { kind: "new", nonce: 0 };
|
|
17
18
|
}
|
|
18
|
-
const CONVERSATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19
19
|
function conversationInAddress(param, search) {
|
|
20
20
|
const value = new URLSearchParams(search).get(param);
|
|
21
|
-
return value &&
|
|
21
|
+
return value && isUuidShape(value) ? value : null;
|
|
22
22
|
}
|
|
23
23
|
function workspaceChatMemoryKey(surfaceKey) {
|
|
24
24
|
return `matrx:workspace-chat:${surfaceKey}`;
|
|
@@ -26,7 +26,7 @@ function workspaceChatMemoryKey(surfaceKey) {
|
|
|
26
26
|
function recallWorkspaceConversation(surfaceKey) {
|
|
27
27
|
try {
|
|
28
28
|
const value = window.localStorage.getItem(workspaceChatMemoryKey(surfaceKey));
|
|
29
|
-
return value &&
|
|
29
|
+
return value && isUuidShape(value) ? value : null;
|
|
30
30
|
} catch {
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
@@ -46,7 +46,7 @@ function reserveWorkspaceConversationId(surfaceKey, fresh) {
|
|
|
46
46
|
try {
|
|
47
47
|
const key = reservedChatKey(surfaceKey);
|
|
48
48
|
const kept = fresh ? null : window.localStorage.getItem(key);
|
|
49
|
-
if (kept &&
|
|
49
|
+
if (kept && isUuidShape(kept)) return kept;
|
|
50
50
|
const id = window.crypto.randomUUID();
|
|
51
51
|
window.localStorage.setItem(key, id);
|
|
52
52
|
return id;
|
|
@@ -10,6 +10,7 @@ import { loadConversation } from "../../agents/redux/execution-system/thunks/loa
|
|
|
10
10
|
import { followWhatIsStillInFlight } from "../../agents/runtime-reconnect/follow-what-is-still-in-flight.js";
|
|
11
11
|
import { toast } from "../../host/notify.js";
|
|
12
12
|
import { isNotAuthenticatedError } from "../../host/identity.js";
|
|
13
|
+
import { isOrganizationSelectionCancelled } from "../../host/org.js";
|
|
13
14
|
import { DEFAULT_AGENT_ID } from "../components/agent/local-agents.js";
|
|
14
15
|
import { replaceAddressWithoutNavigating } from "../../ui/addressWithoutNavigating.js";
|
|
15
16
|
import { ensureAgentCatalog, isAgentCatalogStale } from "../../agents/identity/agent-identity.js";
|
|
@@ -90,7 +91,8 @@ function useInstanceBootstrap() {
|
|
|
90
91
|
resolvedId = result.payload.conversationId;
|
|
91
92
|
instanceByAgentId.current.set(agentId, resolvedId);
|
|
92
93
|
} else if (launchAgentExecution.rejected.match(result) && // Signed out is a state (the sign-in gate), not a failed launch.
|
|
93
|
-
!isNotAuthenticatedError(result.error)
|
|
94
|
+
!isNotAuthenticatedError(result.error) && // A closed organization chooser is the person's "not now", never a failure.
|
|
95
|
+
!isOrganizationSelectionCancelled(result.error)) {
|
|
94
96
|
toast.error(
|
|
95
97
|
result.error.message ?? "Failed to start the agent conversation."
|
|
96
98
|
);
|
package/dist/host/db-types.d.ts
CHANGED
|
@@ -2240,6 +2240,25 @@ export type ChatDatabase = {
|
|
|
2240
2240
|
};
|
|
2241
2241
|
Returns: Json;
|
|
2242
2242
|
};
|
|
2243
|
+
duplicate_agent: {
|
|
2244
|
+
Args: {
|
|
2245
|
+
p_agent_id: string;
|
|
2246
|
+
p_as_system?: boolean;
|
|
2247
|
+
p_follows_source?: boolean;
|
|
2248
|
+
p_name?: string;
|
|
2249
|
+
p_organization_id?: string;
|
|
2250
|
+
};
|
|
2251
|
+
Returns: string;
|
|
2252
|
+
};
|
|
2253
|
+
duplicate_version: {
|
|
2254
|
+
Args: {
|
|
2255
|
+
p_as_system?: boolean;
|
|
2256
|
+
p_name?: string;
|
|
2257
|
+
p_organization_id?: string;
|
|
2258
|
+
p_version_id: string;
|
|
2259
|
+
};
|
|
2260
|
+
Returns: string;
|
|
2261
|
+
};
|
|
2243
2262
|
message_flag_problems: {
|
|
2244
2263
|
Args: never;
|
|
2245
2264
|
Returns: {
|
|
@@ -9789,6 +9808,33 @@ export type ChatDatabase = {
|
|
|
9789
9808
|
};
|
|
9790
9809
|
Relationships: [];
|
|
9791
9810
|
};
|
|
9811
|
+
rag_library_docs_bodies: {
|
|
9812
|
+
Row: {
|
|
9813
|
+
archived_at: string | null;
|
|
9814
|
+
body: string | null;
|
|
9815
|
+
body_hash: string | null;
|
|
9816
|
+
id: string;
|
|
9817
|
+
processed_document_id: string | null;
|
|
9818
|
+
title: string | null;
|
|
9819
|
+
};
|
|
9820
|
+
Insert: {
|
|
9821
|
+
archived_at?: string | null;
|
|
9822
|
+
body?: string | null;
|
|
9823
|
+
body_hash?: string | null;
|
|
9824
|
+
id: string;
|
|
9825
|
+
processed_document_id?: string | null;
|
|
9826
|
+
title?: string | null;
|
|
9827
|
+
};
|
|
9828
|
+
Update: {
|
|
9829
|
+
archived_at?: string | null;
|
|
9830
|
+
body?: string | null;
|
|
9831
|
+
body_hash?: string | null;
|
|
9832
|
+
id?: string;
|
|
9833
|
+
processed_document_id?: string | null;
|
|
9834
|
+
title?: string | null;
|
|
9835
|
+
};
|
|
9836
|
+
Relationships: [];
|
|
9837
|
+
};
|
|
9792
9838
|
research_rs_content_bodies: {
|
|
9793
9839
|
Row: {
|
|
9794
9840
|
archived_at: string;
|
|
@@ -46153,7 +46199,7 @@ export type ChatDatabase = {
|
|
|
46153
46199
|
operation_type: "insert" | "update" | "delete";
|
|
46154
46200
|
org_role: "owner" | "member" | "admin";
|
|
46155
46201
|
orientation: "vertical" | "horizontal" | "default";
|
|
46156
|
-
permission_level: "viewer" | "commenter" | "editor" | "admin";
|
|
46202
|
+
permission_level: "viewer" | "commenter" | "edit_content" | "editor" | "admin";
|
|
46157
46203
|
project_role: "owner" | "admin" | "member";
|
|
46158
46204
|
recipe_status: "live" | "draft" | "in_review" | "active_testing" | "archived" | "other";
|
|
46159
46205
|
reg_func_category: "Recipes" | "Agents" | "Prompts" | "Processors" | "Extractors" | "Files" | "Database" | "Web" | "Media" | "Documents" | "Integrations" | "Commands" | "Executors" | "API" | "Other";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
1
2
|
import { createElement } from "react";
|
|
2
3
|
function DefaultWebpageSnapshotView({
|
|
3
4
|
snapshot,
|
|
@@ -17,7 +18,7 @@ function DefaultWebpageSnapshotView({
|
|
|
17
18
|
{ className: "font-medium text-foreground" },
|
|
18
19
|
variant === "submitted" ? "Snapshot sent with this message" : "Snapshot attached to this draft"
|
|
19
20
|
),
|
|
20
|
-
createElement("span", null, `${charCount
|
|
21
|
+
createElement("span", null, `${formatCount(charCount)} characters`),
|
|
21
22
|
scrapedAt ? createElement("span", null, scrapedAt) : null
|
|
22
23
|
) : null,
|
|
23
24
|
createElement(
|
package/dist/host/ui-slots.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* host/ui-slots — host registrations for app UI the package draws but does not own
|
|
3
|
-
* (PACKAGE-INDEPENDENCE.md: "a host registration", not an import of app code).
|
|
4
|
-
*
|
|
5
|
-
* The host registers once at startup with `registerChatUi({...})`; package code
|
|
6
|
-
* imports the named wrappers below. A component the host did not register renders
|
|
7
|
-
* nothing (or its plain stand-in, marked `data-chat-slot-fallback`) and reports itself
|
|
8
|
-
* ONCE through the host diagnostics port (never silent: Law 4); a function or hook it
|
|
9
|
-
* did not register throws, naming the slot (or runs its declared stand-in, reported once). A test registers a stand-in the same way (`registerChatUi`).
|
|
10
|
-
*
|
|
11
|
-
* Slot shapes are the host component's own props — package call sites keep passing
|
|
12
|
-
* exactly what they passed when they imported the component directly.
|
|
13
|
-
*/
|
|
14
1
|
import { type ComponentType, type ChangeEvent, type ReactNode } from "react";
|
|
15
2
|
import type { EditableContextMenuProps, NonEditableContextMenuProps } from "../context-menu/types.js";
|
|
16
3
|
/** The host context menu's props (its registration is typed against them). */
|
package/dist/host/ui-slots.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { isRfc9562Uuid } from "@ai-matrx/kit/uuid";
|
|
2
3
|
import { createElement, Fragment, useRef } from "react";
|
|
3
4
|
import { formatCost, usdToPoints } from "@ai-matrx/kit/format";
|
|
4
5
|
import { reportUnregisteredHostSlot as reportUnregistered } from "./diagnostics.js";
|
|
@@ -143,7 +144,7 @@ const STAND_INS = {
|
|
|
143
144
|
bookmarksToReferenceDirectives: () => NONE,
|
|
144
145
|
notifyPrintOutcome: () => void 0,
|
|
145
146
|
awaitEffectiveOrganizationId: async () => ({ status: "unavailable", reason: "This host registers no organization source.", cause: "no-selection" }),
|
|
146
|
-
isUuidValue: (value) => typeof value === "string" &&
|
|
147
|
+
isUuidValue: (value) => typeof value === "string" && isRfc9562Uuid(value.trim()),
|
|
147
148
|
useClippedContentGuard: () => void 0,
|
|
148
149
|
answerPreviewText: (text) => String(text ?? ""),
|
|
149
150
|
beginPlaybackSession: () => ({ id: "", update: () => void 0, end: () => void 0 }),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatRelativeTime } from "@ai-matrx/kit/format";
|
|
1
2
|
const SURFACE_CHECK_FRESH_DAYS = 30;
|
|
2
3
|
function daysSinceCheck(row, now = Date.now()) {
|
|
3
4
|
if (!row.last_checked_at) return null;
|
|
@@ -11,13 +12,7 @@ function surfaceCheckState(row, now = Date.now()) {
|
|
|
11
12
|
return days > SURFACE_CHECK_FRESH_DAYS ? "stale" : "fresh";
|
|
12
13
|
}
|
|
13
14
|
function checkAgeLabel(row, now = Date.now()) {
|
|
14
|
-
|
|
15
|
-
if (days === null) return "never";
|
|
16
|
-
if (days === 0) return "today";
|
|
17
|
-
if (days === 1) return "1d";
|
|
18
|
-
if (days < 30) return `${days}d`;
|
|
19
|
-
const months = Math.floor(days / 30);
|
|
20
|
-
return months < 12 ? `${months}mo` : `${Math.floor(days / 365)}y`;
|
|
15
|
+
return formatRelativeTime(row.last_checked_at, { style: "short", suffix: false, now, fallback: "never" });
|
|
21
16
|
}
|
|
22
17
|
function checkSortWeight(row, now = Date.now()) {
|
|
23
18
|
const days = daysSinceCheck(row, now);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import {
|
|
4
5
|
CheckCircle,
|
|
5
6
|
FileSearch,
|
|
@@ -139,7 +140,7 @@ function scrapeHeaderExtras(entry) {
|
|
|
139
140
|
"read"
|
|
140
141
|
] }),
|
|
141
142
|
parsed.totalChars > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
142
|
-
parsed.totalChars
|
|
143
|
+
formatCount(parsed.totalChars),
|
|
143
144
|
" chars"
|
|
144
145
|
] })
|
|
145
146
|
] });
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CtxItemCard — the note-card display for ONE context item.
|
|
3
|
-
*
|
|
4
|
-
* Shared by every CTX renderer (`ctx_get`, `ctx_batch`). Renders a single
|
|
5
|
-
* resolved context object — `{ key, type, label, content }` (or `summary`
|
|
6
|
-
* in summary mode) — as a polished note card: a header row carrying the
|
|
7
|
-
* type icon, the human label, the raw key, and an optional size badge, then
|
|
8
|
-
* the value itself via the recursive `<ResultValue>` field library.
|
|
9
|
-
*
|
|
10
|
-
* The component is deliberately shape-tolerant: the server `type` field is a
|
|
11
|
-
* free string, so the icon is resolved through a string-keyed map with a
|
|
12
|
-
* `Layers` fallback. `content` is handed straight to `<ResultValue>`, which
|
|
13
|
-
* already knows how to render markdown, tables, json, media, and scalars by
|
|
14
|
-
* shape — this card never reimplements rendering.
|
|
15
|
-
*/
|
|
16
1
|
import React from "react";
|
|
17
2
|
import { type ResultDensity } from "../../result-fields/ResultValue.js";
|
|
18
3
|
export interface CtxItem {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import {
|
|
4
5
|
FileText,
|
|
5
6
|
File,
|
|
@@ -34,11 +35,7 @@ function iconForType(type) {
|
|
|
34
35
|
return CTX_TYPE_ICON[type] ?? Layers;
|
|
35
36
|
}
|
|
36
37
|
function formatCharCount(n) {
|
|
37
|
-
|
|
38
|
-
const k = n / 1e3;
|
|
39
|
-
return `${k >= 10 ? Math.round(k) : k.toFixed(1)}k chars`;
|
|
40
|
-
}
|
|
41
|
-
return `${n} char${n === 1 ? "" : "s"}`;
|
|
38
|
+
return `${formatCount(n, { style: "compact" })} char${n === 1 ? "" : "s"}`;
|
|
42
39
|
}
|
|
43
40
|
const CtxItemCard = ({
|
|
44
41
|
item,
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Research overlay tabs (`research_web`, `core_web_search_and_read`).
|
|
3
|
-
*
|
|
4
|
-
* Exposes Report · Sources · Full Text, plus a `researchOverlayTabs` array the
|
|
5
|
-
* tool registry hands to `ToolUpdatesOverlay`. When the tool is opened in the
|
|
6
|
-
* overlay these REPLACE the default "Results" tab and sit beside the standard
|
|
7
|
-
* "Input" and "Raw" admin tabs:
|
|
8
|
-
*
|
|
9
|
-
* [ Report | Sources | Full Text | Input | Raw ]
|
|
10
|
-
*
|
|
11
|
-
* All three parse the raw blob via the ONE canonical `parseSearch` (the
|
|
12
|
-
* deep-research `parser.ts` it used to use is deleted — no duplicate parsers).
|
|
13
|
-
* The Report tab renders through `RichDocument` (full action toolkit); Sources
|
|
14
|
-
* is the dense ranked + deep-read view; Full Text is a copyable synthesized dump.
|
|
15
|
-
*/
|
|
16
1
|
import React from "react";
|
|
17
2
|
import type { ToolOverlayTabSpec, ToolRendererProps } from "../../types.js";
|
|
18
3
|
export declare const ResearchReportTab: React.FC<ToolRendererProps>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import { useMemo, useState } from "react";
|
|
4
5
|
import {
|
|
5
6
|
BookOpen,
|
|
@@ -106,7 +107,7 @@ const ReadCard = ({
|
|
|
106
107
|
content: expanded || !isLong ? read.text : `${read.text.slice(0, 500)}\u2026`
|
|
107
108
|
}
|
|
108
109
|
),
|
|
109
|
-
isLong && /* @__PURE__ */ jsx(Button, { variant: "quiet", tone: "primary", onClick: () => setExpanded((p) => !p), className: "mt-2", children: expanded ? "Show less" : `Show full content (${
|
|
110
|
+
isLong && /* @__PURE__ */ jsx(Button, { variant: "quiet", tone: "primary", onClick: () => setExpanded((p) => !p), className: "mt-2", children: expanded ? "Show less" : `Show full content (${formatCount(read.text.length, { style: "compact" })} chars)` })
|
|
110
111
|
] })
|
|
111
112
|
] })
|
|
112
113
|
] });
|
|
@@ -1,33 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ScrapeInline — the canonical inline renderer for the page-read / scrape family
|
|
3
|
-
* (`web_read`, `core_web_read_web_pages`).
|
|
4
|
-
*
|
|
5
|
-
* Reading 40k tokens off a page is NOT a one-line event — so each page gets a
|
|
6
|
-
* full rectangular CARD (favicon + title + domain + meta; a preview image when
|
|
7
|
-
* one is present; an AI-review line ONLY when present). ONE card per page,
|
|
8
|
-
* multiple cards when reading several pages at once.
|
|
9
|
-
*
|
|
10
|
-
* ─── The two phases ──────────────────────────────────────────────────────────
|
|
11
|
-
*
|
|
12
|
-
* • READING (live): a left-to-right "reading wave" shimmer sweeps across each
|
|
13
|
-
* card (the `readingWave` keyframe in globals.css), mimicking a person
|
|
14
|
-
* reading the page top-to-bottom while the content is fetched. While reading
|
|
15
|
-
* we know the URLs (from `arguments.urls` and/or the real-time "Browsing
|
|
16
|
-
* <url>" activity on `entry.events`) but not yet the body — so the card
|
|
17
|
-
* shows the favicon + domain + a shimmering placeholder.
|
|
18
|
-
* • DONE (persistent): the card fills in — title, a content snippet, the char
|
|
19
|
-
* count, the optional preview image (durable, via `InlineMediaRef`), and the
|
|
20
|
-
* optional AI-review line. A "View page" affordance opens the overlay.
|
|
21
|
-
*
|
|
22
|
-
* Phase is keyed on `selectIsLatestToolActivity(requestId, callId)` when a live
|
|
23
|
-
* requestId is threaded (the card keeps reading until the model moves on), and
|
|
24
|
-
* falls back to `entry.status` for the simulator / persisted snapshots.
|
|
25
|
-
*
|
|
26
|
-
* Images go through `InlineMediaRef` ONLY (never a raw <img>) so a page's
|
|
27
|
-
* preview image self-heals if it's an expiring signed URL. Favicons use the
|
|
28
|
-
* Google favicon service (not owned media → a plain <img> with a Globe fallback
|
|
29
|
-
* is correct). Semantic tokens only; React Compiler is on (no manual memo).
|
|
30
|
-
*/
|
|
31
1
|
import React from "react";
|
|
32
2
|
import type { ToolRendererProps } from "../../types.js";
|
|
33
3
|
export declare const ScrapeInline: React.FC<ToolRendererProps>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import { useMemo, useState } from "react";
|
|
4
5
|
import {
|
|
5
6
|
Globe,
|
|
@@ -168,7 +169,7 @@ const ReadPageCard = ({ page, index, onOpen }) => {
|
|
|
168
169
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pt-0.5 text-xs text-muted-foreground", children: [
|
|
169
170
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
|
|
170
171
|
/* @__PURE__ */ jsx(FileText, { className: "h-3 w-3" }),
|
|
171
|
-
page.charCount
|
|
172
|
+
formatCount(page.charCount),
|
|
172
173
|
" chars"
|
|
173
174
|
] }),
|
|
174
175
|
onOpen && /* @__PURE__ */ jsx(Button, { variant: "quiet", tone: "primary", iconEnd: /* @__PURE__ */ jsx(ArrowRight, {}), onClick: (e) => {
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ScrapeOverlay — the full "hide nothing" page-read view for the scrape family
|
|
3
|
-
* (`web_read`, `core_web_read_web_pages`), rendered in the fullscreen overlay
|
|
4
|
-
* and the floating window panel.
|
|
5
|
-
*
|
|
6
|
-
* Layout: a page list on the left, the selected page's FULL content on the
|
|
7
|
-
* right (rendered as markdown), plus the optional preview image and AI-review
|
|
8
|
-
* when present. Same canonical `parseScrape` as the inline renderer; semantic
|
|
9
|
-
* tokens only; favicons via the Google favicon service; the preview image via
|
|
10
|
-
* `InlineMediaRef` (durable, never a raw <img>).
|
|
11
|
-
*/
|
|
12
1
|
import React from "react";
|
|
13
2
|
import type { ToolRendererProps } from "../../types.js";
|
|
14
3
|
export declare const ScrapeOverlay: React.FC<ToolRendererProps>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import { useMemo, useState } from "react";
|
|
4
5
|
import {
|
|
5
6
|
Globe,
|
|
@@ -58,7 +59,7 @@ const ScrapeOverlay = ({ entry }) => {
|
|
|
58
59
|
] })
|
|
59
60
|
] }),
|
|
60
61
|
parsed.totalChars > 0 && /* @__PURE__ */ jsxs("span", { className: "ml-auto text-xs tabular-nums text-muted-foreground", children: [
|
|
61
|
-
parsed.totalChars
|
|
62
|
+
formatCount(parsed.totalChars),
|
|
62
63
|
" chars total"
|
|
63
64
|
] })
|
|
64
65
|
] }),
|
|
@@ -88,7 +89,7 @@ const ScrapeOverlay = ({ entry }) => {
|
|
|
88
89
|
/* @__PURE__ */ jsxs("div", { className: "truncate text-xs text-muted-foreground", children: [
|
|
89
90
|
p.domain,
|
|
90
91
|
" \xB7 ",
|
|
91
|
-
p.charCount
|
|
92
|
+
formatCount(p.charCount),
|
|
92
93
|
" chars"
|
|
93
94
|
] })
|
|
94
95
|
] })
|
|
@@ -1,36 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KeyValueGrid — renders a plain object as a compact, shape-aware field layout.
|
|
3
|
-
*
|
|
4
|
-
* Fields are partitioned by what their VALUE is, not treated uniformly:
|
|
5
|
-
*
|
|
6
|
-
* inline — short values (scalar / uuid / url / boolean / empty) render as
|
|
7
|
-
* single-line `Label value` rows on a two-column grid. Key, Type,
|
|
8
|
-
* Label never burn two lines each again.
|
|
9
|
-
* chips — numeric metadata (total_chars, count, duration_ms, tokens…)
|
|
10
|
-
* renders as one wrapping row of quiet badges.
|
|
11
|
-
* blocks — anything of unknown length (text, markdown, nested objects,
|
|
12
|
-
* tables, lists, media) keeps a full-width section: small label
|
|
13
|
-
* header, value beneath, recursing through {@link ResultValue}.
|
|
14
|
-
*
|
|
15
|
-
* Display order is inline rows → chips → blocks so unknown-length content
|
|
16
|
-
* always sits last. Identifier keys (`id`, `*_id`, `uuid`) render the full
|
|
17
|
-
* UUID with an always-visible copy button.
|
|
18
|
-
*
|
|
19
|
-
* Every key is reachable: nothing is dropped, only deferred behind a toggle.
|
|
20
|
-
*
|
|
21
|
-
* FIELDS THAT DO NOT APPLY (wall W61, 2026-09-12) obey the document's optional
|
|
22
|
-
* -field policy (`document-presentation.tsx`), not the density. Before that,
|
|
23
|
-
* `inline` silently skipped them and `full` printed the word "None" — the same
|
|
24
|
-
* payload read two different ways, and the `full` reading put "Physician note:
|
|
25
|
-
* None" at the top of a page written for a parent. Under the default `omit`
|
|
26
|
-
* policy they leave the field list and are COUNTED on a quiet trailing line
|
|
27
|
-
* that opens them; under `note` they keep their row with a quiet "Not
|
|
28
|
-
* applicable"; under `show` the producer's value renders untouched.
|
|
29
|
-
*
|
|
30
|
-
* FIELD LABELS come from the producer's own JSON Schema `title` when the host
|
|
31
|
-
* supplied one, and from the key otherwise. The key is the machine's name for
|
|
32
|
-
* a field; the schema's title is the author's.
|
|
33
|
-
*/
|
|
34
1
|
import React from "react";
|
|
35
2
|
import { type ResultDensity } from "./ResultValue.js";
|
|
36
3
|
export interface KeyValueGridProps {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatCount } from "@ai-matrx/kit/format";
|
|
3
4
|
import React from "react";
|
|
4
5
|
import { cn } from "@ai-matrx/design-system";
|
|
5
6
|
import {
|
|
@@ -53,13 +54,7 @@ function isMetaCountField(key, val) {
|
|
|
53
54
|
return typeof val === "number" && Number.isFinite(val) && META_COUNT_KEY.test(key);
|
|
54
55
|
}
|
|
55
56
|
function formatMetaNumber(n) {
|
|
56
|
-
|
|
57
|
-
return new Intl.NumberFormat("en-US", {
|
|
58
|
-
notation: "compact",
|
|
59
|
-
maximumFractionDigits: 1
|
|
60
|
-
}).format(n);
|
|
61
|
-
}
|
|
62
|
-
return n.toLocaleString("en-US");
|
|
57
|
+
return Number.isInteger(n) && Math.abs(n) >= 1e4 ? formatCount(n, { style: "compact" }) : formatCount(n, { locale: "en-US" });
|
|
63
58
|
}
|
|
64
59
|
function isInlineValue(key, val) {
|
|
65
60
|
if (typeof val === "string" && isIdentifierKey(key)) {
|
|
@@ -10298,6 +10298,25 @@ export declare function mandateNotes(supabase: Client): import("@supabase/supaba
|
|
|
10298
10298
|
};
|
|
10299
10299
|
Returns: Json;
|
|
10300
10300
|
};
|
|
10301
|
+
duplicate_agent: {
|
|
10302
|
+
Args: {
|
|
10303
|
+
p_agent_id: string;
|
|
10304
|
+
p_as_system?: boolean;
|
|
10305
|
+
p_follows_source?: boolean;
|
|
10306
|
+
p_name?: string;
|
|
10307
|
+
p_organization_id?: string;
|
|
10308
|
+
};
|
|
10309
|
+
Returns: string;
|
|
10310
|
+
};
|
|
10311
|
+
duplicate_version: {
|
|
10312
|
+
Args: {
|
|
10313
|
+
p_as_system?: boolean;
|
|
10314
|
+
p_name?: string;
|
|
10315
|
+
p_organization_id?: string;
|
|
10316
|
+
p_version_id: string;
|
|
10317
|
+
};
|
|
10318
|
+
Returns: string;
|
|
10319
|
+
};
|
|
10301
10320
|
message_flag_problems: {
|
|
10302
10321
|
Args: never;
|
|
10303
10322
|
Returns: {
|
|
@@ -1941,6 +1941,25 @@ declare const agentShortcutTable: (supabase: Client) => import("@supabase/supaba
|
|
|
1941
1941
|
};
|
|
1942
1942
|
Returns: import("../../host/db-types.js").Json;
|
|
1943
1943
|
};
|
|
1944
|
+
duplicate_agent: {
|
|
1945
|
+
Args: {
|
|
1946
|
+
p_agent_id: string;
|
|
1947
|
+
p_as_system?: boolean;
|
|
1948
|
+
p_follows_source?: boolean;
|
|
1949
|
+
p_name?: string;
|
|
1950
|
+
p_organization_id?: string;
|
|
1951
|
+
};
|
|
1952
|
+
Returns: string;
|
|
1953
|
+
};
|
|
1954
|
+
duplicate_version: {
|
|
1955
|
+
Args: {
|
|
1956
|
+
p_as_system?: boolean;
|
|
1957
|
+
p_name?: string;
|
|
1958
|
+
p_organization_id?: string;
|
|
1959
|
+
p_version_id: string;
|
|
1960
|
+
};
|
|
1961
|
+
Returns: string;
|
|
1962
|
+
};
|
|
1944
1963
|
message_flag_problems: {
|
|
1945
1964
|
Args: never;
|
|
1946
1965
|
Returns: {
|
|
@@ -4137,6 +4156,25 @@ declare const agentContextMenuView: (supabase: Client) => import("@supabase/supa
|
|
|
4137
4156
|
};
|
|
4138
4157
|
Returns: import("../../host/db-types.js").Json;
|
|
4139
4158
|
};
|
|
4159
|
+
duplicate_agent: {
|
|
4160
|
+
Args: {
|
|
4161
|
+
p_agent_id: string;
|
|
4162
|
+
p_as_system?: boolean;
|
|
4163
|
+
p_follows_source?: boolean;
|
|
4164
|
+
p_name?: string;
|
|
4165
|
+
p_organization_id?: string;
|
|
4166
|
+
};
|
|
4167
|
+
Returns: string;
|
|
4168
|
+
};
|
|
4169
|
+
duplicate_version: {
|
|
4170
|
+
Args: {
|
|
4171
|
+
p_as_system?: boolean;
|
|
4172
|
+
p_name?: string;
|
|
4173
|
+
p_organization_id?: string;
|
|
4174
|
+
p_version_id: string;
|
|
4175
|
+
};
|
|
4176
|
+
Returns: string;
|
|
4177
|
+
};
|
|
4140
4178
|
message_flag_problems: {
|
|
4141
4179
|
Args: never;
|
|
4142
4180
|
Returns: {
|
|
@@ -44,12 +44,18 @@ import type { ChatDatabase as Database } from "../host/db-types.js";
|
|
|
44
44
|
* The canonical ladder, ascending. THE ONE PLACE a level is declared.
|
|
45
45
|
* Order is authority order: each entry outranks every entry before it.
|
|
46
46
|
*/
|
|
47
|
-
export declare const PERMISSION_LEVELS: readonly ["viewer", "commenter", "editor", "admin"];
|
|
47
|
+
export declare const PERMISSION_LEVELS: readonly ["viewer", "commenter", "edit_content", "editor", "admin"];
|
|
48
48
|
export type PermissionLevel = (typeof PERMISSION_LEVELS)[number];
|
|
49
49
|
/** Ordinal rank, 1-based, derived from the ladder so it can never drift. */
|
|
50
50
|
export declare const PERMISSION_LEVEL_RANK: Record<PermissionLevel, number>;
|
|
51
51
|
/** Sentence labels, used where the level is described ("Can view"). */
|
|
52
52
|
export declare const PERMISSION_LEVEL_LABELS: Record<PermissionLevel, string>;
|
|
53
|
+
/**
|
|
54
|
+
* One line each, for a picker's tooltip: what the level lets a person do. `edit_content` is
|
|
55
|
+
* Notion's "Can edit content" - change the rows and page content, never the structure
|
|
56
|
+
* (columns, views, settings) or the sharing. It sorts between `commenter` and `editor`.
|
|
57
|
+
*/
|
|
58
|
+
export declare const PERMISSION_LEVEL_HINTS: Record<PermissionLevel, string>;
|
|
53
59
|
/** Noun labels, used in pickers and chips ("Viewer"). */
|
|
54
60
|
export declare const PERMISSION_LEVEL_SHORT_LABELS: Record<PermissionLevel, string>;
|
|
55
61
|
/**
|
|
@@ -63,7 +69,7 @@ export type DbPermissionLevel = Database["public"]["Enums"]["permission_level"];
|
|
|
63
69
|
* below: when `public.permission_level` gains a value and `pnpm db-types` regenerates, this
|
|
64
70
|
* line stops compiling until the new value is added — never a silent gap.
|
|
65
71
|
*/
|
|
66
|
-
export declare const DB_PERMISSION_LEVELS: readonly ["viewer", "commenter", "editor", "admin"];
|
|
72
|
+
export declare const DB_PERMISSION_LEVELS: readonly ["viewer", "commenter", "edit_content", "editor", "admin"];
|
|
67
73
|
/** Type guard for an arbitrary value being a known code-ladder level. */
|
|
68
74
|
export declare function isPermissionLevel(raw: unknown): raw is PermissionLevel;
|
|
69
75
|
/** True when the database enum can currently store this level. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const PERMISSION_LEVELS = [
|
|
2
2
|
"viewer",
|
|
3
3
|
"commenter",
|
|
4
|
+
"edit_content",
|
|
4
5
|
"editor",
|
|
5
6
|
"admin"
|
|
6
7
|
];
|
|
@@ -10,16 +11,25 @@ const PERMISSION_LEVEL_RANK = Object.fromEntries(
|
|
|
10
11
|
const PERMISSION_LEVEL_LABELS = {
|
|
11
12
|
viewer: "Can view",
|
|
12
13
|
commenter: "Can comment",
|
|
14
|
+
edit_content: "Can edit content",
|
|
13
15
|
editor: "Can edit",
|
|
14
16
|
admin: "Full access"
|
|
15
17
|
};
|
|
18
|
+
const PERMISSION_LEVEL_HINTS = {
|
|
19
|
+
viewer: "Can open and read it.",
|
|
20
|
+
commenter: "Can read it and leave comments.",
|
|
21
|
+
edit_content: "Can change the content, not the structure or the sharing.",
|
|
22
|
+
editor: "Can change content and structure.",
|
|
23
|
+
admin: "Can change everything and decide who else may."
|
|
24
|
+
};
|
|
16
25
|
const PERMISSION_LEVEL_SHORT_LABELS = {
|
|
17
26
|
viewer: "Viewer",
|
|
18
27
|
commenter: "Commenter",
|
|
28
|
+
edit_content: "Content editor",
|
|
19
29
|
editor: "Editor",
|
|
20
30
|
admin: "Full access"
|
|
21
31
|
};
|
|
22
|
-
const DB_PERMISSION_LEVELS = ["viewer", "commenter", "editor", "admin"];
|
|
32
|
+
const DB_PERMISSION_LEVELS = ["viewer", "commenter", "edit_content", "editor", "admin"];
|
|
23
33
|
const _everyDbLevelIsListed = true;
|
|
24
34
|
void _everyDbLevelIsListed;
|
|
25
35
|
function isPermissionLevel(raw) {
|
|
@@ -55,6 +65,7 @@ function permissionLevelRank(level) {
|
|
|
55
65
|
export {
|
|
56
66
|
DB_PERMISSION_LEVELS,
|
|
57
67
|
PERMISSION_LEVELS,
|
|
68
|
+
PERMISSION_LEVEL_HINTS,
|
|
58
69
|
PERMISSION_LEVEL_LABELS,
|
|
59
70
|
PERMISSION_LEVEL_RANK,
|
|
60
71
|
PERMISSION_LEVEL_SHORT_LABELS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.30",
|
|
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",
|
|
@@ -226,24 +226,24 @@
|
|
|
226
226
|
"server-only": "^0.0.1",
|
|
227
227
|
"uuid": "^14.0.2",
|
|
228
228
|
"zod": "^4.6.5",
|
|
229
|
-
"@ai-matrx/
|
|
229
|
+
"@ai-matrx/agents": "latest",
|
|
230
230
|
"@ai-matrx/associations": "latest",
|
|
231
|
+
"@ai-matrx/alchemy": "latest",
|
|
231
232
|
"@ai-matrx/browser-audio": "latest",
|
|
232
233
|
"@ai-matrx/canvas": "latest",
|
|
234
|
+
"@ai-matrx/content-ir": "latest",
|
|
233
235
|
"@ai-matrx/content-ir-react": "latest",
|
|
234
|
-
"@ai-matrx/
|
|
236
|
+
"@ai-matrx/data": "latest",
|
|
235
237
|
"@ai-matrx/design-system": "latest",
|
|
236
238
|
"@ai-matrx/diff": "latest",
|
|
237
239
|
"@ai-matrx/icons": "latest",
|
|
240
|
+
"@ai-matrx/kit": "latest",
|
|
238
241
|
"@ai-matrx/print": "latest",
|
|
239
242
|
"@ai-matrx/realtime": "latest",
|
|
243
|
+
"@ai-matrx/tap-target": "latest",
|
|
240
244
|
"@ai-matrx/media": "latest",
|
|
241
245
|
"@ai-matrx/records": "latest",
|
|
242
|
-
"@ai-matrx/
|
|
243
|
-
"@ai-matrx/records-ui": "latest",
|
|
244
|
-
"@ai-matrx/content-ir": "latest",
|
|
245
|
-
"@ai-matrx/data": "latest",
|
|
246
|
-
"@ai-matrx/kit": "latest"
|
|
246
|
+
"@ai-matrx/records-ui": "latest"
|
|
247
247
|
},
|
|
248
248
|
"devDependencies": {
|
|
249
249
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
@@ -1,57 +0,0 @@
|
|
|
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
|
-
}
|
|
24
|
-
export interface AgentDuplicateOutcomeDialogProps {
|
|
25
|
-
open: boolean;
|
|
26
|
-
/** Called when the user dismisses the dialog (X button, escape, or "Stay
|
|
27
|
-
* here"). The dialog is fully controlled — the parent owns lifecycle. */
|
|
28
|
-
onOpenChange: (open: boolean) => void;
|
|
29
|
-
state: DuplicateOutcomeState;
|
|
30
|
-
/** Display name of the new agent (only meaningful in success state). */
|
|
31
|
-
newAgentName?: string;
|
|
32
|
-
/** Path to the new agent — used by both "Open new agent" and "Open in new
|
|
33
|
-
* tab." Defaults to `null` while loading; required in success state. */
|
|
34
|
-
newAgentPath?: string | null;
|
|
35
|
-
/** Optional error message in error state. */
|
|
36
|
-
errorMessage?: string;
|
|
37
|
-
/** Whether the source agent was a builtin/system agent. Tweaks the success
|
|
38
|
-
* copy so admins know the duplicate is also a system agent. */
|
|
39
|
-
asSystem?: boolean;
|
|
40
|
-
/** Required for the "choose" state. */
|
|
41
|
-
chooser?: DuplicateVersionChooser;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Post-duplicate "where would you like to go?" choice surface.
|
|
45
|
-
*
|
|
46
|
-
* Flow:
|
|
47
|
-
* 1. Parent fires the duplicate thunk and immediately mounts this dialog
|
|
48
|
-
* with `state="loading"` so the user sees the in-flight work.
|
|
49
|
-
* 2. On success, the parent flips to `state="success"` and supplies the
|
|
50
|
-
* new agent's id + computed `newAgentPath`. The user picks one of three
|
|
51
|
-
* destinations (open in current tab, open in new tab, stay here).
|
|
52
|
-
* 3. On failure, the parent flips to `state="error"` with a message.
|
|
53
|
-
*
|
|
54
|
-
* Mobile renders as a bottom Drawer (per project mobile rules — never Dialog
|
|
55
|
-
* on mobile); desktop uses a small Dialog (`sm:max-w-md`).
|
|
56
|
-
*/
|
|
57
|
-
export declare function AgentDuplicateOutcomeDialog({ open, onOpenChange, state, newAgentName, newAgentPath, errorMessage, asSystem, chooser, }: AgentDuplicateOutcomeDialogProps): import("react").JSX.Element;
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useTransition } from "react";
|
|
4
|
-
import { useRouter } from "../../../host/navigation.js";
|
|
5
|
-
import {
|
|
6
|
-
AlertCircle,
|
|
7
|
-
ArrowRight,
|
|
8
|
-
CheckCircle2,
|
|
9
|
-
Copy,
|
|
10
|
-
ExternalLink,
|
|
11
|
-
Loader2
|
|
12
|
-
} from "lucide-react";
|
|
13
|
-
import {
|
|
14
|
-
Dialog,
|
|
15
|
-
DialogContent,
|
|
16
|
-
DialogDescription,
|
|
17
|
-
DialogHeader,
|
|
18
|
-
DialogTitle
|
|
19
|
-
} from "@ai-matrx/design-system";
|
|
20
|
-
import {
|
|
21
|
-
Drawer,
|
|
22
|
-
DrawerContent,
|
|
23
|
-
DrawerDescription,
|
|
24
|
-
DrawerHeader,
|
|
25
|
-
DrawerTitle
|
|
26
|
-
} from "@ai-matrx/design-system";
|
|
27
|
-
import { Alert, AlertDescription } from "@ai-matrx/design-system";
|
|
28
|
-
import { Button } from "@ai-matrx/design-system";
|
|
29
|
-
import { Select } from "@ai-matrx/design-system/controls";
|
|
30
|
-
import { useIsMobile } from "@ai-matrx/kit/media-query";
|
|
31
|
-
const CURRENT_VERSION_CHOICE = "current";
|
|
32
|
-
function AgentDuplicateOutcomeDialog({
|
|
33
|
-
open,
|
|
34
|
-
onOpenChange,
|
|
35
|
-
state,
|
|
36
|
-
newAgentName,
|
|
37
|
-
newAgentPath,
|
|
38
|
-
errorMessage,
|
|
39
|
-
asSystem = false,
|
|
40
|
-
chooser
|
|
41
|
-
}) {
|
|
42
|
-
const isMobile = useIsMobile();
|
|
43
|
-
const router = useRouter();
|
|
44
|
-
const [isNavigating, startTransition] = useTransition();
|
|
45
|
-
const handleGoToAgent = () => {
|
|
46
|
-
if (!newAgentPath) return;
|
|
47
|
-
onOpenChange(false);
|
|
48
|
-
startTransition(() => router.push(newAgentPath));
|
|
49
|
-
};
|
|
50
|
-
const handleOpenInNewTab = () => {
|
|
51
|
-
if (!newAgentPath) return;
|
|
52
|
-
window.open(newAgentPath, "_blank", "noopener,noreferrer");
|
|
53
|
-
onOpenChange(false);
|
|
54
|
-
};
|
|
55
|
-
const handleStayHere = () => {
|
|
56
|
-
onOpenChange(false);
|
|
57
|
-
};
|
|
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.";
|
|
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
|
-
] }),
|
|
94
|
-
state === "loading" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 py-6", children: [
|
|
95
|
-
/* @__PURE__ */ jsx(Loader2, { className: "h-10 w-10 animate-spin text-primary" }),
|
|
96
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Please wait\u2026" })
|
|
97
|
-
] }),
|
|
98
|
-
state === "success" && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
99
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-3 rounded-md bg-success/10 border border-success/20", children: [
|
|
100
|
-
/* @__PURE__ */ jsx(CheckCircle2, { className: "h-5 w-5 text-success shrink-0" }),
|
|
101
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Copy created successfully." })
|
|
102
|
-
] }),
|
|
103
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
104
|
-
/* @__PURE__ */ jsxs(
|
|
105
|
-
Button,
|
|
106
|
-
{
|
|
107
|
-
size: "lg",
|
|
108
|
-
onClick: handleGoToAgent,
|
|
109
|
-
disabled: !newAgentPath || isNavigating,
|
|
110
|
-
className: "w-full justify-start gap-2",
|
|
111
|
-
children: [
|
|
112
|
-
isNavigating ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4" }),
|
|
113
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Open new agent" })
|
|
114
|
-
]
|
|
115
|
-
}
|
|
116
|
-
),
|
|
117
|
-
newAgentPath ? /* @__PURE__ */ jsxs(
|
|
118
|
-
Button,
|
|
119
|
-
{
|
|
120
|
-
size: "lg",
|
|
121
|
-
variant: "outline",
|
|
122
|
-
onClick: handleOpenInNewTab,
|
|
123
|
-
className: "w-full justify-start gap-2",
|
|
124
|
-
children: [
|
|
125
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4" }),
|
|
126
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Open in new tab" })
|
|
127
|
-
]
|
|
128
|
-
}
|
|
129
|
-
) : null,
|
|
130
|
-
/* @__PURE__ */ jsxs(
|
|
131
|
-
Button,
|
|
132
|
-
{
|
|
133
|
-
size: "lg",
|
|
134
|
-
variant: "ghost",
|
|
135
|
-
onClick: handleStayHere,
|
|
136
|
-
className: "w-full justify-start gap-2",
|
|
137
|
-
children: [
|
|
138
|
-
/* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" }),
|
|
139
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Stay here" })
|
|
140
|
-
]
|
|
141
|
-
}
|
|
142
|
-
)
|
|
143
|
-
] })
|
|
144
|
-
] }),
|
|
145
|
-
state === "error" && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
146
|
-
/* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
|
|
147
|
-
/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
|
|
148
|
-
/* @__PURE__ */ jsx(AlertDescription, { children: errorMessage ?? "Failed to duplicate agent." })
|
|
149
|
-
] }),
|
|
150
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleStayHere, className: "w-full", children: "Close" })
|
|
151
|
-
] })
|
|
152
|
-
] });
|
|
153
|
-
if (isMobile) {
|
|
154
|
-
return /* @__PURE__ */ jsx(Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DrawerContent, { className: "max-h-[85dvh] pb-safe", children: [
|
|
155
|
-
/* @__PURE__ */ jsxs(DrawerHeader, { children: [
|
|
156
|
-
/* @__PURE__ */ jsxs(DrawerTitle, { className: "flex items-center gap-2", children: [
|
|
157
|
-
/* @__PURE__ */ jsx(Copy, { className: "h-5 w-5" }),
|
|
158
|
-
titleText
|
|
159
|
-
] }),
|
|
160
|
-
/* @__PURE__ */ jsx(DrawerDescription, { children: descriptionText })
|
|
161
|
-
] }),
|
|
162
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 pb-4", children: body })
|
|
163
|
-
] }) });
|
|
164
|
-
}
|
|
165
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
166
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
167
|
-
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
168
|
-
/* @__PURE__ */ jsx(Copy, { className: "h-5 w-5" }),
|
|
169
|
-
titleText
|
|
170
|
-
] }),
|
|
171
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: descriptionText })
|
|
172
|
-
] }),
|
|
173
|
-
body
|
|
174
|
-
] }) });
|
|
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
|
-
}
|
|
185
|
-
export {
|
|
186
|
-
AgentDuplicateOutcomeDialog,
|
|
187
|
-
CURRENT_VERSION_CHOICE
|
|
188
|
-
};
|