@ai-matrx/chat 0.2.9 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/agent-copy/clipboard.d.ts +2 -4
- package/dist/agent-copy/clipboard.js +1 -14
- package/dist/agent-copy/page-capture/pageCapture.d.ts +11 -62
- package/dist/agent-copy/page-capture/pageCapture.js +5 -22
- 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/conversation-actions/conversation-verbs.js +2 -6
- 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 +7 -1
- package/dist/agents/components/run-controls/panels/BackendTargetPanel.js +7 -1
- package/dist/agents/components/shared/AgentSaveStatus.js +22 -1
- package/dist/agents/hooks/useAgentAutoSave.js +14 -2
- package/dist/agents/menu/agent-actions.js +3 -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/agent-shortcuts/thunks.js +9 -5
- package/dist/agents/redux/execution-system/instance-resources/remarks.d.ts +6 -0
- package/dist/agents/redux/execution-system/instance-resources/remarks.js +16 -0
- package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateCore.js +7 -1
- package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateList.js +7 -1
- package/dist/agents/redux/execution-system/message-crud/commit-inline-edit.thunk.js +2 -1
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.d.ts +12 -0
- package/dist/canvas/workspace/useCanvasWorkspaceConversation.js +29 -1
- package/dist/context-menu/types.d.ts +7 -0
- package/dist/cx-chat/components/messages/AssistantMessage.js +8 -10
- package/dist/cx-chat/components/messages/UserMessage.js +7 -1
- package/dist/cx-conversation/AssistantMessage.js +8 -10
- package/dist/cx-conversation/UserMessage.js +7 -1
- package/dist/host/clipboard.d.ts +7 -0
- package/dist/host/clipboard.js +11 -0
- package/dist/host/db-types.d.ts +15 -12
- package/dist/host/ui-slots.d.ts +2 -8
- package/dist/host/ui-slots.js +11 -16
- package/dist/public-chat/components/PublicMessageOptionsMenu.js +9 -16
- package/dist/store/create-chat-store.d.ts +10 -1
- package/dist/store/create-chat-store.js +4 -2
- package/dist/surfaces/runtime/SurfaceRuntimeContext.d.ts +10 -0
- package/dist/surfaces/runtime/SurfaceRuntimeContext.js +23 -1
- 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 -25
- package/dist/tool-call-visualization/result-fields/ResultFile.js +2 -8
- package/dist/tool-call-visualization/result-fields/ShortId.js +1 -23
- package/dist/voice-agent/components/LiveVoiceDoor.js +1 -1
- package/dist/voice-agent/components/VoiceDebugPanel.js +1 -1
- package/dist/voice-agent/hooks/useVoiceAgentInstance.js +1 -1
- package/dist/window-panels/windows/agents/ObservationalMemoryWindow.d.ts +0 -17
- package/dist/window-panels/windows/agents/ObservationalMemoryWindow.js +7 -1
- package/package.json +7 -7
|
@@ -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;
|
|
@@ -333,6 +333,9 @@ const saveShortcutField = createAsyncThunk(
|
|
|
333
333
|
dispatch(markShortcutSaved({ id: shortcutId }));
|
|
334
334
|
}
|
|
335
335
|
);
|
|
336
|
+
function withGlobalShortcutAdminLane(request) {
|
|
337
|
+
return request.setHeader("x-matrx-admin-lane", "1");
|
|
338
|
+
}
|
|
336
339
|
const createShortcut = createAsyncThunk("agentShortcut/create", async (shortcutData, { dispatch, getState }) => {
|
|
337
340
|
const userId = selectUserId(getState());
|
|
338
341
|
const organizationId = await ensureOrgId(shortcutData.organizationId);
|
|
@@ -346,7 +349,9 @@ const createShortcut = createAsyncThunk("agentShortcut/create", async (shortcutD
|
|
|
346
349
|
createdAt: "",
|
|
347
350
|
updatedAt: ""
|
|
348
351
|
};
|
|
349
|
-
const
|
|
352
|
+
const isGlobalWrite = shortcutData.userId === null && shortcutData.organizationId == null && shortcutData.projectId == null && shortcutData.taskId == null;
|
|
353
|
+
const insertRequest = shortcutTable(supabase).insert(agentShortcutToInsert(draft)).select().single();
|
|
354
|
+
const { data, error } = await (isGlobalWrite ? withGlobalShortcutAdminLane(insertRequest) : insertRequest);
|
|
350
355
|
if (error) throw pgErrorToError(error);
|
|
351
356
|
const newShortcut = toGlobalOwnershipRecord(
|
|
352
357
|
dbRowToAgentShortcut(data),
|
|
@@ -392,13 +397,12 @@ const duplicateShortcut = createAsyncThunk("agentShortcut/duplicate", async (arg
|
|
|
392
397
|
const promoteShortcutToGlobal = createAsyncThunk(
|
|
393
398
|
"agentShortcut/promoteToGlobal",
|
|
394
399
|
async ({ shortcutId, targetCategoryId, label }, { dispatch }) => {
|
|
395
|
-
const { data, error } = await
|
|
396
|
-
SHORTCUT_RPCS.promoteToGlobal,
|
|
397
|
-
{
|
|
400
|
+
const { data, error } = await withGlobalShortcutAdminLane(
|
|
401
|
+
supabase.rpc(SHORTCUT_RPCS.promoteToGlobal, {
|
|
398
402
|
p_shortcut_id: shortcutId,
|
|
399
403
|
p_target_category_id: targetCategoryId,
|
|
400
404
|
p_label: label && label.trim().length > 0 ? label.trim() : void 0
|
|
401
|
-
}
|
|
405
|
+
})
|
|
402
406
|
);
|
|
403
407
|
if (error) throw pgErrorToError(error);
|
|
404
408
|
const newShortcutId = data;
|
|
@@ -159,6 +159,12 @@ export interface RemarkDurability {
|
|
|
159
159
|
}
|
|
160
160
|
/** True while some chip of this conversation has not yet been saved server-side. */
|
|
161
161
|
export declare function hasPendingRemarkWrites(conversationId: string): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* True once the app has registered how unsent chips are kept. Without it every
|
|
164
|
+
* chip is browser-only and lost on reload, so a missing port is announced, never
|
|
165
|
+
* silently stood in for (the door a person touches must say when it is not wired).
|
|
166
|
+
*/
|
|
167
|
+
export declare function isRemarkDurabilityRegistered(): boolean;
|
|
162
168
|
/** Save every pending chip write and wait for its row/ref (called before a send). */
|
|
163
169
|
export declare function flushRemarkWrites(conversationId: string): Promise<void>;
|
|
164
170
|
/** Put a conversation's unsent chips back (any device) — the composer calls this once on mount. */
|
|
@@ -72,10 +72,23 @@ function buildSource(item, coalesceKey) {
|
|
|
72
72
|
function hasPendingRemarkWrites(conversationId) {
|
|
73
73
|
return durability?.hasPending(conversationId) ?? false;
|
|
74
74
|
}
|
|
75
|
+
function isRemarkDurabilityRegistered() {
|
|
76
|
+
return durability !== null;
|
|
77
|
+
}
|
|
78
|
+
const announcedMissing = /* @__PURE__ */ new Set();
|
|
79
|
+
function announceMissingDurability(op) {
|
|
80
|
+
if (announcedMissing.has(op)) return;
|
|
81
|
+
announcedMissing.add(op);
|
|
82
|
+
console.error(
|
|
83
|
+
`[remarks] ${op} ran with no durability port registered: unsent chips are NOT kept server-side. The app must call registerRemarkDurability (providers/ChatSurfaceRegistrations).`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
75
86
|
async function flushRemarkWrites(conversationId) {
|
|
87
|
+
if (!durability) announceMissingDurability("flush");
|
|
76
88
|
await durability?.flush(conversationId);
|
|
77
89
|
}
|
|
78
90
|
function restoreComposerRemarks(conversationId) {
|
|
91
|
+
if (!durability) announceMissingDurability("restore");
|
|
79
92
|
durability?.restore(conversationId);
|
|
80
93
|
}
|
|
81
94
|
let durability = null;
|
|
@@ -100,6 +113,7 @@ function stageRemark(conversationId, item, options = {}) {
|
|
|
100
113
|
dispatch(setResourceSource({ conversationId, resourceId: live.resourceId, source }));
|
|
101
114
|
dispatch(setResourcePreview({ conversationId, resourceId: live.resourceId, preview: source.label }));
|
|
102
115
|
if (!options.fromServer && !item.blockStateRef) {
|
|
116
|
+
if (!durability) announceMissingDurability("save");
|
|
103
117
|
durability?.save(conversationId, live.resourceId, coalesceKey, item);
|
|
104
118
|
}
|
|
105
119
|
return live.resourceId;
|
|
@@ -110,6 +124,7 @@ function stageRemark(conversationId, item, options = {}) {
|
|
|
110
124
|
dispatch(addResource({ conversationId, blockType: REMARKS_BLOCK_TYPE, source, resourceId }));
|
|
111
125
|
dispatch(setResourcePreview({ conversationId, resourceId, preview: source.label }));
|
|
112
126
|
if (!options.fromServer && !item.blockStateRef) {
|
|
127
|
+
if (!durability) announceMissingDurability("save");
|
|
113
128
|
durability?.save(conversationId, resourceId, coalesceKey, item);
|
|
114
129
|
}
|
|
115
130
|
return resourceId;
|
|
@@ -205,6 +220,7 @@ export {
|
|
|
205
220
|
dismissRemarkChip,
|
|
206
221
|
flushRemarkWrites,
|
|
207
222
|
hasPendingRemarkWrites,
|
|
223
|
+
isRemarkDurabilityRegistered,
|
|
208
224
|
readStoredRemarks,
|
|
209
225
|
registerRemarkDurability,
|
|
210
226
|
remarkChipTitle,
|
package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateCore.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../../../../../host/notify.js";
|
|
3
5
|
import { Badge, Button, Tile } from "@ai-matrx/design-system/controls";
|
|
4
6
|
import { useState } from "react";
|
|
5
7
|
import { cn } from "@ai-matrx/design-system";
|
|
@@ -18,9 +20,13 @@ import {
|
|
|
18
20
|
selectInstanceTitle
|
|
19
21
|
} from "../instance-ui-state.selectors.js";
|
|
20
22
|
function useCopyText(text) {
|
|
23
|
+
const { copyText } = useClipboard({
|
|
24
|
+
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
25
|
+
});
|
|
21
26
|
const [copied, setCopied] = useState(false);
|
|
22
27
|
const copy = () => {
|
|
23
|
-
|
|
28
|
+
copyText(text).then((copied2) => {
|
|
29
|
+
if (!copied2) return;
|
|
24
30
|
setCopied(true);
|
|
25
31
|
setTimeout(() => setCopied(false), 1500);
|
|
26
32
|
});
|
package/dist/agents/redux/execution-system/instance-ui-state/components/InstanceUIStateList.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../../../../../host/notify.js";
|
|
3
5
|
import { useState } from "react";
|
|
4
6
|
import { cn } from "@ai-matrx/design-system";
|
|
5
7
|
import {
|
|
@@ -19,9 +21,13 @@ import {
|
|
|
19
21
|
} from "../instance-ui-state.selectors.js";
|
|
20
22
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
21
23
|
function useCopyText(text) {
|
|
24
|
+
const { copyText } = useClipboard({
|
|
25
|
+
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
26
|
+
});
|
|
22
27
|
const [copied, setCopied] = useState(false);
|
|
23
28
|
const copy = () => {
|
|
24
|
-
|
|
29
|
+
copyText(text).then((copied2) => {
|
|
30
|
+
if (!copied2) return;
|
|
25
31
|
setCopied(true);
|
|
26
32
|
setTimeout(() => setCopied(false), 1500);
|
|
27
33
|
});
|
|
@@ -6,6 +6,7 @@ import { updateMessageRecord } from "../messages/messages.slice.js";
|
|
|
6
6
|
import { saveAnswerEdit } from "./save-answer-edit.thunk.js";
|
|
7
7
|
import { refetchSingleMessage } from "./refetch-single-message.thunk.js";
|
|
8
8
|
import { toast } from "../../../../host/notify.js";
|
|
9
|
+
import { copyToHostClipboard } from "../../../../host/clipboard.js";
|
|
9
10
|
import { buildContentBlocksForSave } from "../../../../cx-chat/utils/buildContentBlocksForSave.js";
|
|
10
11
|
import { stripCitationMarkers } from "../messages/message-citations.js";
|
|
11
12
|
import { extractFlatText } from "../messages/messages.selectors.js";
|
|
@@ -97,7 +98,7 @@ async function persistInlineEdit(dispatch, conversationId, messageId, entry) {
|
|
|
97
98
|
action: {
|
|
98
99
|
label: "Copy my edit",
|
|
99
100
|
onClick: () => {
|
|
100
|
-
void
|
|
101
|
+
void copyToHostClipboard(draft);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
});
|
|
@@ -62,6 +62,18 @@ export declare function conversationInAddress(param: string, search: string): st
|
|
|
62
62
|
export declare function workspaceChatMemoryKey(surfaceKey: string): string;
|
|
63
63
|
export declare function recallWorkspaceConversation(surfaceKey: string): string | null;
|
|
64
64
|
export declare function rememberWorkspaceConversation(surfaceKey: string, conversationId: string | null): void;
|
|
65
|
+
/**
|
|
66
|
+
* THE RESERVED ID OF AN UNSENT CHAT (2026-10-06). A new chat that has not been
|
|
67
|
+
* sent is unknown to the server, so a reload cannot reopen it and used to mint a
|
|
68
|
+
* different id — which orphaned every chip staged into it (a comment riding
|
|
69
|
+
* "with next message" came back gone). The id is reserved on this device per
|
|
70
|
+
* home and handed to the launch, so the unsent chat comes back under the SAME id
|
|
71
|
+
* and its saved chips are found. Released when the server has the conversation
|
|
72
|
+
* (the memory above takes over) and by New chat (a new chat is a new id).
|
|
73
|
+
*/
|
|
74
|
+
export declare function reservedChatKey(surfaceKey: string): string;
|
|
75
|
+
export declare function reserveWorkspaceConversationId(surfaceKey: string, fresh: boolean): string | undefined;
|
|
76
|
+
export declare function releaseWorkspaceConversationId(surfaceKey: string): void;
|
|
65
77
|
/** `search` with `?<param>=` set to `conversationId`, or removed when null. */
|
|
66
78
|
export declare function addressWithConversation(param: string, search: string, conversationId: string | null): string;
|
|
67
79
|
export interface CanvasWorkspaceConversationController {
|
|
@@ -39,6 +39,27 @@ function rememberWorkspaceConversation(surfaceKey, conversationId) {
|
|
|
39
39
|
} catch {
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
function reservedChatKey(surfaceKey) {
|
|
43
|
+
return `matrx:workspace-chat-reserved:${surfaceKey}`;
|
|
44
|
+
}
|
|
45
|
+
function reserveWorkspaceConversationId(surfaceKey, fresh) {
|
|
46
|
+
try {
|
|
47
|
+
const key = reservedChatKey(surfaceKey);
|
|
48
|
+
const kept = fresh ? null : window.localStorage.getItem(key);
|
|
49
|
+
if (kept && CONVERSATION_ID.test(kept)) return kept;
|
|
50
|
+
const id = window.crypto.randomUUID();
|
|
51
|
+
window.localStorage.setItem(key, id);
|
|
52
|
+
return id;
|
|
53
|
+
} catch {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function releaseWorkspaceConversationId(surfaceKey) {
|
|
58
|
+
try {
|
|
59
|
+
window.localStorage.removeItem(reservedChatKey(surfaceKey));
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
42
63
|
function addressWithConversation(param, search, conversationId) {
|
|
43
64
|
const params = new URLSearchParams(search);
|
|
44
65
|
if (conversationId) params.set(param, conversationId);
|
|
@@ -99,6 +120,7 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
|
|
|
99
120
|
if (addressParam === null || !addressRead || !conversationId) return;
|
|
100
121
|
const shown = request.kind === "open" || serverHasIt ? conversationId : null;
|
|
101
122
|
rememberWorkspaceConversation(surfaceKey, shown);
|
|
123
|
+
if (shown) releaseWorkspaceConversationId(surfaceKey);
|
|
102
124
|
const search = addressWithConversation(addressParam, window.location.search, shown);
|
|
103
125
|
if (search === window.location.search) return;
|
|
104
126
|
replaceAddressWithoutNavigating(`${window.location.pathname}${search}${window.location.hash}`);
|
|
@@ -111,8 +133,11 @@ function useCanvasWorkspaceConversation(surfaceKey, input) {
|
|
|
111
133
|
const stale = () => handled.current !== key;
|
|
112
134
|
if (request.kind === "new" || request.kind === "agent") {
|
|
113
135
|
const runtime = ownSurface ? { runtime: { surfaceName: null } } : {};
|
|
114
|
-
const
|
|
136
|
+
const reserved = addressParam !== null ? reserveWorkspaceConversationId(surfaceKey, request.nonce > 0) : void 0;
|
|
137
|
+
const reservation = reserved ? { conversationId: reserved } : {};
|
|
138
|
+
const launch = request.kind === "agent" ? launchAgent(request.agentId, { surfaceKey, sourceFeature: "chat", ...reservation, ...runtime }) : launchMandate(request.mandateKey ?? DEFAULT_NEW_CHAT_MANDATE_KEY, {
|
|
115
139
|
surfaceKey,
|
|
140
|
+
...reservation,
|
|
116
141
|
// A REGISTERED feature, never a new string: this surface IS the chat.
|
|
117
142
|
sourceFeature: "chat",
|
|
118
143
|
...runtime
|
|
@@ -193,7 +218,10 @@ export {
|
|
|
193
218
|
addressWithConversation,
|
|
194
219
|
conversationInAddress,
|
|
195
220
|
recallWorkspaceConversation,
|
|
221
|
+
releaseWorkspaceConversationId,
|
|
196
222
|
rememberWorkspaceConversation,
|
|
223
|
+
reserveWorkspaceConversationId,
|
|
224
|
+
reservedChatKey,
|
|
197
225
|
useCanvasWorkspaceConversation,
|
|
198
226
|
workspaceChatMemoryKey
|
|
199
227
|
};
|
|
@@ -4,6 +4,7 @@ import type { SourceFeature } from "../agents/types/instance.types.js";
|
|
|
4
4
|
import type { Scope } from "../agents/redux/shared/scope.js";
|
|
5
5
|
import type { ApplicationScope } from "../agents/types/scope.types.js";
|
|
6
6
|
import type { EntityTypeToken } from "@ai-matrx/associations";
|
|
7
|
+
import type { ResolvedItem } from "@ai-matrx/alchemy/declare";
|
|
7
8
|
/**
|
|
8
9
|
* The few shapes the host owns (the rich document's content source and actions, the sharing
|
|
9
10
|
* system's resource types). The package declares them open; the host fills them in by augmenting
|
|
@@ -399,6 +400,12 @@ export interface MenuContentProps {
|
|
|
399
400
|
variant: MenuVariant;
|
|
400
401
|
/** The clicked target's own name for the header (`CONTEXT_MENU_HEADING_KEY`). */
|
|
401
402
|
heading?: ContextMenuHeading | null;
|
|
403
|
+
/**
|
|
404
|
+
* The declared item the menu opened on (a table row: `table_row`, identity `table_id` + `row_id`),
|
|
405
|
+
* raw-free, as `itemSources` resolved it. It becomes the click target's `item`, so Actions and
|
|
406
|
+
* menu rows read the declared item instead of re-deriving it. Null = not on an item.
|
|
407
|
+
*/
|
|
408
|
+
item?: ResolvedItem | null;
|
|
402
409
|
sourceFeature: SourceFeature;
|
|
403
410
|
surfaceName?: string;
|
|
404
411
|
menuVersion: number;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../../../host/notify.js";
|
|
3
5
|
import { recognizeOurFileUrl } from "@ai-matrx/media/files/engine";
|
|
4
6
|
import { useState, useEffect, useCallback } from "react";
|
|
5
7
|
import {
|
|
@@ -22,6 +24,7 @@ import { chatConversationsActions } from "../../_legacy-stubs.js";
|
|
|
22
24
|
import { RichDocumentActions } from "../../../host/rich-document-slots.js";
|
|
23
25
|
import { MessageTimestamp } from "../../../agents/components/messages-display/MessageTimestamp.js";
|
|
24
26
|
import { ErrorAlchemyMenu } from "../../../host/ui-slots.js";
|
|
27
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
25
28
|
function AssistantMessage({
|
|
26
29
|
message,
|
|
27
30
|
sessionId,
|
|
@@ -31,6 +34,9 @@ function AssistantMessage({
|
|
|
31
34
|
isTtsRequest = false,
|
|
32
35
|
onContentChange
|
|
33
36
|
}) {
|
|
37
|
+
const { copyText } = useClipboard({
|
|
38
|
+
notify: (message2, kind) => kind === "error" ? toast.error(message2) : toast.success(message2)
|
|
39
|
+
});
|
|
34
40
|
const [isAppearing, setIsAppearing] = useState(true);
|
|
35
41
|
const [isAudioLinkCopied, setIsAudioLinkCopied] = useState(false);
|
|
36
42
|
const [isDownloading, setIsDownloading] = useState(false);
|
|
@@ -97,15 +103,8 @@ function AssistantMessage({
|
|
|
97
103
|
try {
|
|
98
104
|
const resp = await fetch(audioUrl);
|
|
99
105
|
const blob = await resp.blob();
|
|
100
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
101
106
|
const ext = audioMimeType?.split("/")[1] ?? "wav";
|
|
102
|
-
|
|
103
|
-
link.href = objectUrl;
|
|
104
|
-
link.download = `audio-response.${ext}`;
|
|
105
|
-
document.body.appendChild(link);
|
|
106
|
-
link.click();
|
|
107
|
-
document.body.removeChild(link);
|
|
108
|
-
URL.revokeObjectURL(objectUrl);
|
|
107
|
+
downloadFile(`audio-response.${ext}`, blob, blob.type);
|
|
109
108
|
} catch {
|
|
110
109
|
} finally {
|
|
111
110
|
setIsDownloading(false);
|
|
@@ -186,8 +185,7 @@ function AssistantMessage({
|
|
|
186
185
|
variant: "quiet",
|
|
187
186
|
onClick: async () => {
|
|
188
187
|
if (!audioUrl) return;
|
|
189
|
-
await
|
|
190
|
-
});
|
|
188
|
+
if (!await copyText(audioUrl)) return;
|
|
191
189
|
setIsAudioLinkCopied(true);
|
|
192
190
|
setTimeout(() => setIsAudioLinkCopied(false), 2e3);
|
|
193
191
|
},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../../../host/notify.js";
|
|
3
5
|
import { useState, useEffect, useRef, useMemo } from "react";
|
|
4
6
|
import {
|
|
5
7
|
Check,
|
|
@@ -84,6 +86,9 @@ function UserMessage({
|
|
|
84
86
|
onContentChange,
|
|
85
87
|
compact = false
|
|
86
88
|
}) {
|
|
89
|
+
const { copyText } = useClipboard({
|
|
90
|
+
notify: (message2, kind) => kind === "error" ? toast.error(message2) : toast.success(message2)
|
|
91
|
+
});
|
|
87
92
|
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
88
93
|
const [isCopied, setIsCopied] = useState(false);
|
|
89
94
|
const [shouldBeCollapsible, setShouldBeCollapsible] = useState(false);
|
|
@@ -130,7 +135,8 @@ function UserMessage({
|
|
|
130
135
|
}, [isEditing]);
|
|
131
136
|
const handleCopy = (e) => {
|
|
132
137
|
e.stopPropagation();
|
|
133
|
-
|
|
138
|
+
copyText(message.content).then((copied) => {
|
|
139
|
+
if (!copied) return;
|
|
134
140
|
setIsCopied(true);
|
|
135
141
|
setTimeout(() => setIsCopied(false), 2e3);
|
|
136
142
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../host/notify.js";
|
|
3
5
|
import { recognizeOurFileUrl } from "@ai-matrx/media/files/engine";
|
|
4
6
|
import { useState, useEffect, useCallback } from "react";
|
|
5
7
|
import {
|
|
@@ -22,6 +24,7 @@ import { chatConversationsActions } from "./_legacy-stubs.js";
|
|
|
22
24
|
import { RichDocumentActions } from "../host/rich-document-slots.js";
|
|
23
25
|
import { MessageTimestamp } from "../agents/components/messages-display/MessageTimestamp.js";
|
|
24
26
|
import { ErrorAlchemyMenu } from "../host/ui-slots.js";
|
|
27
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
25
28
|
function AssistantMessage({
|
|
26
29
|
message,
|
|
27
30
|
sessionId,
|
|
@@ -31,6 +34,9 @@ function AssistantMessage({
|
|
|
31
34
|
isTtsRequest = false,
|
|
32
35
|
onContentChange
|
|
33
36
|
}) {
|
|
37
|
+
const { copyText } = useClipboard({
|
|
38
|
+
notify: (message2, kind) => kind === "error" ? toast.error(message2) : toast.success(message2)
|
|
39
|
+
});
|
|
34
40
|
const [isAppearing, setIsAppearing] = useState(true);
|
|
35
41
|
const [isAudioLinkCopied, setIsAudioLinkCopied] = useState(false);
|
|
36
42
|
const [isDownloading, setIsDownloading] = useState(false);
|
|
@@ -97,15 +103,8 @@ function AssistantMessage({
|
|
|
97
103
|
try {
|
|
98
104
|
const resp = await fetch(audioUrl);
|
|
99
105
|
const blob = await resp.blob();
|
|
100
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
101
106
|
const ext = audioMimeType?.split("/")[1] ?? "wav";
|
|
102
|
-
|
|
103
|
-
link.href = objectUrl;
|
|
104
|
-
link.download = `audio-response.${ext}`;
|
|
105
|
-
document.body.appendChild(link);
|
|
106
|
-
link.click();
|
|
107
|
-
document.body.removeChild(link);
|
|
108
|
-
URL.revokeObjectURL(objectUrl);
|
|
107
|
+
downloadFile(`audio-response.${ext}`, blob, blob.type);
|
|
109
108
|
} catch {
|
|
110
109
|
} finally {
|
|
111
110
|
setIsDownloading(false);
|
|
@@ -186,8 +185,7 @@ function AssistantMessage({
|
|
|
186
185
|
variant: "quiet",
|
|
187
186
|
onClick: async () => {
|
|
188
187
|
if (!audioUrl) return;
|
|
189
|
-
await
|
|
190
|
-
});
|
|
188
|
+
if (!await copyText(audioUrl)) return;
|
|
191
189
|
setIsAudioLinkCopied(true);
|
|
192
190
|
setTimeout(() => setIsAudioLinkCopied(false), 2e3);
|
|
193
191
|
},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { toast } from "../host/notify.js";
|
|
3
5
|
import { useState, useEffect, useRef, useMemo } from "react";
|
|
4
6
|
import {
|
|
5
7
|
Check,
|
|
@@ -84,6 +86,9 @@ function UserMessage({
|
|
|
84
86
|
onContentChange,
|
|
85
87
|
compact = false
|
|
86
88
|
}) {
|
|
89
|
+
const { copyText } = useClipboard({
|
|
90
|
+
notify: (message2, kind) => kind === "error" ? toast.error(message2) : toast.success(message2)
|
|
91
|
+
});
|
|
87
92
|
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
88
93
|
const [isCopied, setIsCopied] = useState(false);
|
|
89
94
|
const [shouldBeCollapsible, setShouldBeCollapsible] = useState(false);
|
|
@@ -130,7 +135,8 @@ function UserMessage({
|
|
|
130
135
|
}, [isEditing]);
|
|
131
136
|
const handleCopy = (e) => {
|
|
132
137
|
e.stopPropagation();
|
|
133
|
-
|
|
138
|
+
copyText(message.content).then((copied) => {
|
|
139
|
+
if (!copied) return;
|
|
134
140
|
setIsCopied(true);
|
|
135
141
|
setTimeout(() => setIsCopied(false), 2e3);
|
|
136
142
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The clipboard seam — plain-text copy for package code outside a React component.
|
|
3
|
+
*
|
|
4
|
+
* The kit `copyText` with its success and failure notices routed through the notify seam, so a
|
|
5
|
+
* refused copy always announces itself. Resolves to whether the copy landed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function copyToHostClipboard(text: string, successMessage?: string): Promise<boolean>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { copyText } from "@ai-matrx/kit/clipboard";
|
|
2
|
+
import { toast } from "./notify.js";
|
|
3
|
+
function copyToHostClipboard(text, successMessage) {
|
|
4
|
+
return copyText(text, {
|
|
5
|
+
successMessage,
|
|
6
|
+
notify: (message, kind) => kind === "error" ? toast.error(message) : toast.success(message)
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
copyToHostClipboard
|
|
11
|
+
};
|
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;
|