fix: show structured-output summary instead of raw JSON in OUTCOME
A --json-schema run can echo the structured {"summary":...} JSON as the
result text, which then landed verbatim in task.Result and rendered raw in
the OUTCOME card. Unwrap the summary in StreamAnalyzer when the result text
is itself such a JSON object (a plain markdown result is kept as-is), and
add a UI safety net in ApplyOutcome for rows already persisted as raw JSON.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using ClaudeDo.Data;
|
using ClaudeDo.Data;
|
||||||
@@ -258,16 +259,38 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IMissionContro
|
|||||||
var idx = result.IndexOf(RoadblockMarker, StringComparison.Ordinal);
|
var idx = result.IndexOf(RoadblockMarker, StringComparison.Ordinal);
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
{
|
{
|
||||||
SessionOutcome = result;
|
SessionOutcome = UnwrapSummary(result);
|
||||||
Roadblocks = null;
|
Roadblocks = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var summary = result[..idx].TrimEnd().TrimEnd('⚠').TrimEnd();
|
var summary = result[..idx].TrimEnd().TrimEnd('⚠').TrimEnd();
|
||||||
SessionOutcome = string.IsNullOrWhiteSpace(summary) ? null : summary;
|
SessionOutcome = string.IsNullOrWhiteSpace(summary) ? null : UnwrapSummary(summary);
|
||||||
Roadblocks = result[(idx + RoadblockMarker.Length)..].Trim();
|
Roadblocks = result[(idx + RoadblockMarker.Length)..].Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Safety net for older rows persisted as raw structured-output JSON: if the outcome
|
||||||
|
/// is a JSON object with a string <c>summary</c>, show that; otherwise keep the text as-is.</summary>
|
||||||
|
private static string? UnwrapSummary(string? text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) return text;
|
||||||
|
var trimmed = text.TrimStart();
|
||||||
|
if (trimmed.Length == 0 || trimmed[0] != '{') return text;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(trimmed);
|
||||||
|
if (doc.RootElement.ValueKind == JsonValueKind.Object &&
|
||||||
|
doc.RootElement.TryGetProperty("summary", out var s) &&
|
||||||
|
s.ValueKind == JsonValueKind.String)
|
||||||
|
{
|
||||||
|
var summary = s.GetString();
|
||||||
|
if (!string.IsNullOrWhiteSpace(summary)) return summary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
public async System.Threading.Tasks.Task ReplayLogFileAsync(string? logPath, CancellationToken ct)
|
public async System.Threading.Tasks.Task ReplayLogFileAsync(string? logPath, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(logPath)) return;
|
if (string.IsNullOrWhiteSpace(logPath)) return;
|
||||||
|
|||||||
@@ -91,16 +91,34 @@ public sealed class StreamAnalyzer
|
|||||||
|
|
||||||
private string? FallbackResult()
|
private string? FallbackResult()
|
||||||
{
|
{
|
||||||
|
// A --json-schema run sometimes echoes the structured JSON as the result text; unwrap
|
||||||
|
// that to its summary. A plain markdown result is kept verbatim (even when a separate
|
||||||
|
// structured_output is also present). Only when there is no result text do we fall back
|
||||||
|
// to the structured summary, then the raw JSON.
|
||||||
|
if (SummaryFromJson(_resultMarkdown) is { } fromResult) return fromResult;
|
||||||
if (!string.IsNullOrEmpty(_resultMarkdown)) return _resultMarkdown;
|
if (!string.IsNullOrEmpty(_resultMarkdown)) return _resultMarkdown;
|
||||||
if (_structuredOutputJson is null) return _resultMarkdown;
|
if (SummaryFromJson(_structuredOutputJson) is { } fromStructured) return fromStructured;
|
||||||
|
return _structuredOutputJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Extracts a non-empty string <c>summary</c> from a JSON object, or null when
|
||||||
|
/// the input isn't such an object (a plain markdown result parses to null and is kept).</summary>
|
||||||
|
private static string? SummaryFromJson(string? json)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(json)) return null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var doc = JsonDocument.Parse(_structuredOutputJson);
|
using var doc = JsonDocument.Parse(json);
|
||||||
if (doc.RootElement.TryGetProperty("summary", out var s))
|
if (doc.RootElement.ValueKind == JsonValueKind.Object &&
|
||||||
return s.GetString();
|
doc.RootElement.TryGetProperty("summary", out var s) &&
|
||||||
|
s.ValueKind == JsonValueKind.String)
|
||||||
|
{
|
||||||
|
var text = s.GetString();
|
||||||
|
return string.IsNullOrWhiteSpace(text) ? null : text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
return _structuredOutputJson;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CollectFromAssistant(JsonElement root)
|
private void CollectFromAssistant(JsonElement root)
|
||||||
|
|||||||
Reference in New Issue
Block a user