A new transient per-request toggle state (off→plan→ultracode→fusion→moa) that whitelists the CLI's Workflow + Task tools so Anthropic's native dynamic-workflow orchestration runs in gott's headless -p mode without a permission gate (the engine silently denies Workflow otherwise). Effort is raised to xhigh (the real ultracode equivalent; "ultracode" itself is not a valid effort value) without downgrading an explicit max. Only enabled for Claude models (z.ai/GLM has no workflow runtime) and not in plan mode. The frontend injects a "use a workflow" hint, like plan mode injects its own. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
162 lines
5.3 KiB
C#
162 lines
5.3 KiB
C#
using System.Collections.Generic;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
|
||
namespace Gott.Models;
|
||
|
||
public class ChatMessage
|
||
{
|
||
[JsonPropertyName("role")]
|
||
public string Role { get; set; } = "";
|
||
|
||
[JsonPropertyName("content")]
|
||
public JsonElement Content { get; set; }
|
||
|
||
/// <summary>
|
||
/// Extrahiert den reinen Text aus Content (egal ob string oder content-block array).
|
||
/// </summary>
|
||
public string GetTextContent()
|
||
{
|
||
if (Content.ValueKind == JsonValueKind.String)
|
||
return Content.GetString() ?? "";
|
||
|
||
if (Content.ValueKind == JsonValueKind.Array)
|
||
{
|
||
var parts = new List<string>();
|
||
foreach (var block in Content.EnumerateArray())
|
||
{
|
||
if (block.TryGetProperty("type", out var t) && t.GetString() == "text"
|
||
&& block.TryGetProperty("text", out var txt))
|
||
parts.Add(txt.GetString() ?? "");
|
||
}
|
||
return string.Join("\n", parts);
|
||
}
|
||
|
||
return Content.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Prüft ob Datei-Attachments (image/document) enthalten sind.
|
||
/// </summary>
|
||
public bool HasAttachments()
|
||
{
|
||
if (Content.ValueKind != JsonValueKind.Array) return false;
|
||
foreach (var block in Content.EnumerateArray())
|
||
{
|
||
if (block.TryGetProperty("type", out var t))
|
||
{
|
||
var type = t.GetString();
|
||
if (type == "image" || type == "document") return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt Attachment-Infos zurück (type + media_type).
|
||
/// </summary>
|
||
public List<AttachmentInfo> GetAttachments()
|
||
{
|
||
var result = new List<AttachmentInfo>();
|
||
if (Content.ValueKind != JsonValueKind.Array) return result;
|
||
foreach (var block in Content.EnumerateArray())
|
||
{
|
||
if (block.TryGetProperty("type", out var t))
|
||
{
|
||
var type = t.GetString();
|
||
if (type == "image" || type == "document")
|
||
{
|
||
var mediaType = "";
|
||
if (block.TryGetProperty("source", out var src) && src.TryGetProperty("media_type", out var mt))
|
||
mediaType = mt.GetString() ?? "";
|
||
result.Add(new AttachmentInfo { Type = type, MediaType = mediaType });
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gibt vollständige Attachment-Blöcke zurück (inkl. Base64-Daten).
|
||
/// </summary>
|
||
public List<AttachmentBlock> GetAttachmentBlocks()
|
||
{
|
||
var result = new List<AttachmentBlock>();
|
||
if (Content.ValueKind != JsonValueKind.Array) return result;
|
||
foreach (var block in Content.EnumerateArray())
|
||
{
|
||
if (block.TryGetProperty("type", out var t))
|
||
{
|
||
var type = t.GetString();
|
||
if (type == "image" || type == "document")
|
||
{
|
||
var mediaType = "";
|
||
var data = "";
|
||
var fileName = "";
|
||
if (block.TryGetProperty("source", out var src))
|
||
{
|
||
if (src.TryGetProperty("media_type", out var mt))
|
||
mediaType = mt.GetString() ?? "";
|
||
if (src.TryGetProperty("data", out var d))
|
||
data = d.GetString() ?? "";
|
||
}
|
||
if (block.TryGetProperty("_file_name", out var fn))
|
||
fileName = fn.GetString() ?? "";
|
||
result.Add(new AttachmentBlock { Type = type, MediaType = mediaType, Data = data, FileName = fileName });
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public class AttachmentBlock
|
||
{
|
||
public string Type { get; set; } = "";
|
||
public string MediaType { get; set; } = "";
|
||
public string Data { get; set; } = "";
|
||
public string FileName { get; set; } = "";
|
||
}
|
||
|
||
public class AttachmentInfo
|
||
{
|
||
public string Type { get; set; } = "";
|
||
public string MediaType { get; set; } = "";
|
||
}
|
||
|
||
public class ChatRequest
|
||
{
|
||
[JsonPropertyName("content")]
|
||
public JsonElement Content { get; set; }
|
||
|
||
[JsonPropertyName("sessionId")]
|
||
public string SessionId { get; set; } = "";
|
||
|
||
/// <summary>
|
||
/// Wenn gesetzt: Dies ist eine Antwort auf ein ask_user_choice Tool.
|
||
/// Enthält die tool_use_id des offenen ask_user_choice-Calls.
|
||
/// </summary>
|
||
[JsonPropertyName("choiceToolCallId")]
|
||
public string ChoiceToolCallId { get; set; }
|
||
|
||
/// <summary>
|
||
/// Der gewählte Text aus den Auswahloptionen.
|
||
/// </summary>
|
||
[JsonPropertyName("choiceResult")]
|
||
public string ChoiceResult { get; set; }
|
||
|
||
/// <summary>
|
||
/// Wenn true: Plan-Modus aktiv. Nur Read-only Tools, keine Datei-Änderungen.
|
||
/// </summary>
|
||
[JsonPropertyName("planMode")]
|
||
public bool PlanMode { get; set; }
|
||
|
||
/// <summary>
|
||
/// Wenn true: Ultracode aktiv. Die Anfrage wird als nativer dynamischer Workflow
|
||
/// (mehrere Subagenten) gefahren – das Workflow-Tool wird freigeschaltet, Effort = xhigh.
|
||
/// Nur für den Claude-CLI-Pfad relevant (z.ai/GLM und der API-Pfad haben kein Workflow-Runtime).
|
||
/// </summary>
|
||
[JsonPropertyName("ultracode")]
|
||
public bool Ultracode { get; set; }
|
||
}
|