@ai-matrx/messaging 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +5 -1
- package/dist/index.cjs +29 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -9
- package/dist/index.d.ts +28 -9
- package/dist/index.js +29 -13
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +42 -23
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +34 -10
- package/dist/react.d.ts +34 -10
- package/dist/react.js +42 -23
- package/dist/react.js.map +1 -1
- package/package.json +3 -3
package/dist/react.d.cts
CHANGED
|
@@ -208,7 +208,11 @@ interface Page<TItem, TCursor> {
|
|
|
208
208
|
/** What the host injects. Identity ONLY — every hard part is in the package. */
|
|
209
209
|
interface MessagingIdentity {
|
|
210
210
|
readonly userId: UserId;
|
|
211
|
-
|
|
211
|
+
/**
|
|
212
|
+
* The active organization, or `null` when none is selected. Reads never
|
|
213
|
+
* depend on it; it is only where a NEW conversation lands.
|
|
214
|
+
*/
|
|
215
|
+
readonly organizationId: OrganizationId | null;
|
|
212
216
|
}
|
|
213
217
|
interface DraftMessage {
|
|
214
218
|
readonly conversationId: ConversationId;
|
|
@@ -525,7 +529,9 @@ declare function createOutbox(options: OutboxOptions): Outbox;
|
|
|
525
529
|
* errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
|
|
526
530
|
* warns and retries; it is never a red error.
|
|
527
531
|
*/
|
|
528
|
-
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
532
|
+
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
533
|
+
/** A NEW conversation needs an organization to land in and none is selected. */
|
|
534
|
+
| "organization-required" | "unknown";
|
|
529
535
|
declare class MessagingError extends Error {
|
|
530
536
|
readonly code: MessagingErrorCode;
|
|
531
537
|
/** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
|
|
@@ -817,15 +823,21 @@ interface RepositoryOptions {
|
|
|
817
823
|
*/
|
|
818
824
|
client: MessagingSupabaseClient;
|
|
819
825
|
/**
|
|
820
|
-
* The active organization
|
|
821
|
-
*
|
|
826
|
+
* The active organization, or `null`/omitted when none is selected.
|
|
827
|
+
*
|
|
828
|
+
* 🚨 READS NEVER NEED IT (access belongs to the person, 2026-09-23). Every
|
|
829
|
+
* read here is answered by RLS as the session's user; the organization is
|
|
830
|
+
* only the DESTINATION of a NEW conversation. Without one, starting a
|
|
831
|
+
* conversation is refused with `organization-required` (the host asks the
|
|
832
|
+
* person to choose), and a message or member added to an EXISTING
|
|
833
|
+
* conversation is stamped with that conversation's own organization.
|
|
822
834
|
*
|
|
823
835
|
* 🚨 THERE IS NO `userId` HERE, AND THERE WILL NOT BE ONE (DD-241). Who is
|
|
824
836
|
* acting is read from `client`'s own session — see `session-identity.ts` for
|
|
825
837
|
* the class that closed and why a handed-in id could not be made safe by
|
|
826
838
|
* convention. A caller that still passes `identity` is IGNORED and told so.
|
|
827
839
|
*/
|
|
828
|
-
organizationId
|
|
840
|
+
organizationId?: string | null | undefined;
|
|
829
841
|
/**
|
|
830
842
|
* INTERNAL. The React provider already runs one session reader for its own
|
|
831
843
|
* render loop and hands it here so the two cannot answer differently and so
|
|
@@ -846,8 +858,11 @@ interface RepositoryOptions {
|
|
|
846
858
|
now?: (() => number) | undefined;
|
|
847
859
|
}
|
|
848
860
|
interface MessagingRepository {
|
|
849
|
-
/**
|
|
850
|
-
|
|
861
|
+
/**
|
|
862
|
+
* The active organization new conversations land in, or `null` when none is
|
|
863
|
+
* selected — reads work either way.
|
|
864
|
+
*/
|
|
865
|
+
readonly organizationId: OrganizationId | null;
|
|
851
866
|
/**
|
|
852
867
|
* Who the session says is acting — awaited, because the first `getSession()`
|
|
853
868
|
* is a real read. Rejects with `session-unavailable` when nobody is signed
|
|
@@ -1269,7 +1284,12 @@ interface MessagingProviderProps {
|
|
|
1269
1284
|
* (DD-241). There is no `userId` prop, on purpose.
|
|
1270
1285
|
*/
|
|
1271
1286
|
client: MessagingSupabaseClient | null | undefined;
|
|
1272
|
-
/**
|
|
1287
|
+
/**
|
|
1288
|
+
* The active organization, or `null` when none is selected. Messaging runs
|
|
1289
|
+
* without one: every read is the person's own. It is the destination of a
|
|
1290
|
+
* NEW conversation (refused as `organization-required` without it) and of
|
|
1291
|
+
* AI runs (the AI actions do not render without it).
|
|
1292
|
+
*/
|
|
1273
1293
|
organizationId: string | null | undefined;
|
|
1274
1294
|
/**
|
|
1275
1295
|
* The Matrx API transport for AI features. Omit to run messaging without AI;
|
|
@@ -1778,9 +1798,13 @@ declare function formatLastSeen(lastSeenMs: number | null, now?: number): string
|
|
|
1778
1798
|
|
|
1779
1799
|
declare function projectUserSummary(row: Record<string, unknown>): UserSummary;
|
|
1780
1800
|
declare function projectMessageAction(value: unknown): MessageAction | null;
|
|
1781
|
-
declare function projectMessage(row: Record<string, unknown>,
|
|
1801
|
+
declare function projectMessage(row: Record<string, unknown>,
|
|
1802
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1803
|
+
fallbackOrganizationId: OrganizationId | null): Message;
|
|
1782
1804
|
declare function projectParticipantRole(value: unknown): ParticipantRole;
|
|
1783
|
-
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1805
|
+
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1806
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1807
|
+
fallbackOrganizationId: OrganizationId | null): ConversationSummary;
|
|
1784
1808
|
|
|
1785
1809
|
/**
|
|
1786
1810
|
* MATRX REFERENCES — a message can name a platform entity, and everything it
|
package/dist/react.d.ts
CHANGED
|
@@ -208,7 +208,11 @@ interface Page<TItem, TCursor> {
|
|
|
208
208
|
/** What the host injects. Identity ONLY — every hard part is in the package. */
|
|
209
209
|
interface MessagingIdentity {
|
|
210
210
|
readonly userId: UserId;
|
|
211
|
-
|
|
211
|
+
/**
|
|
212
|
+
* The active organization, or `null` when none is selected. Reads never
|
|
213
|
+
* depend on it; it is only where a NEW conversation lands.
|
|
214
|
+
*/
|
|
215
|
+
readonly organizationId: OrganizationId | null;
|
|
212
216
|
}
|
|
213
217
|
interface DraftMessage {
|
|
214
218
|
readonly conversationId: ConversationId;
|
|
@@ -525,7 +529,9 @@ declare function createOutbox(options: OutboxOptions): Outbox;
|
|
|
525
529
|
* errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
|
|
526
530
|
* warns and retries; it is never a red error.
|
|
527
531
|
*/
|
|
528
|
-
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
532
|
+
type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
|
|
533
|
+
/** A NEW conversation needs an organization to land in and none is selected. */
|
|
534
|
+
| "organization-required" | "unknown";
|
|
529
535
|
declare class MessagingError extends Error {
|
|
530
536
|
readonly code: MessagingErrorCode;
|
|
531
537
|
/** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
|
|
@@ -817,15 +823,21 @@ interface RepositoryOptions {
|
|
|
817
823
|
*/
|
|
818
824
|
client: MessagingSupabaseClient;
|
|
819
825
|
/**
|
|
820
|
-
* The active organization
|
|
821
|
-
*
|
|
826
|
+
* The active organization, or `null`/omitted when none is selected.
|
|
827
|
+
*
|
|
828
|
+
* 🚨 READS NEVER NEED IT (access belongs to the person, 2026-09-23). Every
|
|
829
|
+
* read here is answered by RLS as the session's user; the organization is
|
|
830
|
+
* only the DESTINATION of a NEW conversation. Without one, starting a
|
|
831
|
+
* conversation is refused with `organization-required` (the host asks the
|
|
832
|
+
* person to choose), and a message or member added to an EXISTING
|
|
833
|
+
* conversation is stamped with that conversation's own organization.
|
|
822
834
|
*
|
|
823
835
|
* 🚨 THERE IS NO `userId` HERE, AND THERE WILL NOT BE ONE (DD-241). Who is
|
|
824
836
|
* acting is read from `client`'s own session — see `session-identity.ts` for
|
|
825
837
|
* the class that closed and why a handed-in id could not be made safe by
|
|
826
838
|
* convention. A caller that still passes `identity` is IGNORED and told so.
|
|
827
839
|
*/
|
|
828
|
-
organizationId
|
|
840
|
+
organizationId?: string | null | undefined;
|
|
829
841
|
/**
|
|
830
842
|
* INTERNAL. The React provider already runs one session reader for its own
|
|
831
843
|
* render loop and hands it here so the two cannot answer differently and so
|
|
@@ -846,8 +858,11 @@ interface RepositoryOptions {
|
|
|
846
858
|
now?: (() => number) | undefined;
|
|
847
859
|
}
|
|
848
860
|
interface MessagingRepository {
|
|
849
|
-
/**
|
|
850
|
-
|
|
861
|
+
/**
|
|
862
|
+
* The active organization new conversations land in, or `null` when none is
|
|
863
|
+
* selected — reads work either way.
|
|
864
|
+
*/
|
|
865
|
+
readonly organizationId: OrganizationId | null;
|
|
851
866
|
/**
|
|
852
867
|
* Who the session says is acting — awaited, because the first `getSession()`
|
|
853
868
|
* is a real read. Rejects with `session-unavailable` when nobody is signed
|
|
@@ -1269,7 +1284,12 @@ interface MessagingProviderProps {
|
|
|
1269
1284
|
* (DD-241). There is no `userId` prop, on purpose.
|
|
1270
1285
|
*/
|
|
1271
1286
|
client: MessagingSupabaseClient | null | undefined;
|
|
1272
|
-
/**
|
|
1287
|
+
/**
|
|
1288
|
+
* The active organization, or `null` when none is selected. Messaging runs
|
|
1289
|
+
* without one: every read is the person's own. It is the destination of a
|
|
1290
|
+
* NEW conversation (refused as `organization-required` without it) and of
|
|
1291
|
+
* AI runs (the AI actions do not render without it).
|
|
1292
|
+
*/
|
|
1273
1293
|
organizationId: string | null | undefined;
|
|
1274
1294
|
/**
|
|
1275
1295
|
* The Matrx API transport for AI features. Omit to run messaging without AI;
|
|
@@ -1778,9 +1798,13 @@ declare function formatLastSeen(lastSeenMs: number | null, now?: number): string
|
|
|
1778
1798
|
|
|
1779
1799
|
declare function projectUserSummary(row: Record<string, unknown>): UserSummary;
|
|
1780
1800
|
declare function projectMessageAction(value: unknown): MessageAction | null;
|
|
1781
|
-
declare function projectMessage(row: Record<string, unknown>,
|
|
1801
|
+
declare function projectMessage(row: Record<string, unknown>,
|
|
1802
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1803
|
+
fallbackOrganizationId: OrganizationId | null): Message;
|
|
1782
1804
|
declare function projectParticipantRole(value: unknown): ParticipantRole;
|
|
1783
|
-
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1805
|
+
declare function projectConversationSummary(row: Record<string, unknown>, viewerId: UserId,
|
|
1806
|
+
/** The active organization, or `null` when none is selected (reads never need one). */
|
|
1807
|
+
fallbackOrganizationId: OrganizationId | null): ConversationSummary;
|
|
1784
1808
|
|
|
1785
1809
|
/**
|
|
1786
1810
|
* MATRX REFERENCES — a message can name a platform entity, and everything it
|
package/dist/react.js
CHANGED
|
@@ -762,7 +762,7 @@ function projectMessage(row, fallbackOrganizationId) {
|
|
|
762
762
|
id: requiredStr(row, "id", operation),
|
|
763
763
|
conversationId: requiredStr(row, "conversation_id", operation),
|
|
764
764
|
senderId: requiredStr(row, "sender_id", operation),
|
|
765
|
-
organizationId: str(row, "organization_id") ?? fallbackOrganizationId,
|
|
765
|
+
organizationId: str(row, "organization_id") ?? fallbackOrganizationId ?? "",
|
|
766
766
|
content: typeof row["content"] === "string" ? row["content"] : "",
|
|
767
767
|
kind: MESSAGE_KINDS.has(kindRaw) ? kindRaw : "text",
|
|
768
768
|
// A row read from the DB is at least `sent`. `sending`/`failed` are outbox
|
|
@@ -810,7 +810,7 @@ function projectConversationSummary(row, viewerId, fallbackOrganizationId) {
|
|
|
810
810
|
groupName,
|
|
811
811
|
groupImageUrl,
|
|
812
812
|
createdBy: createdBy === null ? null : createdBy,
|
|
813
|
-
organizationId: str(row, "organization_id") ?? fallbackOrganizationId,
|
|
813
|
+
organizationId: str(row, "organization_id") ?? fallbackOrganizationId ?? "",
|
|
814
814
|
createdAt,
|
|
815
815
|
updatedAt,
|
|
816
816
|
metadata: jsonObject(row["metadata"])
|
|
@@ -1143,7 +1143,11 @@ function createMessagingEngine(options) {
|
|
|
1143
1143
|
optimisticMessage({
|
|
1144
1144
|
conversationId: entry.draft.conversationId,
|
|
1145
1145
|
senderId: identity.userId,
|
|
1146
|
-
|
|
1146
|
+
// Client-only until the confirmed row replaces it: the active
|
|
1147
|
+
// organization, else the conversation's own.
|
|
1148
|
+
organizationId: identity.organizationId ?? store.snapshot().conversations.find(
|
|
1149
|
+
(item) => item.conversation.id === entry.draft.conversationId
|
|
1150
|
+
)?.conversation.organizationId ?? "",
|
|
1147
1151
|
content: entry.draft.content,
|
|
1148
1152
|
clientMessageId: entry.clientMessageId,
|
|
1149
1153
|
...entry.draft.kind !== void 0 ? { kind: entry.draft.kind } : {},
|
|
@@ -1632,12 +1636,15 @@ var RPCS = {
|
|
|
1632
1636
|
};
|
|
1633
1637
|
var MESSAGE_COLUMNS = "id,conversation_id,sender_id,organization_id,content,message_type,status,reply_to_id,client_message_id,action_data,media_url,media_thumbnail_url,media_metadata,created_at,edited_at,deleted_at,deleted_for_everyone,metadata";
|
|
1634
1638
|
var CONVERSATION_COLUMNS = "id,type,group_name,group_image_url,created_by,organization_id,created_at,updated_at,metadata";
|
|
1639
|
+
function optionalOrg(organizationId) {
|
|
1640
|
+
return typeof organizationId === "string" && organizationId.length > 0 ? organizationId : null;
|
|
1641
|
+
}
|
|
1635
1642
|
function requireOrg(organizationId, operation) {
|
|
1636
|
-
if (
|
|
1643
|
+
if (organizationId === null) {
|
|
1637
1644
|
throw new MessagingError(
|
|
1638
|
-
"
|
|
1639
|
-
`${operation}: no
|
|
1640
|
-
"
|
|
1645
|
+
"organization-required",
|
|
1646
|
+
`${operation}: no organization selected`,
|
|
1647
|
+
"A new conversation is filed under one organization. Ask the person to choose one, then pass it as organizationId on <MessagingProvider>. Reading and replying to existing conversations never needs it."
|
|
1641
1648
|
);
|
|
1642
1649
|
}
|
|
1643
1650
|
return organizationId;
|
|
@@ -1649,7 +1656,7 @@ function sleep(ms) {
|
|
|
1649
1656
|
return new Promise((resolve) => setTimeout(resolve, jittered));
|
|
1650
1657
|
}
|
|
1651
1658
|
function createMessagingRepository(options) {
|
|
1652
|
-
const org =
|
|
1659
|
+
const org = optionalOrg(options.organizationId);
|
|
1653
1660
|
const handedIn = options.identity;
|
|
1654
1661
|
if (handedIn !== void 0 && handedIn !== null && handedIn.userId !== void 0) {
|
|
1655
1662
|
options.onDiagnostic?.(
|
|
@@ -1719,6 +1726,11 @@ function createMessagingRepository(options) {
|
|
|
1719
1726
|
});
|
|
1720
1727
|
return found;
|
|
1721
1728
|
}
|
|
1729
|
+
async function organizationOfExisting(conversationId) {
|
|
1730
|
+
if (org !== null) return org;
|
|
1731
|
+
const conversation = await repository.getConversation(conversationId);
|
|
1732
|
+
return conversation.organizationId;
|
|
1733
|
+
}
|
|
1722
1734
|
const repository = {
|
|
1723
1735
|
organizationId: org,
|
|
1724
1736
|
actingUserId,
|
|
@@ -1851,6 +1863,7 @@ function createMessagingRepository(options) {
|
|
|
1851
1863
|
},
|
|
1852
1864
|
async getOrCreateDirectConversation(otherUserId) {
|
|
1853
1865
|
const operation = "getOrCreateDirectConversation";
|
|
1866
|
+
const destination = requireOrg(org, operation);
|
|
1854
1867
|
const me = await actingUser(operation);
|
|
1855
1868
|
const id = await withSessionRetry(
|
|
1856
1869
|
operation,
|
|
@@ -1862,7 +1875,7 @@ function createMessagingRepository(options) {
|
|
|
1862
1875
|
// not a preference.
|
|
1863
1876
|
p_user1_id: me,
|
|
1864
1877
|
p_user2_id: otherUserId,
|
|
1865
|
-
p_organization_id:
|
|
1878
|
+
p_organization_id: destination
|
|
1866
1879
|
},
|
|
1867
1880
|
operation
|
|
1868
1881
|
)
|
|
@@ -1874,13 +1887,14 @@ function createMessagingRepository(options) {
|
|
|
1874
1887
|
},
|
|
1875
1888
|
async createGroupConversation({ name, memberIds }) {
|
|
1876
1889
|
const operation = "createGroupConversation";
|
|
1890
|
+
const destination = requireOrg(org, operation);
|
|
1877
1891
|
const me = await actingUser(operation);
|
|
1878
1892
|
const { data, error } = await withSessionRetry(
|
|
1879
1893
|
operation,
|
|
1880
1894
|
async () => db().from(TABLES.conversations).insert({
|
|
1881
1895
|
type: "group",
|
|
1882
1896
|
group_name: name,
|
|
1883
|
-
organization_id:
|
|
1897
|
+
organization_id: destination,
|
|
1884
1898
|
created_by: me
|
|
1885
1899
|
}).select("id").single()
|
|
1886
1900
|
);
|
|
@@ -1895,7 +1909,7 @@ function createMessagingRepository(options) {
|
|
|
1895
1909
|
conversation_id: conversationId,
|
|
1896
1910
|
user_id: userId,
|
|
1897
1911
|
role: userId === me ? "owner" : "member",
|
|
1898
|
-
organization_id:
|
|
1912
|
+
organization_id: destination,
|
|
1899
1913
|
created_by: me
|
|
1900
1914
|
}))
|
|
1901
1915
|
);
|
|
@@ -1905,10 +1919,11 @@ function createMessagingRepository(options) {
|
|
|
1905
1919
|
async insertMessage(draft, clientMessageId) {
|
|
1906
1920
|
const operation = "insertMessage";
|
|
1907
1921
|
const me = await actingUser(operation);
|
|
1922
|
+
const destination = await organizationOfExisting(draft.conversationId);
|
|
1908
1923
|
const { data, error } = await db().from(TABLES.messages).insert({
|
|
1909
1924
|
conversation_id: draft.conversationId,
|
|
1910
1925
|
sender_id: me,
|
|
1911
|
-
organization_id:
|
|
1926
|
+
organization_id: destination,
|
|
1912
1927
|
created_by: me,
|
|
1913
1928
|
content: draft.content,
|
|
1914
1929
|
message_type: draft.kind ?? "text",
|
|
@@ -1969,12 +1984,13 @@ function createMessagingRepository(options) {
|
|
|
1969
1984
|
async addMembers(conversationId, memberIds) {
|
|
1970
1985
|
const operation = "addMembers";
|
|
1971
1986
|
const me = await actingUser(operation);
|
|
1987
|
+
const destination = await organizationOfExisting(conversationId);
|
|
1972
1988
|
const { error } = await db().from(TABLES.participants).insert(
|
|
1973
1989
|
memberIds.map((userId) => ({
|
|
1974
1990
|
conversation_id: conversationId,
|
|
1975
1991
|
user_id: userId,
|
|
1976
1992
|
role: "member",
|
|
1977
|
-
organization_id:
|
|
1993
|
+
organization_id: destination,
|
|
1978
1994
|
created_by: me
|
|
1979
1995
|
}))
|
|
1980
1996
|
);
|
|
@@ -2085,7 +2101,7 @@ function MessagingProvider(props) {
|
|
|
2085
2101
|
}, [strayUserId]);
|
|
2086
2102
|
const session = useSessionUserId(client, props.onDiagnostic);
|
|
2087
2103
|
const userId = session.snapshot.userId;
|
|
2088
|
-
const ready = client != null && userId !== null
|
|
2104
|
+
const ready = client != null && userId !== null;
|
|
2089
2105
|
const hostProvidesRealtime = useIsRealtimeProviderMounted();
|
|
2090
2106
|
const runtime = /* @__PURE__ */ jsx(
|
|
2091
2107
|
MessagingRuntime,
|
|
@@ -2121,12 +2137,13 @@ function MessagingRuntime(props) {
|
|
|
2121
2137
|
actionRegistry.register(handler);
|
|
2122
2138
|
});
|
|
2123
2139
|
}, [actionRegistry, handlers]);
|
|
2124
|
-
const ready = client != null && manager !== null && sessionIdentity !== null && userId !== null
|
|
2140
|
+
const ready = client != null && manager !== null && sessionIdentity !== null && userId !== null;
|
|
2141
|
+
const activeOrganizationId = typeof organizationId === "string" && organizationId.length > 0 ? organizationId : null;
|
|
2125
2142
|
const engine = useMemo(() => {
|
|
2126
2143
|
if (!ready || manager === null || sessionIdentity === null || userId === null) return null;
|
|
2127
2144
|
const identity = {
|
|
2128
2145
|
userId,
|
|
2129
|
-
organizationId
|
|
2146
|
+
organizationId: activeOrganizationId
|
|
2130
2147
|
};
|
|
2131
2148
|
const report = (event) => {
|
|
2132
2149
|
(diagnosticRef.current ?? defaultDiagnostics)(event);
|
|
@@ -2137,7 +2154,7 @@ function MessagingRuntime(props) {
|
|
|
2137
2154
|
// supabase-shape.ts); everything below this line works against the rich
|
|
2138
2155
|
// contract, which `supabase-shape.test.ts` proves a real client honors.
|
|
2139
2156
|
client,
|
|
2140
|
-
organizationId,
|
|
2157
|
+
organizationId: activeOrganizationId,
|
|
2141
2158
|
// THE SAME READER the render loop above uses. Two readers would be two
|
|
2142
2159
|
// answers during the tick an account switch takes.
|
|
2143
2160
|
sessionIdentity,
|
|
@@ -2170,14 +2187,14 @@ function MessagingRuntime(props) {
|
|
|
2170
2187
|
}
|
|
2171
2188
|
});
|
|
2172
2189
|
return built;
|
|
2173
|
-
}, [ready, manager, sessionIdentity, userId,
|
|
2190
|
+
}, [ready, manager, sessionIdentity, userId, activeOrganizationId]);
|
|
2174
2191
|
useEffect(() => {
|
|
2175
2192
|
if (engine === null) return void 0;
|
|
2176
2193
|
void engine.start().catch((error) => {
|
|
2177
2194
|
(diagnosticRef.current ?? defaultDiagnostics)({
|
|
2178
2195
|
level: "error",
|
|
2179
2196
|
message: `Messaging failed to start: ${String(error?.message ?? error)}`,
|
|
2180
|
-
remedy: "Check the signed-in session
|
|
2197
|
+
remedy: "Check the signed-in session, then reload the surface."
|
|
2181
2198
|
});
|
|
2182
2199
|
});
|
|
2183
2200
|
return () => {
|
|
@@ -2191,9 +2208,10 @@ function MessagingRuntime(props) {
|
|
|
2191
2208
|
const sourceFeature = props.sourceFeature;
|
|
2192
2209
|
const ai = useMemo(() => {
|
|
2193
2210
|
if (transport === void 0 || agents === void 0 || !ready) return null;
|
|
2211
|
+
if (activeOrganizationId === null) return null;
|
|
2194
2212
|
return createMessagingAi({
|
|
2195
2213
|
transport,
|
|
2196
|
-
organizationId,
|
|
2214
|
+
organizationId: activeOrganizationId,
|
|
2197
2215
|
agents,
|
|
2198
2216
|
...maxTranscriptMessages !== void 0 ? { maxTranscriptMessages } : {},
|
|
2199
2217
|
...sourceApp !== void 0 ? { sourceApp } : {},
|
|
@@ -2203,7 +2221,7 @@ function MessagingRuntime(props) {
|
|
|
2203
2221
|
transport,
|
|
2204
2222
|
agents,
|
|
2205
2223
|
ready,
|
|
2206
|
-
|
|
2224
|
+
activeOrganizationId,
|
|
2207
2225
|
maxTranscriptMessages,
|
|
2208
2226
|
sourceApp,
|
|
2209
2227
|
sourceFeature
|
|
@@ -2254,7 +2272,7 @@ function useRequiredMessagingHost() {
|
|
|
2254
2272
|
const host = useMessagingHost();
|
|
2255
2273
|
if (host === null) {
|
|
2256
2274
|
throw new Error(
|
|
2257
|
-
"[@ai-matrx/messaging] No messaging host. Wrap this tree in <MessagingProvider client={supabase}
|
|
2275
|
+
"[@ai-matrx/messaging] No messaging host. Wrap this tree in <MessagingProvider client={supabase} organizationId={orgId}> and render it only once auth has resolved."
|
|
2258
2276
|
);
|
|
2259
2277
|
}
|
|
2260
2278
|
return host;
|
|
@@ -2644,7 +2662,8 @@ function useMessageAction(message) {
|
|
|
2644
2662
|
const context = {
|
|
2645
2663
|
messageId: message.id,
|
|
2646
2664
|
actorId: host.identity.userId,
|
|
2647
|
-
|
|
2665
|
+
// The active organization, else the organization the message lives in.
|
|
2666
|
+
organizationId: host.identity.organizationId ?? message.organizationId,
|
|
2648
2667
|
choice
|
|
2649
2668
|
};
|
|
2650
2669
|
host.actions.execute(action, context).then(setReceipt).catch((cause) => {
|