Commit Graph
61 Commits
Author SHA1 Message Date
Dark-Alex-17 9693cdd4ac feat(TASK-033): add hook config schema across all scopes
Introduce src/hooks.rs as a types-only module (HookDef, HooksMap) and
plumb hooks:/global_hooks: config fields through every scope:

- Config + AppConfig hooks field, both Default impls, the manual
  from_config copy, and a COYOTE_HOOKS env override
- Role frontmatter: hand-parsed hooks arm, private field, getter, and
  JSON-inline re-serialization in Role::export (malformed maps degrade
  to None)
- AgentConfig + Graph: hooks + global_hooks on both structs, copied in
  AgentConfig::from_graph, with a per-agent global_hooks env override
- paths::hooks_dir() = <config_dir>/hooks with COYOTE_HOOKS_DIR override
- Commented hooks: block in the global config template (global scope
  only; no global_hooks key)

No engine or dispatch logic yet; existing configs parse unchanged.
2026-09-16 13:44:34 -06:00
Dark-Alex-17 9e2f3c0f12 feat: auto continuation is unavailable in llm nodes; continuation driven loops should be in agent nodes only
CI / All (ubuntu-latest) (push) Failing after 28s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-09-15 11:42:39 -06:00
Dark-Alex-17 5b9852d80e fix: Require injection of user interaction functions for LLM nodes to be explicit 2026-09-15 11:13:12 -06:00
Dark-Alex-17 b87367bb1b style: Refactored some imports and LLM hints 2026-09-14 10:50:34 -06:00
Dark-Alex-17 ea76331c49 fix(graph): typed ApiStatusError transient classification; warn on bare output_schema
is_transient_error: a typed crate::client::ApiStatusError anywhere in the
chain is now authoritative (positive and negative) — catch_error renders
429s as "… (type: rate_limit_error)" / "… (status: 429)", which none of
the old string anchors matched, so provider rate limits were never
retried, while a 400 whose body mentioned "rate limit" was. Transient
statuses: 429, 500, 502, 503, 504, 529; message text only classifies
untyped errors, and those anchors are now case-insensitive.

validator: warn for an llm/agent node whose output_schema lacks a
non-empty object `properties` map (auto-merge writes nothing since
402d25a) unless the node lifts keys via a non-empty state_updates; the
same absent-or-empty predicate now also gates validate_fallback_capture.
Bundled and graph.example.yaml warning baselines are unchanged.

Tests build the 429 fixture through real catch_error output and a
.context() chain, cover the status table, typed-overrides-text, and all
validator branches.
2026-09-14 10:50:34 -06:00
Dark-Alex-17 1cf42fffa2 fix(adversary): fail closed on PIPELINE-FAULT evidence regardless of status; pin bail-chain and validator shape 2026-09-14 10:50:34 -06:00
Dark-Alex-17 f230c28bf4 docs: removed references to individual task files in the code documentation 2026-09-14 10:50:33 -06:00
Dark-Alex-17 80ace77e44 feat(TASK-016): engine + test fixes from the Phase-1 re-review
- warn-per-retry test via shared test-only WarnCollector (src/testing.rs)
- extraction-placement test: extraction failures are never retried
- is_transient_error: typed reqwest/Elapsed pre-check + anchored 429 matchers
- outcome_from docstring corrected; validator warning for
  fallback+output_schema without state_updates (llm + agent)
- validator error for max_attempts == 0 (llm + agent); runtime backstop kept
- nits: generalized branch-escape error text, Continue payload comment,
  BFS test assignments assert, node_id threaded into AgentNodeExecutor warns
- bundled-graph warning baseline records 3 true-positive step-runner nodes
  (orient, edge_case_sweep, write_handoff) pending state_updates there
2026-09-14 10:50:33 -06:00
Dark-Alex-17 be0c5462ed feat(TASK-009): add fallback routing to agent nodes
Agent nodes gain an optional fallback target with llm-node parity: on any
failure (retries exhausted, hard error, or structured-extraction failure)
the failure chain is written into state as 'Agent node failed: <chain>'
and the chain routes to the fallback node. Without a fallback the error
propagates unchanged. Validator collects agent fallback edges for
missing-target checks, reachability, and map-branch locality.
2026-09-14 10:50:33 -06:00
Dark-Alex-17 1c68331738 feat(TASK-008): add shared transient matcher and retry support to agent nodes
Hoist the llm node's transient-error matcher to graph::is_transient_error,
extend it with broken-pipe / connection-error / error-sending-request
signatures, and give AgentNode an opt-in max_attempts retry loop with
llm-parity semantics: contexts applied before the transient check, fresh
wall-clock budget per attempt, peer identity (assignment + registry)
re-armed before each retry, retirement exactly once after the final
attempt, and structured extraction outside the loop.
2026-09-14 10:50:33 -06:00
Dark-Alex-17 a7528d6e1a feat(graph): warn when a declared variable is shadowed by an initial_state key 2026-09-12 01:37:20 -06:00
Dark-Alex-17 99fb6e24f1 fmt: applied formatting 2026-09-11 10:56:28 -06:00
Dark-Alex-17 24b5504e21 fix(graph): build peer retirement guards before spawning their tasks
A task aborted before its first poll never runs its body, so a guard
constructed inside the future did not exist yet and the pre-provisioned
teammate identity stayed messageable. Built outside and moved in, the
guard drops with the unpolled future on both the frontier and map items.

