Commit graph

31 commits

Author SHA1 Message Date
Bryan Helmkamp
1388f6539a Replace 10 duplicate PipelineEvent variants with Agent wrapper
AgentEvent variants were duplicated in PipelineEvent with `stage: String`
added, requiring a 130-line mechanical bridge to translate between them.
Collapse into a single `PipelineEvent::Agent { stage, event }` variant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 9e9f00828e4b
2026-02-25 15:23:01 -05:00
Bryan Helmkamp
655e207b8e Add 4 missing pipeline events for observability parity
- LlmRetry: wrap agent session stream() with llm::retry, emit
  AgentEvent::LlmRetry and forward to PipelineEvent::LlmRetry
- failure_class on StageFailed/StageCompleted: "transient" for
  retry-eligible failures, "terminal" for final failures, None for success
- ParallelEarlyTermination: emit on fail_fast break with reason,
  completed_count, and pending_count
- SubgraphStarted/SubgraphCompleted: boundary events with timing and
  step count for sub-pipeline execution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 82c1bbcbe2f2
2026-02-25 15:06:25 -05:00
Bryan Helmkamp
66a918be2b Switch skills to directory-per-skill format (skills/<name>/SKILL.md)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: e662e3a9f2b5
2026-02-25 13:42:53 -05:00
Bryan Helmkamp
778bf45a0b Add cache tokens, reasoning tokens, and file change tracking to pipeline logs
Embed the full Usage struct (with cache_read_tokens, cache_write_tokens,
reasoning_tokens) in AssistantMessage events instead of bare input/output
token fields. Add skip_serializing_if annotations to keep NDJSON clean.
Extend StageUsage with cache/reasoning aggregation. Track files touched
via write_file/edit_file tool call correlation in the backend bridge.
Update format functions, cost accumulator, and TypeScript types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2c15cd247556
2026-02-25 13:01:45 -05:00
Bryan Helmkamp
0b37b8f681 Emit AgentEvent::SkillExpanded when a skill is activated
Adds observability for skill expansion so CLI users and pipeline event
consumers know when a skill reference was matched and expanded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: d94221388541
2026-02-25 11:42:40 -05:00
Bryan Helmkamp
3d39e06c29 Add skills system for reusable prompt templates
Skills are markdown files with YAML frontmatter that define reusable
prompt templates (e.g., /commit, /review-pr). When a user references
/skill-name in their input, the skill template expands in place with
{{user_input}} receiving the remaining text.

- Add skills.rs with parse, expand, discover, and formatting functions
- Discover skills from ~/.attractor/skills/, <git-root>/.attractor/skills/,
  and <git-root>/skills/ (overridable with --skills-dir CLI flag)
- Inject available skills into system prompt between project docs and
  user instructions
- Expand skill references in session input before recording user turn

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2a8ed77f26c5
2026-02-25 11:32:07 -05:00
Bryan Helmkamp
1ec822781c Rewrite compaction summarization prompt for better continuity
Use structured handoff-style prompt with explicit sections (Task & Goal,
Completed Work, Current State, Failed Approaches, Open Issues, Next Steps)
instead of generic summarizer prompt, improving context preservation across
compaction boundaries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2a12087def60
2026-02-25 10:26:21 -05:00
Bryan Helmkamp
139f653d33 Add read-before-write guardrail for ExecutionEnvironment
Decorator that tracks which files the agent has read (via read_file or
grep) and returns an error when writing to an existing file that hasn't
been read first. Prevents the model from hallucinating file contents and
blindly overwriting working code. New files are always allowed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: a565c5a5e2f9
2026-02-25 10:24:58 -05:00
Bryan Helmkamp
5420c21042 Add auto-compaction to prevent context window overflow in long agent sessions
When context usage exceeds the configurable threshold (default 80%), older
turns are summarized via a non-streaming LLM call and replaced with a single
System turn. Compaction is non-fatal — errors are emitted as AgentEvent::Error
and the session continues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 18435f31db91
2026-02-24 23:09:38 -05:00
Bryan Helmkamp
52d3536a1d Add LLM conversation events to PipelineEvent for progress.ndjson observability
Restructures agent events from misaligned EventKind+EventData pair into flat
AgentEvent enum, enriches AssistantMessage with model/token/tool_call data,
adds 8 new PipelineEvent variants (Prompt, AssistantMessage, ToolCallStarted,
ToolCallCompleted, SessionError, ContextWindowWarning, LoopDetected,
TurnLimitReached), and forwards agent session events to the pipeline emitter
in AgentBackend so they appear in progress.ndjson.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: d59f9e69008a
2026-02-24 14:08:22 -05:00
Bryan Helmkamp
9665fad133 Revert clippy config to defaults, remove all pedantic/nursery/cargo lint suppressions
Removed the workspace-level clippy lint config that enabled all, pedantic, nursery,
and cargo lint groups. Removed all #[allow(clippy::...)] annotations that were only
needed to suppress those extra lints, and fixed the few default clippy warnings that
were uncovered.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:42:28 -05:00
Bryan Helmkamp
2a2a610654 Replace abort_flag with CancellationToken for abort-aware process cancellation
Thread CancellationToken into tool executors and exec_command so that
running processes are killed (SIGTERM -> 2s -> SIGKILL) when abort fires,
rather than only checking the flag between LLM calls. Key changes:

