diff --git a/docs/core-concepts/agents.mdx b/docs/core-concepts/agents.mdx index 71d34b812..28fe25f5b 100644 --- a/docs/core-concepts/agents.mdx +++ b/docs/core-concepts/agents.mdx @@ -3,3 +3,83 @@ title: "Agents" description: "Core agent concepts in Arc" --- +An agent in Arc is an LLM session with access to tools. When a workflow reaches an agent node, Arc creates a session, sends the prompt and prior context to the model, and lets the agent work autonomously — reading files, running commands, editing code, spawning sub-agents — until it decides the task is complete. + +## The agent loop + +Each agent turn follows the same cycle: + +1. **Send** — Arc sends the conversation history (system prompt, prior messages, tool results) to the LLM +2. **Receive** — The model responds with text, tool calls, or both +3. **Execute** — Arc executes any tool calls in the sandbox and appends the results to the conversation +4. **Repeat** — If the model made tool calls, go back to step 1. If it responded with only text, the agent is done. + +This loop continues until the model stops calling tools, indicating it considers the task complete. Arc also enforces guardrails: token budgets, turn limits, and loop detection to prevent runaway agents. + +## Tools + +Agents have access to a set of built-in tools for interacting with the codebase and environment: + +| Tool | Description | +|---|---| +| `shell` | Run shell commands (bash) | +| `read_file` | Read file contents with optional offset and limit | +| `write_file` | Create or overwrite a file | +| `edit_file` | Make targeted edits to an existing file | +| `grep` | Search file contents with regex patterns | +| `glob` | Find files by name pattern | +| `web_search` | Search the web | +| `web_fetch` | Fetch and summarize a URL | + +Additional tools can be added via [MCP servers](/agents/mcp) for integrations like databases, APIs, or custom services. + +See [Tools](/agents/tools) for the full reference. + +## Prompts + +The agent's behavior is shaped by its prompt — the task instructions set in the `prompt` attribute of the workflow node. Prompts can be inline strings or references to external Markdown files: + +```dot +// Inline prompt +plan [label="Plan", prompt="Analyze the codebase and write a step-by-step plan."] + +// External file reference +simplify [label="Simplify", prompt="@prompts/simplify.md"] +``` + +Arc also injects a system prompt with context about the workflow goal, prior stage outputs, available tools, and the agent's role. See [Prompts](/agents/prompts) for details. + +## Sub-agents + +An agent can spawn **sub-agents** to delegate subtasks. Sub-agents run in their own session with their own tool access, and return results to the parent. This is useful for parallelizing research, isolating risky operations, or breaking complex tasks into manageable pieces. + +See [Sub-agents](/agents/subagents) for details. + +## Skills + +Skills are reusable prompt templates that extend an agent's capabilities for common tasks — code review, test writing, refactoring, and more. They're discovered automatically from the project and can be invoked by the agent during its session. + +See [Skills](/agents/skills) for details. + +## Hooks + +Hooks are shell commands that run in response to agent lifecycle events (e.g. before a tool executes, after a stage completes). They enable custom validation, notifications, and guardrails without modifying the workflow graph. + +See [Hooks](/agents/hooks) for details. + +## Further reading + + + + How prompts are constructed and injected. + + + Built-in tools and custom tool registration. + + + Extend agents with Model Context Protocol servers. + + + Delegate subtasks to child agent sessions. + + diff --git a/docs/core-concepts/workflows.mdx b/docs/core-concepts/workflows.mdx index 8c11e3b6d..5591a6741 100644 --- a/docs/core-concepts/workflows.mdx +++ b/docs/core-concepts/workflows.mdx @@ -99,18 +99,6 @@ quality -> merge merge -> report -> exit ``` -## Model stylesheets - -Assign models to nodes using CSS-like selectors with increasing specificity: - -``` -* { llm_model: claude-haiku-4-5; } /* all nodes */ -.coding { llm_model: claude-sonnet-4-5; } /* nodes with class="coding" */ -#review { llm_model: gemini-3.1-pro-preview; } /* node with id "review" */ -``` - -Selectors cascade by specificity: `*` (0) < `shape` (1) < `.class` (2) < `#id` (3). Explicit node attributes always override stylesheets. See [Model Stylesheets](/workflows/stylesheets) for details. - ## Goal gates Mark critical nodes with `goal_gate=true`. The workflow fails if any goal gate doesn't succeed — even if execution reaches the exit node: diff --git a/docs/workflows/human-in-the-loop.mdx b/docs/workflows/human-in-the-loop.mdx index 2d8c7fded..4f9df92db 100644 --- a/docs/workflows/human-in-the-loop.mdx +++ b/docs/workflows/human-in-the-loop.mdx @@ -3,3 +3,87 @@ title: "Human-in-the-Loop" description: "Adding human review and intervention to workflows" --- +Not every decision should be made by an LLM. Arc provides several mechanisms for humans to participate in workflows — approving plans, choosing between options, providing free-form input, and steering agents while they run. + +## Human gates + +A human gate is a node with shape `hexagon` that pauses the workflow and presents the user with a choice. The options are derived from the outgoing edge labels: + +```dot +approve [shape=hexagon, label="Approve Plan"] + +approve -> implement [label="[A] Approve"] +approve -> plan [label="[R] Revise"] +approve -> skip [label="[S] Skip"] +``` + +When execution reaches the gate, the user sees the node's label ("Approve Plan") and the available options. In the CLI, this appears as an interactive menu. In the web UI, it appears as a set of buttons. + +### Keyboard accelerators + +The prefixes `[A]`, `[R]`, `[S]` in edge labels serve as keyboard accelerators. Arc supports three formats: + +| Format | Example | +|---|---| +| `[K] Label` | `[A] Approve` | +| `K) Label` | `A) Approve` | +| `K - Label` | `A - Approve` | + +When matching the user's selection to an edge, Arc strips the accelerator prefix so typing `A` matches `[A] Approve`. + +### Freeform input + +In addition to fixed choices, a human gate can accept freeform text input. Add an edge with `freeform=true`: + +```dot +review [shape=hexagon, label="Review Changes"] + +review -> implement [label="[A] Approve"] +review -> plan [label="[R] Revise"] +review -> custom [freeform=true] +``` + +If the user types something that doesn't match any fixed option, their input routes to the freeform edge. The text is available to downstream nodes as `human.gate.text` in the run context. + +### Default choice on timeout + +If a human gate has a timeout configured, you can specify a default choice using the `human.default_choice` attribute: + +```dot +approve [shape=hexagon, label="Approve?", human.default_choice="approve"] +``` + +If the timeout elapses without a response, the workflow continues to the default target rather than failing. + +### Auto-approve + +For testing or fully automated runs, pass `--auto-approve` to skip all human gates: + +```bash +arc run start workflow.dot --auto-approve +``` + +Auto-approve selects `Yes` for yes/no gates and the first option for multiple-choice gates. + +## Where to place human gates + +Human gates are most valuable at high-leverage decision points: + +- **Before implementation** — Approve a plan before the agent writes code +- **After review** — Confirm that a code review's findings are worth fixing +- **At branch points** — Choose a strategy when multiple approaches are viable +- **Before external actions** — Approve before deploying, merging, or sending notifications + +A workflow can have multiple human gates. Place them where the cost of a wrong decision is high relative to the cost of a brief pause. + +## Context set by human gates + +When a user makes a selection, the human gate sets several context values for downstream use: + +| Context key | Value | +|---|---| +| `human.gate.selected` | The accelerator key (e.g. `"A"`) or `"freeform"` | +| `human.gate.label` | The full label of the selected edge | +| `human.gate.text` | The user's freeform text (if applicable) | + +These can be read in edge conditions or referenced by downstream agents. diff --git a/docs/workflows/stylesheets.mdx b/docs/workflows/stylesheets.mdx index 9a6b62009..25e8bbe38 100644 --- a/docs/workflows/stylesheets.mdx +++ b/docs/workflows/stylesheets.mdx @@ -1,5 +1,135 @@ --- -title: "Stylesheets" -description: "Styling workflow definitions" +title: "Model Stylesheets" +description: "Assign LLM models to workflow nodes using CSS-like rules" --- +Model stylesheets let you assign LLM models, providers, and settings to workflow nodes using a CSS-like syntax. Instead of hardcoding a model on every node, you write a set of rules that target nodes by ID, class, shape, or a universal wildcard — and Arc applies them by specificity. + +## Defining a stylesheet + +Stylesheets are set in the `model_stylesheet` graph attribute: + +```dot +digraph Example { + graph [ + goal="Build and review a utility function", + model_stylesheet=" + * { llm_model: claude-haiku-4-5; llm_provider: anthropic; } + .coding { llm_model: claude-sonnet-4-5; reasoning_effort: high; } + #review { llm_model: gemini-3.1-pro-preview; llm_provider: gemini; } + " + ] + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + spec [label="Write Spec"] + implement [label="Implement", class="coding"] + test [label="Write Tests", class="coding"] + review [label="Code Review"] + + start -> spec -> implement -> test -> review -> exit +} +``` + +In this example: +- **spec** gets Haiku (matches `*`) +- **implement** and **test** get Sonnet with high reasoning (match `.coding`) +- **review** gets Gemini Pro (matches `#review`) + +## Selectors + +Each rule starts with a selector that determines which nodes it applies to: + +| Selector | Syntax | Matches | Specificity | +|---|---|---|---| +| Universal | `*` | All nodes | 0 | +| Shape | `box`, `tab`, `hexagon`, etc. | Nodes with that Graphviz shape | 1 | +| Class | `.classname` | Nodes with `class="classname"` | 2 | +| ID | `#nodeid` | The node with that specific ID | 3 | + +### Assigning classes + +Set the `class` attribute on a node to target it with class selectors. Multiple classes are space-separated: + +```dot +implement [label="Implement", class="coding critical"] +``` + +This node matches both `.coding` and `.critical` rules. + +## Properties + +Stylesheets support four properties: + +| Property | Description | Example | +|---|---|---| +| `llm_model` | Model ID or alias | `claude-sonnet-4-5`, `opus`, `gemini-pro` | +| `llm_provider` | Provider name | `anthropic`, `openai`, `gemini` | +| `reasoning_effort` | Reasoning effort level | `low`, `medium`, `high` | +| `backend` | Backend type | `cli`, `api` | + +See [Models](/core-concepts/models) for the full list of model IDs and aliases. + +## Specificity and cascading + +When multiple rules match the same node, the rule with the **highest specificity** wins. This follows the same principle as CSS: + +``` +* (0) < shape (1) < .class (2) < #id (3) +``` + +For example: + +``` +* { llm_model: claude-haiku-4-5; } +.coding { llm_model: claude-sonnet-4-5; } +#review { llm_model: gpt-5.2; } +``` + +A node with `id="review"` and `class="coding"` gets `gpt-5.2` because `#id` (specificity 3) beats `.class` (specificity 2). + +If two rules have the same specificity, the **last one** in the stylesheet wins. + +## Explicit attributes override stylesheets + +A model set directly on a node attribute always takes precedence over stylesheets, regardless of specificity: + +```dot +implement [label="Implement", class="coding", llm_model="claude-opus-4-6"] +``` + +Even if `.coding` sets `llm_model: claude-sonnet-4-5`, this node uses Opus because the explicit attribute wins. + +## Syntax reference + +The stylesheet syntax is a simplified subset of CSS: + +``` +selector { property: value; property: value; } +``` + +- Selectors: `*`, `shape`, `.class`, `#id` +- Properties and values are separated by `:` +- Declarations are separated by `;` +- Whitespace is flexible — newlines and indentation are ignored +- CSS comments (`/* ... */`) are not supported + +### Full example + +``` +* { llm_model: claude-haiku-4-5; llm_provider: anthropic; reasoning_effort: low; } +box { reasoning_effort: high; } +tab { reasoning_effort: low; } +.coding { llm_model: claude-sonnet-4-5; llm_provider: anthropic; reasoning_effort: high; } +.review { llm_model: gemini-3.1-pro-preview; llm_provider: gemini; } +#final_check { llm_model: claude-opus-4-6; llm_provider: anthropic; reasoning_effort: high; } +``` + +This stylesheet: +- Defaults everything to Haiku with low reasoning +- Overrides all agent nodes (`box` shape) to high reasoning +- Keeps prompt nodes (`tab` shape) at low reasoning +- Routes `.coding` nodes to Sonnet +- Routes `.review` nodes to Gemini for independent critique +- Routes the `final_check` node to Opus for maximum quality diff --git a/docs/workflows/variables.mdx b/docs/workflows/variables.mdx index f11e67c41..caf5fb801 100644 --- a/docs/workflows/variables.mdx +++ b/docs/workflows/variables.mdx @@ -3,3 +3,66 @@ title: "Variables" description: "Using variables in workflows" --- +Arc supports `$variable` placeholders that let you parameterize workflows without editing the DOT file. + +## Run config variables + +Define variables in the `[vars]` section of a run config TOML file: + +```toml +version = 1 +goal = "Run tests for $repo_name" +graph = "check.dot" + +[vars] +repo_name = "arc" +repo_url = "https://github.com/qltysh/arc" +language = "rust" +``` + +These variables are expanded into the DOT source **before** the graph is parsed. You can use `$variable` anywhere in the DOT file — goals, prompts, labels, scripts, or any other attribute: + +```dot +digraph Check { + graph [goal="Run tests for $repo_name"] + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + clone [label="Clone", shape=parallelogram, script="git clone $repo_url repo"] + test [label="Test", prompt="Run the $language test suite in the repo/ directory."] + + start -> clone -> test -> exit +} +``` + +When launched with `arc run start run.toml`, Arc replaces `$repo_name`, `$repo_url`, and `$language` with their values before parsing the graph. + +### Undefined variables + +If a `$variable` in the DOT file has no matching entry in `[vars]`, Arc raises an error. This catches typos early — a misspelled `$langauge` fails immediately rather than passing a literal `$langauge` to the LLM. + +A bare `$` not followed by an identifier character (e.g. `costs $5`) is left as-is. + +## The `$goal` variable + +Inside agent and prompt node prompts, Arc automatically expands `$goal` to the workflow's `goal` attribute. This happens at runtime, after graph parsing: + +```dot +digraph Example { + graph [goal="Implement the login feature"] + + plan [label="Plan", prompt="Create a plan for: $goal"] +} +``` + +The plan node's prompt becomes `"Create a plan for: Implement the login feature"`. + +## Variable merging + +When using server-level run defaults alongside a run config TOML, variables are merged. Task config vars override default vars when keys collide: + +| Source | Priority | +|---|---| +| Run config TOML `[vars]` | Highest — wins on collision | +| Server defaults `[vars]` | Lowest — provides fallback values |