Ship Assistent 0.10: settings panel and UserPrefs with prompt weight.

Separate About-the-user memory (global + per-persona) from craft RAG, add tabbed settings with persona export/import and craft clear APIs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-22 02:05:12 +03:00
co-authored by Cursor
parent cf89348f85
commit fa73158e1c
18 changed files with 2081 additions and 191 deletions
+90
View File
@@ -30,6 +30,26 @@ public partial class SwarmAssistentExtension
}
}
if (Memory is not null)
{
try
{
JObject asst = Config.LoadAssistant(pid);
double weight = asst["user_prefs_weight"]?.Value<double?>() ?? 1.0;
int maxPrefs = asst["user_prefs_max"]?.Value<int?>() ?? 16;
string about = Memory.FormatUserPrefsBlock(pid, weight, maxPrefs);
if (!string.IsNullOrWhiteSpace(about))
{
system.AppendLine();
system.AppendLine(about);
}
}
catch (Exception ex)
{
Logs.Debug($"BuildOllamaMessages user prefs: {ex.Message}");
}
}
JObject exact = Config.LoadExactForPrompt(pid);
if (exact is not null && exact.Count > 0)
{
@@ -170,6 +190,7 @@ public partial class SwarmAssistentExtension
(reply, lastRaw) = await CallOllamaChat(root, modelName, messages, stream: onDelta is not null, onDelta, pid);
JObject patch = TryParsePatch(reply);
await ApplyMemoryActions(root, patch, embed, pid);
ApplyUserPrefActions(patch, pid);
ApplyPersonaActions(patch, ref pid);
if (hop + 1 >= maxHops)
{
@@ -382,6 +403,19 @@ public partial class SwarmAssistentExtension
ctx = new JObject { ["_raw_context"] = contextJson };
}
ctx["memory_hits"] = hits ?? new JArray();
ctx.Remove("taste_profile");
try
{
string pid = AssistentConfig.SafeId(ctx["persona"]?.ToString()) ?? Config?.DefaultPersonaId() ?? "neutral";
JObject asst = Config?.LoadAssistant(pid) ?? new JObject();
double weight = asst["user_prefs_weight"]?.Value<double?>() ?? 1.0;
int maxPrefs = asst["user_prefs_max"]?.Value<int?>() ?? 16;
ctx["user_prefs_count"] = Memory?.SelectUserPrefsForPrompt(pid, weight, maxPrefs).Count ?? 0;
}
catch
{
ctx["user_prefs_count"] = 0;
}
// Never re-inject full Exact into live context (already in system prompt).
ctx.Remove("exact");
if (ctx["session_exact"] is null)
@@ -624,4 +658,60 @@ public partial class SwarmAssistentExtension
}
}
}
void ApplyUserPrefActions(JObject patch, string personaId)
{
if (patch is null || Memory is null)
{
return;
}
bool upsert = false, forget = false;
if (patch["actions"] is JArray acts)
{
foreach (JToken a in acts)
{
string s = a?.ToString() ?? "";
if (string.Equals(s, "user_pref_upsert", StringComparison.OrdinalIgnoreCase))
{
upsert = true;
}
if (string.Equals(s, "user_pref_forget", StringComparison.OrdinalIgnoreCase))
{
forget = true;
}
}
}
JArray prefs = patch["user_prefs"] as JArray;
if (prefs is null || prefs.Count == 0)
{
return;
}
string pid = AssistentConfig.SafeId(personaId) ?? Config.DefaultPersonaId();
foreach (JToken t in prefs)
{
if (t is not JObject mo)
{
continue;
}
string key = mo["key"]?.ToString() ?? "";
string text = mo["text"]?.ToString() ?? "";
string scope = mo["scope"]?.ToString() ?? "global";
bool pinned = mo["pinned"]?.Value<bool>() == true;
try
{
if (forget && string.IsNullOrWhiteSpace(text))
{
Memory.ForgetUserPref(key, scope, pid);
}
else if (upsert || !string.IsNullOrWhiteSpace(text))
{
Memory.UpsertUserPref(key, text, scope, pid, "agent", pinned);
}
}
catch (Exception ex)
{
Logs.Debug($"ApplyUserPrefActions: {ex.Message}");
}
}
}
}