Commit Graph
130 Commits
Author SHA1 Message Date
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 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 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
Dark-Alex-17 7cf88c030f fix(supervisor): surface finished-but-uncollected tasks in turn-end guardrail
The turn-end guardrail only counted still-running agents, so an agent
that finished before the turn ended was invisible: its uncollected
result was silently dropped. Jobs were never counted at all.

The guardrail now enumerates every registered task (running and
finished, agents and jobs) via Supervisor::list_tasks and renders a
kind-aware prompt with two sections: still-running tasks to reclaim,
and completed-but-uncollected tasks with the exact collect command.

At the force-terminate cap, finished-but-uncollected handles are
explicitly discarded with a warning naming the lost ids, so the
guardrail cannot loop forever on handles nobody will collect.
2026-08-25 17:36:11 -06:00
Dark-Alex-17 7f3f95d89d feat: generalize supervisor registry to TaskHandle enum with job scaffolding, kill discipline, and max_concurrent_jobs config
Implements T1 of plans/background-jobs-design.md (§6, R7/R8/R9):

- Supervisor.handles is now HashMap<String, TaskHandle> where
  TaskHandle = Agent(AgentHandle) | Job(JobHandle); agent-facing
  accessors (active_count, effective_active_count, is_finished, take,
  inbox, abort_signal_for, list_agents) match only Agent variants,
  preserving all existing external behavior byte-for-byte.
- New JobHandle/JobState/JobStatus/JobResult types with pgid-guarded
  process-group kill discipline: Drop and cancel_all/cancel_recursive
  kill the group only while state.pgid is still set (pid-reuse guard),
  via libc::killpg on unix and JoinHandle::abort elsewhere.
- Per-kind job capacity: Supervisor carries max_concurrent_jobs
  (builder-set, default 0); job registration rejects at capacity.
- Cross-kind teaching errors at the four agent-lookup miss sites
  (agent__check/collect/cancel/send_message) when the id is a
  registered job or job_-prefixed; genuinely-unknown ids keep their
  existing messages.
- Supervisor init condition is now can_spawn_agents || jobs_enabled in
  use_agent and both child-agent spawn paths, with agent capacity 0 in
  jobs-only contexts; use_agent cancels the old supervisor recursively
  before replacing it.
- max_concurrent_jobs config plumbing: global Config field, AgentConfig
  override + accessor, all four AppConfig touch points including the
  COYOTE_MAX_CONCURRENT_JOBS env override; shared
  effective_max_concurrent_jobs/jobs_enabled predicates
  (agent override -> global -> default 5; 0 disables).
- Stage dependency-free RingBuf (64 KiB default) in src/function/jobs.rs
  for the upcoming job output pump.
- New sanctioned dependency: libc 0.2 under cfg(unix).
2026-08-25 15:17:57 -06:00
Dark-Alex-17 bfc3b7bfea test: pin current tool-eval, guardrail, and truncation behavior ahead of background-jobs work
T0 characterization safety net per plans/background-jobs-design.md §9.3: 43 tests
pinning handle_collect/check/cancel/spawn, the pending-agents guardrail (incl.
ForceTerminate + counter resets), eval_tool_calls partition/re-sort/soft-fail/
loop-alert/truncation, truncate_if_needed's UTF-8 boundary edge, ToolCall::eval
prefix routing, merge_tool_results shape, and cancel_recursive recursion.

Known-buggy behaviors deliberately pinned for visible later diffs: handle_check
consumes finished handles, guardrail ignores finished-but-uncollected agents,
mid-char truncation returns the full string with marker prepended.

