opencode-mem 2.7.1 → 2.7.3
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/services/user-memory-learning.js +1 -1
- package/dist/services/user-profile/profile-utils.d.ts +2 -2
- package/dist/services/user-profile/profile-utils.d.ts.map +1 -1
- package/dist/services/user-profile/profile-utils.js +45 -2
- package/dist/services/user-profile/user-profile-manager.d.ts +1 -0
- package/dist/services/user-profile/user-profile-manager.d.ts.map +1 -1
- package/dist/services/user-profile/user-profile-manager.js +51 -23
- package/dist/web/app.js +44 -14
- package/dist/web/index.html +1 -0
- package/package.json +1 -1
|
@@ -179,7 +179,7 @@ Use the update_user_profile tool to save the ${existingProfile ? "updated" : "ne
|
|
|
179
179
|
},
|
|
180
180
|
},
|
|
181
181
|
};
|
|
182
|
-
const result = await provider.executeToolCall(systemPrompt, context, toolSchema,
|
|
182
|
+
const result = await provider.executeToolCall(systemPrompt, context, toolSchema, `user-profile-${Date.now()}`);
|
|
183
183
|
if (!result.success || !result.data) {
|
|
184
184
|
throw new Error(result.error || "Failed to analyze user profile");
|
|
185
185
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const safeArray: <T>(arr:
|
|
2
|
-
export declare const safeObject: <T extends object>(obj:
|
|
1
|
+
export declare const safeArray: <T>(arr: any) => T[];
|
|
2
|
+
export declare const safeObject: <T extends object>(obj: any, fallback: T) => T;
|
|
3
3
|
//# sourceMappingURL=profile-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-utils.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/profile-utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"profile-utils.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/profile-utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,KAAK,GAAG,KAAG,CAAC,EA0BxC,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,KAAK,GAAG,EAAE,UAAU,CAAC,KAAG,CAWpE,CAAC"}
|
|
@@ -1,2 +1,45 @@
|
|
|
1
|
-
export const safeArray = (arr) =>
|
|
2
|
-
|
|
1
|
+
export const safeArray = (arr) => {
|
|
2
|
+
if (!arr)
|
|
3
|
+
return [];
|
|
4
|
+
let result = arr;
|
|
5
|
+
if (typeof result === "string") {
|
|
6
|
+
try {
|
|
7
|
+
result = JSON.parse(result);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
try {
|
|
11
|
+
result = JSON.parse(result.trim().replace(/,$/, ""));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (!Array.isArray(result))
|
|
19
|
+
return [];
|
|
20
|
+
const flattened = [];
|
|
21
|
+
const walk = (item) => {
|
|
22
|
+
if (Array.isArray(item)) {
|
|
23
|
+
item.forEach(walk);
|
|
24
|
+
}
|
|
25
|
+
else if (item) {
|
|
26
|
+
flattened.push(item);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
walk(result);
|
|
30
|
+
return flattened;
|
|
31
|
+
};
|
|
32
|
+
export const safeObject = (obj, fallback) => {
|
|
33
|
+
if (!obj)
|
|
34
|
+
return fallback;
|
|
35
|
+
let result = obj;
|
|
36
|
+
if (typeof result === "string") {
|
|
37
|
+
try {
|
|
38
|
+
result = JSON.parse(result);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return fallback;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result && typeof result === "object" && !Array.isArray(result) ? result : fallback;
|
|
45
|
+
};
|
|
@@ -17,6 +17,7 @@ export declare class UserProfileManager {
|
|
|
17
17
|
private rowToProfile;
|
|
18
18
|
private rowToChangelog;
|
|
19
19
|
mergeProfileData(existing: UserProfileData, updates: Partial<UserProfileData>): UserProfileData;
|
|
20
|
+
private ensureArray;
|
|
20
21
|
}
|
|
21
22
|
export declare const userProfileManager: UserProfileManager;
|
|
22
23
|
//# sourceMappingURL=user-profile-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-profile-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/user-profile-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKrF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA0CpB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAapD,aAAa,CACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,MAAM,GACtB,MAAM;
|
|
1
|
+
{"version":3,"file":"user-profile-manager.d.ts","sourceRoot":"","sources":["../../../src/services/user-profile/user-profile-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKrF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;IAQhC,OAAO,CAAC,YAAY;IA0CpB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAapD,aAAa,CACX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,eAAe,EAAE,MAAM,GACtB,MAAM;IAoCT,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,eAAe,EAC5B,yBAAyB,EAAE,MAAM,EACjC,aAAa,EAAE,MAAM,GACpB,IAAI;IAmCP,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,oBAAoB;IAiB5B,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,oBAAoB,EAAE;IAYnF,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA2B7C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAOrD,oBAAoB,IAAI,WAAW,EAAE;IAMrC,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,cAAc;IAYtB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe;IA0F/F,OAAO,CAAC,WAAW;CAWpB;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
|
|
@@ -59,6 +59,11 @@ export class UserProfileManager {
|
|
|
59
59
|
createProfile(userId, displayName, userName, userEmail, profileData, promptsAnalyzed) {
|
|
60
60
|
const id = `profile_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
61
61
|
const now = Date.now();
|
|
62
|
+
const cleanedData = {
|
|
63
|
+
preferences: safeArray(profileData.preferences),
|
|
64
|
+
patterns: safeArray(profileData.patterns),
|
|
65
|
+
workflows: safeArray(profileData.workflows),
|
|
66
|
+
};
|
|
62
67
|
const stmt = this.db.prepare(`
|
|
63
68
|
INSERT INTO user_profiles (
|
|
64
69
|
id, user_id, display_name, user_name, user_email,
|
|
@@ -67,12 +72,17 @@ export class UserProfileManager {
|
|
|
67
72
|
)
|
|
68
73
|
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, 1)
|
|
69
74
|
`);
|
|
70
|
-
stmt.run(id, userId, displayName, userName, userEmail, JSON.stringify(
|
|
71
|
-
this.addChangelog(id, 1, "create", "Initial profile creation",
|
|
75
|
+
stmt.run(id, userId, displayName, userName, userEmail, JSON.stringify(cleanedData), now, now, promptsAnalyzed);
|
|
76
|
+
this.addChangelog(id, 1, "create", "Initial profile creation", cleanedData);
|
|
72
77
|
return id;
|
|
73
78
|
}
|
|
74
79
|
updateProfile(profileId, profileData, additionalPromptsAnalyzed, changeSummary) {
|
|
75
80
|
const now = Date.now();
|
|
81
|
+
const cleanedData = {
|
|
82
|
+
preferences: safeArray(profileData.preferences),
|
|
83
|
+
patterns: safeArray(profileData.patterns),
|
|
84
|
+
workflows: safeArray(profileData.workflows),
|
|
85
|
+
};
|
|
76
86
|
const getVersionStmt = this.db.prepare(`SELECT version FROM user_profiles WHERE id = ?`);
|
|
77
87
|
const versionRow = getVersionStmt.get(profileId);
|
|
78
88
|
const newVersion = (versionRow?.version || 0) + 1;
|
|
@@ -84,8 +94,8 @@ export class UserProfileManager {
|
|
|
84
94
|
total_prompts_analyzed = total_prompts_analyzed + ?
|
|
85
95
|
WHERE id = ?
|
|
86
96
|
`);
|
|
87
|
-
updateStmt.run(JSON.stringify(
|
|
88
|
-
this.addChangelog(profileId, newVersion, "update", changeSummary,
|
|
97
|
+
updateStmt.run(JSON.stringify(cleanedData), newVersion, now, additionalPromptsAnalyzed, profileId);
|
|
98
|
+
this.addChangelog(profileId, newVersion, "update", changeSummary, cleanedData);
|
|
89
99
|
this.cleanupOldChangelogs(profileId);
|
|
90
100
|
}
|
|
91
101
|
addChangelog(profileId, version, changeType, changeSummary, profileData) {
|
|
@@ -191,21 +201,25 @@ export class UserProfileManager {
|
|
|
191
201
|
}
|
|
192
202
|
mergeProfileData(existing, updates) {
|
|
193
203
|
const merged = {
|
|
194
|
-
preferences:
|
|
195
|
-
patterns:
|
|
196
|
-
workflows:
|
|
204
|
+
preferences: this.ensureArray(existing?.preferences),
|
|
205
|
+
patterns: this.ensureArray(existing?.patterns),
|
|
206
|
+
workflows: this.ensureArray(existing?.workflows),
|
|
197
207
|
};
|
|
198
208
|
if (updates.preferences) {
|
|
199
|
-
|
|
209
|
+
const incomingPrefs = this.ensureArray(updates.preferences);
|
|
210
|
+
for (const newPref of incomingPrefs) {
|
|
200
211
|
const existingIndex = merged.preferences.findIndex((p) => p.category === newPref.category && p.description === newPref.description);
|
|
201
212
|
if (existingIndex >= 0) {
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
213
|
+
const existingItem = merged.preferences[existingIndex];
|
|
214
|
+
if (existingItem) {
|
|
204
215
|
merged.preferences[existingIndex] = {
|
|
205
216
|
...newPref,
|
|
206
|
-
confidence: Math.min(1,
|
|
217
|
+
confidence: Math.min(1, (existingItem.confidence || 0) + 0.1),
|
|
207
218
|
evidence: [
|
|
208
|
-
...new Set([
|
|
219
|
+
...new Set([
|
|
220
|
+
...this.ensureArray(existingItem.evidence),
|
|
221
|
+
...this.ensureArray(newPref.evidence),
|
|
222
|
+
]),
|
|
209
223
|
].slice(0, 5),
|
|
210
224
|
lastUpdated: Date.now(),
|
|
211
225
|
};
|
|
@@ -215,18 +229,19 @@ export class UserProfileManager {
|
|
|
215
229
|
merged.preferences.push({ ...newPref, lastUpdated: Date.now() });
|
|
216
230
|
}
|
|
217
231
|
}
|
|
218
|
-
merged.preferences.sort((a, b) => b.confidence - a.confidence);
|
|
232
|
+
merged.preferences.sort((a, b) => (b.confidence || 0) - (a.confidence || 0));
|
|
219
233
|
merged.preferences = merged.preferences.slice(0, CONFIG.userProfileMaxPreferences);
|
|
220
234
|
}
|
|
221
235
|
if (updates.patterns) {
|
|
222
|
-
|
|
236
|
+
const incomingPatterns = this.ensureArray(updates.patterns);
|
|
237
|
+
for (const newPattern of incomingPatterns) {
|
|
223
238
|
const existingIndex = merged.patterns.findIndex((p) => p.category === newPattern.category && p.description === newPattern.description);
|
|
224
239
|
if (existingIndex >= 0) {
|
|
225
|
-
const
|
|
226
|
-
if (
|
|
240
|
+
const existingItem = merged.patterns[existingIndex];
|
|
241
|
+
if (existingItem) {
|
|
227
242
|
merged.patterns[existingIndex] = {
|
|
228
243
|
...newPattern,
|
|
229
|
-
frequency:
|
|
244
|
+
frequency: (existingItem.frequency || 1) + 1,
|
|
230
245
|
lastSeen: Date.now(),
|
|
231
246
|
};
|
|
232
247
|
}
|
|
@@ -235,18 +250,19 @@ export class UserProfileManager {
|
|
|
235
250
|
merged.patterns.push({ ...newPattern, frequency: 1, lastSeen: Date.now() });
|
|
236
251
|
}
|
|
237
252
|
}
|
|
238
|
-
merged.patterns.sort((a, b) => b.frequency - a.frequency);
|
|
253
|
+
merged.patterns.sort((a, b) => (b.frequency || 0) - (a.frequency || 0));
|
|
239
254
|
merged.patterns = merged.patterns.slice(0, CONFIG.userProfileMaxPatterns);
|
|
240
255
|
}
|
|
241
256
|
if (updates.workflows) {
|
|
242
|
-
|
|
257
|
+
const incomingWorkflows = this.ensureArray(updates.workflows);
|
|
258
|
+
for (const newWorkflow of incomingWorkflows) {
|
|
243
259
|
const existingIndex = merged.workflows.findIndex((w) => w.description === newWorkflow.description);
|
|
244
260
|
if (existingIndex >= 0) {
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
261
|
+
const existingItem = merged.workflows[existingIndex];
|
|
262
|
+
if (existingItem) {
|
|
247
263
|
merged.workflows[existingIndex] = {
|
|
248
264
|
...newWorkflow,
|
|
249
|
-
frequency:
|
|
265
|
+
frequency: (existingItem.frequency || 1) + 1,
|
|
250
266
|
};
|
|
251
267
|
}
|
|
252
268
|
}
|
|
@@ -254,10 +270,22 @@ export class UserProfileManager {
|
|
|
254
270
|
merged.workflows.push({ ...newWorkflow, frequency: 1 });
|
|
255
271
|
}
|
|
256
272
|
}
|
|
257
|
-
merged.workflows.sort((a, b) => b.frequency - a.frequency);
|
|
273
|
+
merged.workflows.sort((a, b) => (b.frequency || 0) - (a.frequency || 0));
|
|
258
274
|
merged.workflows = merged.workflows.slice(0, CONFIG.userProfileMaxWorkflows);
|
|
259
275
|
}
|
|
260
276
|
return merged;
|
|
261
277
|
}
|
|
278
|
+
ensureArray(val) {
|
|
279
|
+
if (typeof val === "string") {
|
|
280
|
+
try {
|
|
281
|
+
const parsed = JSON.parse(val);
|
|
282
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return Array.isArray(val) ? val : [];
|
|
289
|
+
}
|
|
262
290
|
}
|
|
263
291
|
export const userProfileManager = new UserProfileManager();
|
package/dist/web/app.js
CHANGED
|
@@ -927,10 +927,40 @@ function renderUserProfile() {
|
|
|
927
927
|
return;
|
|
928
928
|
}
|
|
929
929
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
930
|
+
let data = profile.profileData;
|
|
931
|
+
if (typeof data === "string") {
|
|
932
|
+
try {
|
|
933
|
+
data = JSON.parse(data);
|
|
934
|
+
} catch (e) {
|
|
935
|
+
console.error("Failed to parse profileData string", e);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
const parseField = (field) => {
|
|
940
|
+
if (!field) return [];
|
|
941
|
+
let result = field;
|
|
942
|
+
let lastResult = null;
|
|
943
|
+
while (typeof result === "string" && result !== lastResult) {
|
|
944
|
+
lastResult = result;
|
|
945
|
+
try {
|
|
946
|
+
result = JSON.parse(typeof jsonrepair === "function" ? jsonrepair(result) : result);
|
|
947
|
+
} catch {
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (!Array.isArray(result)) return [];
|
|
952
|
+
const flattened = [];
|
|
953
|
+
const walk = (item) => {
|
|
954
|
+
if (Array.isArray(item)) item.forEach(walk);
|
|
955
|
+
else if (item && typeof item === "object") flattened.push(item);
|
|
956
|
+
};
|
|
957
|
+
walk(result);
|
|
958
|
+
return flattened;
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
const preferences = parseField(data.preferences);
|
|
962
|
+
const patterns = parseField(data.patterns);
|
|
963
|
+
const workflows = parseField(data.workflows);
|
|
934
964
|
|
|
935
965
|
container.innerHTML = `
|
|
936
966
|
<div class="profile-header">
|
|
@@ -965,18 +995,18 @@ function renderUserProfile() {
|
|
|
965
995
|
: `
|
|
966
996
|
<div class="cards-grid">
|
|
967
997
|
${preferences
|
|
968
|
-
.sort((a, b) => b.confidence - a.confidence)
|
|
998
|
+
.sort((a, b) => (b.confidence || 0) - (a.confidence || 0))
|
|
969
999
|
.map(
|
|
970
1000
|
(p) => `
|
|
971
1001
|
<div class="compact-card preference-card">
|
|
972
1002
|
<div class="card-top">
|
|
973
|
-
<span class="category-tag">${escapeHtml(p.category)}</span>
|
|
974
|
-
<div class="confidence-ring" style="--p:${Math.round(p.confidence * 100)}">
|
|
975
|
-
<span>${Math.round(p.confidence * 100)}%</span>
|
|
1003
|
+
<span class="category-tag">${escapeHtml(p.category || "General")}</span>
|
|
1004
|
+
<div class="confidence-ring" style="--p:${Math.round((p.confidence || 0) * 100)}">
|
|
1005
|
+
<span>${Math.round((p.confidence || 0) * 100)}%</span>
|
|
976
1006
|
</div>
|
|
977
1007
|
</div>
|
|
978
1008
|
<div class="card-body">
|
|
979
|
-
<p class="card-text">${escapeHtml(p.description)}</p>
|
|
1009
|
+
<p class="card-text">${escapeHtml(p.description || "")}</p>
|
|
980
1010
|
</div>
|
|
981
1011
|
${
|
|
982
1012
|
p.evidence && p.evidence.length > 0
|
|
@@ -1009,10 +1039,10 @@ function renderUserProfile() {
|
|
|
1009
1039
|
(p) => `
|
|
1010
1040
|
<div class="compact-card pattern-card">
|
|
1011
1041
|
<div class="card-top">
|
|
1012
|
-
<span class="category-tag">${escapeHtml(p.category)}</span>
|
|
1042
|
+
<span class="category-tag">${escapeHtml(p.category || "General")}</span>
|
|
1013
1043
|
</div>
|
|
1014
1044
|
<div class="card-body">
|
|
1015
|
-
<p class="card-text">${escapeHtml(p.description)}</p>
|
|
1045
|
+
<p class="card-text">${escapeHtml(p.description || "")}</p>
|
|
1016
1046
|
</div>
|
|
1017
1047
|
</div>
|
|
1018
1048
|
`
|
|
@@ -1034,16 +1064,16 @@ function renderUserProfile() {
|
|
|
1034
1064
|
.map(
|
|
1035
1065
|
(w) => `
|
|
1036
1066
|
<div class="workflow-row">
|
|
1037
|
-
<div class="workflow-title">${escapeHtml(w.description)}</div>
|
|
1067
|
+
<div class="workflow-title">${escapeHtml(w.description || "")}</div>
|
|
1038
1068
|
<div class="workflow-steps-horizontal">
|
|
1039
|
-
${w.steps
|
|
1069
|
+
${(w.steps || [])
|
|
1040
1070
|
.map(
|
|
1041
1071
|
(step, i) => `
|
|
1042
1072
|
<div class="step-node">
|
|
1043
1073
|
<span class="step-idx">${i + 1}</span>
|
|
1044
1074
|
<span class="step-content">${escapeHtml(step)}</span>
|
|
1045
1075
|
</div>
|
|
1046
|
-
${i < w.steps.length - 1 ? '<i data-lucide="arrow-right" class="step-arrow"></i>' : ""}
|
|
1076
|
+
${i < (w.steps || []).length - 1 ? '<i data-lucide="arrow-right" class="step-arrow"></i>' : ""}
|
|
1047
1077
|
`
|
|
1048
1078
|
)
|
|
1049
1079
|
.join("")}
|
package/dist/web/index.html
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
10
10
|
<script src="https://cdn.jsdelivr.net/npm/marked@17.0.1/lib/marked.umd.min.js"></script>
|
|
11
11
|
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.2/dist/purify.min.js"></script>
|
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/jsonrepair@latest/lib/umd/jsonrepair.min.js"></script>
|
|
12
13
|
</head>
|
|
13
14
|
<body>
|
|
14
15
|
<div class="container">
|