Add a [[bin]] target to the attractor crate with two subcommands:
- `attractor validate <pipeline.dot>` -- parse and validate only
- `attractor run <pipeline.dot>` -- full pipeline execution with LLM backend
The CLI supports --dry-run, --auto-approve, --resume, --model, --provider,
and two-level verbosity (-v one-line summaries, -vv full event details).
Extracts a shared `default_registry()` function in handler/mod.rs so both
the CLI and server can build a fully-wired HandlerRegistry without
duplicating handler registration boilerplate.
The AgentBackend in cli/backend.rs implements CodergenBackend by creating
a coding-agent-loop Session per node invocation, giving LLM nodes access
to file and shell tools.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change server registry_factory to accept Arc<dyn Interviewer> so WaitHumanHandler
shares the same WebInterviewer as the REST API endpoints
- Add full HTTP lifecycle tests: approve-and-complete flow + cancel flow
- Add SSE event stream content parsing test with frame-level verification
- Add sub-pipeline E2E test through the engine with context propagation
- Add manager loop E2E test with SimulatingChildObserver
- Add graph merge E2E test verifying module prefixing and execution ordering
- Add 3 real LLM tests (#[ignore]) using claude-haiku via AnthropicAdapter
- Add dotenvy and http-body-util dev dependencies
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SubPipelineHandler (handler/sub_pipeline.rs) that inline-executes a parsed
sub-graph within the same engine, reading DOT source from node attributes and
propagating context diffs back to the parent pipeline.
Add GraphMergeTransform (transform.rs) that merges nodes and edges from secondary
graphs into a primary graph with namespace-prefixed IDs to avoid collisions.
Add WebInterviewer (interviewer/web.rs) backed by oneshot channels for async
question/answer flow, and HTTP server (server.rs) with 8 axum endpoints behind
a "server" feature flag for pipeline management and human-in-the-loop via web.
Fix tempfile dev-dependency usage in server production code by using std::env::temp_dir.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds the attractor crate implementing all 11 sections of the attractor spec:
- DOT parser (lexer, grammar, semantic analysis) for strict DOT subset
- Pipeline execution engine with edge selection, goal gates, retry logic,
failure routing, checkpoint save/resume, and loop_restart
- 9 node handlers: start, exit, codergen, wait_human, conditional, parallel
(concurrent with join/error policies), fan_in (with LLM eval), tool, manager_loop
- State management: PipelineContext, Outcome, Artifact store, fidelity resolution
- Human-in-the-loop: Interviewer trait with auto_approve, callback, queue,
recording, and console implementations, plus timeout enforcement
- Validation: 14 built-in lint rules with custom rule registration API
- Model stylesheet with universal/shape/class/ID selectors and specificity
- Transforms: variable expansion, stylesheet application, preamble; plus
PipelineBuilder with register_transform and prepare_pipeline
- Condition expression language with =, !=, bare-key truthiness, && combinator
- Event system with all 16 event types emitted by engine and handlers
- Tool call hooks (pre/post) for CodergenHandler
- Run directory with manifest.json and per-node status.json
370 tests (354 unit + 16 integration) covering all spec sections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract shared test infrastructure (MockExecutionEnvironment, TestProfile,
MockLlmProvider) replacing 11 duplicate mock implementations across tests
- Deduplicate tool execution logic between sequential and parallel paths
- Narrow ProviderProfile trait from 14 to 7 required methods via
ProfileCapabilities struct and default implementations
- Replace stringly-typed HashMap event data with typed EventData enum
- Extract shared assemble_system_prompt helper and register_subagent_tools
default method, eliminating copy-paste across all 3 profiles
- Replace fragile shell-based glob with glob crate, fix rg detection
- Add delete_file to ExecutionEnvironment, wire git context into env block
- Remove dead code (AgentError::Io, count_turns, trivial derived-trait tests)
- Use match-based lookups in truncation instead of per-call HashMap allocation
Net reduction: -1,401 lines across 20 files. All 180 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Close spec compliance gaps:
- Add user_instructions to SessionConfig and ProviderProfile trait (spec Section 6.1 layer 5)
- Populate EnvContext with git branch, date, model name during Session::initialize()
- Expand all three profile system prompts with identity, tool guidance, coding practices
- Implement provider_options: Anthropic beta headers, OpenAI reasoning effort, Gemini safety settings
- Add register_subagent_tools() to all profiles for spawn_agent/send_input/wait/close_agent
- Add list_dir tool to Gemini profile (spec Section 3.6)
- Add chrono dependency for date formatting
20 new tests (170 → 190), all passing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Abort now transitions to CLOSED state and returns Err(Aborted)
- Closed sessions no longer emit SessionStart before rejecting input
- Add set_reasoning_effort() for mid-session reasoning effort changes
- Fix spawn_agent truncation limit from 30k to spec-required 20k
- Add JSON Schema validation of tool arguments before execution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all stub tools in profiles with real make_*_tool() factories,
wire up project docs discovery in session, add missing events
(SessionStart, SteeringInjected, Error), enrich environment context
block, fix per-tool truncation modes, improve loop detection to check
all groups, add SIGTERM-before-SIGKILL on timeout, and update subagent
with SubAgentResult struct.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New crate `unified-llm-cli` with binary `ullm` providing:
- `prompt` command: generate text via streaming/non-streaming, with
system prompts, options (-o temperature=0.5), stdin piping, and
token usage display
- `models list` command: browse catalog models with --provider and
--query filters
- Auto-detects provider from catalog for correct API routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add abort signal support using CancellationToken for cooperative cancellation
of generate() and stream() calls
- Fix max_tool_rounds=0 to skip tool execution entirely (was executing first round)
- Add StreamResult wrapper with response(), text_stream(), partial_response()
and multi-step tool loop support in high-level stream()
- Add OpenAI metadata and provider_options.openai pass-through to Responses API
- Add Gemini provider_options.gemini pass-through (safety settings, cached content)
- Add OpenAI-compatible provider_options.<name> pass-through using adapter name
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Parse x-ratelimit-* headers into RateLimitInfo for Anthropic, OpenAI, and
OpenAI-compatible providers (previously hardcoded to None)
- Add "not found"/"does not exist" and "unauthorized"/"invalid key" error
message classification patterns for ambiguous HTTP status codes
- Apply TimeoutConfig.total to wrap the entire multi-step generate() loop
(previously only per_step was used)
- Add metadata field to GenerateParams with builder method, pass through to
Request instead of hardcoding None
- Implement stream_object() for streaming structured output with incremental
JSON parsing via new ObjectStreamEvent type (Partial/Delta/Complete variants)
- Add OpenAI-compatible Chat Completions adapter for third-party endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared provider helpers (parse_error_body, send_and_read_body,
extract_system_prompt, ApiMessage) into providers/common.rs, eliminating
duplicated logic across all three providers. Simplify StepResult and
GenerateResult to derive fields from the embedded Response rather than
storing redundant copies. Remove stringly-typed ToolCall.r#type and
ResponseFormat.r#type in favor of proper enums. Remove unused dependencies
(hyper, clap, rayon, base64, etc.), use LazyLock for the model catalog,
and fix miscellaneous idiom issues (unnecessary Vec collects, inconsistent
error Display, module_name_repetitions).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable clippy::all, pedantic, nursery, and cargo lint groups at the
workspace level. Fix all resulting warnings: merge identical match arms,
add #[must_use] and doc sections, derive Eq, use clone_from, add type
alias for complex types, extract helpers to reduce function length, and
add crate metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>