opencode-mem 2.3.6 → 2.4.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.
Files changed (34) hide show
  1. package/dist/config.d.ts +0 -1
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +4 -38
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +38 -80
  6. package/dist/services/ai/providers/anthropic-messages.d.ts.map +1 -1
  7. package/dist/services/ai/providers/anthropic-messages.js +11 -7
  8. package/dist/services/ai/providers/base-provider.d.ts +3 -3
  9. package/dist/services/ai/providers/base-provider.d.ts.map +1 -1
  10. package/dist/services/ai/providers/openai-chat-completion.d.ts.map +1 -1
  11. package/dist/services/ai/providers/openai-chat-completion.js +11 -6
  12. package/dist/services/ai/providers/openai-responses.d.ts.map +1 -1
  13. package/dist/services/ai/providers/openai-responses.js +4 -2
  14. package/dist/services/ai/validators/user-profile-validator.d.ts +0 -1
  15. package/dist/services/ai/validators/user-profile-validator.d.ts.map +1 -1
  16. package/dist/services/ai/validators/user-profile-validator.js +0 -17
  17. package/dist/services/api-handlers.d.ts +32 -3
  18. package/dist/services/api-handlers.d.ts.map +1 -1
  19. package/dist/services/api-handlers.js +89 -13
  20. package/dist/services/auto-capture.js +11 -4
  21. package/dist/services/logger.d.ts.map +1 -1
  22. package/dist/services/logger.js +3 -1
  23. package/dist/services/sqlite/connection-manager.d.ts.map +1 -1
  24. package/dist/services/user-memory-learning.js +9 -14
  25. package/dist/services/user-profile/types.d.ts +0 -5
  26. package/dist/services/user-profile/types.d.ts.map +1 -1
  27. package/dist/services/user-profile/user-profile-manager.d.ts.map +1 -1
  28. package/dist/services/user-profile/user-profile-manager.js +0 -7
  29. package/dist/services/user-prompt/user-prompt-manager.d.ts +2 -0
  30. package/dist/services/user-prompt/user-prompt-manager.d.ts.map +1 -1
  31. package/dist/services/user-prompt/user-prompt-manager.js +21 -0
  32. package/dist/web/app.js +196 -114
  33. package/dist/web/styles.css +202 -122
  34. package/package.json +1 -1
package/dist/web/app.js CHANGED
@@ -115,12 +115,14 @@ function renderMemories() {
115
115
  return;
116
116
  }
117
117
 
