@ai-matrx/chat 0.7.4 → 0.7.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 +19 -0
- package/dist/agents/components/ambient-assistant/ScrollAssistantLauncherImpl.js +13 -6
- package/dist/agents/components/identity-refs/AiIdentityRef.js +2 -2
- package/dist/agents/redux/conversation-list/conversation-row-actions.thunks.d.ts +5 -1
- package/dist/agents/redux/conversation-list/conversation-row-actions.thunks.js +64 -12
- package/dist/agents/redux/execution-system/message-crud/soft-delete-conversation.thunk.js +22 -12
- package/dist/agents/redux/execution-system/utils/stream-block-accumulator.d.ts +69 -333
- package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +268 -1896
- package/dist/host/defaults/files-ui.js +2 -1
- package/dist/host/defaults/server-api.d.ts +69 -0
- package/dist/host/defaults/server-api.js +6 -0
- package/dist/host/server/call-api.d.ts +69 -0
- package/dist/host/server/call-api.js +8 -0
- package/dist/tool-call-visualization/configured-rows/field-components.js +23 -45
- package/package.json +11 -10
- package/dist/agents/redux/execution-system/utils/content-prefilter.d.ts +0 -50
- package/dist/agents/redux/execution-system/utils/content-prefilter.js +0 -243
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @ai-matrx/chat
|
|
2
2
|
|
|
3
|
+
## 0.7.6
|
|
4
|
+
### Unreleased
|
|
5
|
+
|
|
6
|
+
- The signed-out page assistant pill (`ScrollAssistantLauncher` → `GuestAmbientAssistant`) now sits on a
|
|
7
|
+
card surface reading "Ask AI Matrx about this page"; it was a bare quiet button that read as loose text
|
|
8
|
+
floating over the page beside an X (live /applets audit, 2026-10-09).
|
|
9
|
+
- Configured tool rows: the `Badge` and `Table` field components now render through
|
|
10
|
+
`@ai-matrx/design-system`'s `Badge` (neutral) and `Table` primitives instead of local copies.
|
|
11
|
+
- The default file chip's size line uses `@ai-matrx/kit/format`'s `formatFileSize` (gigabytes read
|
|
12
|
+
as GB, not thousands of MB); the identity refs use `@ai-matrx/kit/uuid`'s `isUuidShape`.
|
|
13
|
+
|
|
14
|
+
**Consumer action:** none.
|
|
15
|
+
|
|
16
|
+
## 0.7.5
|
|
17
|
+
|
|
18
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
19
|
+
`git diff npm/chat/v0.7.4..npm/chat/v0.7.5 -- apps/shared/chat`).
|
|
20
|
+
No source changes intended and no consumer action required.
|
|
21
|
+
|
|
3
22
|
## 0.7.4
|
|
4
23
|
### Unreleased
|
|
5
24
|
|
|
@@ -36,13 +36,20 @@ function GuestAmbientAssistant({
|
|
|
36
36
|
});
|
|
37
37
|
return /* @__PURE__ */ jsxs("div", { className: "ambient-assistant-dock fixed left-1/2 z-[35] w-[min(380px,calc(100vw-2rem))] -translate-x-1/2 animate-in fade-in slide-in-from-bottom-2 duration-200", children: [
|
|
38
38
|
/* @__PURE__ */ jsx(
|
|
39
|
-
|
|
39
|
+
"div",
|
|
40
40
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
"data-testid": "ambient-assistant-guest",
|
|
42
|
+
className: "pointer-events-auto rounded-xl border border-border bg-card shadow-sm",
|
|
43
|
+
children: /* @__PURE__ */ jsx(
|
|
44
|
+
Button,
|
|
45
|
+
{
|
|
46
|
+
type: "button",
|
|
47
|
+
variant: "quiet",
|
|
48
|
+
className: "w-full justify-start",
|
|
49
|
+
onClick: requestSignIn,
|
|
50
|
+
children: "Ask AI Matrx about this page"
|
|
51
|
+
}
|
|
52
|
+
)
|
|
46
53
|
}
|
|
47
54
|
),
|
|
48
55
|
/* @__PURE__ */ jsx(
|
|
@@ -3,14 +3,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
import { withModelClass, selectModelById, selectModelClassName, selectModelIdentityById, selectModelIdentityLookupStatus } from "@ai-matrx/agents/models";
|
|
5
5
|
import { humanizeIdentifier } from "@ai-matrx/kit/text-case";
|
|
6
|
+
import { isUuidShape } from "@ai-matrx/kit/uuid";
|
|
6
7
|
import { getModelRecords, useModelRecords } from "../../identity/model-catalog.js";
|
|
7
8
|
import { loadToolById, selectToolById, selectToolLookupStatus, useToolCatalog } from "../../identity/tool-catalog.js";
|
|
8
9
|
import { EntityRef } from "../../../host/ui-slots.js";
|
|
9
10
|
import { selectIsSuperAdmin } from "../../../host/identity.js";
|
|
10
11
|
import { useAppSelector } from "../../../store/hooks.js";
|
|
11
12
|
const cn = (...parts) => parts.filter(Boolean).join(" ");
|
|
12
|
-
const
|
|
13
|
-
const isUuidValue = (value) => typeof value === "string" && UUID.test(value.trim());
|
|
13
|
+
const isUuidValue = (value) => typeof value === "string" && isUuidShape(value.trim());
|
|
14
14
|
function aiModelHref(modelId) {
|
|
15
15
|
return `/administration/ai/ai-models?model=${encodeURIComponent(modelId)}`;
|
|
16
16
|
}
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
* 1. Snapshot the current value from the canonical entity store.
|
|
8
8
|
* 2. Patch every slice that mirrors the row (list, history scopes, the
|
|
9
9
|
* active `conversations`/`messages` instance if it's loaded).
|
|
10
|
-
* 3. Hit Supabase directly — RLS scopes the update to the owner.
|
|
10
|
+
* 3. Hit Supabase directly — RLS scopes the update to the owner. On THE ADMIN
|
|
11
|
+
* SEAT (an /administration page, host grants admin) the write goes to the
|
|
12
|
+
* server door instead (`PATCH /cx/conversations/{id}`), which admits the
|
|
13
|
+
* verified admin through the admin lane: `chat.conversation` has only
|
|
14
|
+
* `platform_admin_read`, by design, so a direct write there is refused.
|
|
11
15
|
* 4. On failure, revert to the snapshot in the same slices.
|
|
12
16
|
*
|
|
13
17
|
* No new reducers are needed: the existing generic `patchConversation` and
|
|
@@ -20,6 +20,17 @@ import {
|
|
|
20
20
|
} from "../execution-system/conversations/conversations.slice.js";
|
|
21
21
|
import { selectConversationIsEphemeral } from "../execution-system/conversations/conversations.selectors.js";
|
|
22
22
|
import { forkConversationServer } from "../execution-system/message-crud/server/fork-conversation-server.thunk.js";
|
|
23
|
+
import { adminSeatOpen } from "../../../host/admin-seat.js";
|
|
24
|
+
import {
|
|
25
|
+
callConversationSandboxBind,
|
|
26
|
+
callConversationSandboxUnbind,
|
|
27
|
+
callConversationUpdate
|
|
28
|
+
} from "../../../host/server/call-api.js";
|
|
29
|
+
async function writeSettingsThroughServer(dispatch, conversationId, body, action) {
|
|
30
|
+
const result = await dispatch(callConversationUpdate({ conversationId, body }));
|
|
31
|
+
if (result.error) return refusalWords(result.error, action);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
23
34
|
function refusalWords(err, action) {
|
|
24
35
|
const w = describeWriteFailure(err, { action, remedy: "Try again." });
|
|
25
36
|
return `${w.title} ${w.description}`;
|
|
@@ -61,10 +72,12 @@ const renameConversation = createAsyncThunk(
|
|
|
61
72
|
title: nextTitle
|
|
62
73
|
})
|
|
63
74
|
);
|
|
64
|
-
const {
|
|
75
|
+
const error = adminSeatOpen(getState()) ? await writeSettingsThroughServer(dispatch, conversationId, { title: nextTitle }, "rename this conversation").then(
|
|
76
|
+
(message) => message ? { message } : null
|
|
77
|
+
) : (await tryWriteOne(
|
|
65
78
|
supabase.schema("chat").from("conversation").update({ title: nextTitle, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", conversationId).select("id"),
|
|
66
79
|
{ action: "rename", noun: "conversation" }
|
|
67
|
-
);
|
|
80
|
+
)).error;
|
|
68
81
|
if (error) {
|
|
69
82
|
dispatch(revertRename({ conversationId, previousTitle }));
|
|
70
83
|
dispatch(
|
|
@@ -110,9 +123,14 @@ const setConversationArchived = createAsyncThunk(
|
|
|
110
123
|
const before = findConversationRow(getState(), conversationId);
|
|
111
124
|
const nextStatus = archived ? "archived" : "active";
|
|
112
125
|
const action = archived ? "archive this conversation" : "restore this conversation";
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
126
|
+
if (adminSeatOpen(getState())) {
|
|
127
|
+
const refused = await writeSettingsThroughServer(dispatch, conversationId, { status: nextStatus }, action);
|
|
128
|
+
if (refused) return rejectWithValue({ message: refused });
|
|
129
|
+
} else {
|
|
130
|
+
const { data, error } = await supabase.schema("chat").from("conversation").update({ status: nextStatus, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", conversationId).select("id");
|
|
131
|
+
const refusal = error ?? landedOrRefusal(data, `chat.conversation status=${nextStatus} ${conversationId}`);
|
|
132
|
+
if (refusal) return rejectWithValue({ message: refusalWords(refusal, action) });
|
|
133
|
+
}
|
|
116
134
|
dispatch(
|
|
117
135
|
patchConversation({ conversationId, patch: { status: nextStatus } })
|
|
118
136
|
);
|
|
@@ -135,19 +153,27 @@ const setConversationArchived = createAsyncThunk(
|
|
|
135
153
|
);
|
|
136
154
|
const setConversationExcludeFromKg = createAsyncThunk(
|
|
137
155
|
"conversationRow/setExcludeFromKg",
|
|
138
|
-
async ({ conversationId, excludeFromKg }, { dispatch, rejectWithValue }) => {
|
|
156
|
+
async ({ conversationId, excludeFromKg }, { dispatch, getState, rejectWithValue }) => {
|
|
157
|
+
const kgAction = excludeFromKg ? "exclude this conversation from the knowledge graph" : "include this conversation in the knowledge graph";
|
|
158
|
+
if (adminSeatOpen(getState())) {
|
|
159
|
+
const refused = await writeSettingsThroughServer(
|
|
160
|
+
dispatch,
|
|
161
|
+
conversationId,
|
|
162
|
+
{ exclude_from_kg: excludeFromKg },
|
|
163
|
+
kgAction
|
|
164
|
+
);
|
|
165
|
+
if (refused) return rejectWithValue({ message: refused });
|
|
166
|
+
dispatch(patchConversation({ conversationId, patch: { excludeFromKg } }));
|
|
167
|
+
dispatch(patchConversationInScopes({ conversationId, patch: { excludeFromKg } }));
|
|
168
|
+
return { conversationId, excludeFromKg };
|
|
169
|
+
}
|
|
139
170
|
const { data, error } = await supabase.schema("chat").from("conversation").update({
|
|
140
171
|
exclude_from_kg: excludeFromKg,
|
|
141
172
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
142
173
|
}).eq("id", conversationId).select("id");
|
|
143
174
|
const refusal = error ?? landedOrRefusal(data, `chat.conversation exclude_from_kg=${excludeFromKg} ${conversationId}`);
|
|
144
175
|
if (refusal) {
|
|
145
|
-
return rejectWithValue({
|
|
146
|
-
message: refusalWords(
|
|
147
|
-
refusal,
|
|
148
|
-
excludeFromKg ? "exclude this conversation from the knowledge graph" : "include this conversation in the knowledge graph"
|
|
149
|
-
)
|
|
150
|
-
});
|
|
176
|
+
return rejectWithValue({ message: refusalWords(refusal, kgAction) });
|
|
151
177
|
}
|
|
152
178
|
dispatch(patchConversation({ conversationId, patch: { excludeFromKg } }));
|
|
153
179
|
dispatch(
|
|
@@ -199,6 +225,32 @@ const setConversationSandbox = createAsyncThunk(
|
|
|
199
225
|
if (isEphemeral || (record?.cacheOnly ?? true)) {
|
|
200
226
|
return { conversationId, persisted: false };
|
|
201
227
|
}
|
|
228
|
+
if (adminSeatOpen(state)) {
|
|
229
|
+
const revert = () => dispatch(
|
|
230
|
+
patchInstanceConversation({
|
|
231
|
+
conversationId,
|
|
232
|
+
sandboxBinding: previous,
|
|
233
|
+
sandboxBindingPersisted: previousPersisted
|
|
234
|
+
})
|
|
235
|
+
);
|
|
236
|
+
if (ref?.kind === "local-pc") {
|
|
237
|
+
revert();
|
|
238
|
+
return rejectWithValue({
|
|
239
|
+
message: "A local computer can't be bound from the admin section. Open the conversation as its owner to bind it."
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
const result = ref ? await dispatch(
|
|
243
|
+
callConversationSandboxBind({ conversationId, body: { sandbox_instance_id: ref.rowId } })
|
|
244
|
+
) : await dispatch(callConversationSandboxUnbind({ conversationId }));
|
|
245
|
+
if (result.error) {
|
|
246
|
+
revert();
|
|
247
|
+
return rejectWithValue({
|
|
248
|
+
message: refusalWords(result.error, ref ? "bind this sandbox" : "unbind this sandbox")
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
dispatch(patchInstanceConversation({ conversationId, sandboxBindingPersisted: true }));
|
|
252
|
+
return { conversationId, persisted: true };
|
|
253
|
+
}
|
|
202
254
|
const { data: row, error: readError } = await supabase.schema("chat").from("conversation").select("metadata").eq("id", conversationId).single();
|
|
203
255
|
if (readError) {
|
|
204
256
|
dispatch(
|
|
@@ -12,23 +12,33 @@ import { removeConversationFromScopes } from "../../conversation-history/slice.j
|
|
|
12
12
|
import { findConversationRow } from "../../conversation-list/conversation-list.selectors.js";
|
|
13
13
|
import { clearCacheBypass } from "./cache-bypass.slice.js";
|
|
14
14
|
import { invalidateConversationCache } from "./invalidate-conversation-cache.thunk.js";
|
|
15
|
+
import { adminSeatOpen } from "../../../../host/admin-seat.js";
|
|
16
|
+
import { callConversationDelete } from "../../../../host/server/call-api.js";
|
|
15
17
|
const softDeleteConversation = createAsyncThunk(
|
|
16
18
|
"conversations/softDelete",
|
|
17
19
|
async ({ conversationId }, { dispatch, getState, rejectWithValue }) => {
|
|
18
20
|
const snapshot = findConversationRow(getState(), conversationId);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return rejectWithValue({
|
|
30
|
-
message: operationFailed("delete this conversation").message
|
|
21
|
+
if (adminSeatOpen(getState())) {
|
|
22
|
+
const result = await dispatch(callConversationDelete({ conversationId }));
|
|
23
|
+
if (result.error || result.data?.deleted !== true) {
|
|
24
|
+
return rejectWithValue({
|
|
25
|
+
message: operationFailed("delete this conversation", result.error ?? void 0).message
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
const { data, error } = await supabase.rpc("cx_soft_delete_conversation", {
|
|
30
|
+
p_conversation_id: conversationId
|
|
31
31
|
});
|
|
32
|
+
if (error) {
|
|
33
|
+
return rejectWithValue({
|
|
34
|
+
message: operationFailed("delete this conversation", error).message
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (data !== true) {
|
|
38
|
+
return rejectWithValue({
|
|
39
|
+
message: operationFailed("delete this conversation").message
|
|
40
|
+
});
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
void dispatch(invalidateConversationCache({ conversationId }));
|
|
34
44
|
dispatch(clearCacheBypass(conversationId));
|