Not covered (findings): empty-after-dedup bail is unreachable from non-empty
input; run_child_agent needs a mock LLM client (none exists) — manual case;
over-threshold summarization pinned via deterministic unknown-model failure.
2026-08-25 13:55:15 -06:00
Dark-Alex-17 ad9ff3bea8 style: revised a few stylistic choices after I changed my mind 2026-08-25 11:37:41 -06:00
Dark-Alex-17 eb37f8bb46 feat(mcp): bound tool-result passthrough and surface resource audience annotations
Route CallToolResult content through the render.rs content policy per
plans/mcp-resources-prompts-design.md §6 (T8): oversized text sliced at
TEXT_MAX_BYTES_CLAMP with a self-explaining truncation note, image/audio/
embedded blob content spilled (or inlined when UTF-8-clean) instead of
shipping base64 into model context, and structuredContent subject to the
same ceiling. Clamp server-controlled uri/mime metadata strings to the new
METADATA_MAX_BYTES bound in both the read and tool-result paths, sanitize
the terminal rendering of MCP dispatch errors while keeping raw text in
the tool_call_error payload, and surface resource audience annotations in
both mcp_search results and mcp_read metadata via the catalog.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 6fade71e8c feat(mcp): add mcp_prompt meta-tool and harden prompt display rendering
Emit an mcp_prompt_<server> declaration for servers advertising the
prompts capability, execute prompts via McpRuntime::prompt on both tool
dispatch chains, and return the flattened prompt text as the tool
result. Sanitize server-controlled prompt names, descriptions, and
argument names before terminal rendering, and attribute the .prompt
argument inquire label to its server and prompt.

Per plans/mcp-resources-prompts-design.md §5.2 (T7).
2026-08-25 11:37:41 -06:00
Dark-Alex-17 67819784b7 feat(mcp): add mcp_read meta-tool for resource reads
Implements plans/mcp-resources-prompts-design.md section 4.3 (T4):
mcp_read_<server> declaration and handler wired to render.rs, RFC 6570
Level-1-only URI template expansion, defensive ResourceContents parsing,
per-item text paging with pattern filtering, blob spill metadata, an
overall 204800-byte multi-content ceiling, dispatch wiring on both
eval chains, and a render_text paging-stall guard.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 437512fd6d feat(mcp): gate meta-function emission on advertised server capabilities
Per-server McpServerFeatures (tools fail-open, resources/prompts
fail-closed) now drive which meta-functions are declared, with
gated_meta_function_prefixes as the single gating seam; read/prompt
declarations land together with their handlers. The server-enablement
sentinel keys on the always-emitted search name so resources-only
servers survive role filtering.

Implements plans/mcp-resources-prompts-design.md §4.4/D7 (T5).
2026-08-25 11:37:41 -06:00
Dark-Alex-17 d68f4ecaeb feat(mcp): extend the server catalog to resources, templates, and prompts
Implements the unified catalog from plans/mcp-resources-prompts-design.md §4.2 (T2): CatalogItem gains kind/uri/mime_type/size keyed as {kind}:{id}; catalog_items() lists per kind gated by advertised capabilities with warn-and-degrade; mcp_search results carry kind; mcp_describe gains an optional kind param (default tool); write-only registry ServerCatalog removed.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 01ada1da18 refactor(mcp): centralize meta-function prefix predicates and fix list_tools pagination
Implements T1 of plans/mcp-resources-prompts-design.md (§4.1, §4.6):

- Replace list_tools(None) with cursor-following list_all_tools() at the
  three call sites (start_server catalog build, catalog_items, describe)
  so paginating servers no longer silently lose tools past page one.
- Add MCP_READ/MCP_PROMPT prefix constants (declared nowhere yet; wired
  in T4/T7) plus centralized helpers MCP_META_FUNCTION_PREFIXES,
  is_mcp_meta_function, and mcp_meta_function_names.
- Mechanically replace every hand-rolled 3-prefix starts_with triple
  (partition in eval_tool_calls, 3 exclusion triples in
  select_enabled_functions, 3 inclusion triples + per-server name
  construction in select_enabled_mcp_servers) with the helpers,
  preserving the existing lax starts_with matching semantics and the
  mcp_invoke_* enablement sentinel (sentinel moves to search in T5).
