Commit Graph
1053 Commits
Author SHA1 Message Date
Dark-Alex-17 544a6bbbbc feat(cli): rename mcp_config asset category to mcp-config with snake_case alias 2026-08-28 12:22:21 -06:00
Dark-Alex-17 772c19f2bd docs: Added example MCP server allowlisting to the example configuration files 2026-08-28 12:06:58 -06:00
Dark-Alex-17 1e15bc31d5 style: applied uniform style across files 2026-08-28 11:35:45 -06:00
Dark-Alex-17 c170d08654 fix(mcp): omit declared-but-empty prompt/resource capabilities from .info mcp-server 2026-08-28 11:33:03 -06:00
Dark-Alex-17 c707aaf3ee fix(mcp): annotate declared-but-empty prompt/resource capabilities in .info mcp-server 2026-08-28 10:48:43 -06:00
Dark-Alex-17 2d9829106d Merge remote-tracking branch 'origin/main' into feat/mcp-tool-whitelist 2026-08-28 09:55:05 -06:00
Dark-Alex-17 3d24719a71 docs: changed Sharing-Configurations to Bundles
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-27 16:13:05 -06:00
Dark-Alex-17 290f6b257f docs: refined the description of coyote better 2026-08-27 15:55:13 -06:00
Dark-Alex-17 e7811a6b87 feat(mcp): add .info mcp-server, .set mcp_tools, and filtered-server listing to the REPL 2026-08-27 15:45:39 -06:00
Dark-Alex-17 94ede264e5 docs: updated links to be fully qualified in main README
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-27 15:44:09 -06:00
Dark-Alex-17 92c8ff8934 Merge branch 'main' of github.com:Dark-Alex-17/coyote
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-27 15:36:42 -06:00
Dark-Alex-17 af697d129b docs: updated the coyote description to be more accurate to its true use and purpose 2026-08-27 15:34:38 -06:00
Dark-Alex-17 895ecc812f docs: updated the Coyote tagline 2026-08-27 15:30:30 -06:00
Dark-Alex-17 c0224e20cb test(mcp): drive session re-attach and RAG attach filter guards through their real entry points
- use_session_applies_persisted_mcp_tools_immediately now loads a session
  file from disk through the real use_session, proving the post-assignment
  filter refresh applies a re-attached session's persisted allowlist.
- New use_rag_does_not_drop_role_filters loads a yaml-driver RAG through
  the real use_rag and proves the tool-scope rebuild recomputes the role's
  filter layer instead of dropping it.
- Seed the process-wide client/model registries in a pre-main ctor (new
  ctor dev-dependency) so model resolution is deterministic across test
  orderings; the seed exposes only an embedding model so tests that assert
  'no chat model available' keep their premise.
2026-08-27 15:12:22 -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 93e90106a8 feat: create a new probe agent to probe code verifications using usage-pattern testing 2026-08-27 13:59:28 -06:00
Dark-Alex-17 35e4c82f27 feat: Add in the web_search_coyote tool where helpful 2026-08-27 13:59:09 -06:00
Dark-Alex-17 5711432ac4 feat(mcp): add per-server tool allowlist policy module and allowedTools config field 2026-08-27 13:37:46 -06:00
Dark-Alex-17 ee45e42013 fix: also support the .continue edge case for session crashing checkpointing 2026-08-27 12:55:26 -06:00
Dark-Alex-17 3d640b9efa fix: checkpoint sessions that crash for easy resuming 2026-08-27 12:49:07 -06:00
Alex Clarke 730f942bab Merge pull request #20 from Dark-Alex-17/fix/job-output-tailing
fix(jobs): stream the tool's output file into the job peek ring
2026-08-27 12:31:13 -06:00
Dark-Alex-17 2efeda0ba7 fix: job__check ring buffer also collects file output for LLM_OUTPUT as well as stdout 2026-08-27 12:17:34 -06:00
Dark-Alex-17 df62c32822 feat: support mcp.json in the root of bundle repos as well as in the legacy functions directory 2026-08-26 15:48:16 -06:00
Dark-Alex-17 3762bbe09f feat: prefer ~/.config/coyote/mcp.json for the user-scope MCP config
The user-scope MCP config historically lived at
<config-dir>/functions/mcp.json, a leftover from when MCP support was
part of the llm-functions tooling. It now resolves through a single
choke point with these semantics:

- Preferred location: <config-dir>/mcp.json (created there on first run)
- Historical <config-dir>/functions/mcp.json still honored when the
  preferred file does not exist, so existing installs are unchanged
