fix: token persistence, context windows, CSS tables/bullets/hr, image attachments
All checks were successful
Beta Release / beta (push) Successful in 1m1s

- Fix token count reset on app restart: persist realTokens in conversation.json
- Fix token/context window values: Studio 150K (summarize at 120K), Terminal 100K
- Fix table rendering in terminal tab: correct thead/tbody display model
- Fix copy button always top-right in Studio code blocks
- Add markdown horizontal rule (---) support in Studio and Terminal
- Fix bullet list double dot: remove CSS ::before duplicate bullet point
- Add image attachments support (VLM description, file mentions @file.ext)
- Add sudo detection with cache (sync.Once)
- Fix message content serialization (TextContent wrapper)
- Guide AI to use read_file instead of cat in studio prompt

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
Augustin
2026-04-26 15:19:26 +02:00
parent cb3d35756a
commit 12000e523c
17 changed files with 1686 additions and 109 deletions

View File

@@ -20,14 +20,42 @@ var thinkRegex = regexp.MustCompile(`(?s)<[Tt]hink[^>]*>.*?</[Tt]hink>`)
const maxHistorySize = 100
type ContentPart struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
ImageURL *ImageURL `json:"image_url,omitempty"`
}
type ImageURL struct {
URL string `json:"url"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content,omitempty"`
Content json.RawMessage `json:"content,omitempty"`
ToolCalls []ToolCallMsg `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
}
func TextContent(s string) json.RawMessage {
b, _ := json.Marshal(s)
return b
}
func PartsContent(parts []ContentPart) json.RawMessage {
b, _ := json.Marshal(parts)
return b
}
func (m Message) ContentString() string {
var s string
if json.Unmarshal(m.Content, &s) == nil {
return s
}
return string(m.Content)
}
type ToolCallMsg struct {
ID string `json:"id"`
Type string `json:"type"`
@@ -143,7 +171,7 @@ func (o *Orchestrator) Send(userMessage string) (string, error) {
o.histMu.Lock()
o.history = append(o.history, Message{
Role: "user",
Content: userMessage,
Content: TextContent(userMessage),
})
if len(o.history) > maxHistorySize {
@@ -152,7 +180,7 @@ func (o *Orchestrator) Send(userMessage string) (string, error) {
messages := make([]Message, 0, len(o.history)+1)
if o.systemPrompt != "" {
messages = append(messages, Message{Role: "system", Content: o.systemPrompt})
messages = append(messages, Message{Role: "system", Content: TextContent(o.systemPrompt)})
}
messages = append(messages, o.history...)
@@ -173,7 +201,7 @@ func (o *Orchestrator) Send(userMessage string) (string, error) {
o.histMu.Lock()
o.history = append(o.history, Message{
Role: "assistant",
Content: content,
Content: TextContent(content),
})
_ = providerName
o.histMu.Unlock()
@@ -185,7 +213,7 @@ func (o *Orchestrator) SendStream(userMessage string, onChunk func(string)) (str
o.histMu.Lock()
o.history = append(o.history, Message{
Role: "user",
Content: userMessage,
Content: TextContent(userMessage),
})
if len(o.history) > maxHistorySize {
@@ -194,7 +222,7 @@ func (o *Orchestrator) SendStream(userMessage string, onChunk func(string)) (str
messages := make([]Message, 0, len(o.history)+1)
if o.systemPrompt != "" {
messages = append(messages, Message{Role: "system", Content: o.systemPrompt})
messages = append(messages, Message{Role: "system", Content: TextContent(o.systemPrompt)})
}
messages = append(messages, o.history...)
@@ -273,7 +301,7 @@ func (o *Orchestrator) SendStream(userMessage string, onChunk func(string)) (str
o.histMu.Lock()
o.history = append(o.history, Message{
Role: "assistant",
Content: content,
Content: TextContent(content),
})
o.histMu.Unlock()
@@ -283,7 +311,7 @@ func (o *Orchestrator) SendStream(userMessage string, onChunk func(string)) (str
func (o *Orchestrator) SendWithTools(messages []Message) (*ChatResponse, error) {
fullMessages := make([]Message, 0, len(messages)+1)
if o.systemPrompt != "" {
fullMessages = append(fullMessages, Message{Role: "system", Content: o.systemPrompt})
fullMessages = append(fullMessages, Message{Role: "system", Content: TextContent(o.systemPrompt)})
}
fullMessages = append(fullMessages, messages...)
@@ -314,7 +342,7 @@ type ChunkCallback func(content string, toolCalls []ToolCallMsg)
func (o *Orchestrator) SendWithToolsStream(messages []Message, onChunk ChunkCallback) (*ChatResponse, error) {
fullMessages := make([]Message, 0, len(messages)+1)
if o.systemPrompt != "" {
fullMessages = append(fullMessages, Message{Role: "system", Content: o.systemPrompt})
fullMessages = append(fullMessages, Message{Role: "system", Content: TextContent(o.systemPrompt)})
}
fullMessages = append(fullMessages, messages...)