SPB Git

spb/zyquo-agent Public MIT

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

Swift 94.7% Shell 4.1% Python 0.7% Makefile 0.5%
54.0 KB

# AGENT-RESEARCH.md — How Modern Production AI Agents Work

Zyquo Agent — Phase 0.A research document 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.


# Executive Summary

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:

  • 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).
  • 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.
  • 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.
  • Workspaces isolate each task in a working directory; file tracking + checkpoints make agent actions reversible and auditable.
  • 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.
  • 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.
  • 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.

The final section maps every finding onto Zyquo Agent's planned components (AgentLoop, Planner, MemoryManager, LoopGuard, ToolRegistry, PolicyEngine, WorkspaceManager).


# 1. The Core Agentic Loop

# 1.1 Workflows vs. agents

Anthropic's "Building Effective Agents" (https://www.anthropic.com/engineering/building-effective-agents) draws the canonical distinction:

  • Workflows: LLMs and tools orchestrated through predefined code paths (prompt chaining, routing, parallelization, orchestrator-workers, evaluator-optimizer).
  • Agents: systems where "LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks."

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.

# 1.2 The research lineage

  • 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.
  • 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.
  • 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.

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.

# 1.3 Anatomy of one loop iteration (Anthropic wire format)

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:

  1. Send a request: system prompt + tools array (JSON schemas) + full message history.
  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.
  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."
  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>}.
  5. Repeat from step 2 while stop_reason == "tool_use".

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:

Provider "I want to act" "I'm done"
Anthropic Messages stop_reason: "tool_use" + tool_use blocks stop_reason: "end_turn"
OpenAI Chat Completions finish_reason: "tool_calls" + message.tool_calls[] finish_reason: "stop"
OpenAI Responses API function_call output items plain message output items
Gemini candidate parts contain functionCall parts contain only text

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.

Worked example — one full Anthropic iteration (wire shapes from https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works):

jsonc
// → request N
{ "system": "...", "tools": [{ "name": "bash", "description": "...", "input_schema": {...} }],
  "messages": [ ...history..., { "role": "user", "content": "Set up the project and run the tests" } ] }

// ← response N: the model thinks, then acts
{ "stop_reason": "tool_use",
  "content": [
    { "type": "text", "text": "I'll check whether a package manifest exists first." },   // ReAct thought
    { "type": "tool_use", "id": "toolu_01A", "name": "bash",
      "input": { "command": "ls -la" } } ] }                                              // action

// → request N+1: assistant turn appended verbatim + observation
{ "messages": [ ...,
    { "role": "assistant", "content": [ /* the two blocks above */ ] },
    { "role": "user", "content": [
      { "type": "tool_result", "tool_use_id": "toolu_01A",
        "content": "Package.swift\nSources\nTests", "is_error": false } ] } ] }

// ← eventually: no tool_use blocks ⇒ done
{ "stop_reason": "end_turn", "content": [ { "type": "text", "text": "All 12 tests pass. ..." } ] }

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.

# 1.4 System-prompt anatomy for an agent

The loop is only as good as the contract the system prompt establishes. Findings across the sources:

  • 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).
  • 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."
  • 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).
  • 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).

# 1.5 Native tool calling vs. structured output

Native tool calling (function schemas + typed call/response blocks) is strictly preferred over asking the model to emit parseable text/JSON in prose:

  • 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).
  • 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."
  • 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).

Structured output (JSON mode) remains useful for final answers of a fixed shape, not for actions.

# 1.6 Streaming the loop

Everything in an iteration can and should stream to the UI:

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.


# 2. Tool Design

# 2.1 How tools are defined

All three provider families use the same conceptual triple — name, description, JSON-Schema parameters:

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.

# 2.2 Threading tool calls and results, per provider

The three wire formats the loop must normalize:

Anthropic — content blocks inside normal messages:

text
assistant: [ {type:"text", ...thought...}, {type:"tool_use", id:"toolu_1", name:"bash", input:{...}} ]
user:      [ {type:"tool_result", tool_use_id:"toolu_1", content:"...", is_error:false} ]

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.

OpenAI Chat Completions — dedicated roles:

text
assistant: { content:null, tool_calls:[{id:"call_1", type:"function", function:{name:"bash", arguments:"{\"command\":...}"}}] }
tool:      { role:"tool", tool_call_id:"call_1", content:"..." }

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).

Gemini — parts inside contents:

text
model: { parts:[{functionCall:{name:"bash", args:{...}}}] }
user:  { parts:[{functionResponse:{name:"bash", response:{...}}}] }

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}.

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.

# 2.3 Parallel vs. sequential tool calls

  • 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).
  • The runtime may execute them concurrently, but all results must be returned together in the next message, correlated by id.
  • 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.