Also rewords the two timeout: 0 validator warnings: the graph-level one
no longer claims max_loop_iterations always applies (it may be 0 too),
and the node-level one names the enclosing bounds that still end the
node instead of saying it runs until it returns.
2026-09-11 10:56:28 -06:00
Dark-Alex-17 02e2227aa0 fix(graph): reject agents that ship both config.yaml and graph.yaml at load
Agent::init refuses that layout, so any agent node pointing at such a
directory failed at run time while validation passed it as graph-capable.
2026-09-11 10:56:28 -06:00
Dark-Alex-17 778aa6449c fix(graph): bound concurrency caps by the semaphore permit limit
Semaphore::new panics above tokio's MAX_PERMITS, so a fixed or templated
map cap (or settings.max_concurrency) that large crashed the run instead
of erroring. The validator rejects both at load, resolve_max_concurrency
rejects every arm at run time, and the executor checks the graph setting
before building its own semaphore, so validation mode makes no difference.

Also corrects run_child_agent_with_graph_inputs' contract comment: inputs
for a config-only agent are rejected, not ignored.
2026-09-11 10:56:28 -06:00
Dark-Alex-17 32f4adb71f fix(graph): make turn-cap and template checks consistent across validation modes 2026-09-11 10:56:28 -06:00
Dark-Alex-17 727cab11ff fix(graph): validate the whole max_concurrency template string
A string max_concurrency passed the load-time check whenever it merely
contained a {{...}} pair, so forms like "n={{k}}" or "{{ key }}" that
can never resolve at runtime were only caught after the map's inputs had
been computed. The check now requires the trimmed string to be exactly
one template reference, matching what interpolate_raw resolves as a pure
reference; padded forms like " {{budget}} " stay valid.

Also: the bash tool build-failure context names the source file instead
of the destination path, and the map-subgraph validator no longer
shadows its member id list with the resolved member nodes.
2026-09-11 10:56:28 -06:00
Dark-Alex-17 ccec38c5b9 fix(graph): reject as == output_key on map nodes
A map node whose item binding shares its name with output_key would have
the binding collected as the chain's result whenever the chain never
wrote it, while the missing-writer warning stayed silent. The collision
is now a validator error attributed to the map node and, with validation
off, a runtime error in run_map before any item forks.
2026-09-11 10:56:28 -06:00
Dark-Alex-17 d7a16363b8 feat(graph): warn when an llm node's turn cap is disabled 2026-09-11 10:56:27 -06:00
Dark-Alex-17 4c5aa8d895 feat(graph): warn when the visit cap is disabled
validate_timeouts now also flags settings.max_loop_iterations: 0, ahead
of the settings.timeout: 0 warning so both opt-outs read top-to-bottom
as they appear in settings. (design §3.14 / C22)
2026-09-11 10:56:27 -06:00
Dark-Alex-17 cd0318f384 feat(graph): warn when a timeout is disabled
A timeout: 0 on settings.timeout or on an agent/script/llm/rag node is
the author's explicit opt-out of the wall-clock bound. It stays legal;
the validator now emits one warning per zero so the choice is visible in
--validate output. Non-zero and unset values do not warn (unset already
means no bound for settings.timeout and llm nodes, matching the
explicit-zero-only convention of the orchestration-limit warnings).

Design §3.13 / C21.
2026-09-11 10:56:27 -06:00
Dark-Alex-17 10b8fe5cae feat(graph): validate agent node inputs at load time
Inside validate_agents: an agent node with inputs whose target has a
config.yaml but no graph.yaml is an error, and an inputs key named
initial_prompt is rejected. primary_templated_fields now includes every
inputs value for agent nodes, so validate_parallel_reads treats them as
cross-branch reads with no new pass.

Design: plans/map-subgraph-branches-design.md §3.12 V10a–V10c, task T5.
2026-09-11 10:56:27 -06:00
Dark-Alex-17 3393123baf feat(graph): seed a child graph agent's state from an agent node's inputs map
Agent nodes gain an optional `inputs:` map of templates resolved against
the parent state and overlaid on the child graph agent's initial_state
before its executor starts. A lone {{key}} keeps the state value's JSON
type; mixed text renders to a string; initial_prompt is still owned by the
dispatcher. run_child_agent and run_active_agent_graph keep their
signatures as wrappers over the new *_with_inputs variants, and
run_agent_for_graph rejects inputs aimed at a config-only agent or
containing initial_prompt.

