phase0: research docs — AGENT-RESEARCH.md and PROVIDER-REUSE.md, plan checkpoint
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 5 changed files with +1,481 and −0
added
.gitignore
+5 −0
@@ -0,0 +1,5 @@ | ||
| 1 | +.secrets/ | |
| 2 | +.build/ | |
| 3 | +dist/ | |
| 4 | +*.icns.tmp | |
| 5 | +.DS_Store | |
added
CLAUDE.md
+329 −0
@@ -0,0 +1,329 @@ | ||
| 1 | +# CLAUDE.md — Zyquo Agent | |
| 2 | + | |
| 3 | +## Project Identity | |
| 4 | + | |
| 5 | +**Zyquo Agent** is the autonomous-agent member of the **Zyquo** family: a legendary, native macOS app written in **Swift + SwiftUI**, built **without the Xcode IDE** (Swift Package Manager + command-line toolchain). Where **Zyquo Cloud** is a chat client and **Zyquo Local** runs models on-device, **Zyquo Agent** turns those same cloud models into a **deeply agentic assistant that can actually operate the user's Mac** — running `shell`/`bash` commands and `AppleScript`/`osascript`, reading and writing files, and iterating in a real agentic loop until a task is done. | |
| 6 | + | |
| 7 | +This is not a toy. Zyquo Agent must be an **ultra-advanced agent**: a robust plan→act→observe→reflect loop, a rich tool layer, memory/context compression for long tasks, per-task working directories ("workspaces"), streaming visibility into everything the agent thinks and does, and strong safety controls (approvals, sandboxable command policies, dry-run, full audit log). It reuses **exactly the same providers and models as Zyquo Cloud** — specifically the subset capable of deep agentic/tool-use work — by studying the Zyquo Cloud repository first. | |
| 8 | + | |
| 9 | +**Naming conventions (use consistently everywhere):** | |
| 10 | +- Display name / product name: `Zyquo Agent` | |
| 11 | +- App bundle: `Zyquo Agent.app` | |
| 12 | +- Bundle identifier: `com.zyquo.agent` | |
| 13 | +- Executable / SPM target: `ZyquoAgent` (no space) | |
| 14 | +- Data folder: `~/Library/Application Support/ZyquoAgent/` | |
| 15 | +- Workspaces root: `~/Library/Application Support/ZyquoAgent/Workspaces/` | |
| 16 | +- Repo module prefix in file headers: `Zyquo Agent` | |
| 17 | + | |
| 18 | +--- | |
| 19 | + | |
| 20 | +## 📋 MANDATORY FILE HEADER — EVERY CODE FILE | |
| 21 | + | |
| 22 | +**Every single code file you write** (all `.swift` files, plus `Makefile`, shell scripts, `Package.swift`, verification scripts — anything containing code) **MUST begin with this header comment**, adapted to the file's comment syntax: | |
| 23 | + | |
| 24 | +```swift | |
| 25 | +// | |
| 26 | +// <FileName>.swift | |
| 27 | +// Zyquo Agent | |
| 28 | +// | |
| 29 | +// Author: Simon-Pierre Boucher | |
| 30 | +// Mail: contact@spboucher.ai | |
| 31 | +// | |
| 32 | +``` | |
| 33 | + | |
| 34 | +For shell scripts / Makefiles: | |
| 35 | + | |
| 36 | +```bash | |
| 37 | +# | |
| 38 | +# <filename> | |
| 39 | +# Zyquo Agent | |
| 40 | +# | |
| 41 | +# Author: Simon-Pierre Boucher | |
| 42 | +# Mail: contact@spboucher.ai | |
| 43 | +# | |
| 44 | +``` | |
| 45 | + | |
| 46 | +No exceptions. If you ever create or refactor a file and the header is missing, add it. Before declaring the project done, run a sweep over the repository to verify every code file carries the header. | |
| 47 | + | |
| 48 | +--- | |
| 49 | + | |
| 50 | +## 🧭 METHODOLOGY — WORK METHODICALLY, KEEP EVERYTHING COHERENT | |
| 51 | + | |
| 52 | +You must execute this project **strictly in phase order (0 → 8)**. Do not jump ahead, do not interleave phases, do not build the agent loop before the provider layer streams tool calls, and do not write any code before Phase 0 research + Zyquo Cloud study are complete. | |
| 53 | + | |
| 54 | +**Working rules:** | |
| 55 | + | |
| 56 | +1. **One phase at a time.** At the start of each phase, write a checklist into `docs/PLAN.md`; check items off as you go. At the end of each phase, run a **phase checkpoint**: build (`swift build`), run what's runnable, fix all warnings/errors, write a 3–5 line phase summary in `docs/PLAN.md` before moving on. | |
| 57 | +2. **Phase gates:** Phase 0 is complete only when `docs/AGENT-RESEARCH.md` and `docs/PROVIDER-REUSE.md` are complete (see Phase 0). Phase 3 is complete only when a minimal loop can call a model, receive a tool call, execute a real `bash` command, feed the result back, and reach a final answer — proven in a CLI POC. Phase 4 spec is the contract for all UI in Phase 6. Phase 7 is complete only when the multi-scenario agent evaluation passes AND every reused model performs tool-calling correctly with real keys. Phase 8 is complete only when `spctl` says "Notarized Developer ID". | |
| 58 | +3. **Single source of truth, everywhere:** | |
| 59 | + - Provider/model behavior → ported from Zyquo Cloud's client layer (see Phase 0.B); never re-invent request formats. | |
| 60 | + - Colors, fonts, spacing, radii → only from `ZyquoTheme` design tokens. Zero raw hex values or magic numbers in views. | |
| 61 | + - Tool definitions, the agent loop, and command execution → only in the `Agent/` and `Tools/` layers; never leak shell execution into Views or ViewModels. | |
| 62 | + - Product naming → per the conventions above. Never `Zyquo` alone, never `ZyquoAgent` in user-facing text. | |
| 63 | +4. **Coherence sweeps:** after Phases 3, 6, and 8, do a consistency pass over the whole codebase (uniform naming — one term per concept, always `ToolCall`, `AgentStep`, `ProviderClient`; no dead code; headers present; folders match Phase 2). | |
| 64 | +5. **Compile early, compile often.** Never accumulate more than one file of unbuilt changes. | |
| 65 | +6. **Commit discipline:** one logical unit per commit, message prefixed by phase. Never commit secrets or anything from a user workspace. | |
| 66 | +7. **Safety is not optional and cannot be "phased later":** from the very first command-executing code in Phase 3, the approval/policy/audit machinery (Phase 3.C) must be wired in. There is never a code path that runs a shell command without going through the policy gate. | |
| 67 | + | |
| 68 | +--- | |
| 69 | + | |
| 70 | +## ⚠️ PHASE 0 — MANDATORY RESEARCH + ZYQUO CLOUD STUDY (DO THIS FIRST, BEFORE ANY CODE) | |
| 71 | + | |
| 72 | +Two mandatory research tracks. Both produce documents. No Swift until both are done. | |
| 73 | + | |
| 74 | +### 0.A — `docs/AGENT-RESEARCH.md` — how modern agents actually work (INTENSIVE WEB RESEARCH) | |
| 75 | + | |
| 76 | +Do NOT rely on training data. Perform **several intensive web research sessions** and synthesize, with citations, a thorough understanding of production agent design. You must research and document at minimum: | |
| 77 | + | |
| 78 | +1. **The core agentic loop.** How real coding/computer-use agents structure plan → act (tool call) → observe (tool result) → reflect → repeat until done. Study concrete, documented systems and patterns: **the Anthropic tool-use / agent loop**, **ReAct**, **Reflexion / self-critique**, **Plan-and-Execute**, and the way tools are surfaced to the model (native function/tool calling vs. structured output). Document the anatomy of one loop iteration end-to-end, including how the model signals "I'm done." | |
| 79 | +2. **Tool design.** How tools are defined and exposed to models (JSON schema for parameters, names, descriptions), how tool_use/tool_result blocks are threaded back into the conversation for each provider, parallel vs. sequential tool calls, and best practices for writing tool descriptions the model won't misuse. Research how leading agents design a **shell/bash tool**, a **file-edit tool**, and OS-automation tools. | |
| 80 | +3. **Memory & context compression.** This is critical for long tasks. Research: sliding-window context management, **summarization/compaction of old turns**, hierarchical memory (short-term scratchpad vs. long-term notes file), "memory files" the agent maintains on disk, retrieval of past steps, and how agents avoid blowing the context window during long autonomous runs. Document concrete compaction strategies (e.g., summarize completed sub-tasks into a compact record, keep the plan + recent N steps verbatim, offload detail to files). | |
| 81 | +4. **Working repositories / workspaces.** How agents get a scratch working directory, why isolation matters, how they track created/modified files, checkpoints, and how a "project" or "session" maps to a folder on disk. Research how agentic coding tools create and manage their working repo/sandbox. | |
| 82 | +5. **Planning & task decomposition.** Todo lists / task trees, how agents create and update a plan mid-run, sub-agents/delegation patterns, and when to re-plan after failures. | |
| 83 | +6. **Reliability & control.** Loop-guarding (max steps, budgets, stall/no-progress detection, repetition detection), error recovery, timeouts, and human-in-the-loop approval gates. How agents decide an action is risky. | |
| 84 | +7. **Evaluation.** How agentic systems are tested (task suites, success criteria, trajectory inspection). | |
| 85 | + | |
| 86 | +Write it all into `docs/AGENT-RESEARCH.md`. The architecture in later phases must trace back to findings here. | |
| 87 | + | |
| 88 | +### 0.B — `docs/PROVIDER-REUSE.md` — study the Zyquo Cloud repo and reuse its providers | |
| 89 | + | |
| 90 | +**Before writing provider code, read and study the Zyquo Cloud repository** (the sibling project already built from its own CLAUDE.md). Locate it on disk (check the user's projects folder; if you cannot find it, ask the user for its path). From it, understand and document: | |
| 91 | + | |
| 92 | +1. **Exactly how each provider's API is called** in Zyquo Cloud: base URLs, auth headers, request/response `Codable` models, the shared `OpenAICompatibleClient`, and the native `AnthropicClient` / `GeminiClient`. Zyquo Agent must call models **the exact same way** — port or, ideally, factor this code so it is identical to Cloud's. | |
| 93 | +2. **The full model catalog** Zyquo Cloud ships (`ModelCatalog` / `docs/PROVIDERS.md`), including which providers/models support **native tool/function calling** and streaming of tool calls. Reproduce that catalog here. | |
| 94 | +3. **Tool-calling capability per model.** From Cloud's data + fresh docs checks, determine the subset of models that are genuinely suitable for **deep agentic, multi-step tool use** (strong reasoning + reliable function calling + adequate context window). Zyquo Agent's model picker must **offer exactly the same models as Zyquo Cloud**, but clearly mark and default to this "agent-capable" subset. Document the subset and the reasoning in `docs/PROVIDER-REUSE.md`. | |
| 95 | +4. **The secure key vault approach** from Cloud (custom AES-256-GCM encryption, NO Keychain). Zyquo Agent reuses the **same `SecureKeyStore` design** and vault format so a user's keys and behavior feel consistent across the family. (Reuse the code; keep the "no Keychain" rule.) | |
| 96 | +5. Note any provider-specific tool-calling quirks (e.g., Anthropic tool_use blocks vs. OpenAI `tool_calls`, Gemini function calling, how each streams partial tool-call arguments) so the agent loop handles all of them uniformly behind a `ProviderClient` protocol. | |
| 97 | + | |
| 98 | +**Outcome:** Zyquo Agent's provider layer is functionally identical to Zyquo Cloud's, exposes the same models, and adds a normalized tool-calling interface on top. | |
| 99 | + | |
| 100 | +--- | |
| 101 | + | |
| 102 | +## PHASE 1 — Project Setup (No Xcode IDE) | |
| 103 | + | |
| 104 | +- **Toolchain:** Swift Package Manager. `Package.swift`, executable target `ZyquoAgent`. Build `swift build -c release`. | |
| 105 | +- **App bundle:** `Makefile` that builds release, assembles `Zyquo Agent.app` (`Contents/MacOS/ZyquoAgent`, `Info.plist`, `Resources/AppIcon.icns`), signs (Phase 8; ad-hoc for `make dev`). | |
| 106 | +- **Info.plist:** `CFBundleDisplayName` = `Zyquo Agent`, bundle ID `com.zyquo.agent`, `LSMinimumSystemVersion` (macOS 13.0+), `NSHighResolutionCapable`, `LSApplicationCategoryType` (`public.app-category.developer-tools`), and **usage-description strings** for any Automation/AppleScript access (`NSAppleEventsUsageDescription`) and any other TCC-guarded capabilities the agent touches. Universal (arm64 + x86_64) for release. | |
| 107 | +- **Entry point:** `@main` SwiftUI `App`; proper activation from terminal launch. | |
| 108 | +- **Dependencies:** Foundation + SwiftUI + CryptoKit; Apple `swift-markdown` acceptable. Reuse Zyquo Cloud's networking (URLSession) — no external HTTP libs. | |
| 109 | + | |
| 110 | +--- | |
| 111 | + | |
| 112 | +## PHASE 2 — Architecture | |
| 113 | + | |
| 114 | +``` | |
| 115 | +Sources/ZyquoAgent/ | |
| 116 | +├── App/ # @main, windows, menu bar extra | |
| 117 | +├── DesignSystem/ # ZyquoTheme — family token system, Agent palette | |
| 118 | +├── Models/ # Conversation/Task, Message, AgentStep, ToolCall, Provider, AIModel… | |
| 119 | +├── Providers/ # PORTED FROM ZYQUO CLOUD | |
| 120 | +│ ├── ProviderProtocol.swift # + normalized tool-calling interface | |
| 121 | +│ ├── OpenAICompatibleClient.swift # tool_calls streaming | |
| 122 | +│ ├── AnthropicClient.swift # tool_use blocks | |
| 123 | +│ └── GeminiClient.swift # function calling | |
| 124 | +├── Agent/ | |
| 125 | +│ ├── AgentLoop.swift # actor: the plan→act→observe→reflect engine | |
| 126 | +│ ├── AgentStep.swift # one iteration: thought, tool call(s), tool result(s) | |
| 127 | +│ ├── Planner.swift # task decomposition, todo list, re-planning | |
| 128 | +│ ├── MemoryManager.swift # context compression / compaction, memory files | |
| 129 | +│ ├── LoopGuard.swift # max steps, budgets, stall & repetition detection | |
| 130 | +│ └── Transcript.swift # structured run history for UI + audit | |
| 131 | +├── Tools/ | |
| 132 | +│ ├── Tool.swift # protocol: name, JSON schema, execute(args) async throws -> ToolResult | |
| 133 | +│ ├── ToolRegistry.swift # available tools, schemas handed to the model | |
| 134 | +│ ├── ShellTool.swift # bash/sh command execution (via ExecutionService) | |
| 135 | +│ ├── AppleScriptTool.swift # osascript / AppleScript execution | |
| 136 | +│ ├── FileTools.swift # read, write, edit, list, search within workspace | |
| 137 | +│ └── (extensible: HTTP fetch, etc. — designed for easy addition) | |
| 138 | +├── Execution/ | |
| 139 | +│ ├── ExecutionService.swift # runs Process (/bin/bash, /usr/bin/osascript), streams stdout/stderr, timeouts, cancellation | |
| 140 | +│ ├── PolicyEngine.swift # SAFETY gate: allow/ask/deny per command (Phase 3.C) | |
| 141 | +│ └── AuditLog.swift # append-only signed log of every executed action | |
| 142 | +├── Workspace/ | |
| 143 | +│ └── WorkspaceManager.swift # per-task working directory, file tracking, checkpoints | |
| 144 | +├── Services/ | |
| 145 | +│ ├── SecureKeyStore.swift # reused from Zyquo Cloud (no Keychain) | |
| 146 | +│ └── PersistenceService.swift | |
| 147 | +├── ViewModels/ | |
| 148 | +└── Views/ | |
| 149 | +``` | |
| 150 | + | |
| 151 | +- **`AgentLoop` and `ExecutionService` are actors.** Structured concurrency throughout; every long operation cancellable. | |
| 152 | +- **All shell/AppleScript execution goes through `ExecutionService` → `PolicyEngine`.** No exceptions. | |
| 153 | + | |
| 154 | +--- | |
| 155 | + | |
| 156 | +## PHASE 3 — THE AGENT ENGINE (THE CORE) | |
| 157 | + | |
| 158 | +Build this in three tightly-linked parts. **PHASE GATE:** finish with a CLI POC (`ZyquoAgent --run "create a folder ~/zqtest and put a hello.txt in it, then list it"`) that plans, calls the model, executes real tools through the policy gate, compresses memory if needed, and produces a correct final result with a full printed transcript. | |
| 159 | + | |
| 160 | +### 3.A — The agentic loop (`AgentLoop`) | |
| 161 | +- Implement the loop from `docs/AGENT-RESEARCH.md`: send system prompt + tools + conversation → receive assistant turn → if it contains tool call(s), execute them (respecting the policy gate), append tool_result(s), loop; if it's a final answer, stop. | |
| 162 | +- Support **native tool calling for every provider** via the normalized interface (OpenAI `tool_calls`, Anthropic `tool_use`, Gemini function calls). Stream the model's thinking/text AND tool-call arguments live to the UI. | |
| 163 | +- **Planner:** at task start, the agent drafts a plan / todo list (persisted, editable, shown in UI) and updates it as steps complete or fail; re-plans on repeated failure. | |
| 164 | +- **LoopGuard:** enforce max steps and a token/time budget; detect stalls (no state change), repetition (same failing command), and dead-ends; on trip, pause and ask the user rather than spinning. | |
| 165 | +- A strong **system prompt** that establishes the agent's role, the available tools, the workspace, safety expectations, and the "signal done" convention — grounded in the research. | |
| 166 | + | |
| 167 | +### 3.B — Memory / context compression (`MemoryManager`) | |
| 168 | +- Implement compaction from the research: keep the plan + system prompt + most recent N steps verbatim; **summarize older completed steps** into a compact structured record; offload large tool outputs (long logs, file dumps) to files in the workspace and replace them in-context with a short reference + summary. | |
| 169 | +- Maintain a persistent **`MEMORY.md`** (and/or structured notes) in the workspace that the agent reads/writes to carry knowledge across compactions and sessions. | |
| 170 | +- Track context-window usage live; trigger compaction before hitting limits; never silently drop the plan or key facts. | |
| 171 | + | |
| 172 | +### 3.C — Tools, execution & SAFETY (`Tools/`, `Execution/`) — WIRED FROM THE START | |
| 173 | +- **ShellTool:** runs commands via `/bin/bash -lc` (or `/bin/sh`) inside the current **workspace** as cwd; streams stdout/stderr line-by-line to the UI; enforces per-command timeout; captures exit code; cancellable. | |
| 174 | +- **AppleScriptTool:** runs AppleScript via `osascript` (and `osascript -e` / script files) to automate macOS apps (Finder, Notes, Mail, Calendar, System Events for UI scripting, etc.); same streaming/timeout/cancel; surfaces the Automation permission prompts gracefully and explains them. | |
| 175 | +- **FileTools:** read/write/edit/list/search — **scoped to the workspace by default**; escaping the workspace requires explicit user permission. | |
| 176 | +- **PolicyEngine (the safety gate every action passes through):** | |
| 177 | + - Three modes, user-selectable per task: **Manual** (approve every action), **Auto with guardrails** (auto-run read-only/safe commands, ask for anything mutating or matching a risk rule), **YOLO/Autonomous** (run freely within budget — clearly labeled, off by default, still audited). | |
| 178 | + - A **risk classifier** for commands: flag destructive patterns (`rm -rf`, `sudo`, disk/format ops, `mv`/overwrite outside workspace, network installs, `curl … | sh`, killing processes, writing to system paths, `defaults`/`launchctl`, anything needing elevated privileges). Risky actions ALWAYS require approval regardless of mode. | |
| 179 | + - **Approval UI:** show the exact command / AppleScript, an explanation, the cwd, and predicted effect; buttons Approve / Approve & remember (for that safe class) / Edit / Deny; optional **dry-run** where supported. | |
| 180 | + - **Never run `sudo` or request passwords silently.** Elevated actions are surfaced explicitly and require deliberate user confirmation. | |
| 181 | +- **AuditLog:** append-only record of every executed command/script with timestamp, cwd, exit code, and truncated output — viewable and exportable; nothing the agent does is invisible. | |
| 182 | + | |
| 183 | +--- | |
| 184 | + | |
| 185 | +## PHASE 4 — DESIGN SYSTEM & UI SPECIFICATION (LIGHT THEME, PIXEL-PERFECT) | |
| 186 | + | |
| 187 | +Same design DNA and `ZyquoTheme` token system as the rest of the family, with an **"Agent" identity**: focused, capable, a touch of "command center". Where Cloud is sky-blue and Local is emerald, **Agent is a confident violet/plum** — intelligent and operational. | |
| 188 | + | |
| 189 | +### 4.1 — Light theme | |
| 190 | + | |
| 191 | +| Token | Value (light) | Usage | | |
| 192 | +|---|---|---| | |
| 193 | +| `background` | `#FBFAFD` (cool off-white, faint violet undertone) | Main canvas | | |
| 194 | +| `surface` | `#FFFFFF` | Cards, bubbles, input, panels | | |
| 195 | +| `surfaceSecondary` | `#F4F2F8` | Hover, code/terminal blocks | | |
| 196 | +| `sidebar` | `NSVisualEffectView` `.sidebar` material | Sidebar | | |
| 197 | +| `accent` | `#7A5AF0` (confident violet) | Primary actions, selection, links, run button | | |
| 198 | +| `accentSubtle` | `#EFEBFD` | Selected rows, user bubble tint | | |
| 199 | +| `success` / `warning` / `danger` | `#2FA36B` / `#D9822B` / `#D64545` | Tool ok / approval needed / destructive & errors | | |
| 200 | +| `textPrimary` `#1B1A20` · `textSecondary` `#6E6B78` · `textTertiary` `#A09DAC` · `border` `#E7E4EE` | | | | |
| 201 | + | |
| 202 | +Same global rules as the family: no pure black on pure white, 0.5pt hairlines, ultra-soft shadows on floating panels only, dark theme derived (deep plum-charcoal command-center feel), **light theme is the flagship**. Typography, spacing, radii identical to Zyquo Cloud/Local (`body` 13.5pt / line-height 1.45; scale 4–32; radii 6/10/14; content column 760pt for chat). Terminal/command output uses SF Mono at `code` size. | |
| 203 | + | |
| 204 | +### 4.2 — Layout & screens (exact spec) | |
| 205 | + | |
| 206 | +The window is a **command center**, not just a chat. Default 1320×860, min 1040×680. | |
| 207 | + | |
| 208 | +- **Sidebar (260pt, translucent):** "Zyquo Agent" wordmark; **New Task** button (prominent, accent); task list grouped Pinned/Today/Yesterday/Previous 7 Days/Older — each row = task title + status pill (Planning / Running / Awaiting approval / Done / Failed) + model badge + relative time; running tasks show a subtle activity indicator. Footer: settings gear + safety-mode chip (Manual / Guarded / Autonomous) + active model chip. | |
| 209 | +- **Main detail = three coordinated regions:** | |
| 210 | + 1. **Conversation / task column (center, max 760pt):** the human↔agent dialogue with full Markdown. The agent's turns render as a **live transcript of steps**: each `AgentStep` is a card — a "thought" line, then tool call(s) shown as labeled chips (`bash`, `osascript`, `write_file`…) with the exact command in a monospace block, then the streamed **tool result** (stdout/stderr, exit code, truncated with expand). Approval-required steps render an inline **Approval card** (command + explanation + Approve/Edit/Deny) that blocks until resolved. Reasoning models' thinking goes in a collapsible section. The input bar (floating card, radius 14, violet **Run/Send** button ⌘↩, attach text files, Stop button while running) sits at the bottom. | |
| 211 | + 2. **Plan / Todo panel (right, collapsible ~300pt):** the live task plan as a checklist the agent maintains — items with states (pending/active/done/failed), editable by the user; a progress bar; step counter and token/time budget meters (from LoopGuard). | |
| 212 | + 3. **Activity / Terminal drawer (bottom, collapsible):** a live, streaming, color-coded feed of raw command execution (like a built-in terminal) — every stdout/stderr line as it happens, with the current cwd; tabs for **Live**, **Audit Log** (all executed actions), and **Files** (the workspace file tree with created/modified badges, click to preview, reveal in Finder). | |
| 213 | +- **Header (52pt):** editable task title; centered **model chip** (agent-capable models emphasized; same list as Zyquo Cloud); **safety-mode segmented control** (Manual / Guarded / Autonomous) always visible; workspace chip (name + reveal in Finder); export, info popover (system prompt, budgets, totals). | |
| 214 | +- **Empty state:** a beautiful "What should I do on your Mac?" hero with example task cards ("Organize my Downloads folder", "Batch-rename these files", "Set up a Python project and run the tests", "Export my Notes to Markdown"), the safety-mode selector, and the model chip. Must feel powerful and trustworthy. | |
| 215 | + | |
| 216 | +**Settings** (native tabs, 760×560): 1. **Providers & Keys** (reused vault UI; per-provider test) 2. **Models** (same catalog as Cloud; mark agent-capable subset; set default agent model) 3. **Safety** (default mode; manage the allow/deny rule lists; the destructive-pattern list; require-approval-for-AppleScript toggle; workspace-escape policy) 4. **Agent** (max steps, token/time budgets, per-command timeout, parallel tool calls on/off, compaction thresholds) 5. **Appearance** (Light/Dark/System; accent: violet default + graphite, sky, emerald, amber) 6. **Shortcuts** 7. **Advanced** (reveal data/workspaces folder, export audit logs, import/export tasks). | |
| 217 | + | |
| 218 | +**Quick Task panel** (⌥Space): floating Spotlight-style panel to fire a one-off agent task with the default model + Guarded mode; expands to show live steps; can be promoted to a full task. | |
| 219 | + | |
| 220 | +### 4.3 — Motion & micro-interactions | |
| 221 | +Family standard (smooth streaming, blinking caret, 150ms fade+rise, 80ms hovers, 0.97 press, `.snappy` popovers, 60fps, lazy transcript). Agent-specific: step cards animate in as the agent acts; the plan panel check-offs animate; terminal lines stream without reflow jank; approval cards pulse gently to draw attention; status pills transition smoothly (Planning→Running→Done). | |
| 222 | + | |
| 223 | +### 4.4 — Design quality gate | |
| 224 | +Before done, review every screen and every state: empty, planning, running, streaming stdout, awaiting approval, approval denied, tool error, loop-guard tripped, task done, task failed, no model/key configured. Consistent tokens, aligned baselines, no clipped text/commands, correct dark mode, clean font scaling. If it looks "developer-made" rather than "designed", iterate. | |
| 225 | + | |
| 226 | +--- | |
| 227 | + | |
| 228 | +## PHASE 5 — APP ICON: ULTRA-LEGENDARY "AGENT" ICON, DESIGNED IN SVG | |
| 229 | + | |
| 230 | +Designed in SVG first (`assets/icon/zyquo-agent.svg`) → `.icns`. Visual sibling of Cloud & Local: same squircle, same Z-monogram DNA, same premium quality — telling the **autonomous agent / command** story. | |
| 231 | + | |
| 232 | +**Creative direction — the Z that acts.** Two directions (render both, keep the best): | |
| 233 | +1. *Z-command:* the bold Z monogram whose lower stroke resolves into a subtle **command-line prompt / cursor** motif (a refined `›` chevron or blinking-caret block integrated into the letterform) — reads instantly as "an agent that runs commands", elegant not literal. | |
| 234 | +2. *Z-orbit:* the Z at the center with a few small **satellite nodes on a thin orbit ring** (the agent dispatching tools/sub-tasks) glowing around it — autonomy and orchestration. | |
| 235 | + | |
| 236 | +- **Canvas:** Big Sur–style rounded **squircle** (Apple curvature). | |
| 237 | +- **Palette (mirrors the app):** deep violet-to-plum vertical gradient (`#8B6CF5 → #7A5AF0 → #4B3A9E` territory, tune for richness), Z/cursor in white/near-white with a subtle inner luminosity and one restrained top highlight; if using the caret, give it a faint accent glow. Modern, confident, premium — beside Cloud (sky) and Local (silicon), the trio must read as one family, three characters. | |
| 238 | +- **Precision & iteration:** clean paths, `viewBox="0 0 1024 1024"`, optical centering, effects that survive downscaling. Render 16→1024, inspect, refine; simplified small-size variant (drop orbit/nodes, keep Z + caret) for 16/32px. | |
| 239 | + | |
| 240 | +**Pipeline (Makefile):** SVG → PNGs (16→1024 incl. `@2x`) via `rsvg-convert` or a CoreGraphics rasterizer → `AppIcon.iconset` → `iconutil -c icns`. SVG stays as source of truth. Derive the monochrome **menu bar template icon** (18×18pt) and in-app wordmark/empty-state glyph from the same SVG. | |
| 241 | + | |
| 242 | +--- | |
| 243 | + | |
| 244 | +## PHASE 6 — Features (This is where Zyquo Agent becomes LEGENDARY) | |
| 245 | + | |
| 246 | +### Agentic core | |
| 247 | +- Full plan→act→observe→reflect loop over **all agent-capable models** (same set as Zyquo Cloud), native tool calling for every provider | |
| 248 | +- Live streaming of thoughts, tool calls, and command output; stop/cancel at any moment | |
| 249 | +- Editable live **plan/todo**; automatic re-planning on failure; sub-task decomposition | |
| 250 | +- **Memory compaction** for long autonomous runs + persistent workspace `MEMORY.md` | |
| 251 | +- **LoopGuard**: step/token/time budgets, stall & repetition detection, graceful pause-and-ask | |
| 252 | + | |
| 253 | +### Tools | |
| 254 | +- `bash`/`sh` shell execution, `AppleScript`/`osascript` OS automation, and workspace file read/write/edit/list/search — all streaming, cancellable, timeout-bounded | |
| 255 | +- Extensible ToolRegistry (adding a new tool = conform to `Tool` + register); designed so an HTTP-fetch tool and others drop in cleanly | |
| 256 | +- Optional: allow the agent to write and run helper scripts it creates in the workspace | |
| 257 | + | |
| 258 | +### Safety & trust (a headline feature, not an afterthought) | |
| 259 | +- Three safety modes (Manual / Guarded / Autonomous) with per-task selection | |
| 260 | +- Risk classifier + editable allow/deny rules; mandatory approval for destructive/elevated actions; never silent `sudo` | |
| 261 | +- Inline approval cards with command preview, explanation, edit, dry-run; full append-only **audit log**, exportable | |
| 262 | +- Everything scoped to a per-task **workspace** by default; explicit permission required to touch files outside it | |
| 263 | + | |
| 264 | +### Workspaces | |
| 265 | +- Each task gets an isolated working directory under `Workspaces/`; file tree with created/modified tracking; checkpoints; reveal in Finder; reopen a past task with its workspace intact | |
| 266 | + | |
| 267 | +### Productivity & polish | |
| 268 | +- Task library / templates (≥25 ready-made agent tasks) + user templates with variables | |
| 269 | +- Personas (system prompt + preferred agent model + safety default) | |
| 270 | +- Quick Task panel (⌥Space); Export task transcript → Markdown/PDF; full-text search across tasks | |
| 271 | +- Auto-generated task titles; reused encrypted key vault (no Keychain) | |
| 272 | +- Shortcuts: ⌘N new task, ⌘K model/command palette, ⌘⏎ run, ⌘. stop, ⌘F search, ⌘⇧A open audit log, ⌥Space Quick Task; toggleable menu bar extra showing running-task status | |
| 273 | + | |
| 274 | +--- | |
| 275 | + | |
| 276 | +## PHASE 7 — VERIFICATION (MANDATORY) | |
| 277 | + | |
| 278 | +The user will provide **real API keys** (same providers as Zyquo Cloud). You MUST: | |
| 279 | + | |
| 280 | +1. **Provider/tool-calling verification:** for **every agent-capable model** in the shared catalog, run a scripted check that the model correctly (a) receives tool schemas, (b) emits a valid tool call for a trivial task (`"list the files in the workspace using the shell tool"`), (c) consumes the tool_result and produces a correct final answer, (d) streams properly. Produce a table: provider → model → tool-call ✅/❌ → notes. Fix every failure (schema format, tool_result threading, provider quirks) until green. | |
| 281 | +2. **Agent scenario evaluation:** define a suite of ≥8 real end-to-end tasks executed against a scratch workspace / temp directories (NEVER destructive to the user's real data), e.g.: create a folder structure and files; batch-rename files by pattern; write and run a small Python script and report its output; parse a CSV and summarize it; use AppleScript to create a Note or a Reminder (with approval); find and summarize the largest files in a temp dir; multi-step task requiring re-planning after an intentional failure; a long task that triggers memory compaction. For each: verify success criteria, inspect the trajectory, confirm the policy gate blocked/asked on risky steps, and confirm the audit log is complete. Iterate until all pass reliably. | |
| 282 | +3. **Safety tests:** confirm destructive patterns (`rm -rf`, `sudo`, writes outside workspace) ALWAYS require approval in every mode; confirm cancellation actually stops a running command; confirm no `sudo` ever runs silently. | |
| 283 | +4. Never commit, log, or embed the user's keys anywhere; keys live only in the encrypted vault or env vars during testing. Never run destructive test commands against real user data. | |
| 284 | + | |
| 285 | +--- | |
| 286 | + | |
| 287 | +## PHASE 8 — SIGNING & NOTARIZATION (REAL, NOT AD-HOC) | |
| 288 | + | |
| 289 | +The user has an existing, working signing/notarization setup for another project. **Before doing anything, read and inspect the folder:** | |
| 290 | + | |
| 291 | +``` | |
| 292 | +/Users/simon-pierreboucher/Desktop/other/OTHER/zyquo-term | |
| 293 | +``` | |
| 294 | + | |
| 295 | +Locate the **Developer ID Application identity name**, **Team ID**, **notarytool keychain profile (or Apple ID + app-specific password)**, entitlements, and any config there. **Reuse the exact same identity, Team ID, and notarytool credentials/profile for Zyquo Agent.** Never invent placeholders, never print secrets, never commit them. | |
| 296 | + | |
| 297 | +Then implement `make release`: | |
| 298 | +1. Build universal release (arm64 + x86_64, `lipo`), assemble `Zyquo Agent.app`. | |
| 299 | +2. `entitlements.plist` with **Hardened Runtime**; include only what's needed. Because the app spawns `bash`/`osascript` child processes and sends Apple events, verify the correct posture: keep Hardened Runtime, ensure `NSAppleEventsUsageDescription` is present, and add `com.apple.security.automation.apple-events` if entitlement-gated automation requires it. Do NOT add App Sandbox (this app legitimately controls the Mac and would be crippled by it) unless the user explicitly wants a sandboxed variant. Test the minimal set. | |
| 300 | +3. `codesign --force --options runtime --timestamp --entitlements entitlements.plist --sign "Developer ID Application: <identity from zyquo-term>" "Zyquo Agent.app"` — sign nested code first. | |
| 301 | +4. `ditto -c -k --keepParent` → `xcrun notarytool submit "Zyquo Agent.zip" --keychain-profile "<profile from zyquo-term>" --wait`. | |
| 302 | +5. `xcrun stapler staple "Zyquo Agent.app"`; verify `spctl -a -vv` = "accepted, source=Notarized Developer ID" and `stapler validate`. | |
| 303 | +6. Optional signed+stapled DMG (`hdiutil`). | |
| 304 | +7. On failure: `notarytool log`, fix, resubmit until it passes. Keep `make dev` (ad-hoc) for iteration. | |
| 305 | + | |
| 306 | +--- | |
| 307 | + | |
| 308 | +## Engineering Standards | |
| 309 | + | |
| 310 | +- Swift 5.9+ (Swift 6 mode if the toolchain allows); zero warnings | |
| 311 | +- `AgentLoop` and `ExecutionService` as actors; all provider and tool types `Codable` | |
| 312 | +- Every executed action passes the PolicyEngine; every action is audited; cancellation everywhere (loop, command, download-of-nothing — every long op) | |
| 313 | +- Robust, human-readable errors for every failure class (tool failed, command timeout, model refused/looped, budget exceeded, permission denied by macOS TCC → explain how to grant Automation access in System Settings) | |
| 314 | +- Provider layer identical to Zyquo Cloud; tokens-only design system; UI strings centralized | |
| 315 | +- `README.md` (build) + `docs/` (AGENT-RESEARCH, PROVIDER-REUSE, PLAN); never commit secrets or workspace contents | |
| 316 | +- Commit in logical, phase-prefixed increments | |
| 317 | + | |
| 318 | +## Definition of Done | |
| 319 | + | |
| 320 | +- `make release` produces a **Developer ID–signed, notarized, stapled** `Zyquo Agent.app` (verified by `spctl`), built without the Xcode IDE | |
| 321 | +- The agent reliably runs a full plan→act→observe→reflect loop using **the same providers/models as Zyquo Cloud**, with native tool calling verified across the agent-capable subset (Phase 7 table green) | |
| 322 | +- `bash`, `AppleScript`, and workspace file tools work with live streaming output; every action passes the safety policy gate; destructive/elevated actions always require approval; full audit log present | |
| 323 | +- Memory compaction keeps long tasks within context; workspaces isolate and track task files | |
| 324 | +- The violet-themed SVG icon exists, is striking at all sizes, embedded as `.icns` + menu bar template icon; clearly a sibling of the Cloud and Local icons | |
| 325 | +- The violet light theme matches the Phase 4 spec exactly and passes the design quality gate; dark theme derived and correct | |
| 326 | +- Naming coherent everywhere: `Zyquo Agent` user-facing, `com.zyquo.agent`, `ZyquoAgent` target/data folder; keys in the reused encrypted vault (no Keychain) | |
| 327 | +- **Every code file starts with the mandatory Author/Mail header** (verified by a repo-wide sweep) | |
| 328 | +- `docs/PLAN.md` shows every phase completed with its checkpoint summary; `docs/AGENT-RESEARCH.md` and `docs/PROVIDER-REUSE.md` are complete and traceable to the implementation | |
| 329 | +- Zyquo Agent feels like a polished, legendary, and *trustworthy* native Mac agent — clearly the most capable of its kind | |
added
docs/AGENT-RESEARCH.md
+413 −0
@@ -0,0 +1,413 @@ | ||
| 1 | +# AGENT-RESEARCH.md — How Modern Production AI Agents Work | |
| 2 | + | |
| 3 | +**Zyquo Agent — Phase 0.A research document** | |
| 4 | +Compiled: 2026-07-30 · Sources: intensive web research (Anthropic engineering + platform docs, OpenAI docs, Google Gemini docs, academic papers, and production-agent write-ups — Claude Code, OpenAI Codex CLI, Cursor, Aider, OpenHands, Devin). All claims cite the source URL inline. | |
| 5 | + | |
| 6 | +--- | |
| 7 | + | |
| 8 | +## Executive Summary | |
| 9 | + | |
| 10 | +Modern production agents (Claude Code, Codex CLI, Devin, OpenHands) converge on the same skeleton: **a while-loop over a stateful conversation, keyed on the model's stop signal**. The model is given a system prompt + JSON-schema tool definitions + conversation history; it either emits tool calls (loop continues: execute, append results, re-send) or a final text answer (loop ends). Everything else — planning, memory, safety, workspaces — is machinery wrapped around that loop: | |
| 11 | + | |
| 12 | +- **The loop** is the ReAct pattern (reason → act → observe) made native via provider tool-calling APIs. The model signals "done" through its stop reason (`end_turn` vs `tool_use` for Anthropic; `stop` vs `tool_calls` for OpenAI Chat Completions). | |
| 13 | +- **Tools** are the agent-computer interface and deserve as much design effort as prompts (Anthropic reports spending *more* time on tools than on prompts for their SWE-bench agent). Few, consolidated, well-described tools beat many thin API wrappers. | |
| 14 | +- **Context is a finite resource** that degrades with length ("context rot"). Long-running agents survive via compaction (summarize old turns), output offloading (big tool outputs → files + in-context reference), persistent memory files (`MEMORY.md`/`NOTES.md`), and sub-agents with isolated context windows. | |
| 15 | +- **Workspaces** isolate each task in a working directory; file tracking + checkpoints make agent actions reversible and auditable. | |
| 16 | +- **Planning** is externalized into a visible, updatable todo list (Claude Code's TodoWrite/Task tools); re-planning after failure is a normal, expected path, not an error state. | |
| 17 | +- **Reliability** comes from loop guards (max iterations, budgets, repetition detection), and **safety** comes from a layered permission system — deny → ask → allow rule evaluation, read-only auto-approval, destructive-pattern circuit breakers, and human approval gates. The industry lesson (Cursor's bypassed denylist) is that pattern-matching alone is best-effort, not a security boundary; approvals + auditing must back it up. | |
| 18 | +- **Evaluation** is end-to-end and outcome-based: containerized task suites (SWE-bench, Terminal-Bench, OSWorld) with programmatic verifiers, plus LLM-judge rubrics and human trajectory inspection. | |
| 19 | + | |
| 20 | +The final section maps every finding onto Zyquo Agent's planned components (`AgentLoop`, `Planner`, `MemoryManager`, `LoopGuard`, `ToolRegistry`, `PolicyEngine`, `WorkspaceManager`). | |
| 21 | + | |
| 22 | +--- | |
| 23 | + | |
| 24 | +## 1. The Core Agentic Loop | |
| 25 | + | |
| 26 | +### 1.1 Workflows vs. agents | |
| 27 | + | |
| 28 | +Anthropic's "Building Effective Agents" (<https://www.anthropic.com/engineering/building-effective-agents>) draws the canonical distinction: | |
| 29 | + | |
| 30 | +- **Workflows**: LLMs and tools orchestrated through *predefined code paths* (prompt chaining, routing, parallelization, orchestrator-workers, evaluator-optimizer). | |
| 31 | +- **Agents**: systems where "LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks." | |
| 32 | + | |
| 33 | +Zyquo Agent is squarely the second kind. Anthropic's guidance for agents: they need **"ground truth from the environment at each step (such as tool call results or code execution) to assess progress"**, explicit **stopping conditions** to retain control, extensive sandboxed testing, guardrails, and human checkpoints — because autonomy compounds both cost and error. | |
| 34 | + | |
| 35 | +### 1.2 The research lineage | |
| 36 | + | |
| 37 | +- **ReAct** (Yao et al., 2022, <https://arxiv.org/abs/2210.03629>): interleave *reasoning traces* with *actions* in a thought → action → observation cycle. Reasoning induces and tracks plans; actions ground the model in external reality; observations feed exception handling. ReAct beat imitation/RL baselines on ALFWorld (+34% absolute) and WebShop (+10%) and reduces hallucination vs. pure chain-of-thought. Every modern tool-calling loop is ReAct with the "action" formalized as a native tool call. | |
| 38 | +- **Reflexion** (Shinn et al., 2023, <https://arxiv.org/abs/2303.11366>): agents improve via *verbal* reinforcement — after a failure, the agent reflects in natural language and stores that reflection in an **episodic memory buffer** consulted on the next attempt. No weight updates; 91% on HumanEval vs. GPT-4's 80% baseline. Production translation: when a step fails, have the model articulate *why* and keep that diagnosis in context (or in a notes file) before retrying. | |
| 39 | +- **Plan-and-Execute** (LangChain/LangGraph pattern, <https://blog.langchain.com/planning-agents/>, tutorial: <https://langchain-opentutorial.gitbook.io/langchain-opentutorial/17-langgraph/03-use-cases/05-langgraph-plan-and-execute>): a **planner** turns the objective into a structured checklist; an **executor** (often itself a small ReAct loop) works one step at a time; a **replanner** consumes `past_steps` and either refines the remaining plan or emits the final answer. Separating planning from execution is more reliable than pure step-by-step ReAct for complex multi-step tasks. | |
| 40 | + | |
| 41 | +Production agents blend all three: an up-front plan (Plan-and-Execute), a ReAct inner loop per step, and Reflexion-style self-critique on failure. | |
| 42 | + | |
| 43 | +### 1.3 Anatomy of one loop iteration (Anthropic wire format) | |
| 44 | + | |
| 45 | +From Anthropic's tool-use docs (<https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works>), the canonical shape is a `while` loop keyed on `stop_reason`: | |
| 46 | + | |
| 47 | +1. **Send** a request: system prompt + `tools` array (JSON schemas) + full message history. | |
| 48 | +2. **Model responds.** If it wants to act, the response has `stop_reason: "tool_use"` and one or more `tool_use` content blocks, each carrying `{id, name, input}` (input is a JSON object matching the tool's schema). The response may also contain `text` blocks (the "thought" — this is the ReAct reasoning trace) before the tool calls. | |
| 49 | +3. **Execute** each tool in your runtime. The model *never* executes anything itself — "it emits a structured request, your code runs the operation, and the result flows back." | |
| 50 | +4. **Append** the assistant message verbatim, then a **user** message containing one `tool_result` block per call: `{type: "tool_result", tool_use_id: <matching id>, content: <output>, is_error: <bool>}`. | |
| 51 | +5. **Repeat** from step 2 while `stop_reason == "tool_use"`. | |
| 52 | + | |
| 53 | +**How the model signals "I'm done":** the loop exits on any other stop reason — `"end_turn"` (final answer produced), or abnormal ones the app must handle: `"max_tokens"`, `"stop_sequence"`, `"refusal"` (see <https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons>). There is no separate "done" tool needed: *not calling a tool* is the done-signal. Provider equivalents: | |
| 54 | + | |
| 55 | +| Provider | "I want to act" | "I'm done" | | |
| 56 | +|---|---|---| | |
| 57 | +| Anthropic Messages | `stop_reason: "tool_use"` + `tool_use` blocks | `stop_reason: "end_turn"` | | |
| 58 | +| OpenAI Chat Completions | `finish_reason: "tool_calls"` + `message.tool_calls[]` | `finish_reason: "stop"` | | |
| 59 | +| OpenAI Responses API | `function_call` output items | plain message output items | | |
| 60 | +| Gemini | candidate parts contain `functionCall` | parts contain only text | | |
| 61 | + | |
| 62 | +A useful robustness convention on top of stop reasons (used by several agent frameworks and recommended in agent-loop tutorials, e.g. <https://claude-world.com/tutorials/s01-the-agent-loop/>): also require the final text to contain an explicit completion statement, and treat an empty final answer after tool activity as a stall. | |
| 63 | + | |
| 64 | +**Worked example — one full Anthropic iteration** (wire shapes from <https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works>): | |
| 65 | + | |
| 66 | +```jsonc | |
| 67 | +// → request N | |
| 68 | +{ "system": "...", "tools": [{ "name": "bash", "description": "...", "input_schema": {...} }], | |
| 69 | + "messages": [ ...history..., { "role": "user", "content": "Set up the project and run the tests" } ] } | |
| 70 | + | |
| 71 | +// ← response N: the model thinks, then acts | |
| 72 | +{ "stop_reason": "tool_use", | |
| 73 | + "content": [ | |
| 74 | + { "type": "text", "text": "I'll check whether a package manifest exists first." }, // ReAct thought | |
| 75 | + { "type": "tool_use", "id": "toolu_01A", "name": "bash", | |
| 76 | + "input": { "command": "ls -la" } } ] } // action | |
| 77 | + | |
| 78 | +// → request N+1: assistant turn appended verbatim + observation | |
| 79 | +{ "messages": [ ..., | |
| 80 | + { "role": "assistant", "content": [ /* the two blocks above */ ] }, | |
| 81 | + { "role": "user", "content": [ | |
| 82 | + { "type": "tool_result", "tool_use_id": "toolu_01A", | |
| 83 | + "content": "Package.swift\nSources\nTests", "is_error": false } ] } ] } | |
| 84 | + | |
| 85 | +// ← eventually: no tool_use blocks ⇒ done | |
| 86 | +{ "stop_reason": "end_turn", "content": [ { "type": "text", "text": "All 12 tests pass. ..." } ] } | |
| 87 | +``` | |
| 88 | + | |
| 89 | +The same skeleton, re-skinned: OpenAI puts the action in `message.tool_calls` and the observation in a `role:"tool"` message; Gemini puts them in `functionCall`/`functionResponse` parts. The `AgentStep` model should capture exactly this triple — thought text, tool call(s), tool result(s) — plus the stop reason. | |
| 90 | + | |
| 91 | +### 1.4 System-prompt anatomy for an agent | |
| 92 | + | |
| 93 | +The loop is only as good as the contract the system prompt establishes. Findings across the sources: | |
| 94 | + | |
| 95 | +- **Altitude**: Anthropic's context-engineering post prescribes the "Goldilocks zone" — not brittle hardcoded if-then logic, not vague platitudes; "specific enough to guide behavior effectively, yet flexible enough to provide the model with strong heuristics" (<https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents>). | |
| 96 | +- **Canonical sections** (visible in Claude Code's prompt structure and Anthropic's agent guidance): (1) role and capabilities; (2) the environment — workspace path, OS, what the tools can touch; (3) tool-usage policy — when to use which tool, parallel-call rules, output-size discipline; (4) planning discipline — maintain the todo list, mark items in progress/completed as you go; (5) safety expectations — actions are gated, never attempt to bypass approval, prefer workspace-relative paths, never `sudo`; (6) the done-convention — "when the task is complete, respond without calling tools, summarizing what was done and verifying success criteria." | |
| 97 | +- **Standing instructions live outside the transcript**: Claude Code re-injects CLAUDE.md and memory after every compaction (<https://code.claude.com/docs/en/best-practices>); the agent's system prompt + plan must likewise be compaction-immune (see §3). | |
| 98 | +- **Ground the model in verification**: instruct it to verify its own work with tools (run the tests, list the directory) before declaring done — this operationalizes "ground truth from the environment at each step" (<https://www.anthropic.com/engineering/building-effective-agents>). | |
| 99 | + | |
| 100 | +### 1.5 Native tool calling vs. structured output | |
| 101 | + | |
| 102 | +Native tool calling (function schemas + typed call/response blocks) is strictly preferred over asking the model to emit parseable text/JSON in prose: | |
| 103 | + | |
| 104 | +- Models are **fine-tuned on the native format**, so calls are better-formed and error recovery is better. Anthropic explicitly notes its published `bash`/`text_editor` schemas are "trained-in": "Claude has been optimized on thousands of successful trajectories that use these exact tool signatures, so it calls them more reliably" (<https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works>). | |
| 105 | +- The docs' rule of thumb: "if you're writing a regex to extract a decision from model output, that decision should have been a tool call." | |
| 106 | +- OpenAI's **strict mode** (`strict: true`) goes further: constrained decoding guarantees the arguments match the schema exactly (requires `additionalProperties: false` and all fields in `required`; optionals expressed as `"type": ["string","null"]`) (<https://developers.openai.com/api/docs/guides/function-calling>). | |
| 107 | + | |
| 108 | +Structured output (JSON mode) remains useful for *final* answers of a fixed shape, not for actions. | |
| 109 | + | |
| 110 | +### 1.6 Streaming the loop | |
| 111 | + | |
| 112 | +Everything in an iteration can and should stream to the UI: | |
| 113 | + | |
| 114 | +- **Anthropic**: text arrives as `text_delta`; tool-call arguments arrive as `input_json_delta` events carrying `partial_json` string fragments that the client concatenates ("the chunks do not respect JSON boundaries") (<https://platform.claude.com/docs/en/build-with-claude/streaming>). The beta `fine-grained-tool-streaming-2025-05-14` header removes server-side buffering so large arguments stream immediately — at the cost that the accumulated JSON may be *invalid/partial* if the stream ends early (e.g. `max_tokens`), so clients must parse defensively (<https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/fine-grained-tool-streaming>). | |
| 115 | +- **OpenAI**: `response.function_call_arguments.delta` / `.done` events (Responses API), or `delta.tool_calls[i].function.arguments` fragments (Chat Completions) — accumulate per tool-call index until `finish_reason` arrives. | |
| 116 | +- **Gemini**: function-call arguments arrive as deltas that must be aggregated before execution (<https://ai.google.dev/gemini-api/docs/function-calling>). | |
| 117 | + | |
| 118 | +Implication: the normalized `ProviderClient` interface must expose a unified stream of events — `textDelta`, `thinkingDelta`, `toolCallStarted(id, name)`, `toolCallArgumentsDelta(id, fragment)`, `toolCallCompleted(id, input)`, `turnCompleted(stopReason)` — and the UI renders tool arguments live as they stream. | |
| 119 | + | |
| 120 | +--- | |
| 121 | + | |
| 122 | +## 2. Tool Design | |
| 123 | + | |
| 124 | +### 2.1 How tools are defined | |
| 125 | + | |
| 126 | +All three provider families use the same conceptual triple — **name, description, JSON-Schema parameters**: | |
| 127 | + | |
| 128 | +- **Anthropic**: `tools: [{name, description, input_schema}]` where `input_schema` is JSON Schema (<https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works>). | |
| 129 | +- **OpenAI**: `tools: [{type: "function", function|name/description/parameters, strict}]` — full JSON Schema features: "property types, enums, descriptions, nested objects, and recursive objects" (<https://developers.openai.com/api/docs/guides/function-calling>). | |
| 130 | +- **Gemini**: `tools: [{functionDeclarations: [{name, description, parameters}]}]` — parameters use an **OpenAPI-subset** schema, notably more restricted than full JSON Schema (<https://ai.google.dev/gemini-api/docs/function-calling>). | |
| 131 | + | |
| 132 | +Practical consequence for a multi-provider agent: define tools once in an internal type, generate provider-specific schemas from it, and keep schemas within the **lowest common denominator** (flat-ish objects, `type`/`enum`/`description`/`required` — avoid `oneOf`, `$ref`, deep recursion) so a single tool definition works everywhere. | |
| 133 | + | |
| 134 | +### 2.2 Threading tool calls and results, per provider | |
| 135 | + | |
| 136 | +The three wire formats the loop must normalize: | |
| 137 | + | |
| 138 | +**Anthropic** — content blocks inside normal messages: | |
| 139 | +``` | |
| 140 | +assistant: [ {type:"text", ...thought...}, {type:"tool_use", id:"toolu_1", name:"bash", input:{...}} ] | |
| 141 | +user: [ {type:"tool_result", tool_use_id:"toolu_1", content:"...", is_error:false} ] | |
| 142 | +``` | |
| 143 | +All `tool_result` blocks must come **first** in the next user message, one per `tool_use` id, and results for *all* parallel calls go in **one** user message. | |
| 144 | + | |
| 145 | +**OpenAI Chat Completions** — dedicated roles: | |
| 146 | +``` | |
| 147 | +assistant: { content:null, tool_calls:[{id:"call_1", type:"function", function:{name:"bash", arguments:"{\"command\":...}"}}] } | |
| 148 | +tool: { role:"tool", tool_call_id:"call_1", content:"..." } | |
| 149 | +``` | |
| 150 | +Arguments are a **JSON string** (must be parsed; may rarely be malformed without strict mode). The Responses API instead uses `function_call` output items answered by `function_call_output` items keyed on `call_id` (<https://developers.openai.com/api/docs/guides/function-calling>). | |
| 151 | + | |
| 152 | +**Gemini** — parts inside contents: | |
| 153 | +``` | |
| 154 | +model: { parts:[{functionCall:{name:"bash", args:{...}}}] } | |
| 155 | +user: { parts:[{functionResponse:{name:"bash", response:{...}}}] } | |
| 156 | +``` | |
| 157 | +Modes via config: `AUTO` (model decides), `ANY` (must call a function), `NONE` (<https://ai.google.dev/gemini-api/docs/function-calling>). Anthropic's equivalent is `tool_choice: auto|any|tool|none`; OpenAI's is `tool_choice: auto|required|none|{function}`. | |
| 158 | + | |
| 159 | +**Error results:** all providers support signaling tool failure back to the model — Anthropic via `is_error: true` on the `tool_result`, OpenAI/Gemini by putting the error text in the result content. Best practice (universal across Claude Code/Aider/OpenHands): return *actionable* error text ("file not found: /x/y — did you mean /x/z?") so the model can self-correct in the next iteration, rather than a bare stack trace. | |
| 160 | + | |
| 161 | +### 2.3 Parallel vs. sequential tool calls | |
| 162 | + | |
| 163 | +- All three providers can emit **multiple tool calls in one assistant turn** when the calls are independent (Anthropic: multiple `tool_use` blocks; OpenAI: `tool_calls[]`, disable with `parallel_tool_calls: false`; Gemini: parallel `functionCall` parts + separate "compositional" chaining across turns). | |
| 164 | +- The runtime may execute them concurrently, but **all results must be returned together** in the next message, correlated by id. | |
| 165 | +- Dependent calls are inherently sequential — the model needs observation N before choosing action N+1. Production agents run read-only calls (multiple file reads, searches) in parallel and mutating calls sequentially; Zyquo Agent should make parallel execution a per-settings toggle and never parallelize two mutating shell commands. | |
| 166 | + | |
| 167 | +### 2.4 Best practices for tool definitions | |
| 168 | + | |
| 169 | +From Anthropic's "Writing effective tools for agents" (<https://www.anthropic.com/engineering/writing-tools-for-agents>), the ACI section of "Building Effective Agents", and OpenAI's function-calling guide: | |
| 170 | + | |
| 171 | +1. **Fewer, consolidated tools.** "More tools don't always lead to better outcomes" — build a few tools targeting high-impact workflows (`schedule_event`, not `list_users`+`list_events`+`create_event`). OpenAI: "aim for fewer than 20 functions available at the start of a turn." | |
| 172 | +2. **Descriptions are onboarding docs.** Make implicit context explicit; state when to use the tool and when *not* to; unambiguous parameter names (`user_id`, not `user`); the intern test: "an intern can correctly use the function given nothing but what you gave the model." | |
| 173 | +3. **Namespace related tools** (`file_read`, `file_write`, `file_search`) to reduce selection confusion. | |
| 174 | +4. **Token-efficient results.** Pagination/filtering/truncation with sensible defaults — Claude Code truncates tool responses at ~25,000 tokens by default; truncation messages should steer the model ("output truncated; use targeted searches instead"). | |
| 175 | +5. **Return meaningful context**: semantic identifiers over UUIDs; support `concise`/`detailed` response formats. | |
| 176 | +6. **Poka-yoke**: design arguments so misuse is hard (e.g. require absolute paths to eliminate cwd ambiguity — a change Anthropic reports fixed a whole error class in their SWE-bench agent). | |
| 177 | +7. **Make invalid states unrepresentable** with enums and structure; don't make the model fill arguments the app already knows (OpenAI). | |
| 178 | +8. **Evaluate tools like code**: prototype, run realistic multi-call tasks, read the agent's reasoning to find rough edges, iterate. | |
| 179 | + | |
| 180 | +### 2.5 How leading agents design the three core tools | |
| 181 | + | |
| 182 | +**Shell/bash tool.** Anthropic's trained-in `bash_20250124` tool (<https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool>) has a deliberately tiny schema — `{command}` plus a `restart` flag — and the *application* owns a **persistent bash session**: "one bash process alive across tool calls, so state persists between commands. The working directory, environment variables, and any files a command creates are still there for the next command." Implementation guidance in the doc: per-command timeouts, output truncation, and treating sandboxing/command validation as the app's job. Claude Code's own Bash tool adds a per-call `timeout`, an output cap, background execution, and a natural-language `description` field the UI shows the user. OpenHands equivalently exposes `CmdRunAction` (bash) plus an IPython cell action, executed inside a Docker sandbox, with results returned as typed `Observation` events (<https://arxiv.org/abs/2407.16741>). | |
| 183 | + | |
| 184 | +**File-edit tool.** Two dominant designs: | |
| 185 | +- **String-replacement editing** — Anthropic's `str_replace_based_edit_tool` (<https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool>) with commands `view`, `create`, `str_replace`, `insert`. `str_replace` requires the `old_str` to match **exactly and uniquely** in the file — a non-unique or non-matching string is an error returned to the model. This is the same contract as Claude Code's Edit tool, and it's the key safety property: the model must prove it knows the current file content before changing it. | |
| 186 | +- **Diff formats** — Aider's edit formats (<https://aider.chat/docs/more/edit-formats.html>): `whole` (rewrite the file — simple but token-expensive), `diff` (SEARCH/REPLACE blocks styled like git conflict markers — the default because it balances token efficiency with explicit before/after context), `udiff` (unified diffs, adopted for GPT-4 Turbo to fight "lazy coding" elisions), and per-model variants (`diff-fenced` for Gemini). Lesson: the *edit format must match what the model executes reliably*, and exact-match search/replace with clear failure errors is the most robust default for tool-calling models. | |
| 187 | + | |
| 188 | +**OS-automation tools.** Anthropic's `computer` tool drives GUI via screenshots + mouse/keyboard; for macOS-native automation the practical pattern (used by Mac agent projects) is an **osascript tool**: schema `{script, language: applescript|jxa, timeout}`, executed via `/usr/bin/osascript`, with the app pre-declaring `NSAppleEventsUsageDescription` and surfacing TCC Automation prompts to the user. Because AppleScript can do anything the user can (send mail, delete files), production designs treat it like a mutating shell command: always subject to the approval gate, with the exact script shown to the user. Anthropic's computer-use guidance similarly stresses human confirmation for consequential actions and isolated environments. | |
| 189 | + | |
| 190 | +--- | |
| 191 | + | |
| 192 | +## 3. Memory & Context Compression | |
| 193 | + | |
| 194 | +### 3.1 Why: context is finite and rots | |
| 195 | + | |
| 196 | +Anthropic's "Effective context engineering for AI agents" (<https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents>): "context must be treated as a finite resource with diminishing marginal returns." **Context rot** — accuracy degrading as the window fills — stems from attention stretching across n² token relationships and sparse training on very long sequences. So a long-running agent needs an active context-management strategy, not just a big window. | |
| 197 | + | |
| 198 | +### 3.2 Compaction (summarizing old turns) | |
| 199 | + | |
| 200 | +The reference implementation is **Claude Code's auto-compact** (<https://claudelog.com/faqs/what-is-claude-code-auto-compact/>, <https://okhlopkov.com/claude-code-compaction-explained/>, <https://howaiworks.ai/blog/claude-code-auto-compact-context-management>): | |
| 201 | + | |
| 202 | +- Token usage is monitored continuously; near ~95% of the effective window (threshold ≈ window minus a fixed reserve) the turn pauses. | |
| 203 | +- A **summarization pass over the whole history** produces a structured summary (task state, decisions made, files touched, next steps) that *replaces* the older turns; the session continues from the summary. | |
| 204 | +- Content loaded from disk (CLAUDE.md project instructions, memory) is **re-injected after compaction** — compaction only touches conversation history, never standing instructions. | |
| 205 | +- Manual `/compact` triggers the same pipeline on demand, optionally with focus instructions. | |
| 206 | +- **Thrashing guard**: if a single huge file/tool output refills the context immediately after each summary, Claude Code stops auto-compacting after a few attempts and surfaces an error instead of looping. | |
| 207 | + | |
| 208 | +Anthropic's context-engineering post confirms the design and the hard part: "passing the message history to the model to summarize and compress the most critical details" — the art is choosing what to preserve (decisions, unresolved bugs, plan state) vs. discard (raw tool outputs, dead-end exploration). | |
| 209 | + | |
| 210 | +### 3.3 Concrete compaction strategy (synthesized) | |
| 211 | + | |
| 212 | +The strategy production systems converge on, and the one Zyquo Agent's `MemoryManager` should implement: | |
| 213 | + | |
| 214 | +1. **Never compact**: system prompt, tool schemas, the current plan/todo list, standing instructions/memory files (re-inject after compaction). | |
| 215 | +2. **Keep verbatim**: the most recent N steps (recency matters most for the next decision). | |
| 216 | +3. **Summarize into compact structured records**: completed sub-tasks and older turns — e.g. `✔ Step 2: created venv, installed pandas 2.2 (3 commands, all exit 0)` instead of three full command transcripts. | |
| 217 | +4. **Offload large tool outputs to files**: write long stdout/logs/file dumps into the workspace (`.zyquo/outputs/step-014-stdout.txt`) and replace them in-context with a one-line reference + summary ("output 48KB, saved to …; key line: 3 tests failed in test_parser.py"). This mirrors Anthropic's context-editing result: clearing stale tool results enabled 100-turn workflows and cut token use 84% (<https://www.anthropic.com/news/context-management>). | |
| 218 | +5. **Compact preemptively** at a threshold (~80–90%), never mid-tool-execution, and always via a dedicated summarization call with an explicit "preserve: plan, key facts, open problems" prompt. | |
| 219 | +6. **Guard against thrashing** as Claude Code does. | |
| 220 | + | |
| 221 | +### 3.4 Hierarchical memory: scratchpad vs. notes files | |
| 222 | + | |
| 223 | +Two tiers, both external to the context window: | |
| 224 | + | |
| 225 | +- **Structured note-taking / agentic memory** (short-to-medium term): the agent maintains a `NOTES.md`/`MEMORY.md`/todo file **in its workspace**, writing down progress, learned facts, and open questions, and re-reading it after compaction or restart. Anthropic cites this as a core technique — memory persists "with minimal overhead" while the working context stays lean (<https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents>). | |
| 226 | +- **The memory tool** (long term, cross-session): Anthropic's `memory_20250818` tool gives the model file operations (view/create/str_replace/insert/delete/rename) over a client-managed `/memories` directory that survives conversations; combined with context editing it improved agentic-search performance 39% (<https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool>, <https://www.anthropic.com/news/context-management>). Claude Code's CLAUDE.md files are the same idea applied to user/project preferences. | |
| 227 | +- **Sub-agents as context compression**: delegating exploration to a sub-agent with its own clean window, which returns only a 1–2K-token distilled summary, keeps detail out of the orchestrator's context entirely (<https://www.anthropic.com/engineering/multi-agent-research-system>). Claude Code's Task tool works exactly this way: "the parent agent receives only the sub-agent's final output, not its internal reasoning history" (<https://code.claude.com/docs/en/sub-agents>). | |
| 228 | +- **Just-in-time retrieval over pre-loading**: keep lightweight identifiers (paths, ids) in context and load content via tools when needed, rather than stuffing everything up front. | |
| 229 | + | |
| 230 | +--- | |
| 231 | + | |
| 232 | +## 4. Working Repositories / Workspaces | |
| 233 | + | |
| 234 | +### 4.1 Why isolation matters | |
| 235 | + | |
| 236 | +Every serious agent gives the model a **bounded working directory** (and often a stronger sandbox) for three reasons: blast-radius containment (a bad `rm` hits scratch space, not the user's home), reproducibility/auditability (everything the task produced lives in one folder), and clean state per task (no cross-task contamination). | |
| 237 | + | |
| 238 | +Reference points: | |
| 239 | + | |
| 240 | +- **OpenHands**: all code/bash execution happens inside a **Docker-sandboxed runtime**; agent↔environment interaction is an event stream of typed Actions and Observations, and each conversation binds to a workspace (local dir mounted into the sandbox, or a remote workspace) (<https://arxiv.org/abs/2407.16741>, SDK: <https://arxiv.org/html/2511.03690v1>). | |
| 241 | +- **Codex CLI**: OS-level sandboxing — **macOS Seatbelt** (`sandbox-exec` kernel-enforced profiles) and Linux **Landlock + seccomp** — restricting file writes to the workspace and blocking network unless granted; sandbox level is chosen independently of the approval mode (<https://www.vincentschmalbach.com/how-codex-cli-flags-actually-work-full-auto-sandbox-and-bypass/>, <https://agent-safehouse.dev/docs/agent-investigations/codex>). | |
| 242 | +- **SWE-bench / Terminal-Bench**: evaluation itself runs each task in a pinned Docker container — the "workspace = container + repo checkout" mapping is now the standard unit of agent work (<https://openai.com/index/introducing-swe-bench-verified/>, <https://arxiv.org/abs/2601.11868>). | |
| 243 | +- **Claude Code**: softer model — permissions scope reads to the launch directory + `additionalDirectories`, writes require approval, and an optional OS sandbox can enforce path restrictions on *all* subprocesses (the docs note plain Read/Edit deny rules "don't apply to arbitrary subprocesses… For OS-level enforcement… enable the sandbox") (<https://code.claude.com/docs/en/permissions>). | |
| 244 | + | |
| 245 | +### 4.2 Session ↔ folder mapping, file tracking, checkpoints | |
| 246 | + | |
| 247 | +- **Session→folder**: one task = one directory is the norm (OpenHands conversation↔workspace; Devin's cloud VM per session; Zyquo Agent: `~/Library/Application Support/ZyquoAgent/Workspaces/<task-id>/`). The workspace is the shell tool's cwd and FileTools' root; reopening a task reattaches its folder. | |
| 248 | +- **File tracking**: agents record every file they create/modify — Claude Code tracks file state per session (its Edit tool refuses to edit a file that wasn't Read first, a cheap way to prevent blind overwrites); OpenHands records `FileEditObservation`s in the event stream. This powers UI badges (created/modified) and audit. | |
| 249 | +- **Checkpoints**: Claude Code **automatically checkpoints file state at every user prompt**; `/rewind` (or Esc-Esc) restores *code*, *conversation*, or both, with checkpoints persisted across sessions (~30-day retention) (<https://code.claude.com/docs/en/checkpointing>, <https://claudelog.com/faqs/how-to-use-checkpoints-in-claude-code/>). Cursor pioneered per-request checkpoints; Claude Code's separation of code-restore vs. conversation-restore is the more refined design. Implementation options: git-based shadow snapshots or copy-on-write file snapshots inside the workspace; the key property is that checkpoints capture only agent-touched files, cheaply, at step boundaries. | |
| 250 | + | |
| 251 | +--- | |
| 252 | + | |
| 253 | +## 5. Planning & Task Decomposition | |
| 254 | + | |
| 255 | +### 5.1 Externalized todo lists | |
| 256 | + | |
| 257 | +Claude Code makes the plan a first-class, model-maintained artifact: the **TodoWrite** tool (now evolved into structured **TaskCreate/TaskUpdate/TaskGet/TaskList** tools as of v2.1.142) has the agent write a task list with per-item states `pending → in_progress → completed`, updated *as it works*, and the harness renders it live to the user (<https://code.claude.com/docs/en/agent-sdk/todo-tracking>, <https://claudelog.com/faqs/what-is-todo-list-in-claude-code/>). Benefits documented: the plan is observable (user sees progress), it disciplines the model (the harness nudges it to keep exactly one item `in_progress` and mark items done immediately), and it survives compaction because it lives outside raw conversation text. | |
| 258 | + | |
| 259 | +Anthropic's context-engineering post frames the same practice as memory: to-do lists are "structured note-taking" that maintains coherence across long horizons. | |
| 260 | + | |
| 261 | +### 5.2 Plan-first modes and mid-run re-planning | |
| 262 | + | |
| 263 | +- **Plan mode**: Claude Code's `plan` permission mode lets the agent read/explore but not mutate, producing a plan the user approves before execution (<https://code.claude.com/docs/en/permissions>). The recommended workflow is explicitly **explore → plan → code → commit** (<https://code.claude.com/docs/en/best-practices>). | |
| 264 | +- **Re-planning is the normal path**: the LangGraph plan-and-execute pattern includes a replanner node that revises remaining steps after each execution (<https://blog.langchain.com/planning-agents/>). Devin write-ups make the same point: "Devin reasons about the situation using its full context — what the task is, what the test failures say, what it has done so far — and chooses the most appropriate path forward. The plan changes, and that's not a failure state — it's the system working correctly" (<https://cognition.com/blog/how-cognition-uses-devin-to-build-devin>). Devin's design also stresses long-horizon memory of "what it tried, what worked, what failed, and why." | |
| 265 | +- Trigger re-planning on: a step failing twice, an assumption invalidated by an observation, or the user editing the plan. | |
| 266 | + | |
| 267 | +### 5.3 Sub-agents and delegation | |
| 268 | + | |
| 269 | +- **Claude Code Task tool / subagents**: the orchestrator spawns a subagent with a fresh context window containing only the delegation prompt; the subagent runs its own tool loop and returns a concise report; up to ~10 run concurrently (<https://code.claude.com/docs/en/sub-agents>). Used for exploration, research, and parallelizable independent work. | |
| 270 | +- **Anthropic's multi-agent research system** (<https://www.anthropic.com/engineering/multi-agent-research-system>): orchestrator-workers at scale. Hard-won lessons: the lead agent must **save its plan to memory** before spawning workers (context may overflow); delegation prompts must carry *objective, output format, tool guidance, and boundaries* (vague prompts caused duplicated work); **scale effort to complexity** (one agent for a simple lookup, 10+ for open research); errors compound in stateful multi-step systems, so agents must be able to *resume from checkpoints* rather than restart. | |
| 271 | +- **Counterpoint — Cognition's "Don't Build Multi-Agents"** (<https://cognition.ai/blog/dont-build-multi-agents>): for *coding/acting* tasks (vs. read-only research), parallel agents that can't see each other's context make conflicting decisions; principles: share full context ("actions carry implicit decisions"), prefer a single continuous agent with strong context compression. Synthesis for Zyquo Agent: **one primary loop**; sub-agents only for read-only exploration/summarization, never for concurrent mutation of the same workspace. | |
| 272 | + | |
| 273 | +--- | |
| 274 | + | |
| 275 | +## 6. Reliability & Control | |
| 276 | + | |
| 277 | +### 6.1 Loop guards | |
| 278 | + | |
| 279 | +Anthropic's guidance: agents need explicit **stopping conditions** — max iterations and checkpoints — "to maintain control" (<https://www.anthropic.com/engineering/building-effective-agents>). Concrete guards used across production systems and frameworks: | |
| 280 | + | |
| 281 | +- **Max steps/iterations**: a hard per-task cap (framework defaults range ~10–50; agent SDKs expose `max_turns`). Hitting it should pause-and-ask, not silently die. | |
| 282 | +- **Token/cost budget**: track cumulative input+output tokens per task; warn at a threshold, pause at the cap. Anthropic's multi-agent post notes agents can burn ~15× the tokens of a chat, so budgets are economic guards too. | |
| 283 | +- **Wall-clock budget** and **per-command timeout** (the bash-tool docs make per-command timeouts the app's responsibility). | |
| 284 | +- **Repetition detection**: same tool + same (normalized) arguments failing repeatedly ⇒ trip. Reflexion's insight applies: force a self-critique turn ("the last two attempts failed with X; state a different approach") before allowing a retry. | |
| 285 | +- **Stall / no-progress detection**: N consecutive iterations with no plan-item state change, no file mutation, and no new information ⇒ pause and ask the user. Claude Code's compaction thrashing detector is the same pattern applied to memory. | |
| 286 | +- **Server-side analogue**: Anthropic's own internal loop caps iterations and returns `stop_reason: "pause_turn"` so the client can choose to continue — evidence that "pause, hand control back" is the correct trip behavior, not abort (<https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works>). | |
| 287 | + | |
| 288 | +### 6.2 Error recovery | |
| 289 | + | |
| 290 | +- Feed tool errors back as observations (Anthropic `is_error: true`) with actionable text; the model self-corrects on the next iteration — this is the loop's built-in recovery mechanism. | |
| 291 | +- Distinguish *retryable* (transient network, timeout) from *diagnostic* (compile error — the model should read it) from *fatal* (permission denied by policy — surface to user). | |
| 292 | +- **Resume, don't restart**: build the system so a crashed/interrupted task can resume from its transcript + workspace + checkpoints (<https://www.anthropic.com/engineering/multi-agent-research-system>). | |
| 293 | +- Handle abnormal stop reasons explicitly: `max_tokens` mid-tool-call means an incomplete action that must not be executed; `refusal` should surface to the user (<https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons>). | |
| 294 | + | |
| 295 | +### 6.3 Human-in-the-loop approval gates: how the leaders do it | |
| 296 | + | |
| 297 | +**Claude Code** (<https://code.claude.com/docs/en/permissions>) — the most instructive design, worth reproducing in detail: | |
| 298 | + | |
| 299 | +- **Tiered by tool type**: read-only tools (file reads, Grep) run without approval *within the working directory + additional directories*; **Bash commands require approval except a built-in read-only set** (`ls`, `cat`, `pwd`, `grep`, `head`, `tail`, `which`, `diff`, read-only `git`, …); file edits require approval (remembered until session end); Bash approvals can be remembered permanently per repository. | |
| 300 | +- **Rules**: `allow` / `ask` / `deny` lists evaluated in strict order **deny → ask → allow**, first match wins; specificity does *not* override order (a broad `Bash(aws *)` deny beats a narrow allow). Syntax: `Tool` or `Tool(specifier)` with globs — `Bash(npm run test *)`, `Read(~/.zshrc)`, `Edit(/src/**)` (gitignore-style path patterns with `//` = filesystem root, `~/` = home, `/` = settings-source anchor), `WebFetch(domain:example.com)`. | |
| 301 | +- **Compound-command awareness**: shell operators are parsed — `Bash(safe-cmd *)` does **not** authorize `safe-cmd && other-cmd`; each subcommand must independently match. Known wrappers (`timeout`, `nice`, `nohup`, env-var prefixes, bare `xargs`) are stripped before matching; exec-capable wrappers (`watch`, `find -exec`, `devbox run`) deliberately can't be prefix-approved. | |
| 302 | +- **Modes**: `default` (prompt on first use per tool), `acceptEdits` (auto-accept file edits + benign fs commands in-workspace), `plan` (read-only exploration), `dontAsk` (auto-deny anything not pre-approved), `bypassPermissions` (skip prompts — docs say to use it only "in isolated environments like containers or VMs") — and even bypass keeps **circuit breakers**: `rm -rf /` and `rm -rf ~` *always* prompt, including when hidden inside `$(...)`/backtick substitutions. | |
| 303 | +- **Fragility honesty**: the docs warn that argument-constraining patterns (`Bash(curl http://github.com/ *)`) are bypassable via flags/redirects/variables, and recommend structural fixes (deny curl entirely; use a domain-scoped fetch tool; hooks for validation). | |
| 304 | +- **Risk explanation UI**: on a Bash prompt, Ctrl+E asks the model itself to explain the command and label it Low/Med/High risk. | |
| 305 | + | |
| 306 | +**Codex CLI** — approvals and sandbox are **orthogonal axes**: approval policies from `untrusted`/suggest (approve everything) through `on-request`/auto-edit (edits auto, commands ask) to `never`/full-auto; sandbox levels `read-only` → `workspace-write` → `danger-full-access`, enforced at the **OS level** (Seatbelt/Landlock) so even "full-auto" runs are contained unless the user explicitly disables the sandbox (<https://inventivehq.com/knowledge-base/openai/how-to-configure-sandbox-modes>, <https://www.vincentschmalbach.com/how-codex-cli-flags-actually-work-full-auto-sandbox-and-bypass/>). | |
| 307 | + | |
| 308 | +**Cursor** — Run Modes: Auto-review (default; allowlisted commands run, shell is sandboxed when possible, everything else goes through an **LLM safety classifier** that checks the call against the user's intent), Allowlist, and Run Everything; plus user allowlists/denylists (<https://cursor.com/docs/agent/security>). **The cautionary tale**: security researchers found four distinct bypasses of Cursor's denylist (subshells, string manipulation, etc.), and Cursor's own docs concede the guardrails are "best-effort… rather than a hard security boundary" (<https://www.backslash.security/blog/cursor-ai-security-flaw-autorun-denylist>, <https://www.theregister.com/2025/07/21/cursor_ai_safeguards_easily_bypassed/>). | |
| 309 | + | |
| 310 | +**Design conclusions for a policy engine** (synthesized): | |
| 311 | +1. Deny → ask → allow, first-match, no specificity override — simple to reason about, hard to misconfigure. | |
| 312 | +2. Parse shell structure (subcommands, wrappers, substitutions) before matching; never regex the raw string only. | |
| 313 | +3. Auto-allow only a **curated read-only command set**; everything mutating asks by default. | |
| 314 | +4. Destructive patterns (`rm -rf` at scale, `sudo`, `curl | sh`, writes to system paths, `mkfs`/`diskutil`, `killall`, `launchctl`, `defaults write` outside app domain) are **always-ask circuit breakers in every mode** — exactly like Claude Code's `rm -rf ~` breaker surviving bypassPermissions. | |
| 315 | +5. Treat pattern-matching as UX, not security: pair it with approvals, workspace scoping, and a complete audit trail (the Cursor lesson). | |
| 316 | +6. "Approve & remember" should save the *narrowest* rule that covers the action (Claude Code saves per-subcommand rules, max 5, for compound commands). | |
| 317 | + | |
| 318 | +### 6.4 Deciding an action is risky | |
| 319 | + | |
| 320 | +Signals used across these systems: (a) static classification — command family (read/mutate/destroy), target path (inside vs. outside workspace, system paths, dotfiles), privilege (`sudo`), network + execution combos (`curl | sh`), irreversibility; (b) contextual classification — does the action match the user's stated intent (Cursor's classifier, Claude Code's Ctrl+E explainer); (c) mode — the same action can be auto-run in autonomous mode but asked in manual mode, *except* the always-ask class. A hybrid — fast rule-based classifier with an always-ask floor, optionally augmented by a model-generated explanation at prompt time — is the state of the art. | |
| 321 | + | |
| 322 | +--- | |
| 323 | + | |
| 324 | +## 7. Evaluation | |
| 325 | + | |
| 326 | +### 7.1 Benchmark task suites (the pattern to copy) | |
| 327 | + | |
| 328 | +- **SWE-bench Verified** (<https://openai.com/index/introducing-swe-bench-verified/>, <https://www.swebench.com>): 500 human-validated real GitHub issues from 12 Python repos. The agent gets a Docker container with the repo at the pre-fix commit + the issue text; success = the produced patch makes hidden **FAIL_TO_PASS** tests pass (proves the fix) while **PASS_TO_PASS** tests still pass (proves no regression). Key ideas: real tasks, hermetic pinned environments, and a *deterministic programmatic oracle* — success is judged by end state, not by how the agent got there. | |
| 329 | +- **Terminal-Bench** (<https://arxiv.org/abs/2601.11868>, <https://www.tbench.ai>): 89+ hand-crafted end-to-end terminal tasks (compiling, training, sysadmin, security, data science). Each task = natural-language instruction + Docker environment + **verification test suite** + an oracle solution proving solvability. This is the closest published analogue to Zyquo Agent's domain (arbitrary shell work, not just code patches). | |
| 330 | +- **OSWorld / OSWorld 2.0** (<https://os-world.github.io>, <https://arxiv.org/abs/2606.29537>): real-OS computer-use tasks; 2.0 has 108 long-horizon workflows (~1.6 human-hours, ~318 tool calls avg) with execution-based verification scripts inspecting final OS/app state. | |
| 331 | + | |
| 332 | +Common skeleton: **instruction + isolated environment + programmatic success checker + oracle solution**. Metrics: task resolution rate (primary), plus steps/tokens/time-to-completion (efficiency). | |
| 333 | + | |
| 334 | +### 7.2 Beyond pass/fail: judges and trajectory inspection | |
| 335 | + | |
| 336 | +From Anthropic's multi-agent research system (<https://www.anthropic.com/engineering/multi-agent-research-system>): | |
| 337 | + | |
| 338 | +- **End-state evaluation** for tasks with mutable state: judge whether the final state is correct, not whether the agent followed an expected path — agents legitimately find alternate valid routes. | |
| 339 | +- **LLM-as-judge** with a rubric (accuracy, completeness, source/tool quality, efficiency) scales grading of free-form outcomes; single-call judges with 0–1 scores worked best. | |
| 340 | +- **Human trajectory inspection remains essential**: humans caught failure modes rubrics missed (e.g. preferring SEO content farms over authoritative sources). Reading transcripts of *how* the agent worked — wrong tool choices, ignored errors, loops — is the debugging method. | |
| 341 | +- **Start small**: ~20 representative tasks catch most regressions early; don't wait for a big eval harness. | |
| 342 | +- Also evaluate **safety properties as test cases**: "destructive command always prompts in every mode," "cancel actually kills the process," "no sudo runs silently" — assert them like unit tests (this mirrors Phase 7's mandate). | |
| 343 | + | |
| 344 | +--- | |
| 345 | + | |
| 346 | +## 8. Implications for Zyquo Agent Architecture | |
| 347 | + | |
| 348 | +| Finding (section) | Component | Design consequence | | |
| 349 | +|---|---|---| | |
| 350 | +| While-loop on stop_reason; done = non-tool response (§1.3) | `AgentLoop` | Actor loop: send → stream → if toolCalls: execute via gate, append results, repeat; exit on `end_turn`/`stop`; handle `max_tokens`/`refusal`/`pause` explicitly. | | |
| 351 | +| Provider stop/threading formats differ (§1.3, §2.2) | `ProviderClient` protocol | | |
| 352 | +| System prompt: altitude, done-convention, verify-before-done (§1.4) | `AgentLoop` system prompt | Sectioned prompt (role, environment, tool policy, planning discipline, safety, done-convention); compaction-immune; instructs the model to verify with tools before declaring done. | Normalize to `AssistantTurn {text, thinking, [ToolCall], stopReason}` + `ToolResultMessage`; each client (OpenAI-compatible, Anthropic, Gemini) maps to its wire format; results for parallel calls returned in one message, correlated by id. | | |
| 353 | +| Streaming deltas incl. partial tool JSON (§1.6) | `ProviderClient` → UI | Unified event stream (`textDelta`, `toolCallArgsDelta`, …); accumulate partial JSON per call id; UI renders command text as it streams; never execute until arguments finalize. | | |
| 354 | +| Trained-in tool shapes; few consolidated tools (§2.4–2.5) | `ToolRegistry`, `Tools/` | Small tool set: `bash` (persistent session semantics, `{command, timeout?}`), `osascript`, `read_file`/`write_file`/`edit_file` (exact-unique `str_replace` contract)/`list`/`search`. Schemas kept to the JSON-Schema/OpenAPI common subset; descriptions written as onboarding docs; 25K-token result truncation with steering messages. | | |
| 355 | +| Exact-match str_replace + read-before-edit (§2.5, §4.2) | `FileTools`, `WorkspaceManager` | `edit_file` fails loudly on zero/multiple matches; require the file to have been read this task before editing; track every created/modified file. | | |
| 356 | +| Compaction: keep plan+recent verbatim, summarize old, re-inject standing context (§3.2–3.3) | `MemoryManager` | Live token accounting; compact at ~85% via a summarization call; plan, system prompt, MEMORY.md never summarized away and re-injected post-compaction; thrashing guard. | | |
| 357 | +| Offload big outputs to files (§3.3) | `MemoryManager` + `WorkspaceManager` | Tool outputs > threshold written to `workspace/.zyquo/outputs/…`, replaced in-context by path + auto-summary; searchable later via FileTools. | | |
| 358 | +| Memory files / memory tool (§3.4) | `MemoryManager` | Agent-maintained `MEMORY.md` in each workspace (read at task start, updated as facts are learned); persists across compactions and sessions. | | |
| 359 | +| Workspace = task folder; sandbox where possible (§4) | `WorkspaceManager`, `ExecutionService` | One dir per task under `Workspaces/`; bash cwd pinned there; FileTools scoped there by default, escape = explicit permission; consider optional Seatbelt profile for autonomous mode later. | | |
| 360 | +| Checkpoints at step boundaries, code/conversation restore (§4.2) | `WorkspaceManager` | Snapshot agent-touched files per user prompt / per step; restore-files, restore-transcript, or both. | | |
| 361 | +| Externalized todo list with pending/in_progress/completed (§5.1) | `Planner` | Plan drafted at task start, persisted, rendered in the Plan panel, editable by user; the loop updates item states as steps complete; plan survives compaction by construction. | | |
| 362 | +| Re-planning is normal; Reflexion on failure (§1.2, §5.2) | `Planner` + `AgentLoop` | After a step fails twice: forced self-critique turn, then plan revision; plan changes surfaced in UI, never silent. | | |
| 363 | +| Single continuous agent; sub-agents read-only only (§5.3) | `AgentLoop` | v1: one loop, no concurrent mutating sub-agents (Cognition's context-sharing argument); optional later: read-only explorer sub-agent returning summaries. | | |
| 364 | +| Max steps, token/time budgets, repetition & stall detection, pause-not-abort (§6.1) | `LoopGuard` | Configurable caps (Settings › Agent); repetition = same tool+normalized args failing; stall = N iterations without plan/file/info change; on trip → pause task, Awaiting-input state, ask user. | | |
| 365 | +| Deny→ask→allow, parsed subcommands, read-only auto-allow, always-ask circuit breakers (§6.3) | `PolicyEngine` | Rule engine with that exact precedence; shell parser splits `&&`/`;`/`|` and strips wrapper commands before matching; curated read-only allowset; destructive class (`rm -rf` scale, `sudo`, `curl|sh`, system paths, disk ops, `launchctl`…) always asks in **all three modes** incl. Autonomous; "Approve & remember" saves narrowest per-subcommand rules. | | |
| 366 | +| Patterns are UX, not security (§6.3) | `PolicyEngine` + `AuditLog` | Every executed action (approved or auto) appended to the audit log with timestamp, cwd, exit code, truncated output; approval cards show exact command + explanation + risk label. | | |
| 367 | +| Modes mirror industry (§6.3) | `PolicyEngine` | Manual ≈ Claude Code `default`/Codex suggest; Guarded ≈ `acceptEdits`+classifier (auto read-only/safe, ask mutating); Autonomous ≈ bounded full-auto — still budgeted, audited, and circuit-breakered. | | |
| 368 | +| Eval = instruction + env + programmatic checker; trajectory reading; safety as tests (§7) | Phase 7 harness | ≥8 scenario tasks in temp workspaces with scripted success checkers; per-model tool-call conformance table; safety assertions (always-prompt, cancel-kills, no-silent-sudo) as automated tests; keep transcripts for inspection. | | |
| 369 | + | |
| 370 | +**The one-sentence architecture:** Zyquo Agent is a single Anthropic-style tool-use while-loop (`AgentLoop`) whose every action passes a Claude-Code-style deny→ask→allow gate (`PolicyEngine`) into a per-task folder (`WorkspaceManager`), kept honest by an externalized todo plan (`Planner`), kept alive by compaction + memory files + output offloading (`MemoryManager`), and kept bounded by step/token/time/repetition guards that pause rather than abort (`LoopGuard`) — with everything streamed to the UI and appended to an audit log. | |
| 371 | + | |
| 372 | +--- | |
| 373 | + | |
| 374 | +## Source Index | |
| 375 | + | |
| 376 | +**Agentic loop & patterns** | |
| 377 | +- Anthropic, *Building Effective Agents* — <https://www.anthropic.com/engineering/building-effective-agents> | |
| 378 | +- Anthropic docs, *How tool use works* (agentic loop, stop reasons, server-side loop) — <https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works> | |
| 379 | +- Anthropic docs, *Stop reasons and fallback* — <https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons> | |
| 380 | +- Yao et al., *ReAct: Synergizing Reasoning and Acting in Language Models* — <https://arxiv.org/abs/2210.03629> | |
| 381 | +- Shinn et al., *Reflexion: Language Agents with Verbal Reinforcement Learning* — <https://arxiv.org/abs/2303.11366> | |
| 382 | +- LangChain, *Plan-and-Execute agents* — <https://blog.langchain.com/planning-agents/>; tutorial: <https://langchain-opentutorial.gitbook.io/langchain-opentutorial/17-langgraph/03-use-cases/05-langgraph-plan-and-execute> | |
| 383 | + | |
| 384 | +**Tool design & provider APIs** | |
| 385 | +- Anthropic, *Writing effective tools for agents* — <https://www.anthropic.com/engineering/writing-tools-for-agents> | |
| 386 | +- OpenAI, *Function calling guide* — <https://developers.openai.com/api/docs/guides/function-calling> | |
| 387 | +- Google, *Gemini function calling* — <https://ai.google.dev/gemini-api/docs/function-calling> | |
| 388 | +- Anthropic docs, *Bash tool* — <https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool> | |
| 389 | +- Anthropic docs, *Text editor tool* — <https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool> | |
| 390 | +- Anthropic docs, *Streaming* / *Fine-grained tool streaming* — <https://platform.claude.com/docs/en/build-with-claude/streaming>, <https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/fine-grained-tool-streaming> | |
| 391 | +- Aider, *Edit formats* — <https://aider.chat/docs/more/edit-formats.html> | |
| 392 | + | |
| 393 | +**Memory & context** | |
| 394 | +- Anthropic, *Effective context engineering for AI agents* — <https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents> | |
| 395 | +- Anthropic, *Managing context on the Claude Developer Platform* (context editing + memory tool, 84%/39% results) — <https://www.anthropic.com/news/context-management> | |
| 396 | +- Anthropic docs, *Memory tool* — <https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool> | |
| 397 | +- Claude Code auto-compact analyses — <https://claudelog.com/faqs/what-is-claude-code-auto-compact/>, <https://okhlopkov.com/claude-code-compaction-explained/>, <https://howaiworks.ai/blog/claude-code-auto-compact-context-management> | |
| 398 | + | |
| 399 | +**Workspaces, planning, agents in production** | |
| 400 | +- OpenHands platform paper — <https://arxiv.org/abs/2407.16741>; OpenHands Agent SDK — <https://arxiv.org/html/2511.03690v1> | |
| 401 | +- Claude Code docs: *Checkpointing* — <https://code.claude.com/docs/en/checkpointing>; *Sub-agents* — <https://code.claude.com/docs/en/sub-agents>; *Todo/Task tracking* — <https://code.claude.com/docs/en/agent-sdk/todo-tracking>; *Best practices* — <https://code.claude.com/docs/en/best-practices> | |
| 402 | +- Anthropic, *How we built our multi-agent research system* — <https://www.anthropic.com/engineering/multi-agent-research-system> | |
| 403 | +- Cognition, *Don't Build Multi-Agents* — <https://cognition.ai/blog/dont-build-multi-agents>; *How Cognition uses Devin to build Devin* — <https://cognition.com/blog/how-cognition-uses-devin-to-build-devin> | |
| 404 | + | |
| 405 | +**Safety & control** | |
| 406 | +- Claude Code docs, *Configure permissions* (rules, modes, read-only set, circuit breakers) — <https://code.claude.com/docs/en/permissions> | |
| 407 | +- Codex CLI sandbox/approvals — <https://inventivehq.com/knowledge-base/openai/how-to-configure-sandbox-modes>, <https://www.vincentschmalbach.com/how-codex-cli-flags-actually-work-full-auto-sandbox-and-bypass/>, <https://agent-safehouse.dev/docs/agent-investigations/codex> | |
| 408 | +- Cursor, *Agent Security* — <https://cursor.com/docs/agent/security>; Backslash Security, *The Denylist Delusion* — <https://www.backslash.security/blog/cursor-ai-security-flaw-autorun-denylist>; The Register coverage — <https://www.theregister.com/2025/07/21/cursor_ai_safeguards_easily_bypassed/> | |
| 409 | + | |
| 410 | +**Evaluation** | |
| 411 | +- OpenAI, *Introducing SWE-bench Verified* — <https://openai.com/index/introducing-swe-bench-verified/>; SWE-bench — <https://www.swebench.com> | |
| 412 | +- *Terminal-Bench* — <https://arxiv.org/abs/2601.11868>, <https://www.tbench.ai> | |
| 413 | +- *OSWorld* — <https://os-world.github.io>; *OSWorld 2.0* — <https://arxiv.org/abs/2606.29537> | |
added
docs/PLAN.md
+33 −0
@@ -0,0 +1,33 @@ | ||
| 1 | +# Zyquo Agent — Build Plan & Phase Checkpoints | |
| 2 | + | |
| 3 | +## Phase 0 — Research + Zyquo Cloud Study | |
| 4 | +- [x] 0.A Intensive web research → `docs/AGENT-RESEARCH.md` (413 lines, 79 citations: stop_reason-keyed loop, 3 provider wire formats, bash/file-edit tool design, auto-compaction @95% + MEMORY.md, workspace isolation, TodoWrite-style planning, deny→ask→allow policy engine, SWE-bench/Terminal-Bench eval) | |
| 5 | + | |
| 6 | +**Phase 0 checkpoint (2026-07-30):** Both research docs complete. Key decisions locked: port Cloud's two-client provider layer (AnthropicClient + OpenAICompatibleClient, 12 providers, 170 models, ~70 agent-capable, default claude-sonnet-5) and add a normalized tool-calling interface absorbing the 3 wire dialects; agent loop is a stop_reason-keyed while-loop with Planner/MemoryManager/LoopGuard per AGENT-RESEARCH.md; PolicyEngine follows deny→ask→allow with destructive-pattern circuit breakers; SecureKeyStore vault format reused byte-compatible. | |
| 7 | +- [x] 0.B Study `~/Desktop/zyquo-cloud` → `docs/PROVIDER-REUSE.md` (12 providers / 2 clients: AnthropicClient + OpenAICompatibleClient incl. Gemini compat endpoint; 170-model catalog, ~70 agent-capable, default `claude-sonnet-5`; AES-256-GCM SecureKeyStore vault; 3 tool-calling wire dialects documented) | |
| 8 | + | |
| 9 | +## Phase 1 — Project Setup (SPM, no Xcode IDE) | |
| 10 | +- [ ] Package.swift, executable target `ZyquoAgent` | |
| 11 | +- [ ] Makefile (dev bundle, ad-hoc sign), Info.plist (com.zyquo.agent, usage descriptions) | |
| 12 | +- [ ] @main SwiftUI App, terminal-launch activation | |
| 13 | + | |
| 14 | +## Phase 2 — Architecture skeleton (folders per CLAUDE.md) | |
| 15 | + | |
| 16 | +## Phase 3 — Agent Engine (loop, memory, tools+safety) → CLI POC gate | |
| 17 | + | |
| 18 | +## Phase 4 — Design system & UI spec (ZyquoTheme violet) | |
| 19 | + | |
| 20 | +## Phase 5 — Icon (SVG → icns) | |
| 21 | + | |
| 22 | +## Phase 6 — Features / full UI | |
| 23 | + | |
| 24 | +## Phase 7 — Verification with real keys (provider tool-calling table + scenario suite + safety tests) | |
| 25 | + | |
| 26 | +## Phase 8 — Signing & notarization (reuse zyquo-term identity) | |
| 27 | + | |
| 28 | +**Facts gathered from `~/Desktop/other/OTHER/zyquo-term` (2026-07-30):** | |
| 29 | +- Identity: `Developer ID Application: Simon-Pierre Boucher (3YM54G49SN)` (Team ID `3YM54G49SN`) | |
| 30 | +- notarytool keychain profile: `MacLustr-Notarize` (`xcrun notarytool submit … --keychain-profile "MacLustr-Notarize" --wait`) | |
| 31 | +- Pipeline: codesign nested executables first, then bundle, `--options runtime --timestamp --entitlements`, verify `--deep --strict`, DMG signed too, staple after notarize | |
| 32 | +- zyquo-term entitlements: hardened runtime, NO App Sandbox (terminal-class app), `com.apple.security.cs.allow-jit=false`. Zyquo Agent: same posture + `NSAppleEventsUsageDescription` in Info.plist and `com.apple.security.automation.apple-events` entitlement for osascript automation. | |
| 33 | +- Toolchain: Swift 6.4 (arm64, macOS 27) | |
added
docs/PROVIDER-REUSE.md
+701 −0
@@ -0,0 +1,701 @@ | ||
| 1 | +<!-- | |
| 2 | + PROVIDER-REUSE.md | |
| 3 | + Zyquo Agent | |
| 4 | + | |
| 5 | + Author: Simon-Pierre Boucher | |
| 6 | + Mail: contact@spboucher.ai | |
| 7 | +--> | |
| 8 | + | |
| 9 | +# Zyquo Agent — Provider Reuse (Phase 0.B, contract for the provider layer) | |
| 10 | + | |
| 11 | +Compiled 2026-07-30 from an in-depth study of the sibling **Zyquo Cloud** repository at | |
| 12 | +`/Users/simon-pierreboucher/Desktop/zyquo-cloud` (source files under `Sources/ZyquoCloud/`, | |
| 13 | +plus `docs/PROVIDERS.md` — Cloud's own live-probed provider research — and `docs/VERIFICATION.md`). | |
| 14 | + | |
| 15 | +**Rule of this document:** Zyquo Agent calls every model *exactly* the way Zyquo Cloud does. | |
| 16 | +The files listed in §1 are ported (near-)verbatim; the only addition is a **normalized | |
| 17 | +tool-calling interface** layered onto the same `ProviderClient` protocol (§5–§6). The model | |
| 18 | +picker offers **the same catalog as Cloud** (§2) with the agent-capable subset marked and | |
| 19 | +defaulted (§3). The key vault is byte-format-compatible in design (§4). | |
| 20 | + | |
| 21 | +--- | |
| 22 | + | |
| 23 | +## 1. How each provider's API is called (Cloud's provider layer, file by file) | |
| 24 | + | |
| 25 | +### 1.1 Architecture overview | |
| 26 | + | |
| 27 | +Cloud's entire provider layer is **four files** plus shared networking and models: | |
| 28 | + | |
| 29 | +| File (in `zyquo-cloud/Sources/ZyquoCloud/`) | Role | | |
| 30 | +|---|---| | |
| 31 | +| `Providers/ProviderProtocol.swift` | `ChatRequest`, `ChatEvent`, `protocol ProviderClient`, `ProviderError` (typed, human-readable, maps HTTP status + heterogeneous error bodies) | | |
| 32 | +| `Providers/ProviderRegistry.swift` | The *only* place that maps provider → client via `ProviderID.wireFormat` | | |
| 33 | +| `Providers/OpenAICompatibleClient.swift` | ONE client for the 11 OpenAI-schema providers + custom endpoints; all quirks live here | | |
| 34 | +| `Providers/AnthropicClient.swift` | Native Anthropic Messages API (`/v1/messages`) client | | |
| 35 | +| `Models/ProviderID.swift` | The 12 built-in providers + `.custom`: display names, base URLs, `wireFormat`, `supportsModelListing` | | |
| 36 | +| `Models/AIModel.swift` | `AIModel`, `ModelCapabilities` (incl. `tools: Bool`), `ModelPricing`, `ParameterSupport`, `TokenUsage` | | |
| 37 | +| `Models/Message.swift` | `Message` (role/text/reasoning/attachments/citations/usage/cost), `Attachment`, `Citation` | | |
| 38 | +| `Models/Conversation.swift` | `ChatParameters` (temperature, topP, maxTokens, penalties, `reasoningEffort`, `thinkingEnabled`), `Persona` | | |
| 39 | +| `Services/StreamingService.swift` | `SSEEvent`, incremental `SSEParser`, shared `URLSession`, `sseEvents(for:provider:)`, `postJSON`/`getJSON` with backoff | | |
| 40 | +| `Services/ModelCatalog.swift` | `@MainActor ObservableObject` catalog: built-in + custom + live `/models` diff + favorites + `cheapestModel(for:)` + `defaultModel` | | |
| 41 | +| `Services/ModelCatalogData.swift` | The full built-in catalog (generated from `docs/PROVIDERS.md`; 170 models — reproduced in §2) | | |
| 42 | +| `Services/SecureKeyStore.swift` | AES-256-GCM key vault, NO Keychain (§4) | | |
| 43 | +| `Verify/VerifyHarness.swift` | Phase-7 live verification harness pattern (env keys, per-model OK-test, stdout table + doc output) — reuse the pattern for Agent's tool-calling verification | | |
| 44 | + | |
| 45 | +**Key finding: there is NO `GeminiClient` in Zyquo Cloud.** Gemini is served through Google's | |
| 46 | +**OpenAI-compatibility endpoint** (`https://generativelanguage.googleapis.com/v1beta/openai`, | |
| 47 | +Bearer auth) via `OpenAICompatibleClient`. Cloud's research (`docs/PROVIDERS.md` §Gemini, | |
| 48 | +verified live) confirms the compat endpoint supports chat + streaming + **function calling | |
| 49 | +(`tools`)** + structured outputs + vision + `reasoning_effort`. Zyquo Agent's CLAUDE.md sketch | |
| 50 | +lists a `GeminiClient.swift`; per this study the correct, Cloud-identical approach is to **keep | |
| 51 | +Gemini on the compat endpoint** (Gemini tool calls then arrive as standard OpenAI `tool_calls`, | |
| 52 | +one uniform streaming path). A native `GeminiClient` (functionCall/functionResponse parts) is | |
| 53 | +only needed if Phase 7 finds compat-endpoint tool streaming inadequate — see §5.4. | |
| 54 | + | |
| 55 | +### 1.2 Core protocol types (`ProviderProtocol.swift`) | |
| 56 | + | |
| 57 | +```swift | |
| 58 | +struct ChatRequest { // provider-agnostic; clients translate to wire format | |
| 59 | + var model: AIModel | |
| 60 | + var systemPrompt: String? | |
| 61 | + var messages: [Message] | |
| 62 | + var parameters: ChatParameters | |
| 63 | + var stream: Bool = true | |
| 64 | +} | |
| 65 | + | |
| 66 | +enum ChatEvent { // streamed back to the UI | |
| 67 | + case reasoningDelta(String) | |
| 68 | + case textDelta(String) | |
| 69 | + case citations([Citation]) | |
| 70 | + case usage(TokenUsage) | |
| 71 | + case finished(reason: String?) | |
| 72 | +} | |
| 73 | + | |
| 74 | +protocol ProviderClient { | |
| 75 | + var providerID: ProviderID { get } | |
| 76 | + func streamChat(_ request: ChatRequest, apiKey: String) -> AsyncThrowingStream<ChatEvent, Error> | |
| 77 | + func complete(_ request: ChatRequest, apiKey: String) async throws -> Message | |
| 78 | + func listModelIDs(apiKey: String) async throws -> [String] | |
| 79 | +} | |
| 80 | +// + extension ProviderClient.testKey(_:fallbackModel:) -> TimeInterval | |
| 81 | +// (uses /models when supported, else a 16-token "Reply with exactly: OK" completion; | |
| 82 | +// fallbackModel needed only for Perplexity, which has no /models endpoint) | |
| 83 | +``` | |
| 84 | + | |
| 85 | +`ProviderError` cases: `invalidAPIKey`, `rateLimited(retryAfter:)`, `serverError(status:message:)`, | |
| 86 | +`badRequest`, `networkError`, `invalidResponse`, `missingAPIKey`, `noModelAvailable`, `cancelled` — | |
| 87 | +each with a polished `errorDescription`. `ProviderError.from(status:body:provider:)` maps | |
| 88 | +401/403→invalidAPIKey, 429→rateLimited, 400/404/422→badRequest, else serverError, and | |
| 89 | +`extractMessage(from:)` tolerates all observed error shapes: `{"error":{"message":…}}`, | |
| 90 | +`{"error":"…"}`, `{"message":…}`, `{"detail":…}`, and Gemini's array-wrapped errors. | |
| 91 | + | |
| 92 | +### 1.3 Provider table (from `ProviderID.swift`, all verified live by Cloud on 2026-07-30) | |
| 93 | + | |
| 94 | +| # | `ProviderID` case | Display name | Base URL | Auth | Wire format | `/models`? | | |
| 95 | +|---|---|---|---|---|---|---| | |
| 96 | +| 1 | `.openai` | OpenAI | `https://api.openai.com/v1` | `Authorization: Bearer` | OpenAI chat/completions | ✅ | | |
| 97 | +| 2 | `.anthropic` | Anthropic | `https://api.anthropic.com/v1` | `x-api-key` + `anthropic-version: 2023-06-01` | **Anthropic Messages** | ✅ (rich metadata, `?limit=100`) | | |
| 98 | +| 3 | `.xai` | xAI | `https://api.x.ai/v1` | Bearer | OpenAI-compat | ✅ | | |
| 99 | +| 4 | `.mistral` | Mistral | `https://api.mistral.ai/v1` | Bearer | OpenAI-compat | ✅ (per-model capability flags incl. `function_calling`) | | |
| 100 | +| 5 | `.gemini` | Google Gemini | `https://generativelanguage.googleapis.com/v1beta/openai` | Bearer (compat endpoint) | OpenAI-compat | ✅ (IDs prefixed `models/` — client strips) | | |
| 101 | +| 6 | `.qwen` | Alibaba Qwen | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | Bearer | OpenAI-compat | ✅ | | |
| 102 | +| 7 | `.deepseek` | DeepSeek | `https://api.deepseek.com` | Bearer | OpenAI-compat | ✅ (2 models) | | |
| 103 | +| 8 | `.kimi` | Kimi (Moonshot) | `https://api.moonshot.ai/v1` | Bearer | OpenAI-compat | ✅ | | |
| 104 | +| 9 | `.perplexity` | Perplexity | `https://api.perplexity.ai` | Bearer | OpenAI-compat + search extras | ❌ (404 — `supportsModelListing == false`) | | |
| 105 | +| 10 | `.together` | Together AI | `https://api.together.xyz/v1` | Bearer | OpenAI-compat | ✅ (bare array, not `{"data":[…]}`) | | |
| 106 | +| 11 | `.deepinfra` | DeepInfra | `https://api.deepinfra.com/v1/openai` | Bearer | OpenAI-compat | ✅ | | |
| 107 | +| 12 | `.cerebras` | Cerebras | `https://api.cerebras.ai/v1` | Bearer | OpenAI-compat | ✅ (3 models) | | |
| 108 | +| — | `.custom` | Custom | user-supplied `customBaseURL` on the `AIModel` | Bearer | OpenAI-compat | ✅ | | |
| 109 | + | |
| 110 | +`wireFormat`: `.anthropicMessages` for `.anthropic`, `.openAIChatCompletions` for everything else. | |
| 111 | +Env-var names used by the verify harness (reuse for Agent's Phase 7): `OPENAI_API_KEY`, | |
| 112 | +`ANTHROPIC_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `GEMINI_API_KEY`, `DASHSCOPE_API_KEY`, | |
| 113 | +`DEEPSEEK_API_KEY`, `MOONSHOT_API_KEY`, `PERPLEXITY_API_KEY`, `TOGETHER_API_KEY`, | |
| 114 | +`DEEPINFRA_API_KEY`, `CEREBRAS_API_KEY`. | |
| 115 | + | |
| 116 | +### 1.4 `OpenAICompatibleClient` — request/response design | |
| 117 | + | |
| 118 | +- **Endpoint:** `POST {base}/chat/completions` (path appended with `appendingPathComponent`, | |
| 119 | + preserving base paths like `/compatible-mode/v1` and `/v1beta/openai`); `GET {base}/models`. | |
| 120 | +- **Request wire types (private `Encodable` structs):** `WireRequest` (`model`, `messages`, | |
| 121 | + `stream`, `stream_options`, `temperature`, `top_p`, `max_tokens`, `max_completion_tokens`, | |
| 122 | + `frequency_penalty`, `presence_penalty`, `reasoning_effort`, `enable_thinking`), `WireMessage` | |
| 123 | + (`role` + `content`), `WireContent` (plain string OR parts array), `WirePart` | |
| 124 | + (`{"type":"text"}` / `{"type":"image_url","image_url":{"url":"data:…;base64,…"}}`). | |
| 125 | +- **Parameter gating:** every optional field is included **only if** the model's | |
| 126 | + `ParameterSupport` allows it (providers 400 on unknown/unsupported params). Notable: | |
| 127 | + `usesMaxCompletionTokens` → send `max_completion_tokens` instead of `max_tokens` | |
| 128 | + (OpenAI reasoning models, Kimi K-series, Cerebras); Mistral maps `reasoning_effort` | |
| 129 | + medium→"high", low→"none" (only accepts high/none); Qwen `enable_thinking` is only legal | |
| 130 | + when `stream:true`. | |
| 131 | +- **`stream_options: {"include_usage": true}`** is sent for openai/xai/gemini/deepseek/kimi/ | |
| 132 | + together/cerebras/custom; omitted for mistral (rejects unknown params), qwen, deepinfra, | |
| 133 | + perplexity (usage included automatically). | |
| 134 | +- **Response wire types (private `Decodable`):** `WireChunk` (`choices`, `usage`, plus | |
| 135 | + Perplexity `citations` + `search_results`), `WireChoice` (`delta` for streaming, `message` | |
| 136 | + for non-streaming, bare `text` for Together's completions-style streams, `finish_reason`), | |
| 137 | + `WireDelta` (`content`, `reasoning_content`, `reasoning`; custom `init(from:)` also decodes | |
| 138 | + Mistral's content-**array** ThinkChunk/TextChunk shape), `WireUsage` | |
| 139 | + (`prompt_tokens`/`completion_tokens`/`completion_tokens_details.reasoning_tokens`). | |
| 140 | +- **Streaming:** consumes `StreamingService.sseEvents`, stops on `data: [DONE]`, silently | |
| 141 | + tolerates undecodable keep-alive chunks, yields `.reasoningDelta` (from `reasoning_content` | |
| 142 | + or `reasoning`), `.textDelta` (from `delta.content` or `choice.text`), `.citations` (once, | |
| 143 | + Perplexity), `.usage`, then `.finished(reason: finish_reason)`. | |
| 144 | +- **`complete`:** if `parameterSupport.requiresStreaming` (Qwen qwq/qvq, several Together/ | |
| 145 | + DeepInfra-hosted models reject `stream:false`), it aggregates the stream instead | |
| 146 | + (`completeViaStream`); otherwise plain POST via `StreamingService.postJSON` (3 attempts, | |
| 147 | + exponential backoff on 429/5xx honoring `Retry-After`). | |
| 148 | +- **`listModelIDs`:** decodes `{"data":[{"id"}]}` OR Together's bare `[{"id"}]`; strips | |
| 149 | + Gemini's `models/` prefix. | |
| 150 | +- **Attachments:** text files are injected inline as fenced blocks; images become base64 | |
| 151 | + data-URI `image_url` parts (user messages on vision models only). | |
| 152 | + | |
| 153 | +### 1.5 `AnthropicClient` — native Messages API | |
| 154 | + | |
| 155 | +- **Endpoint:** `POST /v1/messages`; headers `x-api-key: <key>`, `anthropic-version: 2023-06-01`, | |
| 156 | + `Content-Type: application/json`. `GET /v1/models?limit=100` for listing. | |
| 157 | +- **Request:** `WireRequest` — `model`, **mandatory `max_tokens`** (default 8192 when unset), | |
| 158 | + `messages` (block-structured: `WireMessage{role, content:[WireBlock]}` with `.text` / | |
| 159 | + `.image(base64 source)` blocks; empty text becomes `" "`), top-level `system` string, | |
| 160 | + `stream`, `temperature`/`top_p` (gated — Claude 4.7+/5 reject them, encoded per-model in | |
| 161 | + `ParameterSupport`), and `thinking: {"type":"enabled","budget_tokens":8000}` / | |
| 162 | + `{"type":"disabled"}` when `thinkingToggle` is supported. (Cloud's PROVIDERS.md documents the | |
| 163 | + full per-model thinking matrix, incl. `{"type":"adaptive"}` for 4.6+ and "omit entirely" for | |
| 164 | + `claude-fable-5` where thinking is always on.) | |
| 165 | +- **Streaming (named SSE events, no `[DONE]`):** the client switches on | |
| 166 | + `sse.event ?? decoded.type`: | |
| 167 | + - `message_start` → capture `message.usage.input_tokens` | |
| 168 | + - `content_block_delta` → `delta.text` → `.textDelta`; `delta.thinking` → `.reasoningDelta` | |
| 169 | + - `message_delta` → `usage.output_tokens` + `delta.stop_reason` | |
| 170 | + - `error` → mid-stream error surfaced as `ProviderError.serverError` | |
| 171 | + - `message_stop` / `ping` / `content_block_start` / `content_block_stop` → currently ignored | |
| 172 | + (Agent's port MUST handle `content_block_start`/`stop` + `input_json_delta` — §5.1) | |
| 173 | +- **Non-streaming:** decodes `content: [Block{type,text,thinking}]`, joins `text` blocks into | |
| 174 | + the message body and `thinking` blocks into `reasoning`; reads `stop_reason` and `usage`. | |
| 175 | + | |
| 176 | +### 1.6 `StreamingService` — shared SSE plumbing (port unchanged) | |
| 177 | + | |
| 178 | +- Shared `URLSession`: `timeoutIntervalForRequest = 120`, `timeoutIntervalForResource = 900`, | |
| 179 | + `User-Agent: ZyquoCloud/1.0 (macOS)` (rename to `ZyquoAgent/1.0 (macOS)`). | |
| 180 | +- `SSEParser`: incremental line parser handling `event:`/`data:` fields, multi-line `data:` | |
| 181 | + joins, `:` comment/keep-alive lines (DeepSeek sends `: keep-alive`), CRLF, and a trailing | |
| 182 | + flush for streams ending without a final blank line. **Critical detail preserved:** | |
| 183 | + `URLSession.AsyncBytes.lines` skips empty lines (the SSE event separators) — the byte stream | |
| 184 | + is split manually on `\n`. | |
| 185 | +- `sseEvents(for:provider:)`: on non-2xx, reads the full error body and throws the typed | |
| 186 | + `ProviderError`; wraps `CancellationError` → `.cancelled`; `onTermination` cancels the task | |
| 187 | + (this is what makes the Stop button actually abort the HTTP stream — same mechanism will | |
| 188 | + make Agent runs cancellable). | |
| 189 | +- Unit tests exist in `Tests/ZyquoCloudTests/SSEParserTests.swift` — port them. | |
| 190 | + | |
| 191 | +--- | |
| 192 | + | |
| 193 | +## 2. The full model catalog (from `Services/ModelCatalogData.swift`, generated 2026-07-30) | |
| 194 | + | |
| 195 | +**170 built-in models across 12 providers.** Zyquo Agent ships this exact catalog. | |
| 196 | +Legend — caps: **V** vision, **T** tools/function-calling, **R** reasoning output, **J** JSON | |
| 197 | +mode, **C** citations; price = USD per 1M tokens in/out (— = not published); | |
| 198 | +⭐ = `isRecommended`, 🕰 = `isLegacy`; **🤖 = agent-capable** (the subset per §3); | |
| 199 | +**bold 🤖** entries are the suggested per-provider agent defaults. | |
| 200 | +Param notes: `mct` = uses `max_completion_tokens`, `re` = `reasoning_effort`, | |
| 201 | +`tt` = thinking toggle, `rs` = requiresStreaming, `no-t/p` = temperature & top_p rejected. | |
| 202 | + | |
| 203 | +### OpenAI (27) | |
| 204 | + | |
| 205 | +| Model ID | Display name | Ctx | Max out | Caps | $/1M | Params | Flags | Agent | | |
| 206 | +|---|---|---|---|---|---|---|---|---| | |
| 207 | +| `gpt-5.6-sol` | GPT-5.6 Sol | 1,050,000 | 128K | VTRJ | 5.00/30.00 | no-t/p, mct, re | ⭐ | 🤖 | | |
| 208 | +| `gpt-5.6-terra` | GPT-5.6 Terra | 1,050,000 | 128K | VTRJ | 2.50/15.00 | no-t/p, mct, re | ⭐ | **🤖 default** | | |
| 209 | +| `gpt-5.6-luna` | GPT-5.6 Luna | 1,050,000 | 128K | VTRJ | 1.00/6.00 | no-t/p, mct, re | | 🤖 | | |
| 210 | +| `chat-latest` | ChatGPT Latest | 128,000 | — | VTJ | 5.00/30.00 | mct | | — (rolling chat tuning) | | |
| 211 | +| `gpt-5.5` | GPT-5.5 | 400,000 | — | VTRJ | 5.00/30.00 | no-t/p, mct, re | | 🤖 | | |
| 212 | +| `gpt-5.4` | GPT-5.4 | 400,000 | 128K | VTRJ | 2.50/15.00 | no-t/p, mct, re | | 🤖 | | |
| 213 | +| `gpt-5.4-mini` | GPT-5.4 mini | 400,000 | — | VTRJ | 0.75/4.50 | no-t/p, mct, re | | 🤖 | | |
| 214 | +| `gpt-5.4-nano` | GPT-5.4 nano | 400,000 | — | VTRJ | 0.20/1.25 | no-t/p, mct, re | | — (nano tier, weak for deep agents) | | |
| 215 | +| `gpt-5.3-chat-latest` | GPT-5.3 Chat Latest | 128,000 | — | VTJ | — | mct | | — | | |
| 216 | +| `gpt-5.2` | GPT-5.2 | 400,000 | 128K | VTRJ | 1.75/14.00 | no-t/p, mct, re | | 🤖 | | |
| 217 | +| `gpt-5.2-chat-latest` | GPT-5.2 Chat Latest | 128,000 | 16K | VTJ | 1.75/14.00 | mct | | — | | |
| 218 | +| `gpt-5.1` | GPT-5.1 | 400,000 | 128K | VTRJ | 1.25/10.00 | no-t/p, mct, re | | 🤖 | | |
| 219 | +| `gpt-5` | GPT-5 | 400,000 | 128K | VTRJ | 1.25/10.00 | no-t/p, mct, re | | 🤖 | | |
| 220 | +| `gpt-5-mini` | GPT-5 mini | 400,000 | 128K | VTRJ | 0.25/2.00 | no-t/p, mct, re | | 🤖 | | |
| 221 | +| `gpt-5-nano` | GPT-5 nano | 400,000 | 128K | VTRJ | 0.05/0.40 | no-t/p, mct, re | | — | | |
| 222 | +| `o3` | OpenAI o3 | 200,000 | 100K | VTRJ | 2.00/8.00 | no-t/p, mct, re | | 🤖 | | |
| 223 | +| `o4-mini` | OpenAI o4-mini | 200,000 | 100K | VTRJ | 1.10/4.40 | no-t/p, mct, re | | 🤖 | | |
| 224 | +| `o3-mini` | OpenAI o3-mini | 200,000 | 100K | TRJ | 1.10/4.40 | no-t/p, mct, re | 🕰 | — | | |
| 225 | +| `o1` | OpenAI o1 | 200,000 | 100K | VTRJ | 15.00/60.00 | no-t/p, mct, re | 🕰 | — | | |
| 226 | +| `gpt-4.1` | GPT-4.1 | 1,047,576 | 32,768 | VTJ | 2.00/8.00 | openAIDefault | 🕰 | — | | |
| 227 | +| `gpt-4.1-mini` | GPT-4.1 mini | 1,047,576 | 32,768 | VTJ | 0.40/1.60 | openAIDefault | 🕰 | — | | |
| 228 | +| `gpt-4.1-nano` | GPT-4.1 nano | 1,047,576 | 32,768 | VTJ | 0.10/0.40 | openAIDefault | 🕰 | — | | |
| 229 | +| `gpt-4o` | GPT-4o | 128,000 | 16,384 | VTJ | 2.50/10.00 | openAIDefault | 🕰 | — | | |
| 230 | +| `gpt-4o-mini` | GPT-4o mini | 128,000 | 16,384 | VTJ | 0.15/0.60 | openAIDefault | 🕰 | — | | |
| 231 | +| `gpt-4-turbo` | GPT-4 Turbo | 128,000 | 4,096 | VTJ | 10.00/30.00 | openAIDefault | 🕰 | — | | |
| 232 | +| `gpt-4` | GPT-4 | 8,192 | 8,192 | T | 30.00/60.00 | openAIDefault | 🕰 | — | | |
| 233 | +| `gpt-3.5-turbo` | GPT-3.5 Turbo | 16,385 | 4,096 | TJ | 0.50/1.50 | openAIDefault | 🕰 | — | | |
| 234 | + | |
| 235 | +### Anthropic (11) | |
| 236 | + | |
| 237 | +| Model ID | Display name | Ctx | Max out | Caps | $/1M | Params | Flags | Agent | | |
| 238 | +|---|---|---|---|---|---|---|---|---| | |
| 239 | +| `claude-opus-5` | Claude Opus 5 | 1,000,000 | 128K | VTRJ | 5.00/25.00 | no-t/p, tt | ⭐ | 🤖 | | |
| 240 | +| `claude-sonnet-5` | Claude Sonnet 5 | 1,000,000 | 128K | VTRJ | 3.00/15.00 | no-t/p, tt | ⭐ | **🤖 default (and overall app default)** | | |
| 241 | +| `claude-fable-5` | Claude Fable 5 | 1,000,000 | 128K | VTRJ | 10.00/50.00 | no-t/p, thinking always on (no toggle) | | 🤖 | | |
| 242 | +| `claude-opus-4-8` | Claude Opus 4.8 | 1,000,000 | 128K | VTRJ | 5.00/25.00 | no-t/p, tt | | 🤖 | | |
| 243 | +| `claude-opus-4-7` | Claude Opus 4.7 | 1,000,000 | 128K | VTRJ | 5.00/25.00 | no-t/p, tt | | 🤖 | | |
| 244 | +| `claude-opus-4-6` | Claude Opus 4.6 | 1,000,000 | 128K | VTRJ | 5.00/25.00 | t/p ok, tt | | 🤖 | | |
| 245 | +| `claude-sonnet-4-6` | Claude Sonnet 4.6 | 1,000,000 | 128K | VTRJ | 3.00/15.00 | t/p ok, tt | | 🤖 | | |
| 246 | +| `claude-haiku-4-5-20251001` | Claude Haiku 4.5 | 200,000 | 64K | VTRJ | 1.00/5.00 | t/p ok, tt | | 🤖 (fast tier; also Cloud's auto-title model) | | |
| 247 | +| `claude-opus-4-5-20251101` | Claude Opus 4.5 | 200,000 | 64K | VTRJ | 5.00/25.00 | t/p ok, tt | 🕰 | — | | |
| 248 | +| `claude-sonnet-4-5-20250929` | Claude Sonnet 4.5 | 1,000,000 | 64K | VTRJ | 3.00/15.00 | t/p ok, tt | 🕰 | — | | |
| 249 | +| `claude-opus-4-1-20250805` | Claude Opus 4.1 | 200,000 | 32K | VTRJ | 15.00/75.00 | t/p ok, tt | 🕰 | — | | |
| 250 | + | |
| 251 | +### xAI / Grok (5) | |
| 252 | + | |
| 253 | +| Model ID | Display name | Ctx | Caps | $/1M | Params | Flags | Agent | | |
| 254 | +|---|---|---|---|---|---|---|---| | |
| 255 | +| `grok-4.5` | Grok 4.5 | 500,000 | VTRJ | 2.00/6.00 | re | ⭐ | **🤖 default** | | |
| 256 | +| `grok-4.3` | Grok 4.3 | 1,000,000 | VTRJ | 1.25/2.50 | re | | 🤖 | | |
| 257 | +| `grok-4.20` | Grok 4.20 Reasoning | 1,000,000 | VTRJ | 1.25/2.50 | | | 🤖 | | |
| 258 | +| `grok-4.20-non-reasoning` | Grok 4.20 Non-Reasoning | 1,000,000 | VTJ | 1.25/2.50 | | | 🤖 | | |
| 259 | +| `grok-code-fast-1` | Grok Code Fast 1 | 256,000 | VTRJ | 1.00/2.00 | | ⭐ | 🤖 (agentic-coding tuned) | | |
| 260 | + | |
| 261 | +### Mistral (10) | |
| 262 | + | |
| 263 | +| Model ID | Display name | Ctx | Caps | $/1M | Params | Flags | Agent | | |
| 264 | +|---|---|---|---|---|---|---|---| | |
| 265 | +| `mistral-medium-latest` | Mistral Medium 3.5 | 262,144 | VTRJ | 1.50/7.50 | pen, re | ⭐ | **🤖 default** | | |
| 266 | +| `mistral-large-latest` | Mistral Large 3 | 262,144 | VTJ | 0.50/1.50 | openAIDefault | ⭐ | 🤖 | | |
| 267 | +| `mistral-small-latest` | Mistral Small 4 | 262,144 | VTRJ | 0.15/0.60 | pen, re | ⭐ | 🤖 | | |
| 268 | +| `codestral-latest` | Codestral | 256,000 | TJ | 0.30/0.90 | openAIDefault | | — (FIM/code-completion focus) | | |
| 269 | +| `ministral-14b-latest` | Ministral 3 14B | 262,144 | VTJ | 0.20/0.20 | openAIDefault | | — (small) | | |
| 270 | +| `ministral-8b-latest` | Ministral 3 8B | 262,144 | VTJ | 0.15/0.15 | openAIDefault | | — | | |
| 271 | +| `ministral-3b-latest` | Ministral 3 3B | 131,072 | VTJ | 0.10/0.10 | openAIDefault | | — | | |
| 272 | +| `magistral-medium-latest` | Magistral Medium | 131,072 | TRJ | 2.00/5.00 | openAIDefault | 🕰 | — | | |
| 273 | +| `devstral-latest` | Devstral 2 | 262,144 | TJ | 0.40/2.00 | openAIDefault | 🕰 | — | | |
| 274 | +| `open-mistral-nemo` | Mistral Nemo | 131,072 | TJ | 0.15/0.15 | openAIDefault | 🕰 | — | | |
| 275 | + | |
| 276 | +### Google Gemini (14) — served via the OpenAI-compat endpoint | |
| 277 | + | |
| 278 | +| Model ID | Display name | Ctx | Max out | Caps | $/1M | Flags | Agent | | |
| 279 | +|---|---|---|---|---|---|---|---| | |
| 280 | +| `gemini-3.6-flash` | Gemini 3.6 Flash | 1,048,576 | 65,536 | VTRJ | 1.50/7.50 | ⭐ | **🤖 default** | | |
| 281 | +| `gemini-3.5-flash` | Gemini 3.5 Flash | 1,048,576 | 65,536 | VTRJ | 1.50/9.00 | | 🤖 | | |
| 282 | +| `gemini-3.5-flash-lite` | Gemini 3.5 Flash-Lite | 1,048,576 | 65,536 | VTRJ | 0.30/2.50 | ⭐ | 🤖 (budget tier) | | |
| 283 | +| `gemini-3.1-pro-preview` | Gemini 3.1 Pro (Preview) | 1,048,576 | 65,536 | VTRJ | 2.00/12.00 | ⭐ | 🤖 | | |
| 284 | +| `gemini-3.1-flash-lite` | Gemini 3.1 Flash-Lite | 1,048,576 | 65,536 | VTRJ | 0.25/1.50 | | — | | |
| 285 | +| `gemini-2.5-pro` | Gemini 2.5 Pro | 1,048,576 | 65,536 | VTRJ | 1.25/10.00 | | 🤖 | | |
| 286 | +| `gemini-2.5-flash` | Gemini 2.5 Flash | 1,048,576 | 65,536 | VTRJ | 0.30/2.50 | | 🤖 | | |
| 287 | +| `gemini-2.5-flash-lite` | Gemini 2.5 Flash-Lite | 1,048,576 | 65,536 | VTRJ | 0.10/0.40 | | — | | |
| 288 | +| `gemini-pro-latest` | Gemini Pro (Latest) | 1,048,576 | 65,536 | VTRJ | — | | 🤖 (rolling alias) | | |
| 289 | +| `gemini-flash-latest` | Gemini Flash (Latest) | 1,048,576 | 65,536 | VTRJ | — | | 🤖 (rolling alias) | | |
| 290 | +| `gemini-flash-lite-latest` | Gemini Flash-Lite (Latest) | 1,048,576 | 65,536 | VTRJ | — | | — | | |
| 291 | +| `gemini-3-flash-preview` | Gemini 3 Flash (Preview) | 1,048,576 | 65,536 | VTRJ | 0.50/3.00 | | — | | |
| 292 | +| `gemma-4-26b-a4b-it` | Gemma 4 26B | 262,144 | 32,768 | J | — | | **excluded — no tools** | | |
| 293 | +| `gemma-4-31b-it` | Gemma 4 31B | 262,144 | 32,768 | J | — | | **excluded — no tools** | | |
| 294 | + | |
| 295 | +All Gemini chat models take `reasoning_effort` on the compat endpoint. | |
| 296 | + | |
| 297 | +### Alibaba Qwen / DashScope (32) | |
| 298 | + | |
| 299 | +| Model ID | Display name | Ctx | Caps | $/1M | Params | Flags | Agent | | |
| 300 | +|---|---|---|---|---|---|---|---| | |
| 301 | +| `qwen3.7-max` | Qwen3.7 Max | 1,000,000 | TRJ | 2.50/7.50 | tt | ⭐ | **🤖 default** | | |
| 302 | +| `qwen3.7-plus` | Qwen3.7 Plus | 1,000,000 | VTRJ | 0.32/1.28 | tt | ⭐ | 🤖 | | |
| 303 | +| `qwen3.7-flash` | Qwen3.7 Flash | 1,000,000 | VTRJ | 0.03/0.13 | tt | ⭐ | 🤖 (budget) | | |
| 304 | +| `qwen3.6-plus` | Qwen3.6 Plus | 1,000,000 | VTRJ | — | tt | | 🤖 | | |
| 305 | +| `qwen3.6-flash` | Qwen3.6 Flash | 1,000,000 | VTRJ | — | tt | | — | | |
| 306 | +| `qwen3.5-plus` | Qwen3.5 Plus | 1,000,000 | VTRJ | — | tt | | 🤖 | | |
| 307 | +| `qwen3.5-flash` | Qwen3.5 Flash | 1,000,000 | VTRJ | — | tt | | — | | |
| 308 | +| `qwen-max` | Qwen Max | 128,000 | TRJ | — | tt | | — | | |
| 309 | +| `qwen-plus` | Qwen Plus | 1,000,000 | TRJ | — | tt | | — | | |
| 310 | +| `qwen-turbo` | Qwen Turbo | 1,000,000 | TRJ | — | tt | 🕰 | — | | |
| 311 | +| `qwen-flash` | Qwen Flash | 1,000,000 | TRJ | — | tt | | — | | |
| 312 | +| `qwen3-coder-plus` | Qwen3 Coder Plus | 1,000,000 | TJ | — | | | 🤖 (agentic-coding tuned) | | |
| 313 | +| `qwen3-coder-flash` | Qwen3 Coder Flash | 1,000,000 | TJ | — | | | 🤖 | | |
| 314 | +| `qwen3-coder-next` | Qwen3 Coder Next | 262,144 | TJ | — | | | 🤖 ("multi-turn tool interactions") | | |
| 315 | +| `qwen3-coder-480b-a35b-instruct` | Qwen3 Coder 480B A35B | 262,144 | TJ | — | | | 🤖 | | |
| 316 | +| `qwen3-vl-plus` | Qwen3 VL Plus | 1,000,000 | VTRJ | — | tt | | — (vision-focused) | | |
| 317 | +| `qwen3-vl-flash` | Qwen3 VL Flash | 1,000,000 | VTRJ | — | tt | | — | | |
| 318 | +| `qwen3-vl-235b-a22b-instruct` | Qwen3 VL 235B Instruct | 131,072 | VTJ | — | | | — | | |
| 319 | +| `qwen3-vl-235b-a22b-thinking` | Qwen3 VL 235B Thinking | 131,072 | VTRJ | — | | | — | | |
| 320 | +| `qvq-max` | QVQ Max | 131,072 | VRJ | — | rs | | **excluded — no tools** | | |
| 321 | +| `qwq-plus` | QwQ Plus | 131,072 | TRJ | — | rs | | — (requiresStreaming; tool-calls unreliable) | | |
| 322 | +| `qwen3.5-397b-a17b` | Qwen3.5 397B A17B | 262,144 | TRJ | — | tt | | 🤖 | | |
| 323 | +| `qwen3.5-122b-a10b` | Qwen3.5 122B A10B | 262,144 | TRJ | — | tt | | — | | |
| 324 | +| `qwen3.5-35b-a3b` | Qwen3.5 35B A3B | 262,144 | TRJ | — | tt | | — | | |
| 325 | +| `qwen3-235b-a22b-instruct-2507` | Qwen3 235B Instruct 2507 | 262,144 | TJ | — | | | — | | |
| 326 | +| `qwen3-235b-a22b-thinking-2507` | Qwen3 235B Thinking 2507 | 262,144 | TRJ | — | | | — | | |
| 327 | +| `qwen3-next-80b-a3b-instruct` | Qwen3 Next 80B Instruct | 262,144 | TJ | — | | | — | | |
| 328 | +| `qwen3-next-80b-a3b-thinking` | Qwen3 Next 80B Thinking | 262,144 | TRJ | — | | | — | | |
| 329 | +| `deepseek-v4-pro` | DeepSeek V4 Pro (DashScope) | 1,000,000 | TRJ | — | tt | | 🤖 | | |
| 330 | +| `deepseek-v4-flash` | DeepSeek V4 Flash (DashScope) | 1,000,000 | TRJ | — | tt | | — (prefer first-party DeepSeek) | | |
| 331 | +| `glm-5.2` | GLM 5.2 (DashScope) | 198,000 | TRJ | — | tt | | 🤖 | | |
| 332 | +| `kimi-k2.7-code` | Kimi K2.7 Code (DashScope) | 262,144 | TRJ | — | tt | | — (prefer first-party Kimi) | | |
| 333 | + | |
| 334 | +### DeepSeek (2) | |
| 335 | + | |
| 336 | +| Model ID | Display name | Ctx | Max out | Caps | $/1M | Params | Flags | Agent | | |
| 337 | +|---|---|---|---|---|---|---|---|---| | |
| 338 | +| `deepseek-v4-flash` | DeepSeek V4 Flash | 1,000,000 | 384K | TRJ | 0.14/0.28 | re, tt | ⭐ | 🤖 | | |
| 339 | +| `deepseek-v4-pro` | DeepSeek V4 Pro | 1,000,000 | 384K | TRJ | 0.435/0.87 | re, tt | ⭐ | **🤖 default** | | |
| 340 | + | |
| 341 | +Both support up to 128 functions per request; `finish_reason` may be the DeepSeek-specific | |
| 342 | +`insufficient_system_resource` ("servers overloaded"). | |
| 343 | + | |
| 344 | +### Kimi / Moonshot (12) | |
| 345 | + | |
| 346 | +| Model ID | Display name | Ctx | Max out | Caps | $/1M | Params | Flags | Agent | | |
| 347 | +|---|---|---|---|---|---|---|---|---| | |
| 348 | +| `kimi-k3` | Kimi K3 | 1,048,576 | 131,072 | VTRJ | 3.00/15.00 | no-t/p, mct, re (thinking always on) | ⭐ | **🤖 default** | | |
| 349 | +| `kimi-k2.7-code` | Kimi K2.7 Code | 262,144 | — | VTRJ | 0.95/4.00 | no-t/p, mct | ⭐ | 🤖 (agentic-coding tuned) | | |
| 350 | +| `kimi-k2.7-code-highspeed` | Kimi K2.7 Code Highspeed | 262,144 | — | VTRJ | 1.90/8.00 | no-t/p, mct | | 🤖 | | |
| 351 | +| `kimi-k2.6` | Kimi K2.6 | 262,144 | — | VTRJ | 0.95/4.00 | no-t/p, mct, tt | | 🤖 | | |
| 352 | +| `kimi-k2.5` | Kimi K2.5 | 262,144 | — | VTRJ | 0.60/3.00 | no-t/p, mct, tt | | 🤖 | | |
| 353 | +| `moonshot-v1-8k` | Moonshot v1 8K | 8,192 | — | TJ | 0.20/2.00 | openAIDefault | 🕰 | — | | |
| 354 | +| `moonshot-v1-32k` | Moonshot v1 32K | 32,768 | — | TJ | 1.00/3.00 | openAIDefault | 🕰 | — | | |
| 355 | +| `moonshot-v1-128k` | Moonshot v1 128K | 131,072 | — | TJ | 2.00/5.00 | openAIDefault | 🕰 | — | | |
| 356 | +| `moonshot-v1-auto` | Moonshot v1 Auto | 131,072 | — | TJ | — | openAIDefault | 🕰 | — | | |
| 357 | +| `moonshot-v1-8k-vision-preview` | Moonshot v1 8K Vision | 8,192 | — | VTJ | 0.20/2.00 | openAIDefault | 🕰 | — | | |
| 358 | +| `moonshot-v1-32k-vision-preview` | Moonshot v1 32K Vision | 32,768 | — | VTJ | 1.00/3.00 | openAIDefault | 🕰 | — | | |
| 359 | +| `moonshot-v1-128k-vision-preview` | Moonshot v1 128K Vision | 131,072 | — | VTJ | 2.00/5.00 | openAIDefault | 🕰 | — | | |
| 360 | + | |
| 361 | +### Perplexity (4) — **entire provider excluded from the agent-capable subset** | |
| 362 | + | |
| 363 | +| Model ID | Display name | Ctx | Caps | $/1M | Flags | Agent | | |
| 364 | +|---|---|---|---|---|---|---| | |
| 365 | +| `sonar` | Sonar | 128,000 | JC | 1.00/1.00 | ⭐ | **excluded** | | |
| 366 | +| `sonar-pro` | Sonar Pro | 200,000 | JC | 3.00/15.00 | ⭐ | **excluded** | | |
| 367 | +| `sonar-reasoning-pro` | Sonar Reasoning Pro | 128,000 | RJC | 2.00/8.00 | | **excluded** | | |
| 368 | +| `sonar-deep-research` | Sonar Deep Research | 128,000 | RJC | 2.00/8.00 | | **excluded** | | |
| 369 | + | |
| 370 | +Cloud's research is explicit: "no vision/image input; **no tool/function calling on the Sonar | |
| 371 | +chat API**". These are web-search answer engines. Keep them in the picker (same list as Cloud) | |
| 372 | +but never selectable as the agent driver — or gray them out with an explanation. | |
| 373 | + | |
| 374 | +### Together AI (16) | |
| 375 | + | |
| 376 | +| Model ID | Display name | Ctx | Caps | $/1M | Params | Flags | Agent | | |
| 377 | +|---|---|---|---|---|---|---|---| | |
| 378 | +| `moonshotai/Kimi-K3` | Kimi K3 | 1,000,000 | TRJ | 3.00/15.00 | openAIDefault | ⭐ | 🤖 | | |
| 379 | +| `moonshotai/Kimi-K2.7-Code` | Kimi K2.7 Code | 262,144 | TRJ | 0.95/4.00 | openAIDefault | | 🤖 | | |
| 380 | +| `moonshotai/Kimi-K2.6` | Kimi K2.6 | 262,144 | TRJ | 1.20/4.50 | openAIDefault | | — | | |
| 381 | +| `deepseek-ai/DeepSeek-V4-Pro` | DeepSeek V4 Pro | 512,000 | TRJ | 1.74/3.48 | openAIDefault | ⭐ | **🤖 default** | | |
| 382 | +| `zai-org/GLM-5.2` | GLM 5.2 | 512,000 | TRJ | 1.40/4.40 | openAIDefault | | 🤖 | | |
| 383 | +| `Qwen/Qwen3.7-Max` | Qwen3.7 Max | 1,000,000 | TRJ | 1.25/3.75 | pen, rs | | 🤖 | | |
| 384 | +| `Qwen/Qwen3.7-Plus` | Qwen3.7 Plus | 1,000,000 | TJ | 0.32/1.28 | pen, rs | | — | | |
| 385 | +| `Qwen/Qwen3.6-Plus` | Qwen3.6 Plus | 1,000,000 | TJ | 0.50/3.00 | pen, rs | | — | | |
| 386 | +| `Qwen/Qwen3.5-9B` | Qwen3.5 9B | 262,144 | TJ | 0.17/0.25 | pen, rs | | — (small) | | |
| 387 | +| `meta-llama/Llama-3.3-70B-Instruct-Turbo` | Llama 3.3 70B Turbo | 131,072 | TJ | 1.04/1.04 | openAIDefault | | — | | |
| 388 | +| `openai/gpt-oss-120b` | GPT-OSS 120B | 131,072 | TRJ | 0.15/0.60 | pen, re | ⭐ | 🤖 | | |
| 389 | +| `openai/gpt-oss-20b` | GPT-OSS 20B | 131,072 | TRJ | 0.05/0.20 | pen, re | | — (may hallucinate tool calls — Cloud note) | | |
| 390 | +| `nvidia/nemotron-3-ultra-550b-a55b` | Nemotron 3 Ultra 550B | 512,288 | TRJ | 0.60/3.60 | openAIDefault | | 🤖 | | |
| 391 | +| `MiniMaxAI/MiniMax-M3` | MiniMax M3 | 524,288 | TRJ | 0.30/1.20 | openAIDefault | | 🤖 | | |
| 392 | +| `google/gemma-4-31B-it` | Gemma 4 31B | 262,144 | TJ (vision disabled — live-verified empty answers) | 0.39/0.97 | pen, rs | | — | | |
| 393 | +| `thinkingmachines/Inkling` | Inkling | 524,288 | TRJ | 1.00/4.05 | openAIDefault | | — | | |
| 394 | + | |
| 395 | +Together quirks preserved in the client: bare-array `/models`, completions-style | |
| 396 | +`choices[].text` streaming for some models, extra `finish_reason: "eos"`. | |
| 397 | + | |
| 398 | +### DeepInfra (34) | |
| 399 | + | |
| 400 | +| Model ID | Display name | Ctx | Caps | $/1M | Flags | Agent | | |
| 401 | +|---|---|---|---|---|---|---| | |
| 402 | +| `anthropic/claude-fable-5` | Claude Fable 5 | 1,000,000 | VTRJ | 10.00/50.00 | | 🤖 | | |
| 403 | +| `anthropic/claude-opus-5` | Claude Opus 5 | 1,000,000 | VTRJ | 5.00/25.00 | | 🤖 | | |
| 404 | +| `anthropic/claude-sonnet-5` | Claude Sonnet 5 | 1,000,000 | VTRJ | 2.00/10.00 | | 🤖 | | |
| 405 | +| `anthropic/claude-opus-4-8` | Claude Opus 4.8 | 1,000,000 | VTRJ | 5.00/25.00 | | 🤖 | | |
| 406 | +| `anthropic/claude-haiku-4-5` | Claude Haiku 4.5 | 200,000 | VTRJ | 1.00/5.00 | | 🤖 | | |
| 407 | +| `google/gemini-3.1-pro` | Gemini 3.1 Pro | 1,000,000 | VTRJ | 2.00/12.00 | | 🤖 | | |
| 408 | +| `google/gemini-3.5-flash` | Gemini 3.5 Flash | 1,000,000 | VTRJ | 1.50/9.00 | | 🤖 | | |
| 409 | +| `google/gemini-3.1-flash-lite` | Gemini 3.1 Flash-Lite | 1,000,000 | VTJ | 0.25/1.50 | | — | | |
| 410 | +| `google/gemini-2.5-pro` | Gemini 2.5 Pro | 1,000,000 | VTRJ | 1.25/10.00 | | — | | |
| 411 | +| `google/gemini-2.5-flash` | Gemini 2.5 Flash | 1,000,000 | VTRJ | 0.30/2.50 | | — | | |
| 412 | +| `deepseek-ai/DeepSeek-V4-Pro` | DeepSeek V4 Pro | 1,048,576 | TRJ | 1.30/2.60 | ⭐ | **🤖 default** | | |
| 413 | +| `deepseek-ai/DeepSeek-V4-Flash` | DeepSeek V4 Flash | 1,048,576 | TJ | 0.09/0.18 | ⭐ | 🤖 | | |
| 414 | +| `deepseek-ai/DeepSeek-V3.1` | DeepSeek V3.1 | 163,840 | TRJ | 0.25/0.95 | | — | | |
| 415 | +| `deepseek-ai/DeepSeek-R1-0528` | DeepSeek R1 0528 | 163,840 | R (no tools) | 0.50/2.15 | | **excluded — no tools** | | |
| 416 | +| `moonshotai/Kimi-K2.7-Code` | Kimi K2.7 Code | 262,144 | TRJ | 0.74/3.50 | | 🤖 | | |
| 417 | +| `moonshotai/Kimi-K2.6` | Kimi K2.6 | 262,144 | TRJ | 0.75/3.50 | | — | | |
| 418 | +| `moonshotai/Kimi-K2.5` | Kimi K2.5 | 262,144 | TJ (rs) | 0.45/2.25 | | — | | |
| 419 | +| `zai-org/GLM-5.2` | GLM 5.2 | 1,048,576 | TRJ | 0.75/2.40 | ⭐ | 🤖 | | |
| 420 | +| `zai-org/GLM-4.7` | GLM 4.7 | 202,752 | TRJ | 0.40/1.75 | | — | | |
| 421 | +| `Qwen/Qwen3.7-Max` | Qwen3.7 Max | 256,000 | TRJ | 2.50/7.50 | | 🤖 | | |
| 422 | +| `Qwen/Qwen3.5-397B-A17B` | Qwen3.5 397B A17B | 262,144 | TRJ | 0.45/3.00 | | — | | |
| 423 | +| `Qwen/Qwen3-235B-A22B-Instruct-2507` | Qwen3 235B Instruct 2507 | 262,144 | TJ | 0.09/0.55 | | — | | |
| 424 | +| `Qwen/Qwen3-235B-A22B-Thinking-2507` | Qwen3 235B Thinking 2507 | 262,144 | TRJ | 0.23/2.30 | | — | | |
| 425 | +| `Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo` | Qwen3 Coder 480B Turbo | 262,144 | TJ | 0.30/1.00 | | 🤖 | | |
| 426 | +| `Qwen/Qwen3-VL-235B-A22B-Instruct` | Qwen3 VL 235B | 262,144 | VTJ | 0.20/0.88 | | — | | |
| 427 | +| `meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8` | Llama 4 Maverick | 1,048,576 | VTJ | 0.20/0.80 | | — | | |
| 428 | +| `meta-llama/Llama-4-Scout-17B-16E-Instruct` | Llama 4 Scout | 327,680 | VTJ | 0.10/0.30 | | — | | |
| 429 | +| `meta-llama/Llama-3.3-70B-Instruct-Turbo` | Llama 3.3 70B Turbo | 131,072 | TJ | 0.10/0.32 | | — | | |
| 430 | +| `meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo` | Llama 3.1 8B Turbo | 131,072 | TJ | 0.02/0.04 | | — | | |
| 431 | +| `openai/gpt-oss-120b` | GPT-OSS 120B | 131,072 | TRJ | 0.037/0.17 | ⭐ | 🤖 | | |
| 432 | +| `openai/gpt-oss-20b` | GPT-OSS 20B | 131,072 | TRJ | 0.03/0.14 | | — | | |
| 433 | +| `MiniMaxAI/MiniMax-M3` | MiniMax M3 | 524,288 | TRJ | 0.30/1.20 | | 🤖 | | |
| 434 | +| `nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B` | Nemotron 3 Ultra 550B | 262,144 | TRJ | 0.50/2.20 | | — | | |
| 435 | +| `mistralai/Mistral-Small-3.2-24B-Instruct-2506` | Mistral Small 3.2 24B | 128,000 | VTJ | 0.075/0.20 | | — | | |
| 436 | + | |
| 437 | +(Note: `google/gemma-4-31B-it` was removed from DeepInfra on 2026-07-30 — endpoint hangs. | |
| 438 | +DeepInfra-proxied Claude/Gemini speak the plain OpenAI schema, no native thinking config.) | |
| 439 | + | |
| 440 | +### Cerebras (3) | |
| 441 | + | |
| 442 | +| Model ID | Display name | Ctx | Max out | Caps | $/1M | Params | Flags | Agent | | |
| 443 | +|---|---|---|---|---|---|---|---|---| | |
| 444 | +| `gpt-oss-120b` | GPT-OSS 120B | 131,072 | 40,000 | TRJ | 0.35/0.75 | pen, mct, re | ⭐ | **🤖 default** (~3,000 tok/s — extremely fast loops) | | |
| 445 | +| `gemma-4-31b` | Gemma 4 31B | 131,072 | 40,000 | VTRJ | 0.99/1.49 | pen, mct, re | | 🤖 (parallel tools, strict schemas) | | |
| 446 | +| `zai-glm-4.7` | GLM 4.7 | 131,072 | 40,000 | TRJ | 2.25/2.75 | pen, mct, re | 🕰 | — (discontinued 2026-08-17) | | |
| 447 | + | |
| 448 | +--- | |
| 449 | + | |
| 450 | +## 3. The agent-capable subset (what Zyquo Agent marks and defaults to) | |
| 451 | + | |
| 452 | +**Criteria** (from the catalog's capability flags + Cloud's per-provider research): | |
| 453 | +1. `capabilities.tools == true` — native function calling, live-verified by Cloud; | |
| 454 | +2. strong multi-step reasoning (flagship/frontier tier, or agentic-coding tuned); | |
| 455 | +3. context window ≥ ~128K (agent transcripts with tool results grow fast); | |
| 456 | +4. reliable streaming of tool-call arguments; | |
| 457 | +5. not legacy, not a niche tuning (chat-alias, vision-only, search-only). | |
| 458 | + | |
| 459 | +**Counts:** ~70 of the 170 catalog models are marked 🤖 agent-capable (per-model marks in the | |
| 460 | +§2 tables). Everything else remains in the picker (same list as Cloud) but is visually | |
| 461 | +de-emphasized and produces a "not recommended for agent tasks" hint if selected. | |
| 462 | + | |
| 463 | +**Overall default agent model: `claude-sonnet-5` (Anthropic)** — 1M context, adaptive thinking, | |
| 464 | +best-in-class tool use, and the Messages API's tool_use blocks are the most explicit | |
| 465 | +tool-calling contract of the twelve providers. Per-provider defaults are bolded in §2. | |
| 466 | + | |
| 467 | +**Recommended top tier** (surface first in the model chip): `claude-sonnet-5`, `claude-opus-5`, | |
| 468 | +`gpt-5.6-terra`, `gpt-5.6-sol`, `gemini-3.6-flash`, `grok-4.5`, `grok-code-fast-1`, | |
| 469 | +`deepseek-v4-pro`, `kimi-k3`, `kimi-k2.7-code`, `qwen3.7-max`, `mistral-medium-latest`, | |
| 470 | +Cerebras `gpt-oss-120b` (speed king for tight loops). | |
| 471 | + | |
| 472 | +**Excluded from agent use, with reasons:** | |
| 473 | + | |
| 474 | +| Exclusion | Reason | | |
| 475 | +|---|---| | |
| 476 | +| Perplexity — all 4 sonar models | No function calling at all on the Sonar chat API (Cloud, live-verified); search answer engines, not agents | | |
| 477 | +| `gemma-4-26b-a4b-it`, `gemma-4-31b-it` (Gemini) | `tools: false` in catalog; function calling unverified for open Gemma on the Gemini API | | |
| 478 | +| `qvq-max` (Qwen) | `tools: false`; visual-reasoning only; requiresStreaming | | |
| 479 | +| `deepseek-ai/DeepSeek-R1-0528` (DeepInfra) | `tools: false` in catalog — R1 has no reliable function calling | | |
| 480 | +| `qwq-plus` (Qwen) | requiresStreaming + first-gen reasoning; tool-calling reliability poor | | |
| 481 | +| All `isLegacy` models (gpt-4/4o/4.1 family, o1/o3-mini, moonshot-v1 classic, Claude 4.5/4.1, magistral/devstral/nemo, zai-glm-4.7@Cerebras) | Superseded; weaker tool use; several deprecate mid-2026 | | |
| 482 | +| Chat-tuned rolling aliases (`chat-latest`, `gpt-5.x-chat-latest`) | Conversational tuning, no reasoning; not built for long agentic runs | | |
| 483 | +| nano/small tiers (`gpt-5.4-nano`, `gpt-5-nano`, ministral 3/8/14B, `Qwen3.5-9B`, `gpt-oss-20b`, Llama 3.x) | Function calling exists but multi-step planning reliability is inadequate for "deep agentic" work (gpt-oss-20b documented by Cerebras/Together as prone to hallucinated tool calls) | | |
| 484 | +| VL-focused Qwen models | Optimized for image understanding, not command loops | | |
| 485 | +| `codestral-latest`, `thinkingmachines/Inkling`, Together `google/gemma-4-31B-it` | Code-completion / unverified-tooling niches | | |
| 486 | + | |
| 487 | +All exclusions are *soft* (UI de-emphasis + default filter), except Perplexity + `tools:false` | |
| 488 | +models which are *hard* exclusions — the agent loop refuses to start with a model whose | |
| 489 | +`capabilities.tools == false`. | |
| 490 | + | |
| 491 | +**Phase 7 contract:** every 🤖 model above gets the scripted (a) schema-receipt, (b) valid | |
| 492 | +tool call for "list the files in the workspace using the shell tool", (c) tool_result | |
| 493 | +consumption, (d) streaming test — following the `VerifyHarness` pattern | |
| 494 | +(`Sources/ZyquoCloud/Verify/VerifyHarness.swift`): env keys, per-model results table, | |
| 495 | +written to `docs/VERIFICATION.md`. | |
| 496 | + | |
| 497 | +--- | |
| 498 | + | |
| 499 | +## 4. SecureKeyStore — the AES-256-GCM vault (NO Keychain), reused verbatim | |
| 500 | + | |
| 501 | +Source: `zyquo-cloud/Sources/ZyquoCloud/Services/SecureKeyStore.swift` | |
| 502 | +(+ tests in `Tests/ZyquoCloudTests/SecureKeyStoreTests.swift`). Port both. | |
| 503 | + | |
| 504 | +- **File:** `~/Library/Application Support/ZyquoCloud/vault.zq` → Agent uses | |
| 505 | + `~/Library/Application Support/ZyquoAgent/vault.zq` (via `PersistenceService.shared.rootDirectory`, | |
| 506 | + which is the app-support folder named after the app). | |
| 507 | +- **Blob layout:** `[salt 32 B][AES-GCM combined: nonce 12 B ‖ ciphertext ‖ tag 16 B]`. | |
| 508 | + Written atomically with `.completeFileProtection`. Load validates | |
| 509 | + `count > 32 + 12 + 16` and throws `VaultError.corrupted` on any decrypt failure. | |
| 510 | +- **Plaintext:** JSON `[String: String]` — provider `rawValue` → API key | |
| 511 | + (`{"openai":"sk-…","anthropic":"sk-ant-…", …}`). | |
| 512 | +- **Key derivation:** `HKDF<SHA256>.deriveKey(ikm: machineEntropy ‖ pepper, salt: vault salt, | |
| 513 | + info: "ZyquoCloud.vault.v1", outputByteCount: 32)` → `AES.GCM` `SymmetricKey`. | |
| 514 | + - `machineEntropy` = IOPlatformUUID (IOKit `IOPlatformExpertDevice` / | |
| 515 | + `kIOPlatformUUIDKey`) ‖ `NSHomeDirectory()` — binds the vault to machine + account. | |
| 516 | + Injectable closure for tests. | |
| 517 | + - `pepper` = 30 compiled-in bytes XOR `0x5A`, assembled at runtime (never a literal in | |
| 518 | + the binary). | |
| 519 | + - Re-saves reuse the **existing salt** (`existingSalt()`) so derivation stays stable. | |
| 520 | +- **API:** `loadKeys()`, `saveKeys(_:)`, `key(for: ProviderID)`, `setKey(_:for:)`, | |
| 521 | + `deleteKey(for:)`, `static redacted(_:)` → `"••••abcd"` (last 4 chars only, everywhere in UI). | |
| 522 | +- **Agent decisions:** keep the **same design and blob format**; change only the HKDF `info` | |
| 523 | + string to `"ZyquoAgent.vault.v1"` and (recommended) a distinct pepper — the two apps keep | |
| 524 | + separate vaults with an identical user experience; keys are entered per-app (a Cloud vault | |
| 525 | + cannot be decrypted by Agent by design — machine-bound, app-info-bound). Keep the rules: | |
| 526 | + decrypt on demand only, never log, never plaintext on disk, no Keychain. | |
| 527 | +- The SwiftUI wrapper `ViewModels/KeyVaultStore.swift` (per-provider key status, test-key | |
| 528 | + latency chip) is also reusable for the Settings → Providers & Keys tab. | |
| 529 | + | |
| 530 | +--- | |
| 531 | + | |
| 532 | +## 5. Provider-specific tool-calling quirks (what the normalized interface must absorb) | |
| 533 | + | |
| 534 | +Zyquo Cloud declares `capabilities.tools` per model but **never sends tools** — Zyquo Agent | |
| 535 | +adds that. Cloud's `docs/PROVIDERS.md` already documents each provider's tool wire format | |
| 536 | +(request schema sections + finish_reason inventories). Consolidated contract: | |
| 537 | + | |
| 538 | +### 5.1 Anthropic (native Messages API) — the odd one out | |
| 539 | + | |
| 540 | +- **Declare:** top-level `tools: [{name, description, input_schema: <JSON Schema>}]`; | |
| 541 | + `tool_choice: {"type":"auto"|"any"|"tool","name":…}` (+ `disable_parallel_tool_use`). | |
| 542 | +- **Model calls a tool:** assistant `content` contains a | |
| 543 | + `{"type":"tool_use","id":"toolu_…","name":…,"input":{…}}` block; `stop_reason == "tool_use"`. | |
| 544 | +- **Streaming:** `content_block_start` announces the `tool_use` block (with `id` + `name`, | |
| 545 | + empty input); arguments then stream as `content_block_delta` with | |
| 546 | + `{"type":"input_json_delta","partial_json":"…"}` fragments — accumulate per block `index` | |
| 547 | + and JSON-parse at `content_block_stop`. (Cloud's client currently ignores | |
| 548 | + `content_block_start/stop` and `input_json_delta` — the port must add these three cases.) | |
| 549 | +- **Return the result:** append a **user** message whose content is | |
| 550 | + `[{"type":"tool_result","tool_use_id":"toolu_…","content":"…", "is_error":bool}]` — | |
| 551 | + NOT a special role. Multiple tool_results go in one user turn for parallel calls. | |
| 552 | + Assistant `thinking` blocks must be passed back **unchanged** on the same model. | |
| 553 | +- **Done signal:** `stop_reason: "end_turn"` (also `max_tokens`, `stop_sequence`, | |
| 554 | + `pause_turn`, `refusal` — Fable 5/Opus 5 can refuse with HTTP 200 — | |
| 555 | + `model_context_window_exceeded`). No `[DONE]` sentinel; stream ends at `message_stop`. | |
| 556 | +- **Other quirks (already in Cloud's code/notes):** `max_tokens` mandatory; per-model | |
| 557 | + thinking-config matrix (adaptive vs enabled+budget vs omit-for-Fable-5); temperature/top_p | |
| 558 | + rejected on 4.7+/5; HTTP 529 `overloaded_error` retry; mid-stream `error` events. | |
| 559 | + | |
| 560 | +### 5.2 OpenAI-compatible family (OpenAI, xAI, Mistral, Gemini-compat, Qwen, DeepSeek, Kimi, Together, DeepInfra, Cerebras, custom) | |
| 561 | + | |
| 562 | +- **Declare:** `tools: [{"type":"function","function":{name, description, parameters: <JSON Schema>}}]`; | |
| 563 | + `tool_choice: "none"|"auto"|"required"|{"type":"function","function":{"name":…}}` | |
| 564 | + (Mistral additionally accepts `"any"`; default `parallel_tool_calls: true` on | |
| 565 | + Mistral/Cerebras; Cerebras supports `strict: true` schemas). | |
| 566 | +- **Model calls tools:** assistant message carries | |
| 567 | + `tool_calls: [{id, type:"function", function:{name, arguments: "<JSON string>"}}]`; | |
| 568 | + `finish_reason == "tool_calls"`. | |
| 569 | +- **Streaming deltas:** `choices[].delta.tool_calls` is an array of **index-keyed fragments**: | |
| 570 | + first fragment for an index carries `id` + `function.name`, subsequent ones carry | |
| 571 | + `function.arguments` string chunks. Accumulate per `index`, then JSON-parse each arguments | |
| 572 | + string when the stream finishes (`finish_reason:"tool_calls"` or `[DONE]`). | |
| 573 | +- **Return the result:** append the assistant message **with its `tool_calls` array intact**, | |
| 574 | + then one message per call: `{"role":"tool","tool_call_id":…, "content":"<result string>"}` | |
| 575 | + (Kimi/DeepSeek/Mistral all follow this; `name` optionally included). | |
| 576 | +- **Done signal:** `finish_reason: "stop"` (`length`, `content_filter`; Together adds `eos`; | |
| 577 | + DeepSeek adds `insufficient_system_resource`); stream terminates with `data: [DONE]`. | |
| 578 | +- **Per-provider notes already encoded or documented by Cloud:** | |
| 579 | + - **OpenAI:** reasoning models need `max_completion_tokens` and reject sampling params; | |
| 580 | + ignore unknown `obfuscation` field in chunks. | |
| 581 | + - **Gemini (compat):** tools/function-calling officially supported on the compat endpoint; | |
| 582 | + ignore `extra_content`/`thought_signature` extras in deltas; `/models` IDs prefixed | |
| 583 | + `models/`; Google Search grounding available via `tools` on Gemini 3+ (don't mix with | |
| 584 | + function tools unless verified). | |
| 585 | + - **Mistral:** content can be an **array** of ThinkChunk/TextChunk objects (handled in | |
| 586 | + `WireDelta.init(from:)`); `tool_choice: "any"`; `reasoning_effort` only high/none. | |
| 587 | + - **Qwen/DashScope:** `enable_thinking` only with `stream:true`; `reasoning_content` | |
| 588 | + deltas; qwq/qvq reject non-streaming. | |
| 589 | + - **DeepSeek:** `reasoning_content` deltas stream **before** content; thinking on by | |
| 590 | + default on v4-flash; ≤128 tools; `: keep-alive` SSE comments. | |
| 591 | + - **Kimi:** `reasoning_content` before content on K-series; built-in `$web_search` tool | |
| 592 | + uses `type:"builtin_function"` (do NOT use for Agent — our tools are local); temperature | |
| 593 | + unsupported on K-series (no-t/p + mct). | |
| 594 | + - **Together:** some models stream completions-style `choices[].text`; bare-array `/models`; | |
| 595 | + `finish_reason:"eos"`. | |
| 596 | + - **Cerebras:** `max_completion_tokens` required-style; `strict:true` tool schemas; | |
| 597 | + gpt-oss-120b may emit malformed/hallucinated tool calls — validate arguments against the | |
| 598 | + schema and re-prompt on failure (this validation belongs in the normalized layer for | |
| 599 | + ALL providers). | |
| 600 | + - **Perplexity:** no tools — hard-excluded (§3); `citations`/`search_results` fields must | |
| 601 | + keep being tolerated by the decoder. | |
| 602 | +- **Reasoning surfaces to keep uniform:** `delta.reasoning_content` (DeepSeek/Qwen/Kimi/some | |
| 603 | + DeepInfra), `delta.reasoning` (Together/Cerebras gpt-oss), Mistral ThinkChunk arrays, | |
| 604 | + Anthropic `thinking_delta`, Perplexity inline `<think>` — all already normalized to | |
| 605 | + `ChatEvent.reasoningDelta` by Cloud's clients; unchanged. | |
| 606 | + | |
| 607 | +### 5.3 The normalized interface Zyquo Agent adds | |
| 608 | + | |
| 609 | +Extend the ported types (one uniform surface; per-provider translation stays inside clients): | |
| 610 | + | |
| 611 | +```swift | |
| 612 | +struct ToolSpec: Codable { // handed to the client from ToolRegistry | |
| 613 | + var name: String | |
| 614 | + var description: String | |
| 615 | + var parametersJSONSchema: String // canonical JSON Schema (object) as a string | |
| 616 | +} | |
| 617 | + | |
| 618 | +struct ToolCall: Codable, Identifiable, Hashable { | |
| 619 | + var id: String // provider call id (toolu_… / call_…); synthesize for providers that omit it | |
| 620 | + var name: String | |
| 621 | + var argumentsJSON: String // raw accumulated JSON string; parsed+validated by the loop | |
| 622 | +} | |
| 623 | + | |
| 624 | +struct ToolResult: Codable { // threaded back on the next request | |
| 625 | + var toolCallID: String | |
| 626 | + var content: String // stringified output (stdout/stderr summary, file text…) | |
| 627 | + var isError: Bool | |
| 628 | +} | |
| 629 | + | |
| 630 | +// ChatRequest additions | |
| 631 | +var tools: [ToolSpec] = [] | |
| 632 | +var toolChoice: ToolChoice = .auto // .auto / .none / .required / .named(String) | |
| 633 | + | |
| 634 | +// Message additions (so history round-trips correctly per provider) | |
| 635 | +var toolCalls: [ToolCall]? = nil // on assistant turns | |
| 636 | +var toolResults: [ToolResult]? = nil // rendered as role:"tool" messages (OpenAI) or | |
| 637 | + // tool_result user blocks (Anthropic) by the client | |
| 638 | + | |
| 639 | +// ChatEvent additions | |
| 640 | +case toolCallStarted(index: Int, id: String, name: String) // live chip in the UI | |
| 641 | +case toolCallArgumentsDelta(index: Int, delta: String) // streamed args | |
| 642 | +case toolCalls([ToolCall]) // finalized, parsed set | |
| 643 | +``` | |
| 644 | + | |
| 645 | +`.finished(reason:)` is normalized by the client to a small enum surfaced alongside the raw | |
| 646 | +string: `endTurn` (`stop`/`end_turn`/`eos`), `toolUse` (`tool_calls`/`tool_use`), `maxTokens` | |
| 647 | +(`length`/`max_tokens`), `refusal`, `other(String)` — the `AgentLoop` branches only on this. | |
| 648 | + | |
| 649 | +### 5.4 Gemini native (only if Phase 7 demands it) | |
| 650 | + | |
| 651 | +If the compat endpoint's tool streaming proves insufficient, a native `GeminiClient` would use | |
| 652 | +`POST /v1beta/models/{model}:streamGenerateContent?alt=sse` with `x-goog-api-key`, declare | |
| 653 | +`tools: [{functionDeclarations:[{name,description,parameters}]}]` + | |
| 654 | +`toolConfig: {functionCallingConfig: {mode: AUTO|ANY|NONE, allowedFunctionNames}}`, receive | |
| 655 | +`candidates[].content.parts[].functionCall {name,args}` (complete JSON, not deltas), and reply | |
| 656 | +with a user part `functionResponse {name, response}`; completion signaled by | |
| 657 | +`finishReason: "STOP"`. **Not planned for the initial port** — Cloud ships without it and all | |
| 658 | +Gemini catalog models advertise tools on the compat endpoint. | |
| 659 | + | |
| 660 | +--- | |
| 661 | + | |
| 662 | +## 6. Porting plan | |
| 663 | + | |
| 664 | +**Port verbatim** (rename module header `Zyquo Cloud` → `Zyquo Agent`, `ZyquoCloud` strings → | |
| 665 | +`ZyquoAgent` where they are app-facing: User-Agent, app-support folder, HKDF info, pepper): | |
| 666 | + | |
| 667 | +| From `zyquo-cloud/Sources/ZyquoCloud/…` | To `zyquo-agent/Sources/ZyquoAgent/…` | Changes | | |
| 668 | +|---|---|---| | |
| 669 | +| `Models/ProviderID.swift` | `Models/ProviderID.swift` | none | | |
| 670 | +| `Models/AIModel.swift` | `Models/AIModel.swift` | add `var agentCapable: Bool` (derived: `capabilities.tools && !isLegacy && curated list from §3`) | | |
| 671 | +| `Models/Message.swift` | `Models/Message.swift` | add `toolCalls` / `toolResults` (§5.3) | | |
| 672 | +| `Models/Conversation.swift` (ChatParameters, Persona) | `Models/ChatParameters.swift` | extract the shared value types; Conversation itself becomes Agent's Task model | | |
| 673 | +| `Providers/ProviderProtocol.swift` | `Providers/ProviderProtocol.swift` | add `tools`/`toolChoice` to `ChatRequest`; add tool `ChatEvent` cases; normalized finish reason | | |
| 674 | +| `Providers/ProviderRegistry.swift` | `Providers/ProviderRegistry.swift` | none | | |
| 675 | +| `Providers/OpenAICompatibleClient.swift` | `Providers/OpenAICompatibleClient.swift` | encode `tools`/`tool_choice`/`parallel_tool_calls`; decode + stream `delta.tool_calls` (index-keyed accumulation); emit assistant `tool_calls` + `role:"tool"` messages when building bodies from history | | |
| 676 | +| `Providers/AnthropicClient.swift` | `Providers/AnthropicClient.swift` | encode `tools`/`tool_choice`; handle `content_block_start`/`input_json_delta`/`content_block_stop` for `tool_use` blocks; encode `tool_use` assistant blocks + `tool_result` user blocks from history | | |
| 677 | +| `Services/StreamingService.swift` | `Services/StreamingService.swift` | User-Agent → `ZyquoAgent/1.0 (macOS)` | | |
| 678 | +| `Services/ModelCatalog.swift` + `Services/ModelCatalogData.swift` | `Services/ModelCatalog.swift` + `ModelCatalogData.swift` | identical catalog; add `agentCapableModels` filter + per-provider agent defaults (§3) | | |
| 679 | +| `Services/SecureKeyStore.swift` | `Services/SecureKeyStore.swift` | info string `ZyquoAgent.vault.v1`, new pepper, vault under `ZyquoAgent/` | | |
| 680 | +| `Services/PersistenceService.swift` | `Services/PersistenceService.swift` | root folder `ZyquoAgent` | | |
| 681 | +| `ViewModels/KeyVaultStore.swift` | `ViewModels/KeyVaultStore.swift` | reuse for Settings → Providers & Keys | | |
| 682 | +| `Tests/ZyquoCloudTests/SSEParserTests.swift`, `SecureKeyStoreTests.swift`, `ModelTests.swift` | `Tests/ZyquoAgentTests/…` | rename; extend with tool-call streaming fixtures | | |
| 683 | +| `Verify/VerifyHarness.swift` | `Verify/VerifyHarness.swift` | replace the "OK" test with the Phase-7 tool-calling battery (§3), run only on 🤖 models | | |
| 684 | + | |
| 685 | +**Add new (Agent-only):** `ToolSpec`/`ToolCall`/`ToolResult` types (§5.3); per-client tool | |
| 686 | +encoding/decoding; argument-JSON validation against the declared schema with a re-prompt path | |
| 687 | +(needed for gpt-oss-class models); the `Agent/`, `Tools/`, `Execution/`, `Workspace/` layers | |
| 688 | +per Phase 2 — none of which touch provider wire code. | |
| 689 | + | |
| 690 | +**Renames for the ZyquoAgent module:** file headers to `Zyquo Agent`; `ZyquoCloud` → | |
| 691 | +`ZyquoAgent` only in User-Agent, PersistenceService folder, HKDF info, pepper, and test module | |
| 692 | +names. Wire formats, base URLs, headers, catalog IDs, error mapping, SSE parsing: **unchanged | |
| 693 | +— that is the whole point.** | |
| 694 | + | |
| 695 | +**Invariants inherited from Cloud (do not regress):** | |
| 696 | +- Decoders tolerate unknown fields and malformed keep-alive chunks; never crash a stream. | |
| 697 | +- Every optional request param is gated by `ParameterSupport`; never send unsupported params. | |
| 698 | +- Cancellation flows through `AsyncThrowingStream.onTermination` → `Task.cancel()` → HTTP abort. | |
| 699 | +- `429/5xx` backoff on non-streaming calls; typed `ProviderError` with human-readable text. | |
| 700 | +- `cheapestModel(for:)` (non-reasoning preferred) for utility calls like auto-titles. | |
| 701 | +- Catalog (`ModelCatalogData.swift`) and this doc stay in sync with any Phase-7 findings. | |
| 702 | ||