Short answer
Serena is an IDE for your agent. Vaelor is a memory of your codebase.
Serena
Symbol-level tools the agent drives: find references, then rename, replace or insert in place. [1]
v1.7.0 · MIT
Vaelor
A graph of the codebase the agent questions: who calls this, what breaks, what the last review said. [9]
v1.72.8 · Apache-2.0
I build Vaelor, so read this with that in mind. The two overlap on who-references-this-symbol; everywhere else they do different jobs.
Which to pick
Pick Serena if…
Pick Vaelor if…
- You ask “what breaks if I change this” beyond one hop. One call replaced 27 on the function measured below. [9]
- You want review verdicts to follow a symbol from one pull request to the next. [9]
- You want a production alert resolved to a
file:function. [9] - You would rather not install language servers: one binary parses with tree-sitter. [9]
Use both when…
The agent checks the blast radius first, then changes the code: Vaelor impact_analysis before, Serena’s rename, review_pr after. Two MCP entries. I have not run them together in one session.
What each one does
| Capability | Serena | Vaelor |
|---|---|---|
| Find references | Yes: Exact, per symbol [1] | Yes: Callers and callees [9] |
| Callers of callers | Partly: One call per level | Yes: One call, depth 5 [9] |
| Rename, move, replace | Yes: Through the language server [1] | Partly: Structural rewrite, no rename [9] |
| Debugger | Partly: JetBrains plugin only [1] | No: No |
| Notes the agent keeps | Yes: Markdown memories [2] | Partly: Review learnings per symbol; needs PostgreSQL [9] |
| Alert to code | No: No | Yes: Alert to file:function [9] |
| Languages | 40+ [1] [3] | 16 [9] |
| Changes your files | By design; runs shell commands [7] | Only when told to apply; review_pr can comment [9] |
| Install | uv tool install serena-agent [1] [3] | Binary or Docker; database optional [9] |
| License | MIT at 1.7.0; next branch GPL [1] [4] [5] [6] | Apache-2.0 [10] |
Measured: who calls ParseFile?
One question: what calls ParseFile within five hops? Repository: Vaelor at v1.65.16. Serena 1.7.0 answered through gopls, Vaelor 1.72.8 as a bare binary. [11] Compared symbol by symbol.
- 249 = 249 symbols within five hops, same distances
- 27 : 1 calls for the production callers
- 94K : 1.8K characters returned to the agent
- 6 s : 27 s cold start to the complete answer, best runs
- Hop 1: 6 production symbols and 98 tests. Serena needed 1 call to reach it.
- Hop 2: 5 production symbols and 18 tests. Serena needed 6 calls to reach it.
- Hop 3: 6 production symbols and 15 tests. Serena needed 5 calls to reach it.
- Hop 4: 9 production symbols and 32 tests. Serena needed 6 calls to reach it.
- Hop 5: 10 production symbols and 50 tests. Serena needed 9 calls to reach it.
Check the tier
Vaelor’s answer has a tier field. enhanced matched the type checker. basic, the first answer on a new checkout, can miss callers (195 of 249 here): repeat the call until it says enhanced. The list shows at most 100 direct callers and names the rest.
Where Serena costs more
Callers of callers take one call per symbol: 27 here, 190 with the tests, each returning code context. That is the price of exact, local operations, and the same references make its rename safe.
All measured numbers
Scroll the table sideways to see every column.
| Metric | Serena 1.7.0 | Vaelor 1.72.8 | Note |
|---|---|---|---|
| Direct callers | 104 (121 call sites in 33 files) | 104 (100 listed) | The 100 listed are all in Serena’s 104 |
| Production symbols within 5 hops | 36 | 36 | All identical |
| All symbols within 5 hops | 249 | 249 (245 listed) | The 245 listed are identical; the 4 left out are named |
| Tool calls, production callers | 27 | 1 | — |
| Tool calls, complete walk with tests | 190 | 1 | — |
| Characters returned | 93,574 over 27 calls | about 1.8 KB inline; the full result, about 70 KB, as a file | Serena includes the code around each reference |
| First call | 9 to 33 s (language server start) | 6 to 7 s on a checkout seen before; 12 to 18 s on a new one | A new checkout first gets tier: basic; the complete answer follows in about 30 to 40 s |
| Later calls | about 2 to 3 s | 0.6 s | — |
| Walk time | 27 to 78 s for 27 calls; 203 s for 190 | 6 to 7 s on a seen checkout; about 30 to 40 s on a new one | Serena: three runs. Vaelor: five and three runs |
Timings come from one laptop (Intel Core i9-9880H, 16 threads, 16 GB), Serena on 2026-10-06 and Vaelor on 2026-10-07, with other programs running (load average 8 to 18). They are an order of magnitude, not a benchmark.
Reproduce the Serena side
Python 3.11+, uv, Go with gopls on PATH. Check out v1.65.16 of the Vaelor repository, then set language_servers: [go] in the project’s .serena/project.yml (Serena creates it on first start). This walks callers one symbol per call, tests included, and writes every symbol it reaches with its distance:
import asyncio, json, sys
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
PROJECT, OUT = sys.argv[1:3]
rows = lambda txt: [(f, s["name_path"]) for f, kinds in json.loads(txt).items() for lst in kinds.values() for s in lst]
async def main():
params = StdioServerParameters(command="uvx", args=["--from", "serena-agent==1.7.0", "serena", "start-mcp-server", "--project", PROJECT, "--context", "ide-assistant"])
seen = {("internal/parser/parser.go", "ParseFile"): 0}
frontier, calls = [("internal/parser/parser.go", "ParseFile")], 0
async with stdio_client(params) as (r, w), ClientSession(r, w) as s:
await s.initialize()
for depth in range(1, 6):
nxt = []
for f, n in frontier:
calls += 1
res = await s.call_tool("find_referencing_symbols", {"name_path": n, "relative_path": f, "max_answer_chars": 2000000})
for key in rows("".join(c.text for c in res.content if getattr(c, "text", None))):
if key not in seen:
seen[key] = depth
nxt.append(key)
frontier = nxt
json.dump({"calls": calls, "seen": [[f, n, d] for (f, n), d in seen.items()]}, open(OUT, "w"))
print(calls, "calls,", len(seen) - 1, "symbols")
asyncio.run(main())Run it with uv run --with mcp python walk.py /path/to/vaelor-v1.65.16 walk.json.
Reproduce the Vaelor side
Build the engine at v1.72.8 (make build; Go 1.26+ and a C compiler), start it with MCP_PORT=18897 OUTPUT_DIR=/tmp/vout ./bin/vaelor, and call impact_analysis over MCP with the path of the v1.65.16 checkout and depth: 5. The inline response is condensed to counts; the full lists are in the file it names under OUTPUT_DIR.
import asyncio, sys
from mcp import ClientSession
try:
from mcp.client.streamable_http import streamable_http_client as streamablehttp_client
except ImportError: # the client was renamed between mcp releases
from mcp.client.streamable_http import streamablehttp_client
async def main():
async with streamablehttp_client("http://127.0.0.1:18897/mcp") as streams:
async with ClientSession(streams[0], streams[1]) as s:
await s.initialize()
r = await s.call_tool("impact_analysis", {"repo": sys.argv[1], "symbol": "ParseFile", "depth": 5})
print("".join(c.text for c in r.content if getattr(c, "text", None)))
asyncio.run(main())The first call builds the index. On a checkout the Go build cache has not seen, it can return "tier":"basic"; repeat the call until the response says "tier":"enhanced". The drivers and raw outputs are in docs/measurements/serena-vs-vaelor-2026-10-06/.
What I could not verify
- I build Vaelor. The Serena column is what its documentation says, read on 2026-10-06, and 1.7.0 was still its latest release on 2026-10-07. I did not read its source. [1]
- One repository, one function, one language (Go), on a laptop that was in use. Serena’s JetBrains backend and its development branch (
2.0.0.dev0, GPL) were not run. [5] [6] goplsis my reference for static references; neither tool sees reflection or runtime dispatch. [11]- Serena’s own evaluation lets the agent grade itself over about twenty tasks. I did not rerun it. [8]