Design: plans/map-subgraph-branches-design.md §3.12 (C18), task T5.
2026-09-11 10:56:27 -06:00
Dark-Alex-17 b65aa76d2d docs: updated the formatting of a few comments 2026-09-11 10:56:27 -06:00
Dark-Alex-17 824edf587b feat(graph): validate map branch subgraphs instead of single branch nodes
`validate_map_branches` checked only the entry node and forbade `next`,
non-`output_key` `state_updates` and `output_schema` on it. The runtime
now executes the whole subgraph reachable from `branch`, so the validator
follows suit as `validate_map_subgraphs`, at the same position in
`validate()`:

- approval/input/end/map nodes are rejected anywhere in the subgraph,
  attributed to the map node; when the offending node is a `fallback`
  target the message says why a previously accepted `fallback` now fails
- a fan-out `next` inside the subgraph is rejected
- a subgraph that shares nodes with the main flow (reachable from
  `start` without entering any map branch, via the new
  `main_flow_reachable`) is rejected, naming the shared nodes
- a new warning fires when no subgraph node declares a write to
  `output_key` via `state_updates` or `output_schema`; any script in the
  subgraph suppresses it, since scripts may write any key
- the `next`, `state_updates` and `output_schema` restrictions are gone:
  intra-branch writes are fork-local scratch

Two maps may share a subgraph. `find_reachable_nodes` and
`compute_parallel_groups` are unchanged.

Tests: the three rules that inverted are renamed to say what they now
assert (`map_branch_with_in_subgraph_next_passes`,
`map_branch_state_updates_scratch_key_passes`,
`map_branch_with_output_schema_passes`); the four
`map_branch_cannot_be_*` tests pass unmodified. Thirteen new tests cover
each rule, the fallback hint, the missing-writer warning and its three
suppressions, a three-node chain, and `main_flow_reachable`.
2026-09-11 10:56:27 -06:00
Dark-Alex-17 0083cf0c42 test(graph): cover map subgraph chains, output contract and teammate lifetime 2026-09-11 10:56:26 -06:00
Dark-Alex-17 f1cf9a6ae7 feat(graph): run map branches as per-item subgraph chains
A map item no longer runs a single branch node; it walks the subgraph
reachable from `branch` over `next`/`fallback` edges (branch_subgraph),
one chain per item, holding one semaphore permit for the whole chain.
Chain steps go through the shared `step()` so llm retries, fallbacks,
script `_next` and agent timeouts keep their existing semantics. A node
without `next` ends the chain (StepContext.branch_mode), a route outside
the subgraph, a fan-out, an end node or a loop past
max_loop_iterations fails the item, and every item error carries
`map node 'X': sub-branch [i] failed at node 'Y' (step n)`.

`output_key` is removed from each item's fork before the chain runs, so
the collected value must be written by the chain rather than inherited
from the parent.

Teammate identities are now per item for the whole chain: provisioned
when any flagged agent is in the subgraph, armed only before
`teammates: true` agent steps, and retired at chain exit. mark_finished
moves out of run_agent_for_graph to the two provisioning sites (frontier
task and chain exit), so timed-out and early-failing agents are also
marked finished.
2026-09-11 10:56:26 -06:00
Dark-Alex-17 2049316364 feat(graph): accept a {{template}} for a map node's max_concurrency
`MapNode.max_concurrency` becomes `Option<ConcurrencyCap>`, where
`ConcurrencyCap` is `Fixed(usize) | Template(String)`. Integers parse
exactly as before (`4` -> `Fixed(4)`, `0` -> `Fixed(0)`, absent -> `None`);
a string parses as a template that the map executor resolves against the
parent state right before the fan-out via `StateManager::interpolate_raw`,
mirroring `over:`. A template must resolve to an integer >= 1 or a numeric
string (scripts often emit numbers as strings); anything else, including 0,
is an error naming the map node, the template, and the resolved value/type.
The fixed path keeps its `.max(1)` clamp for byte-compatibility.

`Deserialize` is hand-written so a malformed value (`-1`, `4.5`, `true`,
a list, a map) reports the offending value and the accepted forms rather
than serde's opaque untagged-enum "did not match any variant".

Validator: a string cap that contains no `{{ ... }}` pair is a load-time
error (catches `"4"` typed for `4`); a templated cap joins the map node's
read set so `validate_parallel_reads` flags a read of a sibling branch's
write. The existing `Fixed(0)` rule and every other rule keep their order.

