# 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" () 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, ): 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, ): 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, , tutorial: ): 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 (), 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: , content: , is_error: }`. 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 ). 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. ): 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 ): ```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" (). - **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 (); 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" (). ### 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" (). - 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"]`) (). 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: - **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") (). 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 (). - **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. - **Gemini**: function-call arguments arrive as deltas that must be aggregated before execution (). 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**: - **Anthropic**: `tools: [{name, description, input_schema}]` where `input_schema` is JSON Schema (). - **OpenAI**: `tools: [{type: "function", function|name/description/parameters, strict}]` — full JSON Schema features: "property types, enums, descriptions, nested objects, and recursive objects" (). - **Gemini**: `tools: [{functionDeclarations: [{name, description, parameters}]}]` — parameters use an **OpenAPI-subset** schema, notably more restricted than full JSON Schema (). 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: ``` 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: ``` 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` (). **Gemini** — parts inside contents: ``` model: { parts:[{functionCall:{name:"bash", args:{...}}}] } user: { parts:[{functionResponse:{name:"bash", response:{...}}}] } ``` Modes via config: `AUTO` (model decides), `ANY` (must call a function), `NONE` (). 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" (), 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 () 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 (). **File-edit tool.** Two dominant designs: - **String-replacement editing** — Anthropic's `str_replace_based_edit_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 (): `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" (): "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** (, , ): - 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% (). 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 (). - **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% (, ). 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 (). 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" (). - **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: - **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) (, SDK: ). - **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 (, ). - **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 (, ). - **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") (). ### 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//`). 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 `FileEditObservation`s 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) (, ). 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 (, ). 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 (). The recommended workflow is explicitly **explore → plan → code → commit** (). - **Re-planning is the normal path**: the LangGraph plan-and-execute pattern includes a replanner node that revises remaining steps after each execution (). 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" (). 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 (). Used for exploration, research, and parallelizable independent work. - **Anthropic's 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"** (): 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" (). 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 (). ### 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 (). - 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 (). ### 6.3 Human-in-the-loop approval gates: how the leaders do it **Claude Code** () — 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-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 (, ). **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 (). **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" (, ). **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** (, ): 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** (, ): 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** (, ): 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 (): - **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. | 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. | | 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 `&&`/`;`/`|` 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. | | 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** - Anthropic, *Building Effective Agents* — - Anthropic docs, *How tool use works* (agentic loop, stop reasons, server-side loop) — - Anthropic docs, *Stop reasons and fallback* — - Yao et al., *ReAct: Synergizing Reasoning and Acting in Language Models* — - Shinn et al., *Reflexion: Language Agents with Verbal Reinforcement Learning* — - LangChain, *Plan-and-Execute agents* — ; tutorial: **Tool design & provider APIs** - Anthropic, *Writing effective tools for agents* — - OpenAI, *Function calling guide* — - Google, *Gemini function calling* — - Anthropic docs, *Bash tool* — - Anthropic docs, *Text editor tool* — - Anthropic docs, *Streaming* / *Fine-grained tool streaming* — , - Aider, *Edit formats* — **Memory & context** - Anthropic, *Effective context engineering for AI agents* — - Anthropic, *Managing context on the Claude Developer Platform* (context editing + memory tool, 84%/39% results) — - Anthropic docs, *Memory tool* — - Claude Code auto-compact analyses — , , **Workspaces, planning, agents in production** - OpenHands platform paper — ; OpenHands Agent SDK — - Claude Code docs: *Checkpointing* — ; *Sub-agents* — ; *Todo/Task tracking* — ; *Best practices* — - Anthropic, *How we built our multi-agent research system* — - Cognition, *Don't Build Multi-Agents* — ; *How Cognition uses Devin to build Devin* — **Safety & control** - Claude Code docs, *Configure permissions* (rules, modes, read-only set, circuit breakers) — - Codex CLI sandbox/approvals — , , - Cursor, *Agent Security* — ; Backslash Security, *The Denylist Delusion* — ; The Register coverage — **Evaluation** - OpenAI, *Introducing SWE-bench Verified* — ; SWE-bench — - *Terminal-Bench* — , - *OSWorld* — ; *OSWorld 2.0* —