- If both exist, the preferred location wins

--info/.info now reports the resolved location as mcp_config_file, and
the --scope help text plus config.agent.example.yaml reference the new
default. This also removes the asymmetry with the workspace scope,
which already used .coyote/mcp.json directly.
2026-08-26 15:34:20 -06:00
Dark-Alex-17 ebe7816600 feat: claude and openai native web search via web_search_coyote
CI / All (ubuntu-latest) (push) Failing after 30s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-26 15:22:24 -06:00
Alex Clarke 3ebe11a4f0 Merge pull request #19 from Dark-Alex-17/feat/background-jobs
feat: background jobs (job__* tools) with push notifications
2026-08-26 15:13:56 -06:00
Dark-Alex-17 429ae3cc8e refactor(function): finish supervisor-to-agent vocabulary migration
The supervisor registry went kind-generic (TaskHandle::Agent | Job)
earlier in this branch, but the module holding the agent__* handlers
and two model-facing error strings still carried the old name:

- src/function/supervisor.rs -> src/function/agents.rs (it contains
  only agent__* tool handlers, pairing with function/jobs.rs; the
  kind-generic src/supervisor/ registry keeps its name)
- 'Supervisor tool failed' -> 'Agent tool failed'
- 'Unknown supervisor action' -> 'Unknown agent action'
2026-08-26 14:57:36 -06:00
Dark-Alex-17 304b8f635f fix(tools): interactive-shell semantics and stderr capture in execute_command
Two long-standing agent-facing defects:

1. bash -e aborted the model's script at the first intermediate
   non-zero status (grep with no matches exits 1, inspecting a failing
   test run, a probing subshell), so trailing guards like '; exit 0'
   never executed and output was partially or entirely lost. Dropped
   -e: the last statement now decides the exit code, matching the
   interactive-shell semantics models expect. pipefail is kept so a
   failing pipeline stage still surfaces in the exit code.

2. Only stdout was redirected into $LLM_OUTPUT, and the harness
   returns just $LLM_OUTPUT on success, so commands whose useful
   output goes to stderr (git push, cargo progress, curl -v) returned
   empty on success. Added 2>&1.
