@ai-matrx/chat 0.6.5 → 0.6.6
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 +55 -0
- package/dist/agents/components/chat/ChatHistorySidebar.d.ts +4 -4
- package/dist/agents/components/chat/ChatHistorySidebar.js +2 -2
- package/dist/agents/components/chat/ChatSidebarMenu.js +1 -1
- package/dist/agents/components/conversation-actions/conversationActionRegistry.d.ts +8 -0
- package/dist/agents/components/conversation-actions/conversationActionRegistry.js +11 -4
- package/dist/agents/components/conversation-history/ConversationArchiveSection.d.ts +7 -0
- package/dist/agents/components/conversation-history/{ConversationTrashSection.js → ConversationArchiveSection.js} +20 -20
- package/dist/agents/components/conversation-history/ConversationHistorySidebar.d.ts +29 -10
- package/dist/agents/components/conversation-history/ConversationHistorySidebar.js +75 -76
- package/dist/agents/components/conversation-history/ConversationPickerWindow.js +1 -2
- package/dist/agents/components/inputs/smart-input/RunControlsTabPanel.d.ts +2 -0
- package/dist/agents/components/inputs/smart-input/RunControlsTabPanel.js +8 -1
- package/dist/agents/components/inputs/smart-input/RunToolPicker.js +2 -1
- package/dist/agents/components/run/AgentRunHeader.js +11 -5
- package/dist/agents/components/shared/AgentHeaderMobile.js +1 -0
- package/dist/agents/components/shared/AgentModeController.js +90 -27
- package/dist/agents/components/shared/AgentSelectorIsland.js +2 -1
- package/dist/agents/redux/agent-definition/run-tier-record.js +1 -0
- package/dist/agents/redux/conversation-history/history-filters.d.ts +6 -0
- package/dist/agents/redux/conversation-history/history-filters.js +4 -1
- package/dist/agents/redux/conversation-history/selectors.js +2 -3
- package/dist/agents/redux/conversation-history/slice.d.ts +6 -9
- package/dist/agents/redux/conversation-history/slice.js +14 -23
- package/dist/agents/redux/conversation-history/thunks.js +1 -23
- package/dist/agents/redux/conversation-history/types.d.ts +12 -12
- package/dist/agents/redux/conversation-history/types.js +4 -6
- package/dist/agents/redux/conversation-list/conversation-list.selectors.d.ts +6 -0
- package/dist/agents/redux/conversation-list/conversation-list.selectors.js +10 -0
- package/dist/agents/redux/conversation-list/conversation-list.types.d.ts +6 -0
- package/dist/agents/redux/conversation-list/conversation-row-actions.thunks.js +11 -2
- package/dist/agents/redux/conversation-list/conversation-trash.thunks.d.ts +11 -4
- package/dist/agents/redux/conversation-list/conversation-trash.thunks.js +41 -20
- package/dist/agents/redux/execution-system/message-crud/soft-delete-conversation.thunk.js +5 -2
- package/dist/window-panels/windows/agents/AgentRunWindow.d.ts +1 -1
- package/dist/window-panels/windows/agents/AgentRunWindow.js +27 -21
- package/dist/window-panels/windows/agents/RunControlsWindow.js +1 -0
- package/package.json +5 -5
- package/dist/agents/components/conversation-history/ConversationTrashSection.d.ts +0 -7
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* THE
|
|
2
|
+
* THE ARCHIVE — reading archived AND deleted conversations back, and restoring one.
|
|
3
|
+
*
|
|
4
|
+
* One place, called "Archived" (Arman, 2026-10-08): a conversation the person
|
|
5
|
+
* archived (`status = 'archived'`) and one they deleted (`deleted_at` stamped)
|
|
6
|
+
* both land in the same section at the foot of every conversation list, and
|
|
7
|
+
* Restore brings either back. The store fields keep their `trash*` names.
|
|
3
8
|
*
|
|
4
9
|
* DD-179. Deleting a conversation was a one-way door: `cx_soft_delete_conversation`
|
|
5
10
|
* stamps `deleted_at` and every reader filters `deleted_at IS NULL`, so the row
|
|
@@ -17,7 +22,7 @@
|
|
|
17
22
|
*/
|
|
18
23
|
import type { ChatDispatch, ChatRootState } from "../../../store/root-state.js";
|
|
19
24
|
import type { ConversationListItem } from "./conversation-list.types.js";
|
|
20
|
-
export declare const
|
|
25
|
+
export declare const fetchArchivedConversations: import("@reduxjs/toolkit").AsyncThunk<{
|
|
21
26
|
items: ConversationListItem[];
|
|
22
27
|
}, void | {
|
|
23
28
|
limit?: number;
|
|
@@ -41,8 +46,10 @@ interface RestoreResult {
|
|
|
41
46
|
restored: boolean;
|
|
42
47
|
}
|
|
43
48
|
/**
|
|
44
|
-
*
|
|
45
|
-
* inverse of
|
|
49
|
+
* Bring a row out of the Archived section. A deleted row goes back through
|
|
50
|
+
* ONE door, `public.cx_restore_conversation` (the exact inverse of
|
|
51
|
+
* `cx_soft_delete_conversation`); an archived row goes back through
|
|
52
|
+
* `setConversationArchived`. A row that is both takes both doors.
|
|
46
53
|
*
|
|
47
54
|
* `false` back from the RPC means the row is absent, was never deleted, or is
|
|
48
55
|
* not this caller's to restore — all of which are a refusal, not a success, so
|
|
@@ -12,11 +12,12 @@ import {
|
|
|
12
12
|
} from "./conversation-list.slice.js";
|
|
13
13
|
import { upsertConversationIntoScopes } from "../conversation-history/slice.js";
|
|
14
14
|
import { getUserId } from "../../../host/identity.js";
|
|
15
|
+
import { setConversationArchived } from "./conversation-row-actions.thunks.js";
|
|
15
16
|
const CONVERSATION_TRASH_PAGE_SIZE_KNOB = {
|
|
16
17
|
feature: "agents.conversations",
|
|
17
18
|
key: "trash_page_size"
|
|
18
19
|
};
|
|
19
|
-
const
|
|
20
|
+
const fetchArchivedConversations = createAsyncThunk(
|
|
20
21
|
"conversationList/fetchTrashed",
|
|
21
22
|
async (rawArgs, { dispatch, getState, rejectWithValue }) => {
|
|
22
23
|
dispatch(setTrashLoading());
|
|
@@ -43,18 +44,18 @@ const fetchTrashedConversations = createAsyncThunk(
|
|
|
43
44
|
}
|
|
44
45
|
limit = resolved;
|
|
45
46
|
} catch (error2) {
|
|
46
|
-
const message = `The
|
|
47
|
+
const message = `The archive could not be opened: the setting that decides how many archived conversations one page shows ("${CONVERSATION_TRASH_PAGE_SIZE_KNOB.feature}.${CONVERSATION_TRASH_PAGE_SIZE_KNOB.key}") could not be read \u2014 ${error2 instanceof Error ? error2.message : String(error2)}.`;
|
|
47
48
|
dispatch(setTrashError(message));
|
|
48
49
|
return rejectWithValue({ message });
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
let query = supabase.schema("chat").from("conversation").select(
|
|
52
53
|
"id, title, description, status, message_count, initial_agent_id, last_model_id, source_app, source_feature, created_at, updated_at, deleted_at, exclude_from_kg"
|
|
53
|
-
).
|
|
54
|
+
).or("deleted_at.not.is.null,status.eq.archived").eq("is_ephemeral", false).order("updated_at", { ascending: false }).limit(limit);
|
|
54
55
|
if (viewerId) query = query.eq("created_by", viewerId);
|
|
55
56
|
const { data, error } = await query;
|
|
56
57
|
if (error) {
|
|
57
|
-
const message = operationFailed("open the
|
|
58
|
+
const message = operationFailed("open the archive", error).message;
|
|
58
59
|
dispatch(setTrashError(message));
|
|
59
60
|
return rejectWithValue({ message });
|
|
60
61
|
}
|
|
@@ -73,7 +74,8 @@ const fetchTrashedConversations = createAsyncThunk(
|
|
|
73
74
|
agentId: row.initial_agent_id ?? null,
|
|
74
75
|
lastModelId: row.last_model_id ?? null,
|
|
75
76
|
sourceApp: row.source_app ?? void 0,
|
|
76
|
-
sourceFeature: row.source_feature ?? void 0
|
|
77
|
+
sourceFeature: row.source_feature ?? void 0,
|
|
78
|
+
deletedAt: row.deleted_at ?? null
|
|
77
79
|
}));
|
|
78
80
|
dispatch(setTrashSuccess({ items }));
|
|
79
81
|
return { items };
|
|
@@ -82,27 +84,46 @@ const fetchTrashedConversations = createAsyncThunk(
|
|
|
82
84
|
const restoreConversation = createAsyncThunk(
|
|
83
85
|
"conversationList/restore",
|
|
84
86
|
async ({ conversationId }, { dispatch, getState, rejectWithValue }) => {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
const item = getState().conversationList.trashByConversationId[conversationId];
|
|
88
|
+
const wasDeleted = !item || Boolean(item.deletedAt);
|
|
89
|
+
const wasArchived = item?.status === "archived";
|
|
90
|
+
if (wasDeleted) {
|
|
91
|
+
const { data, error } = await supabase.rpc("cx_restore_conversation", {
|
|
92
|
+
p_conversation_id: conversationId
|
|
91
93
|
});
|
|
94
|
+
if (error) {
|
|
95
|
+
return rejectWithValue({
|
|
96
|
+
message: operationFailed("restore this conversation", error).message
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (data !== true) {
|
|
100
|
+
return rejectWithValue({
|
|
101
|
+
message: operationFailed("restore this conversation").message
|
|
102
|
+
});
|
|
103
|
+
}
|
|
92
104
|
}
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
if (wasArchived) {
|
|
106
|
+
const unarchived = await dispatch(
|
|
107
|
+
setConversationArchived({ conversationId, archived: false })
|
|
108
|
+
);
|
|
109
|
+
if (setConversationArchived.rejected.match(unarchived)) {
|
|
110
|
+
return rejectWithValue({
|
|
111
|
+
message: unarchived.payload?.message ?? operationFailed("restore this conversation").message
|
|
112
|
+
});
|
|
113
|
+
}
|
|
97
114
|
}
|
|
98
|
-
const item = getState().conversationList.trashByConversationId[conversationId];
|
|
99
115
|
dispatch(removeFromTrash(conversationId));
|
|
100
116
|
if (item) {
|
|
101
|
-
|
|
117
|
+
const row = {
|
|
118
|
+
...item,
|
|
119
|
+
status: wasArchived ? "active" : item.status,
|
|
120
|
+
deletedAt: null
|
|
121
|
+
};
|
|
122
|
+
dispatch(prependConversation(row));
|
|
102
123
|
dispatch(
|
|
103
124
|
upsertConversationIntoScopes({
|
|
104
|
-
row
|
|
105
|
-
agentId:
|
|
125
|
+
row,
|
|
126
|
+
agentId: row.agentId ?? ""
|
|
106
127
|
})
|
|
107
128
|
);
|
|
108
129
|
}
|
|
@@ -110,6 +131,6 @@ const restoreConversation = createAsyncThunk(
|
|
|
110
131
|
}
|
|
111
132
|
);
|
|
112
133
|
export {
|
|
113
|
-
|
|
134
|
+
fetchArchivedConversations,
|
|
114
135
|
restoreConversation
|
|
115
136
|
};
|
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
addToTrash
|
|
10
10
|
} from "../../conversation-list/conversation-list.slice.js";
|
|
11
11
|
import { removeConversationFromScopes } from "../../conversation-history/slice.js";
|
|
12
|
+
import { findConversationRow } from "../../conversation-list/conversation-list.selectors.js";
|
|
12
13
|
import { clearCacheBypass } from "./cache-bypass.slice.js";
|
|
13
14
|
import { invalidateConversationCache } from "./invalidate-conversation-cache.thunk.js";
|
|
14
15
|
const softDeleteConversation = createAsyncThunk(
|
|
15
16
|
"conversations/softDelete",
|
|
16
17
|
async ({ conversationId }, { dispatch, getState, rejectWithValue }) => {
|
|
17
|
-
const snapshot = getState()
|
|
18
|
+
const snapshot = findConversationRow(getState(), conversationId);
|
|
18
19
|
const { data, error } = await supabase.rpc("cx_soft_delete_conversation", {
|
|
19
20
|
p_conversation_id: conversationId
|
|
20
21
|
});
|
|
@@ -31,7 +32,9 @@ const softDeleteConversation = createAsyncThunk(
|
|
|
31
32
|
}
|
|
32
33
|
void dispatch(invalidateConversationCache({ conversationId }));
|
|
33
34
|
dispatch(clearCacheBypass(conversationId));
|
|
34
|
-
if (snapshot)
|
|
35
|
+
if (snapshot) {
|
|
36
|
+
dispatch(addToTrash({ ...snapshot, deletedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
37
|
+
}
|
|
35
38
|
dispatch(removeFromConversationList(conversationId));
|
|
36
39
|
dispatch(removeConversationFromScopes({ conversationId }));
|
|
37
40
|
dispatch(clearMessages(conversationId));
|
|
@@ -5,7 +5,7 @@ import type { AnyMandateKey } from "@ai-matrx/agents/mandates";
|
|
|
5
5
|
* Floating-window equivalent of `/agents/[id]/run`.
|
|
6
6
|
*
|
|
7
7
|
* ┌──────────────────────────────────────────────────────────────┐
|
|
8
|
-
* │
|
|
8
|
+
* │ ●●● ◧ ✎ Chat title ▾ (or agent name) │ ← WindowPanel title bar
|
|
9
9
|
* ├──────────────┬───────────────────────────────────────────────┤
|
|
10
10
|
* │ Conversations│ AgentConversationColumn (main experience) │
|
|
11
11
|
* │ (sidebar) │ │
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { logFailure } from "../../../ui/expectedRefusal.js";
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
|
-
import { AlertTriangle, Brain, Loader2,
|
|
5
|
+
import { AlertTriangle, Brain, Loader2, RotateCw, SquarePen } from "lucide-react";
|
|
6
6
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
7
7
|
import { WindowPanel } from "../../../host/ui-slots.js";
|
|
8
8
|
import { useAppDispatch, useAppSelector } from "../../../store/hooks.js";
|
|
@@ -65,7 +65,7 @@ const AGENT_RUN_WINDOW_POSITIONS = [
|
|
|
65
65
|
"bottom-right",
|
|
66
66
|
"bottom-left"
|
|
67
67
|
];
|
|
68
|
-
const HEADER_NEW_RUN_BTN = "flex
|
|
68
|
+
const HEADER_NEW_RUN_BTN = "flex shrink-0 items-center justify-center rounded p-0.5 text-foreground/60 transition-colors hover:bg-accent/60 hover:text-foreground disabled:opacity-40 [&_svg]:h-3.5 [&_svg]:w-3.5";
|
|
69
69
|
function SessionChatRow({
|
|
70
70
|
chat,
|
|
71
71
|
active,
|
|
@@ -155,7 +155,6 @@ function AgentRunWindowSidebar({
|
|
|
155
155
|
openInPlace: true,
|
|
156
156
|
surfaceKey: surfaceKey ?? void 0,
|
|
157
157
|
getConversationHref,
|
|
158
|
-
historyLabel: "Agent history",
|
|
159
158
|
topSlot: sessionSection,
|
|
160
159
|
className: "bg-transparent"
|
|
161
160
|
}
|
|
@@ -187,28 +186,34 @@ function AgentRunWindowNewRunButton({
|
|
|
187
186
|
title: "New chat",
|
|
188
187
|
"aria-label": "New chat",
|
|
189
188
|
className: HEADER_NEW_RUN_BTN,
|
|
190
|
-
children: /* @__PURE__ */ jsx(
|
|
189
|
+
children: /* @__PURE__ */ jsx(SquarePen, {})
|
|
191
190
|
}
|
|
192
191
|
);
|
|
193
192
|
}
|
|
194
193
|
function WindowTitleContent({
|
|
195
194
|
agentId,
|
|
196
|
-
|
|
195
|
+
agentName,
|
|
196
|
+
conversationId,
|
|
197
197
|
onAgentSelect
|
|
198
198
|
}) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
199
|
+
const listTitle = useAppSelector(
|
|
200
|
+
(state) => conversationId ? selectConversationListItemById(conversationId)(state)?.title ?? null : null
|
|
201
|
+
);
|
|
202
|
+
const localTitle = useAppSelector(
|
|
203
|
+
(state) => conversationId ? state.messages.byConversationId[conversationId]?.title ?? null : null
|
|
204
|
+
);
|
|
205
|
+
const conversationTitle = (listTitle ?? localTitle)?.trim() || null;
|
|
206
|
+
const label = !agentId ? "Select agent\u2026" : conversationTitle ?? agentName;
|
|
207
|
+
return /* @__PURE__ */ jsx(
|
|
208
|
+
AgentListDropdown,
|
|
209
|
+
{
|
|
210
|
+
onSelect: onAgentSelect,
|
|
211
|
+
label,
|
|
212
|
+
noBorder: true,
|
|
213
|
+
compact: true,
|
|
214
|
+
className: "max-w-[260px] md:max-w-[360px] rounded-none bg-transparent"
|
|
215
|
+
}
|
|
216
|
+
);
|
|
212
217
|
}
|
|
213
218
|
function AgentRunBody({
|
|
214
219
|
agentId,
|
|
@@ -450,7 +455,7 @@ function AgentRunBodyMenu({
|
|
|
450
455
|
{
|
|
451
456
|
sourceFeature: "agent-builder",
|
|
452
457
|
contentSource: { type: "raw" },
|
|
453
|
-
entity: { type: "conversation", id: conversationId, title: agentName ?? "
|
|
458
|
+
entity: { type: "conversation", id: conversationId, title: agentName ?? "Chat" },
|
|
454
459
|
extraSections: [agentSection],
|
|
455
460
|
children
|
|
456
461
|
}
|
|
@@ -616,7 +621,8 @@ function AgentRunWindowInner({
|
|
|
616
621
|
WindowTitleContent,
|
|
617
622
|
{
|
|
618
623
|
agentId,
|
|
619
|
-
|
|
624
|
+
agentName,
|
|
625
|
+
conversationId: activeConversationId,
|
|
620
626
|
onAgentSelect: (id) => handleAgentSelect(id)
|
|
621
627
|
}
|
|
622
628
|
),
|
|
@@ -635,7 +641,7 @@ function AgentRunWindowInner({
|
|
|
635
641
|
...addressedConversationId ? { [AGENT_RUN_WINDOW_CONVERSATION_ARG]: addressedConversationId } : {}
|
|
636
642
|
},
|
|
637
643
|
onCollectData: collectData,
|
|
638
|
-
|
|
644
|
+
actionsLeft: agentId && surfaceKey ? /* @__PURE__ */ jsx(
|
|
639
645
|
AgentRunWindowNewRunButton,
|
|
640
646
|
{
|
|
641
647
|
surfaceKey,
|
|
@@ -86,6 +86,7 @@ function RunControlsWindowInner({
|
|
|
86
86
|
id: "run-controls-window",
|
|
87
87
|
overlayId: OVERLAY_ID,
|
|
88
88
|
onClose,
|
|
89
|
+
closeOnEscape: true,
|
|
89
90
|
title: "Chat Options",
|
|
90
91
|
initialRect: roomy ? { x: 72, y: 56, width: 980, height: 680 } : { x: 72, y: 56, width: 480, height: 640 },
|
|
91
92
|
height: roomy ? 680 : 640,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/chat",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
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. One chat, one renderer: the old kernel entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) are retired.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -172,19 +172,19 @@
|
|
|
172
172
|
"@ai-matrx/browser-audio": "latest",
|
|
173
173
|
"@ai-matrx/canvas": "latest",
|
|
174
174
|
"@ai-matrx/content-ir": "latest",
|
|
175
|
-
"@ai-matrx/content-ir-react": "latest",
|
|
176
175
|
"@ai-matrx/data": "latest",
|
|
177
176
|
"@ai-matrx/design-system": "latest",
|
|
178
177
|
"@ai-matrx/diff": "latest",
|
|
179
178
|
"@ai-matrx/icons": "latest",
|
|
179
|
+
"@ai-matrx/content-ir-react": "latest",
|
|
180
180
|
"@ai-matrx/kit": "latest",
|
|
181
181
|
"@ai-matrx/media": "latest",
|
|
182
182
|
"@ai-matrx/print": "latest",
|
|
183
183
|
"@ai-matrx/realtime": "latest",
|
|
184
|
-
"@ai-matrx/records": "latest",
|
|
185
184
|
"@ai-matrx/records-ui": "latest",
|
|
186
185
|
"@ai-matrx/rich-content": "latest",
|
|
187
|
-
"@ai-matrx/rich-editor": "latest"
|
|
186
|
+
"@ai-matrx/rich-editor": "latest",
|
|
187
|
+
"@ai-matrx/records": "latest"
|
|
188
188
|
},
|
|
189
189
|
"devDependencies": {
|
|
190
190
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
@@ -300,5 +300,5 @@
|
|
|
300
300
|
"check:side-panel-bundle": "node ./scripts/check-side-panel-bundle.mjs",
|
|
301
301
|
"check:side-effects": "node ../scripts/check-side-effects-listed.mjs . && node ../scripts/check-side-effects-listed.mjs --self-test"
|
|
302
302
|
},
|
|
303
|
-
"gitHead": "
|
|
303
|
+
"gitHead": "5ddae9857dcebf5690d1b00678ce3b7d1daef758"
|
|
304
304
|
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
interface ConversationTrashSectionProps {
|
|
2
|
-
/** `"consumer"` matches the comfortable /chat sidebar; `"dense"` the rest. */
|
|
3
|
-
variant?: "dense" | "consumer";
|
|
4
|
-
className?: string;
|
|
5
|
-
}
|
|
6
|
-
export declare function ConversationTrashSection({ variant, className, }: ConversationTrashSectionProps): import("react").JSX.Element;
|
|
7
|
-
export default ConversationTrashSection;
|