# 2.4 Best practices for tool definitions

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:

  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."
  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."
  3. Namespace related tools (file_read, file_write, file_search) to reduce selection confusion.
  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").
  5. Return meaningful context: semantic identifiers over UUIDs; support concise/detailed response formats.
  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).
  7. Make invalid states unrepresentable with enums and structure; don't make the model fill arguments the app already knows (OpenAI).
  8. Evaluate tools like code: prototype, run realistic multi-call tasks, read the agent's reasoning to find rough edges, iterate.

# 2.5 How leading agents design the three core tools

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).

File-edit tool. Two dominant designs:

  • 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.
  • 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.

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.


# 3. Memory & Context Compression

# 3.1 Why: context is finite and rots

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.

# 3.2 Compaction (summarizing old turns)

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):

  • Token usage is monitored continuously; near ~95% of the effective window (threshold ≈ window minus a fixed reserve) the turn pauses.
  • 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.
  • Content loaded from disk (CLAUDE.md project instructions, memory) is re-injected after compaction — compaction only touches conversation history, never standing instructions.
  • Manual /compact triggers the same pipeline on demand, optionally with focus instructions.
  • 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.

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).

# 3.3 Concrete compaction strategy (synthesized)

The strategy production systems converge on, and the one Zyquo Agent's MemoryManager should implement:

  1. Never compact: system prompt, tool schemas, the current plan/todo list, standing instructions/memory files (re-inject after compaction).
  2. Keep verbatim: the most recent N steps (recency matters most for the next decision).
  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.
  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).
  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.
  6. Guard against thrashing as Claude Code does.

# 3.4 Hierarchical memory: scratchpad vs. notes files

Two tiers, both external to the context window:

  • 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).
  • 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.
  • 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).
  • 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.

# 4. Working Repositories / Workspaces

# 4.1 Why isolation matters

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).

Reference points:

# 4.2 Session ↔ folder mapping, file tracking, checkpoints

  • 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.
  • 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 FileEditObservations in the event stream. This powers UI badges (created/modified) and audit.
  • 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.

# 5. Planning & Task Decomposition

# 5.1 Externalized todo lists

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.

Anthropic's context-engineering post frames the same practice as memory: to-do lists are "structured note-taking" that maintains coherence across long horizons.

# 5.2 Plan-first modes and mid-run re-planning

  • 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).
  • 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."
  • Trigger re-planning on: a step failing twice, an assumption invalidated by an observation, or the user editing the plan.

# 5.3 Sub-agents and delegation

  • 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.
  • 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.
  • 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.

# 6. Reliability & Control

# 6.1 Loop guards

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:

  • 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.
  • 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.
  • Wall-clock budget and per-command timeout (the bash-tool docs make per-command timeouts the app's responsibility).
  • 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.
  • 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.
  • 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).

# 6.2 Error recovery

  • 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.
  • Distinguish retryable (transient network, timeout) from diagnostic (compile error — the model should read it) from fatal (permission denied by policy — surface to user).
  • 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).
  • 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).

# 6.3 Human-in-the-loop approval gates: how the leaders do it

Claude Code (https://code.claude.com/docs/en/permissions) — the most instructive design, worth reproducing in detail:

  • 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.
  • 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).
  • 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.
  • 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.
  • 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).
  • Risk explanation UI: on a Bash prompt, Ctrl+E asks the model itself to explain the command and label it Low/Med/High risk.

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-onlyworkspace-writedanger-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/).

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/).

Design conclusions for a policy engine (synthesized):

  1. Deny → ask → allow, first-match, no specificity override — simple to reason about, hard to misconfigure.
  2. Parse shell structure (subcommands, wrappers, substitutions) before matching; never regex the raw string only.
  3. Auto-allow only a curated read-only command set; everything mutating asks by default.
  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.
  5. Treat pattern-matching as UX, not security: pair it with approvals, workspace scoping, and a complete audit trail (the Cursor lesson).
  6. "Approve & remember" should save the narrowest rule that covers the action (Claude Code saves per-subcommand rules, max 5, for compound commands).

# 6.4 Deciding an action is risky

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.


# 7. Evaluation

# 7.1 Benchmark task suites (the pattern to copy)

  • 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.
  • 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).
  • 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.

Common skeleton: instruction + isolated environment + programmatic success checker + oracle solution. Metrics: task resolution rate (primary), plus steps/tokens/time-to-completion (efficiency).

# 7.2 Beyond pass/fail: judges and trajectory inspection

From Anthropic's multi-agent research system (https://www.anthropic.com/engineering/multi-agent-research-system):

  • 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.
  • 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.
  • 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.
  • Start small: ~20 representative tasks catch most regressions early; don't wait for a big eval harness.
  • 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).

# 8. Implications for Zyquo Agent Architecture

Finding (section) Component Design consequence
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.
Provider stop/threading formats differ (§1.3, §2.2) ProviderClient protocol
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 &&/;/`
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.
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.
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.

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.


# Source Index

Agentic loop & patterns

Tool design & provider APIs

Memory & context

Workspaces, planning, agents in production

Safety & control

Evaluation