@memtensor/memos-local-openclaw-plugin 1.0.4-beta.10 → 1.0.4-beta.12
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/dist/client/connector.d.ts +5 -0
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +38 -8
- package/dist/client/connector.js.map +1 -1
- package/dist/hub/server.d.ts +1 -0
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +143 -32
- package/dist/hub/server.js.map +1 -1
- package/dist/hub/user-manager.d.ts +9 -0
- package/dist/hub/user-manager.d.ts.map +1 -1
- package/dist/hub/user-manager.js +26 -2
- package/dist/hub/user-manager.js.map +1 -1
- package/dist/ingest/chunker.d.ts +2 -1
- package/dist/ingest/chunker.d.ts.map +1 -1
- package/dist/ingest/chunker.js +14 -10
- package/dist/ingest/chunker.js.map +1 -1
- package/dist/recall/engine.d.ts.map +1 -1
- package/dist/recall/engine.js +7 -2
- package/dist/recall/engine.js.map +1 -1
- package/dist/sharing/types.d.ts +1 -1
- package/dist/sharing/types.d.ts.map +1 -1
- package/dist/skill/evolver.d.ts +2 -0
- package/dist/skill/evolver.d.ts.map +1 -1
- package/dist/skill/evolver.js +56 -5
- package/dist/skill/evolver.js.map +1 -1
- package/dist/skill/generator.d.ts +2 -0
- package/dist/skill/generator.d.ts.map +1 -1
- package/dist/skill/generator.js +45 -3
- package/dist/skill/generator.js.map +1 -1
- package/dist/skill/installer.d.ts +26 -0
- package/dist/skill/installer.d.ts.map +1 -1
- package/dist/skill/installer.js +80 -4
- package/dist/skill/installer.js.map +1 -1
- package/dist/skill/upgrader.d.ts +2 -0
- package/dist/skill/upgrader.d.ts.map +1 -1
- package/dist/skill/upgrader.js +139 -1
- package/dist/skill/upgrader.js.map +1 -1
- package/dist/skill/validator.d.ts +3 -0
- package/dist/skill/validator.d.ts.map +1 -1
- package/dist/skill/validator.js +75 -0
- package/dist/skill/validator.js.map +1 -1
- package/dist/storage/sqlite.d.ts +28 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +155 -16
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +64 -24
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +39 -20
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +338 -33
- package/package.json +1 -1
- package/src/client/connector.ts +43 -8
- package/src/hub/server.ts +142 -31
- package/src/hub/user-manager.ts +42 -6
- package/src/ingest/chunker.ts +19 -13
- package/src/recall/engine.ts +7 -2
- package/src/sharing/types.ts +1 -1
- package/src/skill/evolver.ts +58 -6
- package/src/skill/generator.ts +44 -5
- package/src/skill/installer.ts +107 -4
- package/src/skill/upgrader.ts +139 -1
- package/src/skill/validator.ts +79 -0
- package/src/storage/sqlite.ts +174 -16
- package/src/types.ts +11 -0
- package/src/viewer/html.ts +64 -24
- package/src/viewer/server.ts +39 -20
package/src/storage/sqlite.ts
CHANGED
|
@@ -114,6 +114,8 @@ export class SqliteStore {
|
|
|
114
114
|
this.migrateHubTables();
|
|
115
115
|
this.migrateHubFtsToTrigram();
|
|
116
116
|
this.migrateLocalSharedTasksOwner();
|
|
117
|
+
this.migrateHubUserIdentityFields();
|
|
118
|
+
this.migrateClientHubConnectionIdentityFields();
|
|
117
119
|
this.log.debug("Database schema initialized");
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -131,6 +133,49 @@ export class SqliteStore {
|
|
|
131
133
|
} catch { /* table may not exist yet */ }
|
|
132
134
|
}
|
|
133
135
|
|
|
136
|
+
private migrateHubUserIdentityFields(): void {
|
|
137
|
+
try {
|
|
138
|
+
const cols = this.db.prepare("PRAGMA table_info(hub_users)").all() as Array<{ name: string }>;
|
|
139
|
+
if (cols.length === 0) return;
|
|
140
|
+
if (!cols.some(c => c.name === "identity_key")) {
|
|
141
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN identity_key TEXT NOT NULL DEFAULT ''");
|
|
142
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_hub_users_identity_key ON hub_users(identity_key)");
|
|
143
|
+
this.log.info("Migrated: added identity_key to hub_users");
|
|
144
|
+
}
|
|
145
|
+
if (!cols.some(c => c.name === "left_at")) {
|
|
146
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN left_at INTEGER");
|
|
147
|
+
this.log.info("Migrated: added left_at to hub_users");
|
|
148
|
+
}
|
|
149
|
+
if (!cols.some(c => c.name === "removed_at")) {
|
|
150
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN removed_at INTEGER");
|
|
151
|
+
this.log.info("Migrated: added removed_at to hub_users");
|
|
152
|
+
}
|
|
153
|
+
if (!cols.some(c => c.name === "rejected_at")) {
|
|
154
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN rejected_at INTEGER");
|
|
155
|
+
this.log.info("Migrated: added rejected_at to hub_users");
|
|
156
|
+
}
|
|
157
|
+
if (!cols.some(c => c.name === "rejoin_requested_at")) {
|
|
158
|
+
this.db.exec("ALTER TABLE hub_users ADD COLUMN rejoin_requested_at INTEGER");
|
|
159
|
+
this.log.info("Migrated: added rejoin_requested_at to hub_users");
|
|
160
|
+
}
|
|
161
|
+
} catch { /* table may not exist yet */ }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private migrateClientHubConnectionIdentityFields(): void {
|
|
165
|
+
try {
|
|
166
|
+
const cols = this.db.prepare("PRAGMA table_info(client_hub_connection)").all() as Array<{ name: string }>;
|
|
167
|
+
if (cols.length === 0) return;
|
|
168
|
+
if (!cols.some(c => c.name === "identity_key")) {
|
|
169
|
+
this.db.exec("ALTER TABLE client_hub_connection ADD COLUMN identity_key TEXT NOT NULL DEFAULT ''");
|
|
170
|
+
this.log.info("Migrated: added identity_key to client_hub_connection");
|
|
171
|
+
}
|
|
172
|
+
if (!cols.some(c => c.name === "last_known_status")) {
|
|
173
|
+
this.db.exec("ALTER TABLE client_hub_connection ADD COLUMN last_known_status TEXT NOT NULL DEFAULT ''");
|
|
174
|
+
this.log.info("Migrated: added last_known_status to client_hub_connection");
|
|
175
|
+
}
|
|
176
|
+
} catch { /* table may not exist yet */ }
|
|
177
|
+
}
|
|
178
|
+
|
|
134
179
|
private migrateOwnerFields(): void {
|
|
135
180
|
const chunkCols = this.db.prepare("PRAGMA table_info(chunks)").all() as Array<{ name: string }>;
|
|
136
181
|
if (!chunkCols.some((c) => c.name === "owner")) {
|
|
@@ -762,6 +807,20 @@ export class SqliteStore {
|
|
|
762
807
|
CREATE INDEX IF NOT EXISTS idx_hub_users_status ON hub_users(status);
|
|
763
808
|
CREATE INDEX IF NOT EXISTS idx_hub_users_role ON hub_users(role);
|
|
764
809
|
|
|
810
|
+
CREATE TABLE IF NOT EXISTS hub_groups (
|
|
811
|
+
id TEXT PRIMARY KEY,
|
|
812
|
+
name TEXT NOT NULL,
|
|
813
|
+
description TEXT NOT NULL DEFAULT '',
|
|
814
|
+
created_at INTEGER NOT NULL
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
CREATE TABLE IF NOT EXISTS hub_group_members (
|
|
818
|
+
group_id TEXT NOT NULL REFERENCES hub_groups(id) ON DELETE CASCADE,
|
|
819
|
+
user_id TEXT NOT NULL REFERENCES hub_users(id) ON DELETE CASCADE,
|
|
820
|
+
joined_at INTEGER NOT NULL,
|
|
821
|
+
PRIMARY KEY (group_id, user_id)
|
|
822
|
+
);
|
|
823
|
+
|
|
765
824
|
CREATE TABLE IF NOT EXISTS hub_tasks (
|
|
766
825
|
id TEXT PRIMARY KEY,
|
|
767
826
|
source_task_id TEXT NOT NULL,
|
|
@@ -1731,16 +1790,18 @@ export class SqliteStore {
|
|
|
1731
1790
|
|
|
1732
1791
|
setClientHubConnection(conn: ClientHubConnection): void {
|
|
1733
1792
|
this.db.prepare(`
|
|
1734
|
-
INSERT INTO client_hub_connection (id, hub_url, user_id, username, user_token, role, connected_at)
|
|
1735
|
-
VALUES (1, ?, ?, ?, ?, ?, ?)
|
|
1793
|
+
INSERT INTO client_hub_connection (id, hub_url, user_id, username, user_token, role, connected_at, identity_key, last_known_status)
|
|
1794
|
+
VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1736
1795
|
ON CONFLICT(id) DO UPDATE SET
|
|
1737
1796
|
hub_url = excluded.hub_url,
|
|
1738
1797
|
user_id = excluded.user_id,
|
|
1739
1798
|
username = excluded.username,
|
|
1740
1799
|
user_token = excluded.user_token,
|
|
1741
1800
|
role = excluded.role,
|
|
1742
|
-
connected_at = excluded.connected_at
|
|
1743
|
-
|
|
1801
|
+
connected_at = excluded.connected_at,
|
|
1802
|
+
identity_key = excluded.identity_key,
|
|
1803
|
+
last_known_status = excluded.last_known_status
|
|
1804
|
+
`).run(conn.hubUrl, conn.userId, conn.username, conn.userToken, conn.role, conn.connectedAt, conn.identityKey ?? "", conn.lastKnownStatus ?? "");
|
|
1744
1805
|
}
|
|
1745
1806
|
|
|
1746
1807
|
getClientHubConnection(): ClientHubConnection | null {
|
|
@@ -1847,8 +1908,8 @@ export class SqliteStore {
|
|
|
1847
1908
|
|
|
1848
1909
|
upsertHubUser(user: HubUserRecord): void {
|
|
1849
1910
|
this.db.prepare(`
|
|
1850
|
-
INSERT INTO hub_users (id, username, device_name, role, status, token_hash, created_at, approved_at)
|
|
1851
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1911
|
+
INSERT INTO hub_users (id, username, device_name, role, status, token_hash, created_at, approved_at, identity_key, left_at, removed_at, rejected_at, rejoin_requested_at)
|
|
1912
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1852
1913
|
ON CONFLICT(id) DO UPDATE SET
|
|
1853
1914
|
username = excluded.username,
|
|
1854
1915
|
device_name = excluded.device_name,
|
|
@@ -1856,21 +1917,32 @@ export class SqliteStore {
|
|
|
1856
1917
|
status = excluded.status,
|
|
1857
1918
|
token_hash = excluded.token_hash,
|
|
1858
1919
|
created_at = excluded.created_at,
|
|
1859
|
-
approved_at = excluded.approved_at
|
|
1860
|
-
|
|
1920
|
+
approved_at = excluded.approved_at,
|
|
1921
|
+
identity_key = excluded.identity_key,
|
|
1922
|
+
left_at = excluded.left_at,
|
|
1923
|
+
removed_at = excluded.removed_at,
|
|
1924
|
+
rejected_at = excluded.rejected_at,
|
|
1925
|
+
rejoin_requested_at = excluded.rejoin_requested_at
|
|
1926
|
+
`).run(user.id, user.username, user.deviceName ?? "", user.role, user.status, user.tokenHash, user.createdAt, user.approvedAt, user.identityKey ?? "", user.leftAt ?? null, user.removedAt ?? null, user.rejectedAt ?? null, user.rejoinRequestedAt ?? null);
|
|
1861
1927
|
}
|
|
1862
1928
|
|
|
1863
1929
|
getHubUser(userId: string): HubUserRecord | null {
|
|
1864
1930
|
const row = this.db.prepare('SELECT * FROM hub_users WHERE id = ?').get(userId) as HubUserRow | undefined;
|
|
1865
1931
|
if (!row) return null;
|
|
1866
|
-
|
|
1932
|
+
const user = rowToHubUser(row);
|
|
1933
|
+
user.groups = this.getGroupsForHubUser(userId);
|
|
1934
|
+
return user;
|
|
1867
1935
|
}
|
|
1868
1936
|
|
|
1869
1937
|
listHubUsers(status?: UserStatus): HubUserRecord[] {
|
|
1870
1938
|
const rows = status
|
|
1871
1939
|
? this.db.prepare('SELECT * FROM hub_users WHERE status = ? ORDER BY created_at').all(status) as HubUserRow[]
|
|
1872
1940
|
: this.db.prepare('SELECT * FROM hub_users ORDER BY created_at').all() as HubUserRow[];
|
|
1873
|
-
return rows.map(
|
|
1941
|
+
return rows.map(r => {
|
|
1942
|
+
const user = rowToHubUser(r);
|
|
1943
|
+
user.groups = this.getGroupsForHubUser(r.id);
|
|
1944
|
+
return user;
|
|
1945
|
+
});
|
|
1874
1946
|
}
|
|
1875
1947
|
|
|
1876
1948
|
deleteHubUser(userId: string, cleanResources = false): boolean {
|
|
@@ -1881,7 +1953,18 @@ export class SqliteStore {
|
|
|
1881
1953
|
const result = this.db.prepare('DELETE FROM hub_users WHERE id = ?').run(userId);
|
|
1882
1954
|
return result.changes > 0;
|
|
1883
1955
|
}
|
|
1884
|
-
const result = this.db.prepare("UPDATE hub_users SET status = 'removed', token_hash = '' WHERE id = ?").run(userId);
|
|
1956
|
+
const result = this.db.prepare("UPDATE hub_users SET status = 'removed', token_hash = '', removed_at = ? WHERE id = ?").run(Date.now(), userId);
|
|
1957
|
+
return result.changes > 0;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
findHubUserByIdentityKey(identityKey: string): HubUserRecord | null {
|
|
1961
|
+
if (!identityKey) return null;
|
|
1962
|
+
const row = this.db.prepare('SELECT * FROM hub_users WHERE identity_key = ?').get(identityKey) as HubUserRow | undefined;
|
|
1963
|
+
return row ? rowToHubUser(row) : null;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
markHubUserLeft(userId: string): boolean {
|
|
1967
|
+
const result = this.db.prepare("UPDATE hub_users SET status = 'left', token_hash = '', left_at = ? WHERE id = ?").run(Date.now(), userId);
|
|
1885
1968
|
return result.changes > 0;
|
|
1886
1969
|
}
|
|
1887
1970
|
|
|
@@ -1889,6 +1972,35 @@ export class SqliteStore {
|
|
|
1889
1972
|
this.db.prepare('UPDATE hub_users SET last_ip = ?, last_active_at = ? WHERE id = ?').run(ip, timestamp ?? Date.now(), userId);
|
|
1890
1973
|
}
|
|
1891
1974
|
|
|
1975
|
+
// ─── Hub Groups ───
|
|
1976
|
+
|
|
1977
|
+
upsertHubGroup(group: { id: string; name: string; description?: string; createdAt: number }): void {
|
|
1978
|
+
this.db.prepare(`
|
|
1979
|
+
INSERT INTO hub_groups (id, name, description, created_at)
|
|
1980
|
+
VALUES (?, ?, ?, ?)
|
|
1981
|
+
ON CONFLICT(id) DO UPDATE SET name = excluded.name, description = excluded.description
|
|
1982
|
+
`).run(group.id, group.name, group.description ?? "", group.createdAt);
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
addHubGroupMember(groupId: string, userId: string, joinedAt: number): void {
|
|
1986
|
+
this.db.prepare(`
|
|
1987
|
+
INSERT OR IGNORE INTO hub_group_members (group_id, user_id, joined_at)
|
|
1988
|
+
VALUES (?, ?, ?)
|
|
1989
|
+
`).run(groupId, userId, joinedAt);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
removeHubGroupMember(groupId: string, userId: string): void {
|
|
1993
|
+
this.db.prepare('DELETE FROM hub_group_members WHERE group_id = ? AND user_id = ?').run(groupId, userId);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
getGroupsForHubUser(userId: string): Array<{ id: string; name: string; description: string }> {
|
|
1997
|
+
return this.db.prepare(`
|
|
1998
|
+
SELECT g.id, g.name, g.description FROM hub_groups g
|
|
1999
|
+
JOIN hub_group_members m ON m.group_id = g.id
|
|
2000
|
+
WHERE m.user_id = ?
|
|
2001
|
+
`).all(userId) as Array<{ id: string; name: string; description: string }>;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
1892
2004
|
getHubUserContributions(): Record<string, { memoryCount: number; taskCount: number; skillCount: number }> {
|
|
1893
2005
|
const result: Record<string, { memoryCount: number; taskCount: number; skillCount: number }> = {};
|
|
1894
2006
|
const memRows = this.db.prepare('SELECT source_user_id, COUNT(*) as cnt FROM hub_memories GROUP BY source_user_id').all() as Array<{ source_user_id: string; cnt: number }>;
|
|
@@ -2001,20 +2113,37 @@ export class SqliteStore {
|
|
|
2001
2113
|
return out;
|
|
2002
2114
|
}
|
|
2003
2115
|
|
|
2116
|
+
getVisibleHubSkillEmbeddings(): Array<{ skillId: string; vector: Float32Array }> {
|
|
2117
|
+
const rows = this.db.prepare(`
|
|
2118
|
+
SELECT hse.skill_id, hse.vector, hse.dimensions
|
|
2119
|
+
FROM hub_skill_embeddings hse
|
|
2120
|
+
JOIN hub_skills hs ON hs.id = hse.skill_id
|
|
2121
|
+
`).all() as Array<{ skill_id: string; vector: Buffer; dimensions: number }>;
|
|
2122
|
+
return rows.map(r => ({
|
|
2123
|
+
skillId: r.skill_id,
|
|
2124
|
+
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
2125
|
+
}));
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2004
2128
|
searchHubChunks(query: string, options?: { userId?: string; maxResults?: number }): Array<{ hit: HubSearchRow; rank: number }> {
|
|
2005
2129
|
const limit = options?.maxResults ?? 10;
|
|
2006
2130
|
const userId = options?.userId ?? "";
|
|
2007
2131
|
const rows = this.db.prepare(`
|
|
2008
|
-
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
2132
|
+
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
2133
|
+
COALESCE(hg.name, '') as group_name, hu.username as owner_name,
|
|
2009
2134
|
bm25(hub_chunks_fts) as rank
|
|
2010
2135
|
FROM hub_chunks_fts f
|
|
2011
2136
|
JOIN hub_chunks hc ON hc.rowid = f.rowid
|
|
2012
2137
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
2013
2138
|
LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
|
|
2139
|
+
LEFT JOIN hub_groups hg ON hg.id = ht.group_id
|
|
2014
2140
|
WHERE hub_chunks_fts MATCH ?
|
|
2141
|
+
AND (ht.visibility = 'public'
|
|
2142
|
+
OR ht.source_user_id = ?
|
|
2143
|
+
OR EXISTS (SELECT 1 FROM hub_group_members gm WHERE gm.group_id = ht.group_id AND gm.user_id = ?))
|
|
2015
2144
|
ORDER BY rank
|
|
2016
2145
|
LIMIT ?
|
|
2017
|
-
`).all(sanitizeFtsQuery(query), limit) as HubSearchRow[];
|
|
2146
|
+
`).all(sanitizeFtsQuery(query), userId, userId, limit) as HubSearchRow[];
|
|
2018
2147
|
return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
|
|
2019
2148
|
}
|
|
2020
2149
|
|
|
@@ -2039,7 +2168,10 @@ export class SqliteStore {
|
|
|
2039
2168
|
FROM hub_embeddings he
|
|
2040
2169
|
JOIN hub_chunks hc ON hc.id = he.chunk_id
|
|
2041
2170
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
2042
|
-
|
|
2171
|
+
WHERE ht.visibility = 'public'
|
|
2172
|
+
OR ht.source_user_id = ?
|
|
2173
|
+
OR EXISTS (SELECT 1 FROM hub_group_members gm WHERE gm.group_id = ht.group_id AND gm.user_id = ?)
|
|
2174
|
+
`).all(userId, userId) as Array<{ chunk_id: string; vector: Buffer; dimensions: number }>;
|
|
2043
2175
|
return rows.map(r => ({
|
|
2044
2176
|
chunkId: r.chunk_id,
|
|
2045
2177
|
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
@@ -2048,14 +2180,19 @@ export class SqliteStore {
|
|
|
2048
2180
|
|
|
2049
2181
|
getVisibleHubSearchHitByChunkId(chunkId: string, userId: string): HubSearchRow | null {
|
|
2050
2182
|
const row = this.db.prepare(`
|
|
2051
|
-
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
2183
|
+
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
2184
|
+
COALESCE(hg.name, '') as group_name, hu.username as owner_name,
|
|
2052
2185
|
0 as rank
|
|
2053
2186
|
FROM hub_chunks hc
|
|
2054
2187
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
2055
2188
|
LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
|
|
2189
|
+
LEFT JOIN hub_groups hg ON hg.id = ht.group_id
|
|
2056
2190
|
WHERE hc.id = ?
|
|
2191
|
+
AND (ht.visibility = 'public'
|
|
2192
|
+
OR ht.source_user_id = ?
|
|
2193
|
+
OR EXISTS (SELECT 1 FROM hub_group_members gm WHERE gm.group_id = ht.group_id AND gm.user_id = ?))
|
|
2057
2194
|
LIMIT 1
|
|
2058
|
-
`).get(chunkId) as HubSearchRow | undefined;
|
|
2195
|
+
`).get(chunkId, userId, userId) as HubSearchRow | undefined;
|
|
2059
2196
|
return row ?? null;
|
|
2060
2197
|
}
|
|
2061
2198
|
|
|
@@ -2549,6 +2686,8 @@ interface ClientHubConnection {
|
|
|
2549
2686
|
userToken: string;
|
|
2550
2687
|
role: UserRole;
|
|
2551
2688
|
connectedAt: number;
|
|
2689
|
+
identityKey?: string;
|
|
2690
|
+
lastKnownStatus?: string;
|
|
2552
2691
|
}
|
|
2553
2692
|
|
|
2554
2693
|
interface ClientHubConnectionRow {
|
|
@@ -2558,6 +2697,8 @@ interface ClientHubConnectionRow {
|
|
|
2558
2697
|
user_token: string;
|
|
2559
2698
|
role: string;
|
|
2560
2699
|
connected_at: number;
|
|
2700
|
+
identity_key?: string;
|
|
2701
|
+
last_known_status?: string;
|
|
2561
2702
|
}
|
|
2562
2703
|
|
|
2563
2704
|
function rowToClientHubConnection(row: ClientHubConnectionRow): ClientHubConnection {
|
|
@@ -2568,6 +2709,8 @@ function rowToClientHubConnection(row: ClientHubConnectionRow): ClientHubConnect
|
|
|
2568
2709
|
userToken: row.user_token,
|
|
2569
2710
|
role: row.role as UserRole,
|
|
2570
2711
|
connectedAt: row.connected_at,
|
|
2712
|
+
identityKey: row.identity_key || "",
|
|
2713
|
+
lastKnownStatus: row.last_known_status || "",
|
|
2571
2714
|
};
|
|
2572
2715
|
}
|
|
2573
2716
|
|
|
@@ -2577,6 +2720,11 @@ interface HubUserRecord extends UserInfo {
|
|
|
2577
2720
|
approvedAt: number | null;
|
|
2578
2721
|
lastIp: string;
|
|
2579
2722
|
lastActiveAt: number | null;
|
|
2723
|
+
identityKey?: string;
|
|
2724
|
+
leftAt?: number | null;
|
|
2725
|
+
removedAt?: number | null;
|
|
2726
|
+
rejectedAt?: number | null;
|
|
2727
|
+
rejoinRequestedAt?: number | null;
|
|
2580
2728
|
}
|
|
2581
2729
|
|
|
2582
2730
|
interface HubUserRow {
|
|
@@ -2590,6 +2738,11 @@ interface HubUserRow {
|
|
|
2590
2738
|
approved_at: number | null;
|
|
2591
2739
|
last_ip: string;
|
|
2592
2740
|
last_active_at: number | null;
|
|
2741
|
+
identity_key?: string;
|
|
2742
|
+
left_at?: number | null;
|
|
2743
|
+
removed_at?: number | null;
|
|
2744
|
+
rejected_at?: number | null;
|
|
2745
|
+
rejoin_requested_at?: number | null;
|
|
2593
2746
|
}
|
|
2594
2747
|
|
|
2595
2748
|
function rowToHubUser(row: HubUserRow): HubUserRecord {
|
|
@@ -2605,6 +2758,11 @@ function rowToHubUser(row: HubUserRow): HubUserRecord {
|
|
|
2605
2758
|
approvedAt: row.approved_at,
|
|
2606
2759
|
lastIp: row.last_ip || "",
|
|
2607
2760
|
lastActiveAt: row.last_active_at ?? null,
|
|
2761
|
+
identityKey: row.identity_key || "",
|
|
2762
|
+
leftAt: row.left_at ?? null,
|
|
2763
|
+
removedAt: row.removed_at ?? null,
|
|
2764
|
+
rejectedAt: row.rejected_at ?? null,
|
|
2765
|
+
rejoinRequestedAt: row.rejoin_requested_at ?? null,
|
|
2608
2766
|
};
|
|
2609
2767
|
}
|
|
2610
2768
|
|
package/src/types.ts
CHANGED
|
@@ -66,6 +66,8 @@ export interface ChunkRef {
|
|
|
66
66
|
|
|
67
67
|
// ─── Search / Recall ───
|
|
68
68
|
|
|
69
|
+
export type SearchHitOrigin = "local" | "local-shared" | "hub-memory" | "hub-remote";
|
|
70
|
+
|
|
69
71
|
export interface SearchHit {
|
|
70
72
|
summary: string;
|
|
71
73
|
original_excerpt: string;
|
|
@@ -74,6 +76,7 @@ export interface SearchHit {
|
|
|
74
76
|
taskId: string | null;
|
|
75
77
|
skillId: string | null;
|
|
76
78
|
owner?: string;
|
|
79
|
+
origin?: SearchHitOrigin;
|
|
77
80
|
source: {
|
|
78
81
|
ts: number;
|
|
79
82
|
role: Role;
|
|
@@ -249,6 +252,10 @@ export interface SkillEvolutionConfig {
|
|
|
249
252
|
minConfidence?: number;
|
|
250
253
|
maxSkillLines?: number;
|
|
251
254
|
autoInstall?: boolean;
|
|
255
|
+
autoRecallSkills?: boolean;
|
|
256
|
+
autoRecallSkillLimit?: number;
|
|
257
|
+
preferUpgradeExisting?: boolean;
|
|
258
|
+
redactSensitiveInSkill?: boolean;
|
|
252
259
|
/** Optional independent LLM config for skill evaluation/validation. Falls back to main summarizer if not set. */
|
|
253
260
|
summarizer?: SummarizerConfig;
|
|
254
261
|
}
|
|
@@ -344,6 +351,10 @@ export const DEFAULTS = {
|
|
|
344
351
|
skillMinConfidence: 0.7,
|
|
345
352
|
skillMaxLines: 400,
|
|
346
353
|
skillAutoInstall: false,
|
|
354
|
+
skillAutoRecall: true,
|
|
355
|
+
skillAutoRecallLimit: 2,
|
|
356
|
+
skillPreferUpgrade: true,
|
|
357
|
+
skillRedactSensitive: true,
|
|
347
358
|
} as const;
|
|
348
359
|
|
|
349
360
|
// ─── Plugin Hooks (OpenClaw integration) ───
|
package/src/viewer/html.ts
CHANGED
|
@@ -804,6 +804,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
804
804
|
.recall-score.high{background:rgba(34,197,94,.12);color:#22c55e}
|
|
805
805
|
.recall-score.mid{background:rgba(251,191,36,.12);color:#f59e0b}
|
|
806
806
|
.recall-score.low{background:rgba(248,113,113,.1);color:var(--text-muted)}
|
|
807
|
+
.recall-origin{flex-shrink:0;font-size:9px;font-weight:600;padding:1px 5px;border-radius:4px}
|
|
808
|
+
.recall-origin.local-shared{background:rgba(59,130,246,.12);color:#3b82f6}
|
|
809
|
+
.recall-origin.hub-memory{background:rgba(139,92,246,.12);color:#8b5cf6}
|
|
810
|
+
.recall-origin.hub-remote{background:rgba(139,92,246,.12);color:#8b5cf6}
|
|
807
811
|
.recall-summary-short{flex:1;color:var(--text-sec);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
808
812
|
.recall-expand-icon{flex-shrink:0;font-size:10px;color:var(--text-muted);transition:transform .15s}
|
|
809
813
|
.recall-item.expanded .recall-expand-icon{transform:rotate(90deg)}
|
|
@@ -1216,7 +1220,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1216
1220
|
</div>
|
|
1217
1221
|
<button class="btn btn-ghost btn-sm" onclick="doLogout()" data-i18n="logout">Logout</button>
|
|
1218
1222
|
</div>
|
|
1219
|
-
|
|
1223
|
+
</div>
|
|
1220
1224
|
</div>
|
|
1221
1225
|
|
|
1222
1226
|
<div class="main-content">
|
|
@@ -2229,6 +2233,9 @@ const I18N={
|
|
|
2229
2233
|
'logs.recall.noHits':'No matching memories',
|
|
2230
2234
|
'logs.recall.noneRelevant':'LLM filter: none relevant',
|
|
2231
2235
|
'logs.recall.more':'{n} more...',
|
|
2236
|
+
'recall.origin.localShared':'Local Shared',
|
|
2237
|
+
'recall.origin.hubMemory':'Team Cache',
|
|
2238
|
+
'recall.origin.hubRemote':'Team',
|
|
2232
2239
|
'tab.import':'\u{1F4E5} Import',
|
|
2233
2240
|
'tab.settings':'\u2699 Settings',
|
|
2234
2241
|
'settings.modelconfig':'Model Configuration',
|
|
@@ -2940,6 +2947,9 @@ const I18N={
|
|
|
2940
2947
|
'logs.recall.noHits':'未匹配到记忆',
|
|
2941
2948
|
'logs.recall.noneRelevant':'LLM 过滤:无相关记忆',
|
|
2942
2949
|
'logs.recall.more':'还有 {n} 条...',
|
|
2950
|
+
'recall.origin.localShared':'本机共享',
|
|
2951
|
+
'recall.origin.hubMemory':'团队缓存',
|
|
2952
|
+
'recall.origin.hubRemote':'团队',
|
|
2943
2953
|
'tab.import':'\u{1F4E5} 导入',
|
|
2944
2954
|
'tab.settings':'\u2699 设置',
|
|
2945
2955
|
'settings.modelconfig':'模型配置',
|
|
@@ -3686,7 +3696,14 @@ function switchView(view){
|
|
|
3686
3696
|
else if(view==='skills') loadSkills();
|
|
3687
3697
|
else if(view==='analytics') loadMetrics();
|
|
3688
3698
|
else if(view==='logs') loadLogs();
|
|
3689
|
-
else if(view==='settings'){loadConfig()
|
|
3699
|
+
else if(view==='settings'){loadConfig().then(function(){
|
|
3700
|
+
var notDismissed=localStorage.getItem('memos-team-guide-dismissed')!=='1';
|
|
3701
|
+
var sharingOn=document.getElementById('cfgSharingEnabled');
|
|
3702
|
+
var sharingNotEnabled=!sharingOn||!sharingOn.checked;
|
|
3703
|
+
if(notDismissed&&sharingNotEnabled){
|
|
3704
|
+
switchSettingsTab('hub',document.querySelector('.settings-tab-btn[data-tab="hub"]'));
|
|
3705
|
+
}
|
|
3706
|
+
});loadModelHealth();}
|
|
3690
3707
|
else if(view==='import'){if(!window._migrateRunning) migrateScan(false);}
|
|
3691
3708
|
else if(view==='admin'){loadAdminData();}
|
|
3692
3709
|
}
|
|
@@ -3726,6 +3743,13 @@ function onTaskScopeChange(){
|
|
|
3726
3743
|
|
|
3727
3744
|
var _clientPendingPollTimer=null;
|
|
3728
3745
|
var _lastSharingConnStatus='';
|
|
3746
|
+
function _updateScopeSelectorsVisibility(hubAvailable){
|
|
3747
|
+
var ids=['memorySearchScope','taskSearchScope','skillSearchScope'];
|
|
3748
|
+
for(var i=0;i<ids.length;i++){
|
|
3749
|
+
var el=document.getElementById(ids[i]);
|
|
3750
|
+
if(el) el.style.display=hubAvailable?'':'none';
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3729
3753
|
async function loadSharingStatus(forcePending){
|
|
3730
3754
|
try{
|
|
3731
3755
|
const r=await fetch('/api/sharing/status');
|
|
@@ -3738,15 +3762,19 @@ async function loadSharingStatus(forcePending){
|
|
|
3738
3762
|
if(!d||!d.enabled){
|
|
3739
3763
|
if(_clientPendingPollTimer){clearInterval(_clientPendingPollTimer);_clientPendingPollTimer=null;}
|
|
3740
3764
|
_lastSharingConnStatus='';
|
|
3765
|
+
_updateScopeSelectorsVisibility(false);
|
|
3741
3766
|
return;
|
|
3742
3767
|
}
|
|
3743
3768
|
var conn=d.connection||{};
|
|
3744
3769
|
var curStatus=conn.rejected?'rejected':conn.pendingApproval?'pending':conn.connected?'connected':'none';
|
|
3770
|
+
var hubActive=d.role==='hub'||curStatus==='connected';
|
|
3771
|
+
_updateScopeSelectorsVisibility(hubActive);
|
|
3745
3772
|
if(_lastSharingConnStatus==='pending'&&curStatus==='rejected'){
|
|
3746
3773
|
toast(t('sharing.rejected.toast'),'error');
|
|
3747
3774
|
}
|
|
3748
3775
|
if(_lastSharingConnStatus==='pending'&&curStatus==='connected'){
|
|
3749
3776
|
toast(t('sharing.approved.toast'),'success');
|
|
3777
|
+
loadMemories();loadTasks();loadSkills();
|
|
3750
3778
|
}
|
|
3751
3779
|
_lastSharingConnStatus=curStatus;
|
|
3752
3780
|
if(curStatus==='pending'&&!_clientPendingPollTimer){
|
|
@@ -3760,6 +3788,7 @@ async function loadSharingStatus(forcePending){
|
|
|
3760
3788
|
renderSharingSidebar(null);
|
|
3761
3789
|
renderSharingSettings(null);
|
|
3762
3790
|
updateTeamGuide(null);
|
|
3791
|
+
_updateScopeSelectorsVisibility(false);
|
|
3763
3792
|
}
|
|
3764
3793
|
}
|
|
3765
3794
|
|
|
@@ -4087,7 +4116,7 @@ function adminPaginateHtml(total,page,refilterFn){
|
|
|
4087
4116
|
if(end<pages) html+=(end<pages-1?'<span class="pg-info">...</span>':'')+'<button class="pg-btn" onclick="'+refilterFn+'Page('+(pages-1)+')">'+pages+'</button>';
|
|
4088
4117
|
html+='<button class="pg-btn'+(page>=pages-1?' disabled':'')+'" onclick="'+refilterFn+'Page('+(page+1)+')">\\u2192</button>';
|
|
4089
4118
|
html+='<span class="pg-info">'+total+' '+t('pagination.total')+'</span>';
|
|
4090
|
-
|
|
4119
|
+
html+='</div>';
|
|
4091
4120
|
return html;
|
|
4092
4121
|
}
|
|
4093
4122
|
|
|
@@ -4149,12 +4178,12 @@ async function loadAdminData(){
|
|
|
4149
4178
|
var fetches;
|
|
4150
4179
|
if(isAdmin){
|
|
4151
4180
|
fetches=await Promise.all([
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4181
|
+
fetch('/api/sharing/users').then(function(r){return r.json();}),
|
|
4182
|
+
fetch('/api/admin/shared-tasks').then(function(r){return r.json();}),
|
|
4183
|
+
fetch('/api/admin/shared-skills').then(function(r){return r.json();}),
|
|
4184
|
+
fetch('/api/sharing/pending-users').then(function(r){return r.json();}),
|
|
4185
|
+
fetch('/api/admin/shared-memories').then(function(r){return r.json();})
|
|
4186
|
+
]);
|
|
4158
4187
|
}else{
|
|
4159
4188
|
fetches=await Promise.all([
|
|
4160
4189
|
Promise.resolve({users:[]}),
|
|
@@ -5389,6 +5418,13 @@ function parseMemoryAddEntries(out){
|
|
|
5389
5418
|
return results;
|
|
5390
5419
|
}
|
|
5391
5420
|
|
|
5421
|
+
function recallOriginBadge(origin){
|
|
5422
|
+
if(origin==='local-shared') return '<span class="recall-origin local-shared">'+t('recall.origin.localShared')+'</span>';
|
|
5423
|
+
if(origin==='hub-memory') return '<span class="recall-origin hub-memory">'+t('recall.origin.hubMemory')+'</span>';
|
|
5424
|
+
if(origin==='hub-remote') return '<span class="recall-origin hub-remote">'+t('recall.origin.hubRemote')+'</span>';
|
|
5425
|
+
return '';
|
|
5426
|
+
}
|
|
5427
|
+
|
|
5392
5428
|
function buildLogSummary(lg){
|
|
5393
5429
|
let inputObj=null;
|
|
5394
5430
|
try{inputObj=JSON.parse(lg.input);}catch(_){}
|
|
@@ -5413,8 +5449,9 @@ function buildLogSummary(lg){
|
|
|
5413
5449
|
var scoreClass=c.score>=0.7?'high':c.score>=0.5?'mid':'low';
|
|
5414
5450
|
var shortText=escapeHtml(c.summary||c.content||c.original_excerpt||'');
|
|
5415
5451
|
var fullText=escapeHtml(c.content||c.original_excerpt||c.summary||'');
|
|
5452
|
+
var oBadge=recallOriginBadge(c.origin);
|
|
5416
5453
|
html+='<div class="recall-item" onclick="event.stopPropagation();this.classList.toggle(\\\'expanded\\\')">';
|
|
5417
|
-
html+='<div class="recall-item-head"><span class="recall-score '+scoreClass+'">'+c.score.toFixed(2)+'</span><span class="log-msg-role '+(c.role||'user')+'">'+(c.role||'user')+'</span
|
|
5454
|
+
html+='<div class="recall-item-head"><span class="recall-score '+scoreClass+'">'+c.score.toFixed(2)+'</span><span class="log-msg-role '+(c.role||'user')+'">'+(c.role||'user')+'</span>'+oBadge+'<span class="recall-summary-short">'+shortText+'</span><span class="recall-expand-icon">\u25B6</span></div>';
|
|
5418
5455
|
html+='<div class="recall-summary-full">'+fullText+'</div>';
|
|
5419
5456
|
html+='</div>';
|
|
5420
5457
|
});
|
|
@@ -5427,8 +5464,9 @@ function buildLogSummary(lg){
|
|
|
5427
5464
|
var scoreClass=f.score>=0.7?'high':f.score>=0.5?'mid':'low';
|
|
5428
5465
|
var shortText=escapeHtml(f.summary||f.content||f.original_excerpt||'');
|
|
5429
5466
|
var fullText=escapeHtml(f.content||f.original_excerpt||f.summary||'');
|
|
5467
|
+
var oBadge=recallOriginBadge(f.origin);
|
|
5430
5468
|
html+='<div class="recall-item" onclick="event.stopPropagation();this.classList.toggle(\\\'expanded\\\')">';
|
|
5431
|
-
html+='<div class="recall-item-head"><span class="recall-score '+scoreClass+'">'+f.score.toFixed(2)+'</span><span class="log-msg-role '+(f.role||'user')+'">'+(f.role||'user')+'</span
|
|
5469
|
+
html+='<div class="recall-item-head"><span class="recall-score '+scoreClass+'">'+f.score.toFixed(2)+'</span><span class="log-msg-role '+(f.role||'user')+'">'+(f.role||'user')+'</span>'+oBadge+'<span class="recall-summary-short">'+shortText+'</span><span class="recall-expand-icon">\u25B6</span></div>';
|
|
5432
5470
|
html+='<div class="recall-summary-full">'+fullText+'</div>';
|
|
5433
5471
|
html+='</div>';
|
|
5434
5472
|
});
|
|
@@ -5492,8 +5530,9 @@ function buildRecallDetailHtml(rd){
|
|
|
5492
5530
|
var scoreClass=c.score>=0.7?'high':c.score>=0.5?'mid':'low';
|
|
5493
5531
|
var shortText=escapeHtml(c.summary||c.content||c.original_excerpt||'');
|
|
5494
5532
|
var fullText=escapeHtml(c.content||c.original_excerpt||c.summary||'');
|
|
5533
|
+
var oBadge=recallOriginBadge(c.origin);
|
|
5495
5534
|
html+='<div class="recall-item" onclick="event.stopPropagation();this.classList.toggle(\\\'expanded\\\')">';
|
|
5496
|
-
html+='<div class="recall-item-head"><span class="recall-idx">'+(i+1)+'</span><span class="recall-score '+scoreClass+'">'+c.score.toFixed(3)+'</span><span class="log-msg-role '+(c.role||'user')+'">'+(c.role||'user')+'</span
|
|
5535
|
+
html+='<div class="recall-item-head"><span class="recall-idx">'+(i+1)+'</span><span class="recall-score '+scoreClass+'">'+c.score.toFixed(3)+'</span><span class="log-msg-role '+(c.role||'user')+'">'+(c.role||'user')+'</span>'+oBadge+'<span class="recall-summary-short">'+shortText+'</span><span class="recall-expand-icon">\u25B6</span></div>';
|
|
5497
5536
|
html+='<div class="recall-summary-full">'+fullText+'</div>';
|
|
5498
5537
|
html+='</div>';
|
|
5499
5538
|
});
|
|
@@ -5507,8 +5546,9 @@ function buildRecallDetailHtml(rd){
|
|
|
5507
5546
|
var scoreClass=f.score>=0.7?'high':f.score>=0.5?'mid':'low';
|
|
5508
5547
|
var shortText=escapeHtml(f.summary||f.content||f.original_excerpt||'');
|
|
5509
5548
|
var fullText=escapeHtml(f.content||f.original_excerpt||f.summary||'');
|
|
5549
|
+
var oBadge=recallOriginBadge(f.origin);
|
|
5510
5550
|
html+='<div class="recall-item" onclick="event.stopPropagation();this.classList.toggle(\\\'expanded\\\')">';
|
|
5511
|
-
html+='<div class="recall-item-head"><span class="recall-idx">'+(i+1)+'</span><span class="recall-score '+scoreClass+'">'+f.score.toFixed(3)+'</span><span class="log-msg-role '+(f.role||'user')+'">'+(f.role||'user')+'</span
|
|
5551
|
+
html+='<div class="recall-item-head"><span class="recall-idx">'+(i+1)+'</span><span class="recall-score '+scoreClass+'">'+f.score.toFixed(3)+'</span><span class="log-msg-role '+(f.role||'user')+'">'+(f.role||'user')+'</span>'+oBadge+'<span class="recall-summary-short">'+shortText+'</span><span class="recall-expand-icon">\u25B6</span></div>';
|
|
5512
5552
|
html+='<div class="recall-summary-full">'+fullText+'</div>';
|
|
5513
5553
|
html+='</div>';
|
|
5514
5554
|
});
|
|
@@ -6714,8 +6754,8 @@ async function saveHubConfig(){
|
|
|
6714
6754
|
if(!td.ok){
|
|
6715
6755
|
var errMsg=td.error==='cannot_join_self'?t('sharing.cannotJoinSelf'):(td.error||t('settings.hub.test.fail'));
|
|
6716
6756
|
done();toast(errMsg,'error');return;
|
|
6717
|
-
|
|
6718
|
-
|
|
6757
|
+
}
|
|
6758
|
+
}catch(e){
|
|
6719
6759
|
done();toast(t('settings.hub.test.fail')+': '+String(e),'error');return;
|
|
6720
6760
|
}
|
|
6721
6761
|
}
|
|
@@ -7536,8 +7576,8 @@ function getFilterParams(){
|
|
|
7536
7576
|
if(scope==='local'){
|
|
7537
7577
|
p.set('owner',_currentAgentOwner);
|
|
7538
7578
|
}else if(scope==='allLocal'){
|
|
7539
|
-
|
|
7540
|
-
|
|
7579
|
+
const owner=document.getElementById('filterOwner').value;
|
|
7580
|
+
if(owner) p.set('owner',owner);
|
|
7541
7581
|
}
|
|
7542
7582
|
return p;
|
|
7543
7583
|
}
|
|
@@ -7567,11 +7607,11 @@ async function loadMemories(page,silent){
|
|
|
7567
7607
|
renderPagination();
|
|
7568
7608
|
}catch(e){
|
|
7569
7609
|
if(!silent){
|
|
7570
|
-
|
|
7571
|
-
|
|
7610
|
+
list.innerHTML='';
|
|
7611
|
+
totalPages=1;totalCount=0;
|
|
7572
7612
|
_lastMemoriesFingerprint='';
|
|
7573
|
-
|
|
7574
|
-
|
|
7613
|
+
renderMemories([]);
|
|
7614
|
+
renderPagination();
|
|
7575
7615
|
}
|
|
7576
7616
|
}
|
|
7577
7617
|
}
|
|
@@ -7598,9 +7638,9 @@ async function loadHubMemories(silent){
|
|
|
7598
7638
|
}catch(e){
|
|
7599
7639
|
if(!silent){
|
|
7600
7640
|
_lastMemoriesFingerprint='';
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7641
|
+
document.getElementById('searchMeta').textContent='0'+t('search.meta.results');
|
|
7642
|
+
renderMemories([]);
|
|
7643
|
+
document.getElementById('pagination').innerHTML='';
|
|
7604
7644
|
}
|
|
7605
7645
|
}
|
|
7606
7646
|
}
|