A cache that holds
The history window only moves when that saves more than the cache would. Earlier images and reasoning stay where they were, so the cached prompt isn’t rewritten.
Dulus is an AI agent for your desktop and terminal. It works in your files, your shell, your browser and your screen — on any model you choose. And when one mind isn’t enough, it calls the flock.
Runs on the models you already use
Measured, not claimed
Six coding sessions, from an 8-turn fix to a 120-turn audit, replayed against Claude Code, Cursor, Codex CLI, Cline, Aider and GitHub Copilot: same transcript, same tokenizer, same prices. Dulus came first under all five ways we weighed the results.
The history window only moves when that saves more than the cache would. Earlier images and reasoning stay where they were, so the cached prompt isn’t rewritten.
The model gets the recent turns; the whole conversation stays on your machine. When it needs something older, Dulus searches it with its own Loopback tool.
A persistent Python kernel keeps big data in memory instead of in the conversation. Only what it prints reaches the context.
Memory you can open: markdown in a palace of halls. The essentials load every session, and each turn brings back up to three relevant memories.
Dulus was measured running its own context code. The other agents are modelled from how each one manages its context, not run from their source. The method and its caveats are on the benchmarks page.
Watch it work
Every tool call happens in the open — the tool, its arguments, its result — and you can steer Dulus before it takes the next step.
You said
The session refresh test is failing. Find out why and fix it.
src/auth/session.py:43: def refresh(self, refresh_token: str): src/auth/session.py:44: session = self.db.get(refresh_token) src/auth/session.py:58:def new_refresh_token() -> str: src/api/routes.py:88: return sessions.refresh(body.refresh_token)
FAILED tests/test_session.py::test_refresh_rotates_token E AssertionError: token not rotated 1 failed, 3 passed in 0.41s
You said, while Dulus was working
Also log when a token expires.
Will do. The failure is real: refresh() hands back the session with its old refresh token, so the token is never rotated.
@@ -44,5 +44,8 @@ def refresh(self, refresh_token: str): session = self.db.get(refresh_token) if session.expired: - raise SessionExpired() + log.info("token expired: session %s", session.id) + raise SessionExpired(session.id) + session.refresh_token = new_refresh_token() + self.db.rotate(refresh_token, session) session.expires_at = now() + SESSION_TTL return session
.............. [100%] 14 passed in 1.12s
Fixed. The test caught a real bug: refresh() never issued a new refresh token.
All 14 tests pass, and the file was checkpointed before the edit.
You said
Postgres or SQLite for the sync engine’s state? I want this one argued properly.
Table convened with 3 members on: sync engine storage A dulus-x-397b B claude (your key) C gemini (your key)
SQLite on each device: one file, nothing to run, and reads stay local and fast. I am less sure about the sync server.
A is right about the device. The server is different: many devices write at once, and that is Postgres work.
Agree with both. Keep one schema for the two stores and run the conflict tests against each, or they will drift.
Recommendation: SQLite on each device, Postgres on the sync server. A and B agree on the split, and B’s point about concurrent writers settles the server. C’s caveat is the real work: one schema, two migrations, conflict tests on both. Want me to draft the schema?
You said
The receipt PDF from Downloads is open next to the expense form. Check they match, then submit it.
Screen grounded: 23 text elements | logical 1920x1080 ( 214, 188) conf=0.99 'Receipt #0412' ( 236, 402) conf=0.98 'Total RD$ 2,350.00' ( 652, 318) conf=0.99 'Amount 2,350.00' ( 662, 372) conf=0.97 'Date 14 Oct 2026' ( 812, 604) conf=0.99 'Submit'
They match: RD$ 2,350.00, dated 14 Oct 2026. Submitting the form.
( 760, 640) conf=0.99 'Expense submitted'
Done. The form matched the receipt and it is submitted — the screen now says “Expense submitted”.
You said
What are USD/DOP and BTC at right now? With sources, please.
USD/DOP [yahoo:USDDOP=X] = 60 DOP source: https://finance.yahoo.com/quote/USDDOP=X
BTC/USDT [binance:BTCUSDT] = 100,000 USDT source: https://www.binance.com/en/trade/BTC_USDT?type=spot
Both fetched live a moment ago, each with the page it came from. Want me to keep an eye on either?
You said
/verify yahoo USDDOP=X 60 1%
Scripted example runs, drawn from the desktop app. File names and figures are illustrative.
Type while Dulus works. It reads your message before its next step, and anything it didn’t get to comes back to your composer.
Each call shows the tool, its arguments and its result as it runs — Grep, Bash, Edit, even ScreenClick.
Files are snapshotted before each Write and Edit, so /checkpoint can restore the files, the conversation, or both.
Anatomy of an agent
Seven parts, seven jobs — from the beak that runs your tools to the song that talks back.
In flight, lateral view
Seven parts, one agent
It reads, writes and runs.
Read, Write, Edit, Glob, Grep, notebooks, and diagnostics (pyright, mypy, flake8, tsc, shellcheck).Bash for a real shell, and git: status, diff and log.WebFetch and WebSearch — about 80 tools, built in.Computer use by OCR — no vision-model tokens.
ScreenGround reads every text element on your screen, with click-ready coordinates.ScreenClick clicks by on-screen text or by x,y, on Windows and macOS.dulus-vl-72b.When one mind isn’t enough.
/mesa convenes a Mesa Redonda: 2–26 seats, each a full agent on its own model, talking to each other mid-turn. Dulus chairs.Precise edits — and a way back from each one.
Write and Edit; /checkpoint restores the conversation, the files, or both./undo takes back the last turn. /fork branches the session at any turn.auto, manual, accept-all and plan. In auto, anything that changes things asks first.New feathers, whenever the job needs them.
SKILL.md) from the official Anthropic marketplace, skills.sh and more: the Skills Market searches tens of thousands./plugin install name@git_url.It remembers — in files you can read.
/memory consolidate extracts the long-term insights.It talks back — in Dominican Spanish, unless you say otherwise.
/voice, /tts and a wake word, “hey Dulus”./lang switches among 34 languages — or free-form, like “formal British English”.Any model
Bring your own keys, run local models, or use Dulus-hosted models on Fuel — and switch any time with /model. When one opinion isn’t enough, seat several at a Mesa Redonda.
Example round
The question
BAnthropicyour key
Profile it before anyone opens a .rs file. My bet: the inserts, not the parsing.
DDeepSeekyour key
Same bet as B: one INSERT per row. Batch them, a thousand per transaction.
CGeminiyour key
D fixes the writes. The reads are worse: the whole file sits in memory before row one. Stream it.
EKimiyour key
C is right, and it’s one line: csv.reader already streams. The code wraps it in list().
FOllamalocal
Ran B’s profile on this machine: inserts first, parsing a distant second. Python isn’t the bottleneck.
Adulus-x-397bDulus-hosted
F’s numbers settle it: two small fixes, not a rewrite. Keep Rust for the day a profile asks for it.
Chair — synthesis: no rewrite. Stream the rows (C, E), batch the inserts (D), and rerun B’s profile before and after (F). Rust waits for a profile that asks for it (A).
/mesa convene …, /roundtable or the Round Table tab in the app.Three places a model can come from. Mix them freely — even at one table.
Use a key from a provider you already pay. Costs nothing from Dulus.
Vertex AI signs in with your gcloud login; AMD points at your own vLLM server.
Run a model on your own hardware. Costs nothing from Dulus.
localhost:11434localhost:1234llama-server :8080/v1/model ollama/qwen2.5-coder
Model set to ollama/qwen2.5-coder
Paid per token in Fuel. Cached tokens are free, and a failed call is refunded in full.
dulus-a-9bfast and cheap1×dulus-b-27bbalanced2×dulus-coder-30b2×dulus-vl-72bvision3×dulus-x-397bthe big one4×dulus-free-9b dulus-free-7b dulus-free-20b0×0× is the holder free tier: 40 messages every 24 hours while you keep at least 1,000,000 $DULUS as Fuel in your Dulus balance — held, never spent.
$DULUS is a utility and community token used to pay for Fuel on Dulus. Nothing on this site is financial advice.
/thinking 0–4
Every model family spells reasoning its own way — a token budget here, an effort word there. You turn one dial; Dulus translates the level for whichever model is answering.
/thinking debug prints the whole 0–4 ladder for the model you’re on.
Off. No reasoning asked for. A model that can’t switch it off gets its lowest setting.
On the wire
thinking not sentenable_thinking: falsereasoning_effort: "none"A light pass. A small budget for quick, simple work.
On the wire
budget_tokens: 2048thinking_budget: 2048reasoning_effort: "medium"rounded upBalanced. Room to think, without a long wait.
On the wire
budget_tokens: 6000thinking_budget: 8192reasoning_effort: "medium"Deep — the default. Time to think it through before answering.
On the wire
budget_tokens: 16000thinking_budget: 16384reasoning_effort: "medium"tops outThe ceiling, wherever each family puts it.
On the wire
budget_tokens: 32000thinking_budget: 32768reasoning_effort: "medium"tops outWhere a family has no matching level, Dulus sends the nearest one it accepts.
Fuel
Dulus-hosted models run on Fuel, and 1 Fuel = 1 $DULUS. You can follow every unit from your deposit to the header on the response.
POST /v1/chat/completions
Illustrative numbers. Every step is real.
Deposit
Send $DULUS on Solana to your personal deposit address.
Ledger
Credited once per transaction, plus a 1% Fuel bonus. The balance never goes negative.
Key
Mint an API key in the desktop app. It is shown to you once.
Reserve
Every paid call first holds an upper bound, atomically. No Fuel, no call.
Run
The hosted model runs. Its input, output and reasoning tokens are counted.
Settle
Billed on actual tokens. The rest of the hold comes back; a failed call is refunded.
$DULUS is a utility and community token used to pay for Fuel on Dulus. Nothing on this site is financial advice.
Memory & control
Dulus keeps its context lean, compacts it when the provider’s cache has gone cold anyway, and writes what it learns to markdown you can open. While it works, you can steer it, approve it, rewind it or cap it.
01Memory & context
Every turn, Dulus decides what the model sees. What earns its place goes in; the rest stays local, one call away.
Context window 62%49%26% / 128K
Prompt cache 1h TTL
keep going
[cache: 1h 12m idle > 1h TTL — cached prompt expired; this turn re-writes the prefix at write price (re-grounding + compacting now so it is paid once)]
70%
Layer 1 prunes old tool results without calling a model. Before layer 2 summarizes older turns, Dulus saves a checkpoint you can roll back to.
1h TTL
A provider’s prompt cache goes cold after a while — an hour, by default. Dulus knows when it has, and compacts exactly then, because the rewrite is already being paid for.
Loopback
Only the last N turns are sent. The full archive stays local, and the model fetches from it on demand.
Loopback(action="search", query="token expiry")
Memories are markdown files with a little frontmatter — one set for you, one per project, and an index Dulus keeps current. Read them, edit them, open the folder in Obsidian.
user, feedback, project or referenceuser_prefers_spanish.md
Gold · loaded at startup
--- type: user gold: true ---
Prefers replies in Spanish. Code and commit messages stay in English.
project_payments.md
0.74 recalled
--- type: project created: 2026-10-02 ---
Webhooks move to /v2 on 2026-11-03. Keep /v1 alive until then.
feedback_tests_first.md
0.41 recalled
--- type: feedback created: 2026-09-18 ---
Write the failing test before the fix. Why: two regressions shipped without one.
reference_release_wiki.md
0.06 below the floor
--- type: reference created: 2026-08-30 ---
Release checklist: team wiki → Engineering → Releases.
02Control
Autonomy with clear limits. You set them; Dulus works inside them.
You, mid-turn
also log when a token expires
Steer Dulus It reads this before its next step.
also log when a token expires
QueuedDelivered
Type while Dulus works. It reads your message before its next step, and anything it didn’t get to comes back to your composer.
Bashrm -rf build/
Allow: Bash(rm -rf build/) [y/N/a(ccept-all)]
autoThe default. Reads and safe commands run; anything that changes things asks.manualEvery tool call asks first.accept-allNothing asks. For when you mean it.planRead-only. Dulus plans; nothing changes.Choose how much Dulus may do on its own. Anything you haven’t allowed waits for your y — in the app, in the terminal or on Telegram.
Files are snapshotted before every Write and Edit. /checkpoint restores the conversation, the files, or both; /fork branches a session at any turn and /undo takes back the last one.
Changes made through Bash aren’t tracked.
/budget tokens 200000
/budget tool_calls 300
tokens128,400 / 200,000
tool_calls96 / 300
/isolate on
Isolate: ON — writes locked to ~/code/payments
Audit trail Write · Edit · Bash · ScreenClick
/budget caps tokens and tool calls for a session. /isolate locks writes to the workspace, best effort. Every Write, Edit, Bash and ScreenClick lands in an audit trail.
Everywhere you work
One agent in your window, your shell, your browser tab and your pocket — and it grows with every server, skill and plugin you give it.
The CLI comes inside the app: one click opens dulus in a real terminal — the whole agent, banner, themes and bird spinner included. The open-source edition brings it to macOS, Linux, WSL and Termux.
Example terminal session: dulus prints its orange-to-gold banner. /model dulus/dulus-b-27b switches the model; on “fix the flaky login test” it reads a file, runs a command, and its spinner says “Sharpening talons on the AST…”.
Type while it works — your message is read before its next step. Its browser and terminal run in panes you can watch.
Example: the Dulus desktop app with its sidebar, a Bash tool call running npm test, a steer message waiting to be read before the next step, and side panes showing the live browser and the terminal.
/webchat lan on — the same session in your phone’s browser, over your Wi-Fi.
Example: a phone browser open on the computer’s local address, showing Dulus WebChat. You ask whether the tests are done; Dulus answers that all twelve pass and it is writing the changelog line.
Send it photos and voice notes, and answer its permission prompts without walking back to your desk.
Example Telegram chat with the Dulus bot: you send a four-second voice note, Dulus transcribes it, asks “Allow: Bash(npm test)”, you reply “y”, and it reports that twelve tests pass.
Astral Voice answers out loud, sentence by sentence — cut in just by talking. The CLI has /voice, /tts and a wake word.
Example: Astral Voice is speaking when you interrupt with “Wait — check staging first.” It stops and listens.
It lives on your desktop and runs the long jobs — security reviews, repo adaptations — so your chat stays free. One small Dulus per running sub-agent.
Example: the Dulus mascot sits on the desktop with a speech bubble that says “listo”, Spanish for ready. On the right edge, three small mascots show sub-agents: coder in Edit for 42 seconds, tester in Bash for 18 seconds, and reviewer done after 63 seconds.
Thousands of MCP servers, with a security review offered before every install. Tens of thousands of skills. Plugins that hot-reload, and AutoAdapter turns a Python repo into tools.
Example: three market cards — a plugin, the pdf skill, and the postgres MCP server with an Install button and a security review offered first — next to the commands /mcp search, /plugin install name@git_url, /autoadapter and /lucky.
/sandbox opens a desktop in your browser — dozens of apps, from a terminal to chess. Its AI runs on Dulus-hosted models.
Example: a browser tab with Dulus OS — a desktop with app icons for Terminal, VS Code, Files, Spreadsheet, Notes, Music, Fuel Wallet and Chess, a Chat window answering on a Dulus-hosted model, an open chess game, and a dock.
-p runs one prompt and exits. --output json streams JSONL events, and the exit code tells CI how it went.
Example: dulus -p --accept-all --output json -- "add a test" prints three JSONL frames — step_start, text, and step_finish with token usage — and exits with status 0. Then dulus -p "write the release notes" > NOTES.md writes a plain answer to a file.
Desktop app for Windows 10 or later. The CLI needs Python 3.11+. Both sign in with a free Dulus account. All downloads
Why a bird
The palmchat — Dulus dominicus, la cigua palmera — is the national bird of the Dominican Republic. Found only on Hispaniola, it is the only species in its family.
Palmchats live in flocks. Together they build one great nest of twigs, high in the royal palms, and inside it every pair keeps its own chamber with its own entrance.
A multi-agent harness in the wild:
many minds, one nest,
each with its own room.
Made in Santo Domingo 18°28′N 69°53′W
Dulus is made the same way. One human, Kevin Rojo, sets the direction; a flock of agents does the work. The product is built the way it works.
Named after the bird, not the rocket.
Join the flock
Free to download. Bring your own keys, run a local model, or fuel the hosted ones with $DULUS.