feat(providers): give agents the user's own tools over MCP, starting with FixMap - #226
aryamthecodebreaker wants to merge 2 commits into
Conversation
session/new, session/load and session/resume always sent `mcpServers: []`, so Ari had no way to give an agent a tool without the user configuring each CLI separately. They now carry the host's list, and it ships one entry: FixMap (https://usefixmap.vercel.app), a local repo map that answers which files and tests a task touches without a model or an account. No FixMap code enters Ari. Ari names a command the user already installed and the agent spawns it, the same rule launches.ts states for agent CLIs. Nothing is downloaded, and with FixMap absent the list is empty and agents behave exactly as before. findCommand tries .cmd/.exe before the bare name on Windows: npm writes an extensionless sh shim beside the .cmd, and the agent is what spawns this, so the shim would fail with ENOENT inside a subprocess Ari never sees. The list is read per turn rather than captured at registration, so the Advanced > Agent tools toggle takes effect on the next turn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The preamble naming `ari` was prepended to every prompt whenever the session env carried ARI_ENV=1. An agent cannot tell it from text the user typed, and on seeing it turn after turn Claude concluded it was a prompt injection riding along in the input and refused to act on it — costing Ari the control surface the line exists to advertise, and paying tokens every turn to repeat something already in the agent's transcript. It now goes out on the first prompt of a provider thread only. Once the provider has a thread to resume, it already holds the line; a provider that reports no thread id starts cold each turn and still gets it every time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Devin Review found 2 potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| runtimeEnv?.ARI_ENV === '1' && resumeOf === null | ||
| ? `${ARI_CONTROL_PREAMBLE}\n\n${prompt}` |
There was a problem hiding this comment.
🟡 Fresh fallback threads miss control preamble
When resuming fails or ACP falls back to a one-shot driver, resumeOf suppresses the preamble for the resulting fresh thread. openSession creates fresh sessions, while AcpDriver.create can switch transports. The agent then lacks Ari's control commands for that turn.
Learn more
The engine decides whether to add the preamble before the adapter knows whether resumeOf can actually resume. The ACP adapter degrades to session/new when resume and load fail. The ACP wrapper also degrades to the legacy one-shot driver when setup fails. Both paths create a fresh provider thread from a prompt whose preamble was already omitted.
Example: A session stores sess_old. On the next turn, session/resume and session/load reject, so openSession calls session/new. The new agent receives only the user text, while a first turn would also receive the Ari control instructions.
Recommended fix: Move first-thread preamble selection behind transport/session opening, or let the driver report whether the resulting adapter resumed. Ensure ACP's session/new degradation and legacy fallback both receive the preamble, while successful resumes do not.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const full = join(dir, candidate) | ||
| if (existsSync(full)) return full | ||
| } |
There was a problem hiding this comment.
🟡 Unrunnable FixMap paths get offered
With a directory or non-executable file named FixMap, findCommand returns it as an MCP command. FixMap remains unavailable despite being advertised, and the agent can report a server startup failure.
Learn more
Existence does not establish that a path is a runnable command. existsSync returns true for directories, and POSIX files can exist without execute permission. The returned path is passed directly to the ACP agent as the MCP server command.
Example: If /home/alice/.local/bin/fixmap is a directory or has mode 0644, findCommand returns it. The session advertises FixMap, but spawning that command fails instead of treating FixMap as absent.
Recommended fix: Require a regular file and verify execute permission on POSIX before returning a candidate. Keep the existing Windows extension ordering, and add tests for directories and non-executable files.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Can not consider your FixMap PR at the moment. Have to wait until Ari fully supports MCPs & Plugins. Seperate both PRs. |
|
Please split this, as above. The FixMap half still needs to wait until Ari supports MCPs and plugins properly. The control-surface half is small and there is already a fix in flight for that same banner, so it belongs there rather than in its own PR — see #227, which changes the wording on the same line and would just conflict with a separate one. Worth handling when you rebase it onto that: |
|
Split done. FixMap is out of here entirely and parked until Ari has real MCP/plugin support — no point reviewing wiring that has to be rebuilt on top of that surface. The detector fix from the other PR is now #228, and #223 is background work only, in draft. On the control surface, your Recording the announced thread id in the journal and re-announcing when it changes is the right shape: it keeps the once-per-thread saving and closes both paths, since a recreated thread gets a new id either way. Since #227 already changes the wording on that same line, I have not opened this separately. Happy to send it as a commit on top of #227 once that lands, or as a suggestion against your branch if you would rather fold it in — your call which is less work for you. |
Two changes to what Ari hands an agent at the start of a session. They are separate concerns and I am happy to split them — say so and I will open the second as its own PR.
1.
feat(providers): offer the user's own tools to agents over MCPsession/new,session/loadandsession/resumehave always sentmcpServers: []. Ari therefore had no way to give its agents a tool, short of the user configuring each CLI separately — which is the thing Ari exists to avoid.This sends the list instead, and ships one entry: FixMap, a local, deterministic repo map that answers "which files and tests does this task touch" without a model or an account. Agents get
fixmap_plan,fixmap_context,fixmap_graph,fixmap_explain,fixmap_compare,fixmap_verifyandfixmap_doctoras tools, on by default.No FixMap code enters Ari. Ari names a command the user already installed and the agent spawns it, exactly as
launches.tstreats agent CLIs: "Ari never bundles adapter runtimes: the user's own CLI must be installed." Nothing is downloaded, and with FixMap absent the list is empty and agents behave precisely as they do today.Details worth review:
findCommandtries.cmd/.exebefore the bare name. npm writes an extensionless sh shim beside the.cmd, and here the agent is what spawns the command, so handing it the sh shim fails withENOENTinside a subprocess Ari never sees. (The same lookup bug affectsfindBinaryfor the agent CLIs themselves; that fix is separate.)2.
fix(engine): stop repeating the control-surface preamble on every message#runTurnprepends this to every prompt whenever the session env carriesARI_ENV=1:Every turn, not once. Two things follow, and I hit both with Claude driving a real session:
Now it goes out on the first prompt of a provider thread only —
resumeOf === null. Once the provider has a thread to resume, the line is already in that transcript. Providers that report no thread id (the one-shot CLI transport, which genuinely starts cold each turn) still get it every time, so nothing regresses there.Worth noting the control surface itself was working:
ari.cmdis on the agent's PATH and answers. The agent's claim that it had no such command was wrong — but it had good reason to disbelieve the block telling it otherwise.How verified
Windows 11, Node 24.19, pnpm 10.33, FixMap 0.9.0 installed globally.
FixMap's server was driven directly over stdio the way an agent will, with the command Ari resolves:
New tests: lookup order on win32 vs posix and the not-installed path (
mcp-servers.test.ts); the servers riding on all three session calls, not just the first (connection.test.ts); the toggle's default, an older settings file without the section, and patch validation (settings-tools.test.ts); the Advanced toggle and its bundle round-trip (AdvancedSettings.test.tsx); and the preamble appearing on a fresh thread but not on a resumed one (control-surface.test.ts). The existingagent-runtimeassertion that the first turn carriesari --skillstill passes unchanged.pnpm verify— typecheck and lint clean across the workspace; tests:The engine failure is
git-service.test.ts > parses porcelain v2 status into branch and typed entries, which spawns realgitin a temp dir and times out at 5000ms under full-suite load onthis machine. It passes on its own here (16/16) and touches none of this code. Those 5s budgets
look tight for a loaded Windows box, but that is its own issue.
🤖 Generated with Claude Code