2026-08-26 14:15:34 -06:00
Dark-Alex-17 198c9f42df fix(function): gate test-only declaration appender behind cfg(test)
append_declaration is exercised only by unit tests; in the plain bin
target it tripped dead_code under CI's RUSTFLAGS --deny warnings.
2026-08-26 13:50:44 -06:00
Dark-Alex-17 404a45a311 feat(jobs): node-local job ownership and capability-gated job__* visibility
Graph LLM nodes now own the jobs they start, on every exit path. A new
node_job_scope on RequestContext records job ids started while a node
runs: the turn-end guardrail nags only about the node's own jobs
(parallel branches no longer see each other's), and the node executor
reaps — cancels and deregisters — anything left registered when the
node exits, including error, timeout, and retry-exhaustion paths.
Cross-node job handoff is no longer possible; a crashed node takes its
in-flight jobs with it.

With inheritance gone, job__* declarations are gated on capability:
the family is only declared when at least one declared tool would pass
job__start's whitelist (shared predicate: is_backgroundable_tool). One
carve-out — while a context still owns registered jobs (job started,
tool disabled mid-session), the lifecycle verbs stay declared so a
running job can never become unreachable; job__start alone disappears.
A graph node with tools: [] now sees no job__* tools at all.

Prompt instructions, tool declarations, and graph.example.yaml updated
to the node-local semantics; +7 tests, 8 visibility pins rewritten.
2026-08-26 13:43:05 -06:00
Dark-Alex-17 bfb8105682 fix(graph): gate unix-only test imports behind cfg(unix)
The executor integration-test module hoisted job-test paths into
module-level imports, but their only consumer is a #[cfg(unix)] test —
on Windows the imports went unused and failed -D warnings.
2026-08-26 13:00:16 -06:00
Dark-Alex-17 1650196cae docs(config): document max_concurrent_jobs in agent example config 2026-08-26 12:53:17 -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 074083af31 feat(jobs): allow uncapped collect via full_result
job__collect's 50k-char tail cap is a safety default, but collect is
consume-once and the cap was mandatory — a model that genuinely needed
the complete output had no recourse. Add a full_result boolean that
skips the cap (tail_lines still honored; the session-wide
max_tool_result_chars limit still applies downstream), teach the
truncation banner to name the recourse, and point job__check's
output_bytes_captured at the collect decision.
2026-08-26 12:53:17 -06:00
Dark-Alex-17 c376737bbd refactor(function): rename agent-tool symbols out of supervisor vocabulary
Since the supervisor registry became kind-generic (agents AND jobs),
'supervisor' naming on the agent__* tool plumbing was misleading:
job__* handlers operate on the same supervisor. Rename
SUPERVISOR_FUNCTION_PREFIX -> AGENT_FUNCTION_PREFIX,
supervisor_function_declarations -> agent_function_declarations,
handle_supervisor_tool -> handle_agent_tool. No behavior change.
2026-08-26 12:53:17 -06:00
Dark-Alex-17 4e50b4ff4a refactor: Modified the naming of several generalized supervisor values 2026-08-26 12:43:51 -06:00
Dark-Alex-17 5a9f8c42b9 test: assert memory routing without depending on host memory files
memory_config() only reports enabled when a global memory index or a
workspace memory store exists on disk, so asserting the handler's
'name is required' detail was environment-dependent even with the
memory pref forced on. The routing test now accepts either
memory-handler-owned message: the 'Memory tool failed' prefix alone
proves the memory__ prefix reached the memory handler.
2026-08-25 20:52:31 -06:00
Dark-Alex-17 2eb63cfc0d test: pin memory config on in eval-routing test for environment-independent CI
The eval_routes_memory_prefix_to_memory_handler characterization test
inherited the host machine's memory configuration: on runners without a
memory setup, should_register_memory_tools() gates the handler off and the
error message differs. Force memory = Some(true) at ctx construction so
the test asserts the same handler path everywhere.
2026-08-25 20:47:34 -06:00
Dark-Alex-17 28018f33c9 test(jobs): add feature, hardening, and surface test matrix for background jobs
Covers the plan's T7 matrix: zero-diff invariants when jobs are off
(byte-identical tool lists and prompts, None-vs-Some select_functions),
validation hardening (shell/path-shaped/PATH-resolvable names, undeclared
MCP servers, non-whitelisted and context-filtered tools, mapping-tool
aliases, mid-batch tool-scope freshness), process lifecycle (grandchild
process-group kill, pgid clear after normal completion, panic skips the
completion notification), guardrail behavior (finished-job discard on
force-terminate, bounded inject-then-terminate iteration burn), surface
conformance (concrete_tool_names exclusion, toggle rejection, tools_info
listing, infra preservation under empty filters), supervisor swaps
(use_agent/exit_agent kill running jobs, child contexts cannot reach
parent job ids), and graph-node job lifecycle with deferred notification
drain.
2026-08-25 20:33:10 -06:00
Dark-Alex-17 6256b5fcfa docs: document background jobs across prompts, config example, and README
- Extend the injected Background Jobs prompt guidance: system_notifications
  push on completion, collect-only-when-idle wait protocol, and the graph
  LLM-node collect-before-final-turn rule
- Mention the system_notifications push in the agent spawning guidance and
  in the sisyphus/architect wait-protocol text (agent completions push
  notifications too)
- config.example.yaml: max_concurrent_jobs (default 5, 0 = disabled)
- README: features-list entry pointing at the Background-Jobs wiki page
2026-08-25 18:24:44 -06:00
Dark-Alex-17 24ed674952 feat(jobs): exempt polling tools from loop tracker and hint on unchanged checks 2026-08-25 18:17:15 -06:00
Dark-Alex-17 caabf41b65 feat(supervisor): push agent completion notifications to the spawning context
The spawned-agent task now pushes an agent_completed/agent_failed event
into the spawning context's notification queue before returning, so a
parent that keeps working learns mid-turn that a child finished instead
of discovering it only at the turn-end guardrail. Cancelled or
already-collected agents are suppressed by the existing drain-time
registration filter. This delivery applies regardless of whether
background jobs are enabled.
2026-08-25 17:52:19 -06:00
Dark-Alex-17 2d874f1d7c feat(jobs): push background-job completion notifications via per-context queue
- add NotificationQueue/SystemNotification: every context owns a fresh
  queue (children never inherit the parent's, avoiding first-drainer-wins
  races between transcripts)
- job tasks push job_completed/job_failed events on completion, failure,
  and timeout; a panic skips the push and is surfaced by the guardrail's
  finished-handle enumeration and collect's JoinError mapping instead
- events for jobs already collected or cancelled are dropped at drain time
  by filtering against live supervisor registration
- replace inject_escalation_notification with single-pass
  merge_system_channel: pending_escalations (root-only) ordered before
  system_notifications (any depth) on the last tool result of a batch;
  byte-identical output when notifications are empty, proven by the
  unmodified pre-merger characterization tests
2026-08-25 17:50:28 -06:00
Dark-Alex-17 6a694d10db test(jobs): assert kind-aware guardrail surfaces running jobs
Reconciles the T2 guardrail delta with the T3 jobs-only-supervisor test
at merge time, per plans/background-jobs-design.md §11 merge order.
2026-08-25 17:38:57 -06:00
Dark-Alex-17 177d61cf94 fix: harden job runner lifecycle and whitelist conformance
Reject fast built-in file tools (fs_* / ast_grep) in job__start per the
backgroundable-tools whitelist; clean up env-snapshot temp files on every
exit of run_process_job via a drop guard; bound the output-pump awaits and
abort them on the failure path; treat signal death (no exit code) as a
failure with a teaching message; bound job__collect's post-drain join with
a SIGKILL escalation so a TERM-ignoring process cannot hang collect after
a Ctrl-C teardown; document the unguarded SIGTERM pid-reuse window; give
the injected Background Jobs prompt section a fresh line on both sides;
extract the MCP server name with strip_prefix instead of replace.

Capacity-0 audit for jobs-disabled contexts: REPL displays have no
supervisor consumers (only Ctrl-C/exit cancel_recursive at
repl/mod.rs:460,473, kind-agnostic); session save/load does not persist
supervisor state (src/config/session.rs has no supervisor references) --
nothing to test for either.
2026-08-25 17:36:17 -06:00
Dark-Alex-17 4025b8dacd feat: inject background-jobs prompt guidance when jobs are enabled
Agents whose function pool includes job__* declarations get a Background
Jobs section teaching start/check/collect/cancel discipline and the
snapshot/no-persistence semantics. Presence of the declarations doubles
as the jobs_enabled predicate, so a context with function calling off or
max_concurrent_jobs 0 sees no job prompt text.
2026-08-25 17:36:17 -06:00
Dark-Alex-17 cb025b7fff feat: add background job runner, job__* handlers, and start gates
Detached tokio::process runner with a frozen JobEnvSnapshot (env-derived
bin dirs, vault-interpolated agent envs, COYOTE_TOOL_TIMEOUT resolved at
start), process_group(0) with pgid-guarded SIGTERM/SIGKILL escalation,
capture-only ring-buffer telemetry, and LLM_OUTPUT read after wait().
MCP jobs snapshot a single-entry McpRuntime holding only the validated
server and render through the same free fn as the foreground path.

job__start enforces its gates synchronously before any spawn:
jobs_enabled, the backgroundable whitelist with directionality teaching
errors, the per-request declared-names stash captured in
before_chat_completion, then capacity (lazy supervisor get-or-init in
plain sessions). job__check/list read the shared JobState cell without
consuming; job__collect blocks with the escalation early-out and applies
a tail-biased char-boundary cap plus optional tail_lines; job__cancel
kills the group with a 5s grace.

Job declarations are injected iff jobs are enabled at agent init, the
plain-session function-init sites, and the exit_agent rebuild; job__ is
carved out of enabled_tools filtering and excluded from
concrete_tool_names so REPL toggles cannot grant or revoke it.
2026-08-25 17:36:17 -06:00
Dark-Alex-17 fcc3756634 fix(function): floor tool-output truncation cut to a UTF-8 char boundary
When max_chars landed inside a multi-byte UTF-8 character of the
serialized output, s.get(..max_chars) returned None and the code fell
back to the FULL untruncated string while still prepending the
truncation marker — the "truncated" output actually grew. The cut is
now floored to the previous char boundary so the prefix is always a
valid, genuinely truncated slice.
2026-08-25 17:36:11 -06:00
Dark-Alex-17 257b06bbd4 fix(supervisor): make agent__check a pure status probe that never consumes the handle
agent__check on a finished agent delegated to agent__collect, which
returned the full (unbounded) result and consumed the handle. That
contradicted the tool's own docs and broke the check-then-collect
pattern: a second collect on the same id failed.

check now reports { status: finished } with a pointer to
agent__collect and leaves the handle registered; collect is the single
retrieval verb. The tool description and prompt table are updated to
stop promising that check returns the result.
2026-08-25 17:36:11 -06:00