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>
When CodergenHandler receives CodergenResult::Text, it now extracts
preferred_next_label, suggested_next_ids, and context_updates from
the last JSON object in the response. This enables edge selection
via condition matching and preferred label instead of always falling
through to unconditional edges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
Bun-based React app with pipeline dashboard UI including event log,
graph view, context/checkpoint panels, question panel, and status bar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
When --docker is passed, agent tools execute inside a Docker container
via DockerExecutionEnvironment instead of running on the host. The host
working directory is bind-mounted into the container at /workspace.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a list endpoint that returns all in-memory pipelines with their
status, and a "Recent Pipelines" section in the start form that polls
every 3s so users can navigate to any pipeline started since server launch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
ApiQuestion now includes options and allow_freeform fields so API clients
can render multiple-choice questions. submit_answer accepts an optional
selected_option_key to produce AnswerValue::Selected instead of always
creating AnswerValue::Text.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
The handler was converting all backend errors into Ok(Outcome::fail(...)),
which the engine treats as a terminal result. Retryable errors (timeouts,
network failures) are now propagated as Err so the engine retry loop kicks in.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
The Responses API requires that reasoning items (type: "reasoning",
id: "rs_xxx") are included alongside their associated function_call
items when replaying conversation history. Without them:
"function_call was provided without its required 'reasoning' item"
Store reasoning output items as ContentPart::Other and replay them
as raw input items in translate_input. Handles both non-streaming
(parse_output) and streaming (handle_output_item_done) paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Responses API returns two distinct IDs on function_call items:
- `id` (item-level, starts with `fc_`)
- `call_id` (call-level, starts with `call_`)
When sending function calls back as input, the `id` field must start
with `fc_`. Previously we used `call_id` for both fields, causing:
"Invalid 'input[1].id': Expected an ID that begins with 'fc'."
Now we preserve the item-level `id` in provider_metadata and use it
for the `id` field, while `call_id` continues to link tool results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers two recent runtime failures that lacked test coverage:
- Anthropic: assert deprecated beta header values are not sent
- Gemini: test function call parsing/translation with and without thoughtSignature
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gemini 3 models include a thoughtSignature field on function call
parts that must be returned in subsequent conversation turns. Add
provider_metadata to ToolCall to carry this through, and extract/emit
it in both the non-streaming and streaming Gemini code paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Move the ullm binary from a separate crate into unified-llm as
src/bin/ullm.rs, consolidating CLI dependencies into the library crate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stores DOT source in ManagedPipeline and pipes it through `dot -Tsvg`
on request. Returns image/svg+xml on success, 502 if graphviz is
unavailable, 404 if pipeline not found. Resolves spec gap #1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The SSE stream terminated prematurely when a network chunk contained
only unhandled event types (e.g. response.in_progress, reasoning
output_item.added). dispatch_sse_messages returned an empty vec, which
the unfold interpreted as stream-end. Now continues reading when
dispatch produces no StreamEvents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Closes the subgraph class derivation test gap by adding a parse() pipeline
test that verifies DOT subgraph labels produce correct CSS-like classes on
contained nodes. Updates gap analysis to remove resolved items, add spec
contradictions section, and narrow remaining gaps to SVG rendering and
retry predicate customization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Gap 1: Real preamble synthesis per fidelity mode (truncate, compact,
summary:low/medium/high) in new preamble.rs module, replacing placeholder
- Gap 2: Pass thread_id to CodergenBackend.run() so backends can reuse
LLM sessions across nodes sharing the same thread
- Gap 7: Engine cancellation via AtomicBool token checked between nodes,
wired to server cancel endpoint, new Cancelled error variant
- Gap 8: RecordingInterviewer serialization (to_json/from_json, file I/O)
and new ReplayInterviewer for replaying recorded Q&A sessions
- Gap 12: Preset retry policies selectable from DOT via retry_policy attr
(none, standard, aggressive, linear, patient)
- Gap 15: Engine calls Interviewer.inform() at pipeline start, stage
start, and stage complete lifecycle points
37 new tests (450 unit + 49 integration, all passing).
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>
Eliminate ~300 lines of duplicated test infrastructure:
- Add MockExecutionEnvironment::linux() constructor, replacing 4
identical linux_env() helpers across profile test modules
- Extend MockExecutionEnvironment with written_files, captured_timeout,
and apply_read_offset_limit fields to replace 4 specialized mocks
(ReadFileEnv, WriteFileEnv, EditFileEnv, ShellCapturingEnv) in tools.rs
- Add MutableMockExecutionEnvironment for apply_patch tests that need
writes visible to subsequent reads, replacing MockFileEnv in openai.rs
- Merge ParallelTestProfile into TestProfile with configurable
parallel_tool_calls and context_window fields
- Replace ProviderTestProfile with TestProfile in provider_profile.rs
tests, updating TestProfile::build_system_prompt to include user
instructions like real profiles
- Extract shared CapturingLlmProvider into test_support.rs, replacing
two inline capturing provider structs in session.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Proposal 7.1: Introduce required_str() helper in tools.rs to replace ~15
repetitions of the args.get("param").and_then(|v| v.as_str()).ok_or_else(...)
pattern across tools.rs and subagent.rs. Provides consistent error messages.
- Proposal 4.2: Simplify SubAgentManager::spawn success path by using ? on
process_input() result directly, eliminating the redundant success variable
(always true on the Ok path) and the if-let-Err-return pattern.
- Proposal 8.2: Mark SubAgent::depth() and SubAgentManager::get() as
#[cfg(test)] since they are only used in tests. Remove SubAgent::id()
entirely as it was unused even in tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 2.2: Introduce BaseProfile struct with shared id/model/registry fields;
AnthropicProfile, GeminiProfile, OpenAiProfile now delegate to it
- 5.4: ProviderProfile::id() and ::model() return &str instead of owned String,
eliminating unnecessary heap allocations on every call
- 6.1: Rename EnvContext fields: date -> current_date, model_name -> model
for consistency with trait method names
- 8.1: Mark build_env_context_block (no-context variant) as #[cfg(test)]
since it is only used in one test
- 9.1: Convert SubAgentManager methods (spawn, send_input, wait, close) from
Result<T, String> to Result<T, AgentError>; tool executors convert at boundary
via .map_err(|e| e.to_string())
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove History::new() (redundant with #[derive(Default)]), replace all callers with History::default()
- Replace match with let-else in extract_signatures_from_assistant for clearer happy path
- Add doc comments to Turn::System and Turn::Steering explaining their LLM role mapping
- Verified: Arc import in openai.rs is used in production code, mutex .expect() messages are consistent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>