Ship Assistent 0.15.2: apply closed generate patches immediately, recover stalled streams, and skip repeat greetings when a chat already has replies.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-23 09:28:04 +03:00
co-authored by Cursor
parent 4b36850e2b
commit e53f483a00
19 changed files with 790 additions and 287 deletions
+69 -8
View File
@@ -46,7 +46,7 @@ public partial class SwarmAssistentExtension
patch["look_at"] = patch["vision_slots"];
}
}
if (ActionsContain(patch, "generate"))
if (ActionsContain(patch, "generate") || GenerateFlagOn(patch))
{
patch["generate"] = true;
}
@@ -98,7 +98,28 @@ public partial class SwarmAssistentExtension
// not json
}
}
return lastTerminal ?? lastAny;
if (lastTerminal is not null || lastAny is not null)
{
return lastTerminal ?? lastAny;
}
int brace = reply.LastIndexOf('{');
if (brace < 0)
{
return null;
}
try
{
JObject obj = JObject.Parse(reply.Substring(brace).Trim());
if (obj is not null && Array.Exists(PatchKeys, k => obj[k] is not null))
{
return NormalizePatch(obj);
}
}
catch
{
// not json
}
return null;
}
/// <summary>
@@ -114,10 +135,6 @@ public partial class SwarmAssistentExtension
return false;
}
MatchCollection matches = JsonFenceRe.Matches(reply);
if (matches.Count == 0)
{
return false;
}
for (int i = 0; i < matches.Count; i++)
{
Match match = matches[i];
@@ -137,6 +154,23 @@ public partial class SwarmAssistentExtension
// incomplete / invalid json inside fence
}
}
int brace = reply.LastIndexOf('{');
if (brace >= 0)
{
try
{
JObject obj = NormalizePatch(JObject.Parse(reply.Substring(brace).Trim()));
if (obj is not null && FenceIsTerminalPatch(obj))
{
truncated = reply.TrimEnd();
return true;
}
}
catch
{
// incomplete unfenced json
}
}
return false;
}
@@ -149,7 +183,7 @@ public partial class SwarmAssistentExtension
{
return false;
}
if (obj["generate"]?.Type == JTokenType.Boolean && obj["generate"].Value<bool>())
if (GenerateFlagOn(obj))
{
return true;
}
@@ -176,7 +210,8 @@ public partial class SwarmAssistentExtension
}
if (HasValue(obj, "loras") || HasValue(obj, "aspect") || HasValue(obj, "steps")
|| HasValue(obj, "width") || HasValue(obj, "height") || HasValue(obj, "cfg")
|| HasValue(obj, "seed") || HasValue(obj, "controls"))
|| HasValue(obj, "seed") || HasValue(obj, "controls")
|| HasValue(obj, "checkpoint") || HasValue(obj, "negative"))
{
return true;
}
@@ -220,6 +255,32 @@ public partial class SwarmAssistentExtension
return string.Equals(patch["ask"]?.ToString()?.Trim(), name, StringComparison.OrdinalIgnoreCase);
}
static bool GenerateFlagOn(JObject obj)
{
if (obj is null)
{
return false;
}
if (obj["generate"]?.Type == JTokenType.Boolean && obj["generate"].Value<bool>())
{
return true;
}
if (obj["generate"]?.Type == JTokenType.Integer && obj["generate"].Value<int>() != 0)
{
return true;
}
string raw = obj["generate"]?.ToString()?.Trim();
if (!string.IsNullOrWhiteSpace(raw)
&& (raw.Equals("true", StringComparison.OrdinalIgnoreCase)
|| raw == "1"
|| raw.Equals("yes", StringComparison.OrdinalIgnoreCase)
|| raw.Equals("on", StringComparison.OrdinalIgnoreCase)))
{
return true;
}
return ActionsContain(obj, "generate");
}
static bool ActionsContain(JObject patch, string action)
{
if (patch?["actions"] is not JArray acts)