118
- container.innerHTML = state.memories
119
- .map((item) => {
120
- if (item.type === "prompt") {
121
- return renderPromptCard(item);
118
+ container.innerHTML = groupMemories(state.memories)
119
+ .map((group) => {
120
+ if (group.isPair) {
121
+ return renderCombinedCard(group);
122
+ } else if (group.type === "prompt") {
123
+ return renderPromptCard(group.item);
122
124
  } else {
123
- return renderMemoryCard(item);
125
+ return renderMemoryCard(group.item);
124
126
  }
125
127
  })
126
128
  .join("");
@@ -132,6 +134,82 @@ function renderMemories() {
132
134
  lucide.createIcons();
133
135
  }
134
136
 
137
+ function groupMemories(items) {
138
+ const map = new Map();
139
+ const pairs = [];
140
+ const processed = new Set();
141
+
142
+ items.forEach((item) => map.set(item.id, item));
143
+
144
+ items.forEach((item) => {
145
+ if (processed.has(item.id)) return;
146
+
147
+ if (item.type === "memory" && item.linkedPromptId && map.has(item.linkedPromptId)) {
148
+ const prompt = map.get(item.linkedPromptId);
149
+ pairs.push({ isPair: true, memory: item, prompt: prompt });
150
+ processed.add(item.id);
151
+ processed.add(prompt.id);
152
+ } else if (item.type === "prompt" && item.linkedMemoryId && map.has(item.linkedMemoryId)) {
153
+ const memory = map.get(item.linkedMemoryId);
154
+ pairs.push({ isPair: true, memory: memory, prompt: item });
155
+ processed.add(item.id);
156
+ processed.add(memory.id);
157
+ } else {
158
+ pairs.push({ isPair: false, type: item.type, item: item });
159
+ processed.add(item.id);
160
+ }
161
+ });
162
+
163
+ return pairs.sort((a, b) => {
164
+ const timeA = a.isPair ? a.memory.createdAt : a.item.createdAt;
165
+ const timeB = b.isPair ? b.memory.createdAt : b.item.createdAt;
166
+ return new Date(timeB) - new Date(timeA);
167
+ });
168
+ }
169
+
170
+ function renderCombinedCard(pair) {
171
+ const { memory, prompt } = pair;
172
+ const isSelected = state.selectedMemories.has(memory.id);
173
+ const similarityHtml =
174
+ memory.similarity !== undefined
175
+ ? `<span class="similarity-score">${Math.round(memory.similarity * 100)}%</span>`
176
+ : "";
177
+
178
+ return `
179
+ <div class="combined-card ${isSelected ? "selected" : ""}" data-id="${memory.id}">
180
+ <div class="combined-prompt-section">
181
+ <div class="combined-header">
182
+ <span class="badge badge-prompt">USER PROMPT</span>
183
+ <span class="prompt-date">${formatDate(prompt.createdAt)}</span>
184
+ </div>
185
+ <div class="prompt-content">${escapeHtml(prompt.content)}</div>
186
+ </div>
187
+
188
+ <div class="combined-divider">
189
+ <i data-lucide="arrow-down" class="divider-icon"></i>
190
+ </div>
191
+
192
+ <div class="combined-memory-section">
193
+ <div class="memory-header">
194
+ <div class="meta">
195
+ <input type="checkbox" class="memory-checkbox" data-id="${memory.id}" ${isSelected ? "checked" : ""} />
196
+ <span class="badge badge-memory">MEMORY</span>
197
+ ${memory.memoryType ? `<span class="badge badge-type">${memory.memoryType}</span>` : ""}
198
+ ${similarityHtml}
199
+ <span class="memory-display-name">${escapeHtml(memory.displayName || memory.id)}</span>
200
+ </div>
201
+ <div class="memory-actions">
202
+ <button class="btn-delete" onclick="deleteMemoryWithLink('${memory.id}', true)">
203
+ <i data-lucide="trash-2" class="icon"></i> Delete Pair
204
+ </button>
205
+ </div>
206
+ </div>
207
+ <div class="memory-content markdown-content">${renderMarkdown(memory.content)}</div>
208
+ </div>
209
+ </div>
210
+ `;
211
+ }
212
+
135
213
  function renderPromptCard(prompt) {
136
214
  const isLinked = !!prompt.linkedMemoryId;
137
215
  const isSelected = state.selectedMemories.has(prompt.id);
@@ -166,14 +244,17 @@ function renderMemoryCard(memory) {
166
244
  const isSelected = state.selectedMemories.has(memory.id);
167
245
  const isPinned = memory.isPinned || false;
168
246
  const isLinked = !!memory.linkedPromptId;
169
- const similarityHtml =
247
+ const similarityHtml =
170
248
  memory.similarity !== undefined
171
249
  ? `<span class="similarity-score">${memory.similarity}%</span>`
172
250
  : "";
173
251
 
174
252
  let displayInfo = memory.displayName || memory.id;
175
253
  if (memory.projectPath) {
176
- const pathParts = memory.projectPath.replace(/\\/g, "/").split("/").filter((p) => p);
254
+ const pathParts = memory.projectPath
255
+ .replace(/\\/g, "/")
256
+ .split("/")
257
+ .filter((p) => p);
177
258
  displayInfo = pathParts[pathParts.length - 1] || memory.projectPath;
178
259
  }
179
260
 
@@ -720,129 +801,130 @@ function renderUserProfile() {
720
801
  const preferences = data.preferences || [];
721
802
  const patterns = data.patterns || [];
722
803
  const workflows = data.workflows || [];
723
- const skillLevel = data.skillLevel || {};
724
804
 
725
805
  container.innerHTML = `
726
806
  <div class="profile-header">
727
807
  <div class="profile-info">
728
808
  <h3>${profile.displayName || profile.userId}</h3>
729
- <p class="profile-meta">
730
- <span>Version: ${profile.version}</span>
731
- <span class="separator">|</span>
732
- <span>Analyzed: ${profile.totalPromptsAnalyzed} prompts</span>
733
- <span class="separator">|</span>
734
- <span>Updated: ${formatDate(profile.lastAnalyzedAt)}</span>
735
- </p>
809
+ <div class="profile-stats">
810
+ <div class="stat-pill">
811
+ <span class="label">VERSION</span>
812
+ <span class="value">${profile.version}</span>
813
+ </div>
814
+ <div class="stat-pill">
815
+ <span class="label">PROMPTS</span>
816
+ <span class="value">${profile.totalPromptsAnalyzed}</span>
817
+ </div>
818
+ <div class="stat-pill">
819
+ <span class="label">LAST UPDATED</span>
820
+ <span class="value">${formatDate(profile.lastAnalyzedAt)}</span>
821
+ </div>
822
+ </div>
736
823
  </div>
737
- <button id="view-changelog-btn" class="btn-secondary">
738
- <i data-lucide="history" class="icon"></i> Version History
824
+ <button id="view-changelog-btn" class="btn-secondary compact">
825
+ <i data-lucide="history" class="icon"></i> History
739
826
  </button>
740
827
  </div>
741
828
 
742
- <div class="profile-section">
743
- <h4><i data-lucide="heart" class="icon"></i> Preferences (${preferences.length})</h4>
744
- ${
745
- preferences.length === 0
746
- ? '<p class="empty-text">No preferences learned yet</p>'
747
- : `
748
- <div class="preferences-list">
749
- ${preferences
750
- .map(
751
- (p) => `
752
- <div class="preference-item">
753
- <div class="preference-header">
754
- <span class="preference-name">${escapeHtml(p.description)}</span>
755
- <span class="confidence-badge">${Math.round(p.confidence * 100)}%</span>
756
- </div>
757
- <div class="confidence-bar">
758
- <div class="confidence-fill" style="width: ${p.confidence * 100}%"></div>
829
+ <div class="dashboard-grid">
830
+ <div class="dashboard-section preferences-section">
831
+ <h4><i data-lucide="heart" class="icon"></i> PREFERENCES <span class="count">${preferences.length}</span></h4>
832
+ ${
833
+ preferences.length === 0
834
+ ? '<p class="empty-text">No preferences learned yet</p>'
835
+ : `
836
+ <div class="cards-grid">
837
+ ${preferences
838
+ .sort((a, b) => b.confidence - a.confidence)
839
+ .map(
840
+ (p) => `
841
+ <div class="compact-card preference-card">
842
+ <div class="card-top">
843
+ <span class="category-tag">${escapeHtml(p.category)}</span>
844
+ <div class="confidence-ring" style="--p:${Math.round(p.confidence * 100)}">
845
+ <span>${Math.round(p.confidence * 100)}%</span>
846
+ </div>
847
+ </div>
848
+ <div class="card-body">
849
+ <p class="card-text">${escapeHtml(p.description)}</p>
850
+ </div>
851
+ ${
852
+ p.evidence && p.evidence.length > 0
853
+ ? `
854
+ <div class="card-footer">
855
+ <span class="evidence-toggle" title="${escapeHtml(Array.isArray(p.evidence) ? p.evidence.join("\n") : p.evidence)}">
856
+ <i data-lucide="info" class="icon-xs"></i> ${Array.isArray(p.evidence) ? p.evidence.length : 1} evidence
857
+ </span>
858
+ </div>`
859
+ : ""
860
+ }
759
861
  </div>
760
- <p class="preference-evidence">${escapeHtml(Array.isArray(p.evidence) ? p.evidence.join(", ") : p.evidence)}</p>
761
- <p class="preference-meta">Category: ${escapeHtml(p.category)}</p>
762
- </div>
763
- `
764
- )
765
- .join("")}
766
- </div>
767
- `
768
- }
769
- </div>
862
+ `
863
+ )
864
+ .join("")}
865
+ </div>
866
+ `
867
+ }
868
+ </div>
770
869
 
771
- <div class="profile-section">
772
- <h4><i data-lucide="activity" class="icon"></i> Patterns (${patterns.length})</h4>
773
- ${
774
- patterns.length === 0
775
- ? '<p class="empty-text">No patterns detected yet</p>'
776
- : `
777
- <div class="patterns-list">
778
- ${patterns
779
- .map(
780
- (p) => `
781
- <div class="pattern-item">
782
- <div class="pattern-header">
783
- <span class="pattern-name">${escapeHtml(p.description)}</span>
784
- <span class="category-badge">${escapeHtml(p.category)}</span>
870
+ <div class="dashboard-section patterns-section">
871
+ <h4><i data-lucide="activity" class="icon"></i> PATTERNS <span class="count">${patterns.length}</span></h4>
872
+ ${
873
+ patterns.length === 0
874
+ ? '<p class="empty-text">No patterns detected yet</p>'
875
+ : `
876
+ <div class="cards-grid">
877
+ ${patterns
878
+ .map(
879
+ (p) => `
880
+ <div class="compact-card pattern-card">
881
+ <div class="card-top">
882
+ <span class="category-tag">${escapeHtml(p.category)}</span>
883
+ </div>
884
+ <div class="card-body">
885
+ <p class="card-text">${escapeHtml(p.description)}</p>
886
+ </div>
785
887
  </div>
786
- </div>
787
- `
788
- )
789
- .join("")}
790
- </div>
791
- `
792
- }
793
- </div>
888
+ `
889
+ )
890
+ .join("")}
891
+ </div>
892
+ `
893
+ }
894
+ </div>
794
895
 
795
- <div class="profile-section">
796
- <h4><i data-lucide="workflow" class="icon"></i> Workflows (${workflows.length})</h4>
797
- ${
798
- workflows.length === 0
799
- ? '<p class="empty-text">No workflows identified yet</p>'
800
- : `
801
- <div class="workflows-list">
802
- ${workflows
803
- .map(
804
- (w) => `
805
- <div class="workflow-item">
806
- <div class="workflow-header">
807
- <span class="workflow-name">${escapeHtml(w.description)}</span>
808
- </div>
809
- <div class="workflow-steps">
810
- ${w.steps
811
- .map(
812
- (step) => `
813
- <div class="workflow-step">
814
- <span class="step-text">${escapeHtml(step)}</span>
815
- </div>
816
- `
817
- )
818
- .join("")}
896
+ <div class="dashboard-section workflows-section full-width">
897
+ <h4><i data-lucide="workflow" class="icon"></i> WORKFLOWS <span class="count">${workflows.length}</span></h4>
898
+ ${
899
+ workflows.length === 0
900
+ ? '<p class="empty-text">No workflows identified yet</p>'
901
+ : `
902
+ <div class="workflows-grid">
903
+ ${workflows
904
+ .map(
905
+ (w) => `
906
+ <div class="workflow-row">
907
+ <div class="workflow-title">${escapeHtml(w.description)}</div>
908
+ <div class="workflow-steps-horizontal">
909
+ ${w.steps
910
+ .map(
911
+ (step, i) => `
912
+ <div class="step-node">
913
+ <span class="step-idx">${i + 1}</span>
914
+ <span class="step-content">${escapeHtml(step)}</span>
915
+ </div>
916
+ ${i < w.steps.length - 1 ? '<i data-lucide="arrow-right" class="step-arrow"></i>' : ""}
917
+ `
918
+ )
919
+ .join("")}
920
+ </div>
819
921
  </div>
820
- </div>
821
- `
822
- )
823
- .join("")}
824
- </div>
825
- `
826
- }
827
- </div>
828
-
829
- <div class="profile-section">
830
- <h4><i data-lucide="award" class="icon"></i> Skill Level</h4>
831
- <div class="skill-level">
832
- <div class="skill-item">
833
- <span class="skill-label">Overall</span>
834
- <span class="skill-value">${escapeHtml(skillLevel.overall || "unknown")}</span>
835
- </div>
836
- ${Object.entries(skillLevel.domains || {})
837
- .map(
838
- ([domain, level]) => `
839
- <div class="skill-item">
840
- <span class="skill-label">${escapeHtml(domain)}</span>
841
- <span class="skill-value">${escapeHtml(level)}</span>
922
+ `
923
+ )
924
+ .join("")}
842
925
  </div>
843
926
  `
844
- )
845
- .join("")}
927
+ }
846
928
  </div>
847
929
  </div>
848
930
  `;