// // Tool.swift // Zyquo Agent // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // // The tool contract. A Tool exposes a JSON-Schema described capability to the // model and executes validated calls inside a task's workspace. Execution is // streaming (chunks surface live in the UI/terminal drawer), cancellable, and // — for anything that touches the system — always pre-cleared by the // PolicyEngine before `execute` runs a side effect. // import Foundation /// Live output emitted while a tool runs (streamed to the transcript and the /// Activity/Terminal drawer line by line). enum ToolOutputChunk: Sendable { case stdout(String) case stderr(String) /// Informational progress that is neither stdout nor stderr (e.g. "wrote 3 files"). case note(String) } /// Everything a tool needs from its surroundings to run one call. struct ToolExecutionContext: Sendable { /// The task's working directory — cwd for shell commands, root for file tools. let workspaceURL: URL /// Safety gate: every side-effecting action is classified and, if needed, /// held for user approval before it runs. let policy: PolicyEngine /// Append-only record of every executed action. let audit: AuditLog /// Streams live output chunks to the UI as they happen. let onOutput: @Sendable (ToolOutputChunk) -> Void /// Tracks files the agent creates/modifies (badges, checkpoints). Optional /// so contexts without a managed workspace (tests, scratch runs) still work. var workspaceManager: WorkspaceManager? = nil } /// Outcome of one tool call, before it is threaded back to the model as a /// `ToolResult`. struct ToolExecutionResult: Sendable { /// Text handed back to the model (stdout+stderr, file contents, listings…). var content: String /// True when the tool failed (non-zero exit, missing file, denied action). var isError: Bool = false static func success(_ content: String) -> ToolExecutionResult { ToolExecutionResult(content: content) } static func failure(_ message: String) -> ToolExecutionResult { ToolExecutionResult(content: message, isError: true) } } /// A capability the agent can invoke. Conform and register in `ToolRegistry` /// to make a new tool available to every agent-capable model. protocol Tool: Sendable { /// Wire name the model calls (snake_case, stable across releases). var name: String { get } /// Model-facing usage documentation — written like onboarding docs: what it /// does, when to use it, constraints, failure modes. var description: String { get } /// JSON Schema for the arguments object. var parametersSchema: JSONValue { get } /// Runs one validated call. Must route any system side effect through /// `context.policy` first and honor Task cancellation promptly. func execute(arguments: JSONValue, context: ToolExecutionContext) async throws -> ToolExecutionResult } extension Tool { /// The provider-neutral spec handed to models via the normalized interface /// (ToolSpec carries the schema as a serialized JSON string). var toolSpec: ToolSpec { let schemaString: String if let data = try? JSONEncoder().encode(parametersSchema), let encoded = String(data: data, encoding: .utf8) { schemaString = encoded } else { schemaString = #"{"type":"object"}"# } return ToolSpec(name: name, description: description, parametersJSONSchema: schemaString) } }