- Behavior-neutral: dispatch chains keep their 3 arms, emission stays
  at exactly 3 meta-functions per server, existing tests unmodified.
- Add unit tests: helper classification, prefix-soundness property,
  lax-matching pin, ordered candidate-name construction.
2026-08-25 11:37:41 -06:00
Dark-Alex-17 30c1637dff refactor: Refactored some bundle const locations 2026-08-24 11:15:13 -06:00
Dark-Alex-17 6b5535956d fix: harden the bundle lifecycle per code review
The path-escape guard that uninstall applies to recorded paths now also
covers update's obsolete-file deletion through a shared check, so a
tampered store cannot turn either delete site into an arbitrary file
removal. Updates gain a working non-interactive path: --yes now applies
to --update-bundle (locally modified files, obsolete files, and modified
mcp entries are all kept; everything else refreshes), owned mcp entries
whose recorded hash still matches the local entry take the remote side
without prompting, and the non-TTY conflict bails name the flag that
actually works per surface. An update records its new commit and version
only after files and mcp entries land, so an aborted update cannot claim
content it never wrote. The store gains a version field and rejects
stores from newer builds, the corrupt-store error no longer advises the
removal that would forfeit ownership tracking, and duplicate records
tracking one source abort a rename instead of overwriting a record.
Reinstalling from a source URL reclassifies owned unmodified files as
silent refreshes just like updates. git runs with GIT_TERMINAL_PROMPT=0
and a null stdin so private or mistyped URLs fail instead of hanging.
File comparison fills buffers fully before comparing, deleting an
obsolete file prunes emptied directories, mcp.json backfill uses the
fsynced atomic writer, --list-bundles no longer triggers builtin
backfill, bundle-name completion logs store errors instead of swallowing
them and offers --yes, and REPL .uninstall rejects unknown flags.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 80b082423c fix: address code review findings on the bundle lifecycle
The user-origin marker on replaced mcp.json entries is now sticky:
re-records and cross-bundle transfers only upgrade replaced to
transferred when the prior record proves bundle origin, so updating a
bundle can no longer make uninstall delete a key the user had before the
bundle replaced it. Canonical source URLs lowercase only the host, since
self-hosted forges treat repository paths as case-sensitive and
collapsing distinct repos misdirects updates and uninstalls. git clone
invocations pass '--' before the URL so a crafted source cannot be
parsed as a git flag. Lifecycle flags (--install, --install-builtins,
--update-bundle, --uninstall) and their companions now conflict
explicitly instead of first-match dispatch silently dropping actions.
--install-from returns as a hidden tombstone that errors with the
replacement instead of feeding the flag to the LLM as prompt text.
--list-bundles dispatches before config load so a pure read no longer
boots MCP servers. write_file_atomic fsyncs before the rename so a crash
cannot persist a truncated store. REPL: .uninstall accepts --yes,
.install rejects trailing tokens after a category, and .install remote
gets a migration hint. Plus polish: host validation rejects '#' and '?',
renamed_to no longer serializes null, derived names get a debug assert
against the validator, completions share DEFAULT_GIT_HOST, README
mentions skills.
2026-08-24 11:15:13 -06:00
Dark-Alex-17 7eeff2a226 fix: include tool output to LLM_OUTPUT in errors as well as stderr 2026-08-17 11:38:21 -06:00
Dark-Alex-17 374e8c0bf7 fix: prevent tmp-overwriting
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-15 19:35:12 -06:00
Dark-Alex-17 ede87df960 fix: Corrected a rare edge case on how tool files are generated during parallel executions 2026-08-15 18:41:13 -06:00
Dark-Alex-17 f57bd21ee4 fix: Improved subagent escalation handling 2026-08-14 16:53:37 -06:00
Dark-Alex-17 644d899f78 fix: Prevent infinite hangs in coder agent and implement timeouts for LLM API calls and interactive tools 2026-08-14 15:22:25 -06:00
Dark-Alex-17 68135b97d1 feat: append new built-in rag__query function to RAG contexts to allow further querying by LLMs
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-08-12 16:25:50 -06:00
Dark-Alex-17 233c212d2a feat: improved function calling performance by allowing parallel tool calling
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-29 12:40:52 -06:00
Dark-Alex-17 d50de7c06a lint: fixed test ordering
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-27 19:52:51 -06:00
Dark-Alex-17 2128390f99 fmt: applied formatting
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-27 19:26:29 -06:00
Dark-Alex-17 d79787bf96 fix: suppress tool-call display in headless mode; initialize session on session/new 2026-07-27 19:14:28 -06:00
Dark-Alex-17 b711e4983b feat: implement ACP user-interaction to request_permission bridge 2026-07-27 18:00:07 -06:00
Dark-Alex-17 f1415067f2 feat: add --headless flag for unattended operation 2026-07-27 17:17:56 -06:00
Dark-Alex-17 5eb77ab467 feat: Improved coloring of LLM tool invocation outputs to make LLM output more readable and cohesive 2026-07-23 18:27:54 -06:00
Dark-Alex-17 a2c4f05c8c feat: renamed the user__ask to user__select and improved descriptions to improve model usage 2026-07-23 18:22:18 -06:00
Dark-Alex-17 89fadcca15 feat: Improved coloring/highlighting of tool calls to make LLM invocation logs easier to read 2026-07-23 18:12:18 -06:00
Dark-Alex-17 54c5079cb7 feat: long-running session improvements
- Hang fix: 120s timeout on compression LLM call + raw_stream
  channel-close handling (None => break instead of spinning)