Design: plans/map-subgraph-branches-design.md §3.5, §3.11 (C3, I3).
2026-09-11 10:56:26 -06:00
Dark-Alex-17 47edaa35ff feat: teammates flag enabling peer messaging between concurrent graph agent nodes
Agent nodes marked teammates: true whose sub-agents run concurrently (map fan-out branches or multiple flagged agent nodes in one super-step) now form a team: each member gets a pre-provisioned inbox in a messaging-only PeerRegistry before any branch starts, a teammate roster injected into its prompt, peer resolution in agent__send_message, and a peers section in agent__list_running. Registries are scoped to a single fan-out and carry no lifecycle semantics: outputs still flow exclusively through collect_into/state reducers, finished peers reject new messages, and nothing registers in a supervisor. Also hardens the agent-recursion depth guard to work without a supervisor and adds validator warnings for zero-valued orchestration limits.
2026-09-09 16:44:23 -06:00
Dark-Alex-17 1e15bc31d5 style: applied uniform style across files 2026-08-28 11:35:45 -06:00
Dark-Alex-17 92d9464b38 feat(mcp): enforce per-server tool allowlists across runtime, jobs, agents, and graph nodes 2026-08-27 14:43:18 -06:00
Dark-Alex-17 a92ebb5b94 feat(mcp): add mcp_tools allowlist config surfaces across roles, sessions, agents, graphs, and skills 2026-08-27 14:13:35 -06:00
Dark-Alex-17 fa04e09373 feat(graph): support max_concurrent_jobs at the graph level
Graph agents could only inherit the app-wide job budget; the agent-level
header in graph.yaml now accepts max_concurrent_jobs alongside
model/temperature, flowing through AgentConfig::from_graph into the
run-wide supervisor. Deliberately graph-wide, not per-node: jobs outlive
the node that started them.
2026-08-26 12:53:17 -06:00
Dark-Alex-17 860566bf50 feat: let workflow rag nodes select a RAG driver
`RagNode` gains an optional `driver`, forwarded into `RagInitConfig` so a
graph node can build its knowledge base on duckdb instead of yaml. Nodes
that name no driver forward `None`, which still resolves to yaml, so
existing workflows are unaffected.

An unknown driver is rejected up front rather than at construction time.
`Rag::create` dispatches unknown drivers to its yaml catch-all, so a typo
would otherwise embed every document and persist the bogus string, after
which every subsequent load fails validation and the agent cannot start.
The check asks `RagData::validate()` through a probe value instead of
restating the list of valid drivers, so the two cannot drift.
2026-08-11 13:46:59 -06:00
Dark-Alex-17 e9a8c01dc4 feat: Added support for modifying the reasoning effort of reasoning models 2026-07-16 12:28:04 -06:00
Dark-Alex-17 7fc06ad9bc feat: Implemented graph-based RAG 2026-07-08 21:20:50 -06:00
Dark-Alex-17 165d0d113d feat: added skill hint prompt injection and configuration 2026-06-05 14:48:54 -06:00
Dark-Alex-17 b997e9493c fix: accidental regression on enabled_skills being empty = all 2026-06-04 16:12:32 -06:00
Dark-Alex-17 e54a2e42c9 test: improve vault password file errors by propagating up 2026-06-04 15:32:31 -06:00
Dark-Alex-17 6c4e042dad fix: add agent context check to skill visibility validation 2026-06-04 14:19:38 -06:00
Dark-Alex-17 ebf3b5f776 test: improved skill validation test in graph validator 2026-06-04 14:02:34 -06:00
Dark-Alex-17 84dcb3078b feat: validate visible_skills field at config load time 2026-06-04 13:43:40 -06:00
Dark-Alex-17 7b320e08c4 fix: enforced global visible_skills in llm node validation and improved skill loading error handling across the project 2026-06-04 13:09:43 -06:00
Dark-Alex-17 5ebf8649a6 fmt: applied uniform formatting across refactored vault code 2026-06-03 11:15:11 -06:00
Dark-Alex-17 2acff31213 feat: llm graph nodes support skills 2026-06-02 12:39:43 -06:00
Dark-Alex-17 558b764db8 feat: Added agent variables support for graph agents and improved script executor to use the same environment variables as normal agent tool calling for further flexibility 2026-05-21 13:27:33 -06:00
Dark-Alex-17 597f823bdf fmt: cleaned up graph implementation 2026-05-21 11:27:29 -06:00
Dark-Alex-17 3c7d19da07 fix: Added additional graph validation for parallel reads and writes with dependencies between nodes states 2026-05-20 17:35:33 -06:00
Dark-Alex-17 a9f2a5edc2 feat: validation support for parallel graph execution; restricted map nodes to only run for nodes without next targets and not supporting chained map nodes 2026-05-20 12:50:29 -06:00
Dark-Alex-17 8fb0eece4b feat: scaffolding work for fan-out nodes for parallel branch execution support and stubbed out Map node types 2026-05-20 11:37:23 -06:00