- stage-d: next-line / block-close / multi-line fixes, symbols format 2, TS errors in the header. Held-out: 0.426 exact / 0.700 edit sim (stage-c 0.405 / 0.689); block close 0.27 -> 0.79. - stage-e: next-edit (80% exact region rewrites, abstains on 100% of no-edit samples). - Manifest: nextEditModel (companion) and nextEditFormat (older extensions ignore it, so they never use next-edit with the empty-output deletion bug). - Daemon: next-edit served by the companion model in a second llama-server, started after the main model loads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J39imRDT8L7xYbWzsVWd8A
acd: local completion daemon
Builds fill-in-the-middle prompts in exactly the format the models were trained on (it shares
../shared/ with the training pipeline) and serves completions through a llama-server it manages.
Editors talk to it over localhost HTTP. Design notes: ../docs/architecture.md.
You usually don't run this yourself: the VS Code extension bundles it and runs it in managed mode. Run it standalone for external mode, for other editors, or when developing.
Running standalone
Requires Node ≥ 22.18 (runs TypeScript directly) and a llama-server binary (brew install llama.cpp on macOS,
or a llama.cpp release on Linux).
npm install
npm start # node src/main.ts --config acd.config.json
node src/acctl.ts status # or: mode simple|symbols|raw · model <name> · on · off
npm run smoke # end-to-end test (needs the models from ../models, via Git LFS)
acd.config.json uses llama-server from your PATH with -ngl 99 (Apple GPU), and the models in ../models.
- Linux / CPU: copy it to
acd.config.local.json(git-ignored), set"binary"to your llama-server and"args": ["-t", "<physical cores>"], then runnode src/main.ts --config acd.config.local.json. - Serving another machine (e.g. an M1 Max serving a laptop): set
"host": "0.0.0.0"and point the editor'sserverUrlat it, usingdaemon.mode: "external". There's no auth, so only do this on a trusted network. - Start on login:
acd.serviceis a systemd user unit.
Context modes
| mode | prompt | models |
|---|---|---|
symbols |
repo header + language-service symbols (imports' signatures, repo-local types), from ../shared/symbols.ts, the same code that built the training data. Requires the editor to send workspaceRoot; otherwise the request runs as simple. |
stage-c |
simple |
repo name + file path header | stage-c |
raw |
plain suffix-first FIM | stage-b (spm), base-0.5b (psm) |
The mode and model are global to the daemon and persisted in acd.state.json. VS Code, Zed and acctl all see the same toggle.
How symbols mode stays fast:
- Context is built in a worker thread (one TypeScript language service per workspace) and cached per anchor: the file, the enclosing top-level declaration and the import block. While you type inside one function the header is identical and the KV cache keeps hitting; the smoke test shows the same ~2 re-processed tokens per keystroke as
simple. - When the anchor changes, the daemon waits up to
symbols.waitMsfor fresh context, else uses the file's previous context (stale-while-revalidate). - Member completions after
.are off (symbols.members): every.would change the header and force a cold prefill.
this. repair (thisRepair.enabled, any mode, needs workspaceRoot): when the identifier you're typing only resolves as a member of the enclosing class, the daemon completes as if this. were there. It returns replaceBefore + filterText so the editor replaces getU with this.getUser(id).
Edit recordings: POST /v1/edits appends opted-in editor edit events to recording.dir (default recordings/, git-ignored). These are the future training data for next-edit prediction.
API
POST /v1/fim:{ requestId?, repo, path, languageId?, prefix, suffix, prefixLineOffset?, mode?, singleLine?, maxLines? }returns{ text, mode, singleLine, timings, … }. A newer request cancels the in-flight one; closing the connection cancels too.GET|PUT /v1/config:{ enabled, model, mode }. Changing the model restarts llama-server.POST /v1/feedback: editor events, written to the local log only whentelemetry.enabled.POST /v1/edits:{ events }from editors that opted in to edit recording.- The
/v1/fimrequest may includeworkspaceRoot(absolute path on the daemon's machine). Responses may includereplaceBefore/filterText(thethis.repair) andsymbols: { status: hit|fresh|stale|none, items }. GET /health