- ToolExecutor type gains CancellationToken parameter
- ExecutionEnvironment::exec_command gains cancel_token param
- LocalExecutionEnvironment uses tokio::select! (completion vs timeout
  vs cancellation) with extracted sigterm_then_kill helper
- DockerExecutionEnvironment uses same select! pattern
- Session replaces Arc<AtomicBool> with CancellationToken, passes
  child_token() per tool call
- Shell tool forwards cancel token to exec_command
- CLI SIGINT handler calls cancel_token.cancel()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:10:10 -05:00
Bryan Helmkamp
3dfa0f9044 Add shape selectors, LLM stream timeouts, MultiSelect questions, and test coverage
- Stylesheet: add bare-word Shape selector (specificity between Universal and Class)
- LLM: apply per_step timeout to connection and total timeout to stream (Section 4.7)
- Interviewer: add MultiSelect question type alongside MultipleChoice
- Session: move SessionStart/SessionEnd to initialize()/close(), deduplicate close logic
- Engine: return Ok(fail outcome) instead of error when goal gate unsatisfied with no retry_target
- Docker: mark Docker-dependent tests with #[ignore]
- Validation: add extensive unit test coverage for all rule types
- Integration: update tests to match engine/fidelity changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 17:43:05 -05:00
Bryan Helmkamp
ceaa9bf560 Improve session abort handling, emit tool output deltas, and tighten validation
- Break out of streaming loop on abort and drop the stream before emitting
  SessionEnd to properly cancel the HTTP connection
- Emit ToolCallOutputDelta events for tool call results in both sequential
  and parallel execution paths
