@ai-matrx/associations 0.13.137 → 0.13.138
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 +13 -0
- package/dist/react/index.cjs +74 -51
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +160 -138
- package/dist/react/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/associations
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
## 0.13.138
|
|
5
|
+
|
|
6
|
+
**A conversation's file inventory is read once per tab.** `useContainerLinks` kept the
|
|
7
|
+
`conversation_files` answer in its own state, so a chat tile that slept and woke, a Remove +
|
|
8
|
+
Undo and every second view of the same conversation read it again. It now lives in one cache
|
|
9
|
+
entry per conversation per store: the first view reads, later views and remounts render the
|
|
10
|
+
kept list, a read in flight is joined, a store write to the conversation still re-reads it,
|
|
11
|
+
and an older answer never lands over a newer one. Guard: `react/__tests__/hooks.test.tsx`
|
|
12
|
+
"reads a conversation's files once for a remount and a second view" (red on 0.13.137).
|
|
13
|
+
|
|
14
|
+
Consumer action: none.
|
|
15
|
+
|
|
3
16
|
## Unreleased
|
|
4
17
|
|
|
5
18
|
**Resource groups are a dot and a name.** `AssociationCardGrid` no longer renders the one-line group descriptions ("Incoming sources of truth (Knowledge In).", "Filed under"'s line). Everything else of the restored tiles look stays. Arman 2026-09-30: descriptions are banned on in-app pages (`common-docs/policies/interface-text-is-layout.md`). `CONTENT_ROLES[].tagline` stays in the data for now. No consumer action required.
|
package/dist/react/index.cjs
CHANGED
|
@@ -1823,6 +1823,66 @@ function metadataRecord(metadata) {
|
|
|
1823
1823
|
}
|
|
1824
1824
|
return Object.fromEntries(Object.entries(metadata));
|
|
1825
1825
|
}
|
|
1826
|
+
var conversationFileCaches = /* @__PURE__ */ new WeakMap();
|
|
1827
|
+
function conversationFilesFor(store) {
|
|
1828
|
+
const existing = conversationFileCaches.get(store);
|
|
1829
|
+
if (existing) return existing;
|
|
1830
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1831
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
1832
|
+
const seqs = /* @__PURE__ */ new Map();
|
|
1833
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1834
|
+
const set = (entry) => {
|
|
1835
|
+
entries.set(entry.key, entry);
|
|
1836
|
+
for (const listener of listeners) listener();
|
|
1837
|
+
};
|
|
1838
|
+
const cache = {
|
|
1839
|
+
get: (key) => entries.get(key) ?? null,
|
|
1840
|
+
subscribe(listener) {
|
|
1841
|
+
listeners.add(listener);
|
|
1842
|
+
return () => {
|
|
1843
|
+
listeners.delete(listener);
|
|
1844
|
+
};
|
|
1845
|
+
},
|
|
1846
|
+
load(key, conversationId, force) {
|
|
1847
|
+
const kept = entries.get(key);
|
|
1848
|
+
if (!force) {
|
|
1849
|
+
const running = inflight.get(key);
|
|
1850
|
+
if (running) return running;
|
|
1851
|
+
if (kept && !kept.loading && kept.error === null) return Promise.resolve();
|
|
1852
|
+
}
|
|
1853
|
+
const seq = (seqs.get(key) ?? 0) + 1;
|
|
1854
|
+
seqs.set(key, seq);
|
|
1855
|
+
set({ key, files: kept?.files ?? [], error: kept?.error ?? null, loading: true });
|
|
1856
|
+
const request = store.services.associations.listConversationFiles(conversationId).then((result) => {
|
|
1857
|
+
if (seqs.get(key) !== seq) return;
|
|
1858
|
+
if (isAssociationsRpcErr(result)) {
|
|
1859
|
+
set({ key, files: entries.get(key)?.files ?? [], error: result.error.message, loading: false });
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
set({
|
|
1863
|
+
key,
|
|
1864
|
+
files: result.data.files.map((file) => ({
|
|
1865
|
+
edgeId: `conversation-file:${file.fileId}`,
|
|
1866
|
+
resourceId: file.fileId,
|
|
1867
|
+
token: "file",
|
|
1868
|
+
role: null,
|
|
1869
|
+
label: file.label,
|
|
1870
|
+
metadata: file.metadata
|
|
1871
|
+
})),
|
|
1872
|
+
error: null,
|
|
1873
|
+
loading: false
|
|
1874
|
+
});
|
|
1875
|
+
});
|
|
1876
|
+
const tracked = request.finally(() => {
|
|
1877
|
+
if (inflight.get(key) === tracked) inflight.delete(key);
|
|
1878
|
+
});
|
|
1879
|
+
inflight.set(key, tracked);
|
|
1880
|
+
return tracked;
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
conversationFileCaches.set(store, cache);
|
|
1884
|
+
return cache;
|
|
1885
|
+
}
|
|
1826
1886
|
function useContainerLinks(args) {
|
|
1827
1887
|
const { containerType, containerId, orgId } = args;
|
|
1828
1888
|
const outgoing = args.direction === "outgoing";
|
|
@@ -1838,58 +1898,21 @@ function useContainerLinks(args) {
|
|
|
1838
1898
|
id: containerId
|
|
1839
1899
|
});
|
|
1840
1900
|
const conversationKey = !outgoing && containerType === "conversation" && containerId ? `conversation:${containerId}` : null;
|
|
1841
|
-
const
|
|
1842
|
-
const
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
key,
|
|
1855
|
-
files: current?.key === key ? current.files : [],
|
|
1856
|
-
// Keep the visible failure context while retrying so the same control
|
|
1857
|
-
// can disable itself and show progress instead of disappearing.
|
|
1858
|
-
error: current?.key === key ? current.error : null,
|
|
1859
|
-
loading: true
|
|
1860
|
-
}));
|
|
1861
|
-
const result = await store.services.associations.listConversationFiles(containerId);
|
|
1862
|
-
if (generation !== conversationGeneration.current || activeConversationKey.current !== key) {
|
|
1863
|
-
return;
|
|
1864
|
-
}
|
|
1865
|
-
if (isAssociationsRpcErr(result)) {
|
|
1866
|
-
setConversationResult((current) => ({
|
|
1867
|
-
key,
|
|
1868
|
-
files: current?.key === key ? current.files : [],
|
|
1869
|
-
error: result.error.message,
|
|
1870
|
-
loading: false
|
|
1871
|
-
}));
|
|
1872
|
-
return;
|
|
1873
|
-
}
|
|
1874
|
-
setConversationResult({
|
|
1875
|
-
key,
|
|
1876
|
-
files: result.data.files.map((file) => ({
|
|
1877
|
-
edgeId: `conversation-file:${file.fileId}`,
|
|
1878
|
-
resourceId: file.fileId,
|
|
1879
|
-
token: "file",
|
|
1880
|
-
role: null,
|
|
1881
|
-
label: file.label,
|
|
1882
|
-
metadata: file.metadata
|
|
1883
|
-
})),
|
|
1884
|
-
error: null,
|
|
1885
|
-
loading: false
|
|
1886
|
-
});
|
|
1887
|
-
}, [containerId, conversationKey, store]);
|
|
1901
|
+
const files = conversationFilesFor(store);
|
|
1902
|
+
const conversationResult = (0, import_react3.useSyncExternalStore)(
|
|
1903
|
+
files.subscribe,
|
|
1904
|
+
() => conversationKey ? files.get(conversationKey) : null,
|
|
1905
|
+
() => null
|
|
1906
|
+
);
|
|
1907
|
+
const loadConversationFiles = (0, import_react3.useCallback)(
|
|
1908
|
+
async (force = true) => {
|
|
1909
|
+
if (!conversationKey || !containerId) return;
|
|
1910
|
+
await files.load(conversationKey, containerId, force);
|
|
1911
|
+
},
|
|
1912
|
+
[containerId, conversationKey, files]
|
|
1913
|
+
);
|
|
1888
1914
|
(0, import_react3.useEffect)(() => {
|
|
1889
|
-
|
|
1890
|
-
void loadConversationFiles();
|
|
1891
|
-
}, 0);
|
|
1892
|
-
return () => clearTimeout(timeout);
|
|
1915
|
+
void loadConversationFiles(false);
|
|
1893
1916
|
}, [loadConversationFiles]);
|
|
1894
1917
|
const lastGenericFetch = (0, import_react3.useRef)({
|
|
1895
1918
|
key: conversationKey,
|