SPB Git

spb/zyquo-agent Public MIT

The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.

Swift 94.7% Shell 4.1% Python 0.7% Makefile 0.5%
4.5 KB · 134 lines swift
Raw Blame History
1//2//  Message.swift3//  Zyquo Agent4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//8//  Ported from Zyquo Cloud, extended with the normalized tool-calling fields9//  (docs/PROVIDER-REUSE.md §5.3): `toolCalls` on assistant turns and10//  `toolResults` on the turn that feeds results back. Clients render these11//  per wire format (OpenAI `tool_calls` + role:"tool" messages; Anthropic12//  `tool_use` assistant blocks + `tool_result` user blocks) — Message is the13//  provider-neutral turn representation the agent loop works with.14//1516import Foundation1718/// One turn in a conversation transcript.19struct Message: Codable, Identifiable, Hashable {20    enum Role: String, Codable {21        case system22        case user23        case assistant24    }2526    let id: UUID27    var role: Role28    var text: String29    /// Reasoning/thinking text streamed by reasoning models (collapsible in the UI).30    var reasoning: String?31    /// Image attachments (user messages, vision models).32    var attachments: [Attachment]33    /// Web-search citations (Perplexity).34    var citations: [Citation]35    /// Tool calls emitted by the model on this assistant turn. Threaded back to36    /// the provider verbatim so multi-step tool history round-trips correctly.37    var toolCalls: [ToolCall]?38    /// Tool results being returned to the model. Clients render these as39    /// role:"tool" messages (OpenAI schema) or tool_result user blocks (Anthropic).40    var toolResults: [ToolResult]?41    /// Model that produced this message (assistant turns) or was targeted (user turns).42    var modelID: String?43    var provider: ProviderID?44    var usage: TokenUsage?45    /// Estimated USD cost computed from catalog pricing at receive time.46    var estimatedCost: Double?47    var createdAt: Date48    /// Set while a response is streaming; exactly one message can be streaming at a time.49    var isStreaming: Bool = false50    /// Human-readable error if generation failed mid-message.51    var errorText: String?5253    init(54        id: UUID = UUID(),55        role: Role,56        text: String,57        reasoning: String? = nil,58        attachments: [Attachment] = [],59        citations: [Citation] = [],60        toolCalls: [ToolCall]? = nil,61        toolResults: [ToolResult]? = nil,62        modelID: String? = nil,63        provider: ProviderID? = nil,64        usage: TokenUsage? = nil,65        estimatedCost: Double? = nil,66        createdAt: Date = Date()67    ) {68        self.id = id69        self.role = role70        self.text = text71        self.reasoning = reasoning72        self.attachments = attachments73        self.citations = citations74        self.toolCalls = toolCalls75        self.toolResults = toolResults76        self.modelID = modelID77        self.provider = provider78        self.usage = usage79        self.estimatedCost = estimatedCost80        self.createdAt = createdAt81    }8283    /// An assistant turn carrying the model's tool calls (text may be empty).84    static func assistantToolCalls(_ calls: [ToolCall], text: String = "", reasoning: String? = nil) -> Message {85        Message(role: .assistant, text: text, reasoning: reasoning, toolCalls: calls)86    }8788    /// The turn that returns tool results to the model. Multiple results in one89    /// turn cover parallel tool calls.90    static func toolResultsMessage(_ results: [ToolResult]) -> Message {91        Message(role: .user, text: "", toolResults: results)92    }93}9495/// A file attached to a user message. Images go to vision models as base64;96/// text files are injected into the prompt.97struct Attachment: Codable, Identifiable, Hashable {98    enum Kind: String, Codable {99        case image100        case textFile101    }102103    let id: UUID104    var kind: Kind105    var fileName: String106    /// image: raw image bytes; textFile: UTF-8 contents.107    var data: Data108    /// MIME type for images (image/png, image/jpeg, image/webp, image/gif).109    var mimeType: String110111    init(id: UUID = UUID(), kind: Kind, fileName: String, data: Data, mimeType: String) {112        self.id = id113        self.kind = kind114        self.fileName = fileName115        self.data = data116        self.mimeType = mimeType117    }118}119120/// A numbered web source backing an assistant answer (Perplexity sonar family).121struct Citation: Codable, Identifiable, Hashable {122    let id: UUID123    var index: Int124    var url: URL125    var title: String?126127    init(id: UUID = UUID(), index: Int, url: URL, title: String? = nil) {128        self.id = id129        self.index = index130        self.url = url131        self.title = title132    }133}134