- Supervisor effective active count: stop counting finished
  JoinHandles as occupying capacity slots
- Universal tool result size cap: truncate_if_needed() on
  ToolResult, applied in eval_tool_calls() after escalation block;
  configurable via max_tool_result_chars in AppConfig/AgentConfig
- Windowed compression: compression_keep_last config param keeps
  the N most recent messages visible after compression
- Fix pre-existing flaky test: add #[serial] to
  handle_list_available_unrestricted_when_no_whitelist so it does
  not race with TestConfigDirGuard-based tests that temporarily
  populate the agents data dir
2026-07-23 12:25:54 -06:00
Dark-Alex-17 df948c69bf feat: new spawnable_agents field in agents to let users restrict what agents can be spawned by a parent agent
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 14:29:55 -06:00
Dark-Alex-17 c60d3e7cda style: updated some stylistic things across the new markdown rendering implementation
CI / All (ubuntu-latest) (push) Failing after 24s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-22 13:20:56 -06:00
Dark-Alex-17 420db4bb88 feat: created a new builtin function for agents who can spawn other agents to list available agents via agent__list_available 2026-07-22 12:50:41 -06:00
Dark-Alex-17 6f2594712f refactor: Standardized paths module function names to not use 'path' in the name and to just always be either 'dir' or 'file'
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-21 10:15:42 -06:00
Dark-Alex-17 8629c1ca15 feat: Improved support for Anthropic's extended thinking 2026-07-17 17:26:41 -06:00
Dark-Alex-17 078e6e3744 fix: model narration included in history and between tool calls to prevent repetition
CI / All (ubuntu-latest) (push) Failing after 25s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 16:57:51 -06:00
Dark-Alex-17 058810137c fix: Don't terminate agent loops early for null tool output
CI / All (ubuntu-latest) (push) Failing after 26s
CI / All (macos-latest) (push) Has been cancelled
CI / All (windows-latest) (push) Has been cancelled
2026-07-17 16:17:29 -06:00
Dark-Alex-17 a45e66c634 feat: Improved workspace instructions support 2026-07-17 14:52:35 -06:00
Dark-Alex-17 7b00638476 style: removed redundant '&' from functions module 2026-07-13 18:11:03 -06:00
Dark-Alex-17 382916c3ee style: Removed redundant '&' from paths module function calls 2026-07-13 17:12:58 -06:00