Runs the pipeline engine with a live Daytona sandbox: handler produces
150KB output, engine offloads to local artifact store, syncs to the
sandbox, and the checkpoint pointer references the remote path. Verifies
the file is readable in the sandbox.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update daytona-sdk-rust to 5d370099 which fixes multipart file upload
(upload_file was sending an empty form). Add E2E test that verifies
artifact pointers are rewritten and files uploaded to a live Daytona
sandbox.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After offloading large context values to local disk, check whether each
artifact file is accessible in the execution environment. When it isn't
(Docker/Daytona), read the local file and upload it via write_file,
rewriting the pointer to the remote path. Local envs skip the sync
since file_exists returns true for local paths.
Also switches the pointer prefix from artifact:// to file://.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node stage directories ({logs_root}/{node_id}/) could collide with
{logs_root}/artifacts/ if a node has id="artifacts". Fix by nesting
all node directories under a nodes/ subdirectory so the layout becomes:
{logs_root}/manifest.json
{logs_root}/checkpoint.json
{logs_root}/nodes/{node_id}/status.json, prompt.md, response.md, ...
{logs_root}/artifacts/{key}.json
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After a handler returns, the engine automatically offloads context values
exceeding 100KB into the ArtifactStore, replacing them with artifact://
pointer strings. This prevents bloating Context, Checkpoints, and
status.json files. The preamble renders "See: {path}" for artifact
pointers instead of inlining large content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop the `tool.output` context key (unused alias from tool→script rename)
and rewrite the failure branch to include stdout in both `failure_reason`
and `script.output` context, so build/test output isn't lost on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
summary:medium now uses compact handler-specific rendering (script
command/stdout/stderr, codergen model/files) instead of dumping raw
context_updates. summary:low adds minimal handler info (type, script
command, or model name) to each stage line.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use ? operator instead of if-let-err pattern in backend.rs
- Collapse nested if blocks in preamble.rs summary:medium path
- Allow too_many_arguments on CodergenBackend trait and llm_evaluate fn
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Compact mode now renders nested-bullet summaries with handler-specific
sub-items (script: command/stdout/stderr; codergen: model/tokens/files)
under a ## Completed stages heading. Summary:high renders per-stage
## Stage sections with full detail and a ## Current context table.
Also captures script.stderr in context_updates for both success and
failure branches, and improves context filtering across all modes to
exclude engine keys (graph.*, thread.*, response.*, last_stage, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CodergenHandler now reads `current.preamble` from context and prepends it
to the prompt before dispatching to backends. For `full` fidelity the engine
sets preamble to empty, so the prepend is a no-op. This ensures downstream
LLM nodes see prior node outputs (e.g. script stdout) without any trait
changes.
AgentBackend gains a `sessions` cache keyed by thread ID. When fidelity is
`full` and a thread_id is present, sessions are reused across nodes so the
LLM sees the full conversation history. Usage aggregation only counts turns
added after the reuse point to prevent double-counting. Failed sessions are
dropped rather than cached.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused set_event_callback on MockExecutionEnvironment and
replace always-true u64 >= 0 comparisons with is_u64() checks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exercises the old tool_command attribute through validate and
dry-run to catch regressions in the tool → script compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Register "tool" as an alias for ScriptHandler, fall back to
tool_command attribute, dual-write tool.output context key, and
add "tool" to known handler types for validation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename the entire "tool" concept to "script": ToolHandler → ScriptHandler,
tool_command attribute → script, tool.output → script.output, and all
related artifact filenames. Add a language attribute (shell | python,
default shell) so script nodes can run Python via python3 -c natively.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SSH git URLs (git@github.com:...) cause "invalid auth method" errors
since Daytona uses HTTPS token auth. Added ssh_url_to_https() to
convert them before cloning. Also removed the static sandbox name
config field and auto-generate unique names with timestamps to prevent
"already exists" errors on repeated runs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces ExecutionEnvEvent enum (15 variants covering lifecycle, Docker
image pull, Daytona snapshot, and git clone operations) with callback
injection on all execution environment implementations. Adds
PipelineEvent::ExecutionEnv wrapper and Setup* variants for setup command
tracking. Wires callbacks in run.rs so exec env and setup events flow
through the pipeline event emitter to stderr and progress.ndjson.
Also changes PipelineEngine::new to accept Arc<EventEmitter> so the
emitter can be shared with exec env callbacks before engine construction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of creating a separate Docker/Daytona sandbox per stage inside
AgentBackend::run(), create the execution environment once in
run_command(), initialize it, run setup commands, and pass it through
EngineServices to all handlers. This lets multi-stage pipelines share
file changes across nodes and avoids redundant container/sandbox creation.
Key changes:
- Add execution_env field to EngineServices
- Add execution_env param to CodergenBackend::run() trait method
- Remove env creation, setup_commands, daytona_config from AgentBackend
- Lift env lifecycle (create, initialize, setup, cleanup) to run_command()
- Use scopeguard for cleanup on error/panic paths
- Update PipelineEngine constructors to accept execution_env
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 92cbe0d3c133
Allow specifying execution environment (local/docker/daytona) in TOML
task configs so pipelines are self-contained. CLI flag takes precedence
over TOML, which takes precedence over the default (local).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 600b7720bbda
Replace the `--docker` boolean flag with `--execution-env <local|docker|daytona>`
to support three execution environments. The new `DaytonaExecutionEnvironment`
uses the Daytona cloud sandbox SDK to run agent tools remotely, auto-cloning the
current git repo into the sandbox via `gh auth token` credentials.
Setup commands from TOML task configs now run inside the execution environment
(via `exec_command` after `initialize()`) rather than locally, so they work
correctly for both Docker and Daytona sandboxes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 306da8ff6de1
Allows users to define variables in their TOML task config that get
expanded as $name placeholders in the DOT file before parsing. This
enables parameterized pipelines without duplicating DOT files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 293af13a0e54
Support `attractor run task.toml` as an alternative to `.dot` files.
Auto-detects format by file extension. TOML bundles pipeline config
(graph path, model, setup commands, working directory) into a single
file with precedence: CLI flag > TOML > DOT graph attrs > defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 8744d57d6053
Adds 8 log file outputs to reach parity with Kilroy's per-node and
run-level logging:
- panic.txt: written on handler panic with the panic message
- graph.dot: DOT source saved to logs dir at run start
- run.pid: process ID saved to logs dir at run start
- final.json: run outcome (status, duration, failure_reason) written
after engine completes, before error propagation
- parallel_results.json: branch results written per parallel node
- provider_used.json: mode/provider/model written by AgentBackend
- api_request.json: serialized LLM request in one_shot mode
- api_response.json: serialized LLM response in one_shot mode
Adds stage_dir parameter to CodergenBackend trait methods so backends
can write artifacts to the correct per-node log directory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: a3934db0f826
ToolHandler now writes 4 files to {logs_root}/{node_id}/:
- tool_invocation.json (command, timeout_ms) before execution
- stdout.log and stderr.log after command completes
- tool_timing.json (duration_ms, exit_code, timed_out) after execution
The timeout branch also writes tool_timing.json with timed_out: true
and exit_code: null. Inlined process_output since stdout/stderr are
now needed in the surrounding scope for file writes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 6c9fde8bdc72
Enables simple generation nodes (summarization, classification, routing)
without the overhead of a full agent session. Nodes default to agent_loop
when the attribute is absent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 1574218eca96
Enrich three AgentEvent variants with payload data (UserInput.text,
TurnLimitReached.max_turns, SteeringInjected.text) and forward
UserInput/SteeringInjected to the pipeline event stream. Add cost
field to StageUsage (computed from model pricing catalog) and
total_cost to PipelineCompleted (summed from stage outcomes).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: adb2363c9ec4
Apply redact::redact_jsonl_line at all three serialization sites so
secrets (AWS keys, GitHub PATs, private keys, etc.) are scrubbed
before reaching disk or the network.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: e23c92750cd9
Cyclic graphs (e.g. consensus_task.dot, semport.dot) loop infinitely
under --dry-run because the mock backend always returns outcome=success,
which never matches conditional edges. Add a configurable max_node_visits
limit (graph attr, default 0=disabled) that defaults to 10 in dry-run
mode, terminating execution when any node is visited more than the limit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 633b59435252
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
The run and serve commands now resolve model aliases (e.g. "claude-haiku"
→ "claude-haiku-4-5-20251001") through the catalog before passing to the
backend, matching the behavior of the ullm CLI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 2b1f7b77b1ab
- 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
The parser used separated_list1 for attr_block which required at least
one attribute inside brackets. DOT files from kilroy use empty brackets
(e.g., `consolidate_dod []`) which caused parse failures. Changed to
separated_list0 to accept empty attribute blocks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: bccbc8222bde
Makes Haiku discoverable via list_models and get_model_info with
aliases "haiku" and "claude-haiku".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: ed7df801c2a4
Emit missing progress NDJSON data identified in gap analysis:
- Add attempt/max_attempts fields to StageStarted, StageCompleted, StageRetrying
- Add EdgeSelected event emitted after edge selection with label/condition
- Add LoopRestart event emitted before recursive loop-restart execution
- Simplify backend.rs usage aggregation to use Usage::Add impl
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f47850c1422d
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
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
Thread existing data through to pipeline events to match kilroy's
progress.ndjson schema: handler_type on StageStarted, failure_reason on
StageFailed/StageCompleted, notes on StageCompleted, join_policy and
error_policy on ParallelStarted, status (replacing success bool) on
ParallelBranchCompleted, and question_type on InterviewStarted. Adds
Display impls for QuestionType, JoinPolicy, and ErrorPolicy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2d4a98c3b5f0
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
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
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
Layered library for blob/tree/commit/ref operations without touching
the working directory. Four modules: gitobj (primitives), branchstore
(key-value on a branch), snapshot (working dir captures), trailerlink
(commit message trailers). 56 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 1e40b3267885
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