- Retry on StageStatus::Fail in addition to Retry in pipeline engine
- Set preferred_label on WaitHumanHandler choice outcomes
- Enforce exactly one terminal node in pipeline validation
- Downgrade unreachable node diagnostic from Error to Warning
- Update context window test to match 1M token limit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:39:04 -05:00
Bryan Helmkamp
834b7f6160 Display relative paths in CLI tool call output
Strip the CWD prefix from string arguments in format_tool_args so tool
call output shows relative paths instead of absolute ones.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:53:19 -05:00
Bryan Helmkamp
9342b5be76 Preserve thinking block signatures for Anthropic multi-turn conversations
The Anthropic API requires a `signature` field on thinking blocks when
they are sent back in conversation history. Previously, thinking blocks
were stored as plain text in Turn::Assistant.reasoning, losing the
signature. Now Thinking/RedactedThinking content parts are preserved
in provider_parts (which retains signatures), and the lossy
reconstruction path is skipped when provider_parts already contains
thinking blocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:37:12 -05:00
Bryan Helmkamp
242e82fffd Use Claude Opus 4.6 with 1M context window as default Anthropic model
Switch default model from claude-sonnet-4-5 to claude-opus-4-6 across
run and serve commands. Enable the 1M token context window via the
context-1m-2025-08-07 beta header for opus-4-6 requests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:23:45 -05:00
Bryan Helmkamp
f4f58078de Update default Gemini model to gemini-3.1-pro-preview and print model name on startup
Updates the default Gemini model from gemini-3-pro-preview to
gemini-3.1-pro-preview across agent, attractor, and ullm CLIs.
Adds "Using model:" output to stderr in both agent and ullm CLIs
so users can see which model is being used.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:05:16 -05:00
Bryan Helmkamp
e64c0a3328 Switch default model to claude-opus-4-6 and use adaptive thinking
Updates the default Anthropic model from claude-sonnet-4-5 to
claude-opus-4-6 and replaces beta headers with adaptive thinking
provider options for 4.6 models.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:46:31 -05:00
Bryan Helmkamp
60dad3c1cd Add DockerExecutionEnvironment for sandboxed agent tool execution
Implements ExecutionEnvironment trait backed by Docker containers via
bollard. Host working directory is bind-mounted; all file ops, commands,
grep, and glob execute inside the container via docker exec. Extracts
shared format_lines_numbered() helper from LocalExecutionEnvironment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:29:38 -05:00
Bryan Helmkamp
e4fc345912 Switch agent session from complete() to stream() to avoid request timeouts
Long Opus responses hit the 120s request timeout with complete(). With
stream(), the request timeout only covers initial connection + first
chunk, then the 30s stream_read timeout guards against stalls between
chunks. Also emits AssistantTextDelta events during streaming.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:10:54 -05:00
Bryan Helmkamp
5573e0e8a2 Fix truncated LLM responses by populating catalog max_output
Requests were always sent with max_tokens: None, which defaulted to
4096 in the Anthropic provider, causing large outputs to be truncated.
Now build_request() looks up the model's max_output from the catalog
and passes it through, giving each model its full output capacity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:30:25 -05:00
Bryan Helmkamp
dddc6df8c8 Extract terminal crate and prettify attractor CLI output
Move ANSI Styles struct from agent/cli.rs into a shared terminal crate
so both binaries can use it. Add green and yellow color codes. Prettify
all attractor CLI output: bold headers, colored diagnostics by severity,
green/red status, yellow warnings, dimmed event details, and styled
interviewer prompts. Move pipeline status output from stdout to stderr.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:06:24 -05:00
Bryan Helmkamp
b4397564ec Prettify agent CLI stderr output with ANSI colors
Add a Styles struct that pre-resolves ANSI escape codes based on whether
stderr is a TTY. Tool calls show as "● tool_name(args)" with bold+cyan
names, errors use red "✗", summaries are dimmed, and debug/verbose
middleware output is styled. No ANSI codes emitted when piped.

Also passes tool call arguments through to EventData::ToolCall so the
event handler can format them inline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:45:13 -05:00
Bryan Helmkamp
df8502d83b Merge agent-cli crate into agent as cli module
The agent-cli binary was a thin wrapper over the agent library with
nothing else depending on it. Moving it into the agent crate as a
`pub mod cli` with a `[[bin]]` entry reduces workspace complexity
and follows the attractor crate pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:14:25 -05:00
Bryan Helmkamp
eaf934f355 Add tests for tool_approval and agent-cli
- 5 session tests exercising ToolApprovalFn callback (deny, allow,
  arg capture, None passthrough, error event emission)
- 18 unit tests for agent-cli pure functions (tool_category,
  is_auto_approved, default_model, validate_api_key,
  build_tool_approval, build_profile)
- 4 integration tests for the agent binary (usage, help, missing
  API key, invalid permissions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:44 -05:00
Bryan Helmkamp
4915574f96 Preserve provider-specific content parts across agent turns
The agent's Turn::Assistant decomposed responses into text, tool_calls,
and reasoning fields, discarding ContentPart::Other items. This lost
OpenAI reasoning items needed for Responses API round-tripping.

Add provider_parts field to Turn::Assistant to carry opaque content
parts through the history, and emit them first in convert_to_messages
so they precede function_call items as the API requires.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:54:35 -05:00
Bryan Helmkamp
b7883d6bef Add agent CLI with tool approval callback
Introduce `ToolApprovalFn` callback in `SessionConfig` to gate tool
execution by permission level. Create `agent-cli` crate as a thin CLI
binary wrapping `Session` with provider/model resolution, permission
model (read-only/read-write/full), interactive approval prompts,
real-time event rendering, debug middleware, and SIGINT handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:50:40 -05:00
Bryan Helmkamp
7dddc49121 Remove deprecated anthropic-beta header values
The `extended-thinking-2025-04-14` and `max-tokens-3-5-sonnet-2025-04-14`
beta headers are no longer accepted by the Anthropic API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:05:43 -05:00
Bryan Helmkamp
4bb2de5a49 Rename unified-llm crate to llm
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:22:14 -05:00
Bryan Helmkamp
30cf3a6787 Rename coding-agent-loop crate to agent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:17:48 -05:00