
 
## 🔥🔥🔥 News (Pacific Time)


- May 16, 2026: **New community $DULUS token live on pump.fun** — fresh community launch on Solana: [`9R8rrjXxcfQPmLTCLhmVpjr2uesjjkcgkinE6Lwdpump`](https://pump.fun/coin/9R8rrjXxcfQPmLTCLhmVpjr2uesjjkcgkinE6Lwdpump). Same posture as before: community-launched, I didn't create it, I'm part of the journey, the open-source project stays free forever. Website FAQ + links updated. 🦅🇩🇴

- May 16, 2026 (**v0.2.93**): **IA without an API key, first-run flow** — Dulus's actual differentiator finally got promoted out of the slash-command obscurity it lived in. The welcome wizard now leads with a single question: *"¿Probamos AHORA con Gemini gratis (sin login)? [gemini]"*. Press Enter → on the next REPL boot, after `/doctor` finishes, Dulus auto-fires `/harvest-gemini`. Playwright opens `gemini.google.com`, the user types `hola` once in the GUEST session (no Google account needed), Dulus captures the session token, and the REPL is now talking to real Gemini through the web UI. From `pip install dulus` to working IA in 30 seconds, zero accounts created, $0 spent. Same flow works for Claude.ai / Kimi.com / Qwen / DeepSeek if the user has those accounts. Also: a soft yellow tip resurfaces on every REPL start for returning users who never harvested AND have no cloud API key configured — the feature stops being invisible. Plus: PyPI/wheel description rewritten to lead with "Use frontier IA as an agent — no API key, no subscription, \$0" instead of the old "Spanish-first" line that gated the door for the 90% of the planet who doesn't read Spanish.

- May 16, 2026 (**v0.2.93**): **Autoadapter no longer loops on legitimately-verbose plugins** — `_smoke_test_tool` had a hardcoded `BLOAT_CAP = 2500` chars. Any plugin tool whose default-params response exceeded that was marked OUTPUT_TOO_VERBOSE and the worker entered a refactor-fix loop. Problem: 2500 is way too tight — yfinance `get_info` returns ~5-15 KB of legit company metadata, sherlock multi-site results land at 3-8 KB, pokemon_state dumps at 2-4 KB. Every adapt cycle for those plugins burned 20 fix attempts trying to compact data that was inherently that size. Raised the default cap to 8000 (still blocks raw DataFrame.to_string() dumps and json.dumps of full library objects), made it tunable via `config["adapter_bloat_cap"]`, and added a skip path for tools whose schema exposes a `verbose` / `limit` / `max_results` / `full` param — the user opted into large output deliberately, refactoring it would break the contract.

- May 16, 2026 (**v0.2.93**): **Telegram multi-chat-id authorization is live-refresh and cache-smart** — `_tg_poll_loop` set `authorized = set(chat_ids)` ONCE at startup. After that, `/config telegram_chat_ids=12345,67890` took effect in `config.json` but the poll loop kept the old set in memory; second user got `⛔ Unauthorized` despite the config showing them as added. Refactored to a cached `is_authorized()` closure: fast-path set lookup for known users (99% of inbound messages — zero config reads), and on cache MISS we re-read `_tg_get_chat_ids(config)` once. New user that was just added via `/config` is admitted on their first message AND gets a one-shot welcome. Existing users never see a duplicate greeting.

- May 16, 2026 (**v0.2.92**): **`/lang` command — Dulus speaks 34 languages or any voice you describe** — One slash command to swap reply language. ISO-639 codes (`/lang en`, `/lang zh`, `/lang ja`, `/lang pt-br`, `/lang ar`, `/lang hi`, …) get mapped to a human-readable instruction. Free-form descriptors pass verbatim — `/lang "tigre de calle dominicano"`, `/lang "pirate"`, `/lang "Yoda"`, `/lang "speak as my gym tutor"` — and the model role-plays the voice across the whole session. Default stays Dominican Spanish so existing users see no change. Trigger for shipping it: doubao.com (China's biggest AI assistant, ByteDance) appeared in the GitHub referrer panel — meaning at least one Chinese-speaking dev was clicking through from a Chinese-language IA query. Time to meet them in their language. Zero-friction cross-cultural onboarding.

- May 16, 2026 (**v0.2.91**): **CORS headers on the webchat daemon — Android sandbox APK can finally talk to Dulus live** — The bundled APK rendered the OS shell smoothly but every app inside stayed disconnected; opening `http://10.0.0.6:5000/sandbox` in the phone's regular browser worked perfectly though, identical bundle, identical daemon. Same-origin vs cross-origin was the only difference. The APK loads its UI from `https://appassets.androidplatform.net/` (synthetic origin via WebViewAssetLoader) and fetches `http://10.0.0.6:5000/api/*` over LAN HTTP — that's cross-origin, and Android WebView enforces CORS strictly. With no `Access-Control-Allow-Origin`, every fetch silently aborts. Added `@app.after_request` that decorates every response with `Access-Control-Allow-Origin: *`, `Allow-Methods`, `Allow-Headers`, plus a catch-all `OPTIONS` preflight handler. APK apps connect live now.

- May 15, 2026 (**v0.2.90**): **README hero swap — stale FALCON image replaced with the Cigua Palmera (Dulus dominicus)** — The PyPI README still showed the old `docs/hero.svg` with FALCON branding from before the rename. Replaced with `docs/dulus-bird.png` (the warrior cigua palmera holding a spear — the Dominican national bird the project is named after) + caption "Named after the bird, not the rocket." Refreshed badges: version `v0.2.60 → v0.2.89`, providers `11 → 100+ via LiteLLM`, tools `27 → 30+`. The PyPI landing page now matches the website's brand.

- May 15, 2026 (**v0.2.89**): **Sandbox embedded INSIDE the desktop GUI via Win32 SetParent** — Click "🦅 Sandbox" in the GUI Web tab and the Dulus OS renders inside the content frame, not in a popup window. Architecture: spawn pywebview as a SUBPROCESS (so pywebview owns its own main thread — pywebview 5.x raises `WebViewException("pywebview must be run on a main thread")` on every platform now, killing the threading-based embed), then poll `FindWindowW(unique_title)` from the parent process, strip `WS_CAPTION` / `WS_BORDER` / `WS_THICKFRAME` + add `WS_CHILD`, and `SetParent(child_hwnd, content_frame_hwnd)` — the pywebview surface becomes a native child widget of the tkinter frame. `MoveWindow(0,0,w,h)` fits it; a `_pin_origin` 250ms poll snaps it back to (0,0) if any stray move sneaks through. `frameless=False` prevents EdgeChromium's body-drag-to-move from making the embed jitter when you drag stuff INSIDE the sandbox. Smart URL: probes `:5000/sandbox/` first (full daemon API access), falls back to a bundled local `SandboxServer` on a random port when `/webchat` isn't running.

- May 15, 2026 (**v0.2.89**): **Local OCR shipped first-class** — New `/ocr` REPL command and `ExtractTextFromImage` tool. pytesseract first, easyocr fallback (~1GB extra opt-in). Zero vision-model tokens for receipts, code screenshots, error stacks, dense tables. `WebBridgeScreenshot` auto-OCRs the saved PNG and returns BOTH the path AND the text in a single response — no more "let me Read this PNG" → 8KB of binary garbage in the model's context. `/img` now sends the image AND the verbatim OCR transcription side-by-side: vision models cross-check (fewer digit/punctuation hallucinations on receipts), text-only models gracefully degrade to reading the OCR. Optional dep via `pip install dulus[ocr]`.

- May 15, 2026 (**v0.2.89**): **kepano/obsidian-skills bundled — Dulus writes Obsidian-flavored markdown by default** — 5 skills shipped at `skill/bundled/`: `defuddle`, `json-canvas`, `obsidian-bases`, `obsidian-cli`, `obsidian-markdown`. Loaded as `source="bundled"` (lowest priority — easily shadowed by user/project SKILL.md files of the same name). MemPalace memories at `~/.dulus/memory/` now get authored with wikilinks `[[Note]]`, callouts `> [!note]`, frontmatter properties, embeds. Open that folder as an Obsidian vault → the graph view connects related memories automatically. Zero config from the user side; Obsidian auto-creates the vault on first open.

- May 15, 2026 (**v0.2.89**): **🦅 Cigua Palmera lands across every UI surface** — Replaced the orange-pentagon ▲ in the webchat sidebar / header (and the Mesa Redonda variant), the Task Dashboard nav-logo (+ proper favicon), and added a favicon link to the Sandbox `index.html`. Same palmchat artwork everywhere. Favicon route in `webchat_server.py` falls back to `docs/dulus-bird.png` when the repo-root png isn't present (i.e. inside an installed wheel). The "named after the bird" promise is now visible in every browser tab.

- May 15, 2026 (**v0.2.88**): **LiteLLM gateway — one provider entry, 100+ backends** — New welcome wizard option pulls in OpenRouter, Groq, Together, Bedrock, Vertex, Cohere, Perplexity, xAI, Mistral, Fireworks, Anyscale, Replicate, Azure, DeepInfra — anything LiteLLM speaks. The wizard auto-installs the package in-place (no exit-pip-install-rerun dance), parses the backend from the model string (`openrouter/anthropic/claude-3-5-sonnet`), and asks for the right per-backend API key (`OPENROUTER_API_KEY`, `GROQ_API_KEY`, etc.) — stored in config and auto-mirrored into `os.environ` at dispatch time. `stream_litellm()` is a real native streamer (text + tool-call deltas in Dulus's internal shape), not a monkey-patched OpenAI client. Optional dep via `pip install dulus[litellm]`.

- May 15, 2026 (**v0.2.88**): **First-run `/doctor` auto-runs LAST, after every boot print** — Welcome wizard now flags `pending_first_run_doctor` and the REPL runs `cmd_doctor` after all the splash / Memory Palace / Soul / Tool Schema / Gold Memories / Shell-detect lines have flushed. The PASS/FAIL/WARN table is the LAST thing on screen before the prompt — what providers got keys, what voice/TTS bits are live, what optional deps are missing. The flag key avoids `_` prefix (those get stripped by `save_config` as runtime-only — earlier attempts at this kept silently dropping the flag).

- May 15, 2026 (**v0.2.88**): **`install.sh` heals `~/.dulus` permissions on Linux / WSL** — Recurring footgun: user runs `sudo ./install.sh` (or pip pulls in a dep that touches `~/.dulus` during install) and afterwards `~/.dulus` is owned by root. Dulus then bombs on first launch with PermissionError. Now the installer detects the real user (`SUDO_USER` over `$USER` when sudo'd), creates `~/.dulus` as that user, recursively `chown`s back anything root-owned, and `chmod u+rwX,go+rX` as belt-and-suspenders. Falls back to a printed sudo command if non-interactive sudo isn't available.

- May 15, 2026 (**v0.2.88**): **Toolbar clamps to terminal width — wake-word "Listening…" no longer wraps on Windows Terminal** — The split-input layout's bottom toolbar is a `Window(height=1)`, but emojis (🧠 📁 📊 🔒 🎙️) and the wake-word energy-bar glyphs (▁▂▃▄▅▆▇█) render as 2 cells on Windows Terminal while prompt_toolkit's cwidth math treats them as 1. The right tail spilled onto a second row — "Waking up..." on row 1, "Listening..." on row 2. Now `_get_toolbar_text()` strips newlines + ANSI-aware truncates to terminal-width minus a margin (12 cells on Windows for the emoji underestimate, 2 elsewhere). Stays single-row in both terminals.

- May 15, 2026 (**v0.2.88**): **Clearer WebSearch / WebFetch vs WebBridge boundary + voice-typo flag** — Tool descriptions tightened: `WebSearch` + `WebFetch` self-describe as TEXT-ONLY info lookups and explicitly call out the failure mode ("when the user says 'play / pon / reproduce X', returning a search-result link is the wrong tool — that's WebBridge territory"). `WebBridgeNavigate` gains a matching `WHEN TO USE` block listing experiential / interactive cases (play media, log in, fill forms, scrape JS). Voice transcription: local `/voice` now tags transcripts with `🎙 Transcribed:` so the system prompt's tolerate-Whisper-typos rule actually fires (previously only Telegram voice prepended the marker).

- May 15, 2026 (**v0.2.85**): **Slim wheel — 11 MB → 2.5 MB** — Discovered `docs/uploads/` holding 9.9 MB of Claude Design source images + the `_bundles/sandbox.tar.gz` artifact double-shipping with the sandbox source. Both removed. `pyproject.toml` excludes tightened. Wheel went from 11.4 MB (0.2.84) to 2.5 MB (smaller than the original 0.2.81 baseline at 3 MB). Repo working tree dropped from ~32 MB to ~25 MB.

- May 15, 2026 (**v0.2.84**): **Sandbox sandbox/dist ships in the wheel — back to the layout that worked** — Reverted the tarball-extract-on-first-run system that broke between 0.2.82-0.2.83. `sandbox_bootstrap.ensure_sandbox()` now just returns the packaged `sandbox/dist/` path directly. Vite-built `index.html` restored to use absolute `/sandbox/assets/...` paths (the broken build had used `./assets/...` which 404'd when `/sandbox` was hit without a trailing slash). GitHub Linguist still kept Python-flavored via `.gitattributes` (`sandbox/** linguist-vendored=true`), not by hiding the files from the wheel.

- May 15, 2026 (**v0.2.72**): **Cross-platform one-liner installer + `install.sh` / `install.ps1` profile picker** — `curl -fsSL https://raw.githubusercontent.com/KevRojo/Dulus/main/install.sh | bash` detects OS / package manager / Python ≥ 3.11 / uv / pipx / pip, asks for a profile (full / standard / basic / custom), installs only missing system packages, and verifies the install with a Dulus binary probe. Windows PowerShell equivalent at `install.ps1`. Plain `pip install dulus` flow on `install.ps1` no longer drops to a Scripts directory that isn't on PATH (was the cause of silently-broken installs into uv-managed Python envs).

- May 13, 2026 (**v0.2.62**): **Wake-word engine rebuilt — zero-gap continuous stream + better phrase detection** — Replaced the old start/stop InputStream loop with a single continuous callback stream using a ring-buffer pre-roll. This eliminates all audio gaps and ensures we never miss the start of an utterance. Added visual energy bar (🎙️) while recording commands. Expanded wake phrases: `adulus`, `aduluz`, `DOLOS`. Removed `¡!` punctuation from phrases since Whisper doesn't transcribe them. Extended wake-record window from 2.5s to 4.5s for better catch rate. Also cleaned up `MANIFEST.in`: include `pyproject.toml` in sdist and global-exclude `__pycache__` / `*.pyc`.

- May 09, 2026 (**v0.2.30**): **`/bg start` daemon is now truly windowless on Windows** — `python.exe` is a console-subsystem binary, so even with `DETACHED_PROCESS` Windows still spun up a visible console window for the daemon. Closing that window killed the daemon. Switched to `pythonw.exe` (the GUI-subsystem variant) + `CREATE_NO_WINDOW` so the daemon spawns with NO console window at all. Verified: `Get-Process` reports `MainWindowHandle = 0` after spawn — there's literally nothing to close. Telegram + WebChat + IPC keep running in background until `/bg stop` or `/bg kill`.

- May 09, 2026 (**v0.2.29**): **`/bg start` actually works from inside a REPL + daemon-mode webchat default-on + tested end-to-end this time**
  - **The whole point of `/bg start` was broken from day one.** A REPL itself binds `127.0.0.1:5151` to serve `dulus "..."` shell calls, so the moment you typed `/bg start` from inside that REPL, the duplicate-detection check saw "port in use" and refused — by the very REPL invoking the command. `/bg kill` then killed the only thing on the port: your own REPL. Pure logic flaw on me.
  - **Now `/bg start` releases the REPL's own IPC first.** When invoked from inside a REPL, the command stops the REPL's IPC thread, force-closes the socket with `SO_LINGER {1, 0}` (skips TIME_WAIT), waits ~600ms for the OS to free the port, and only then spawns the daemon. The REPL keeps running — it just becomes a normal client whose `dulus "..."` dispatches now go to the daemon.
  - **Daemon mode auto-starts WebChat by default.** Previously WebChat only fired when `/bg start` injected an env var; running `dulus --daemon` directly gave you IPC + Telegram but no browser endpoint. Now `--daemon` always starts WebChat on `127.0.0.1:5000` (loopback), opt-out with `webchat_disabled: true` in config or `DULUS_DAEMON_NO_WEB=1`.
  - **Daemon callback was calling `agent.run()` with the wrong signature.** Earlier I passed `agent_run(state, msg, config, is_background=True)` but the real signature is `(user_message, state, config, system_prompt, ...)`. Every Telegram/IPC turn raised `TypeError` silently, daemon looked alive but never answered. Fixed and verified by `inspect.signature`.
  - **`/bg status` now distinguishes REPL from daemon.** Source of truth is the `BG_PID` file (only `/bg start` writes it). If port is in use but no PID file, status says "likely your own Dulus REPL — for a true headless daemon, run `dulus --daemon` from a fresh shell".
  - **`/bg kill` only targets the real daemon now.** Reads `BG_PID`, kills only that PID (refuses if PID matches the calling process). Will not nuke the REPL you're typing in. Falls through to `taskkill /F` on Windows if SIGTERM doesn't work.
  - **End-to-end smoke tested.** A throwaway harness boots a fake REPL on 5151, sends a JSON request and gets `{"response": "REPL echoes: hello"}`, releases the port, spawns a fake daemon on the same port, sends another request and gets `{"response": "DAEMON echoes: klk papi"}`. Handoff works without TIME_WAIT bind failures.

- May 09, 2026 (**v0.2.28**): **IPC server thread no longer crashes on idle / dropped connections** — `conn.recv()` was hitting its 60s read timeout and raising `TimeoutError` out of the worker, killing the IPC thread silently. After that, the daemon was still running but `dulus "..."` from any shell would hang forever. Caught socket.timeout / ConnectionReset / BrokenPipe / OSError around the request-handling block so a single bad client can't take down the server.

- May 09, 2026 (**v0.2.27**): **`/bg` no longer leaves stale state** — when something else (a REPL, an old daemon) is already on `127.0.0.1:5151`, `/bg start` now refuses to spawn a duplicate that would just fail to bind, explains what's happening, and clears the stale PID file. `/bg status` self-heals: when it sees a stale PID it auto-removes it instead of complaining on every call. The previous "Some Dulus is listening on IPC, but our PID file is stale" warning is gone for good.

- May 09, 2026 (**v0.2.26**): **`/bg start` daemon crash fix + defensive `clr()` + composio fallback**
  - **Daemon was silently crashing on launch.** `_run_daemon` printed its banner with `clr("...", "accent", "bold")`, but the default theme palette only ships {blue, cyan, gray, green, magenta, red, white, yellow} — `"accent"` raised `KeyError` and killed the process before the prompt loop started. WebChat + IPC threads, being daemon=True, died with it. Switched the banner to `"yellow"` and wrapped in try/except so a stale theme color name never takes the daemon down.
  - **`clr()` is now defensive.** Missing color keys are silently dropped instead of raising. One typo in a theme name no longer crashes the REPL.
  - **`/skill list composio` no longer errors out.** The public `/api/v3/toolkits` endpoint requires elevated auth (returns 401/403 even with a valid API key for free tiers). Added a curated 32-toolkit fallback (Gmail, Slack, GitHub, Notion, Linear, Asana, ClickUp, Jira, Discord, Stripe, etc.) so the menu always shows useful targets. Authenticated path is still attempted first when an API key is configured.

- May 09, 2026 (**v0.2.25**): **`/skill list awesome` no longer hangs** — was fetching 235 SKILL.md files sequentially (50-120 seconds, looked frozen). Now uses one GitHub tree API call (instant, <1s, names only) by default; pass `--full` to also pull per-skill descriptions in parallel via a 12-worker thread pool (~5s instead of 120s). Cache stores the with_descriptions flag so future calls reuse the right data.

- May 09, 2026 (**v0.2.24**): **Auto-adapter prompt — 5 fixes from a sherlock postmortem**
  - **Reconciled `limit` default** — the prompt had two contradictory rules ("default: 50, max: 200" vs "default: 10, NOT 50"). Models burned tokens reasoning about which to follow. Unified on `default: 10, hard max: 200` everywhere.
  - **"READ the source first" rule** at the top of the wrapper guidelines. Adapters were inferring upstream function signatures from class names and shipping plugins that compile/import/export cleanly but crash at runtime due to type-shape mismatches. Now the prompt explicitly tells the model to read the consumer code (`param.get(...)` / `for x in param`) before guessing shapes.
  - **Notifier/callback pattern hint** — when the upstream library has a notify/callback class, prefer collecting results via that callback over parsing the return value. Callbacks tend to stay stable; return shapes drift between versions.
  - **`ADAPTATION_GUIDE.md` now requires a `## Type Contracts` section** documenting the exact shape of every non-trivial parameter. Read by the verifier and by future re-adaptations — eliminates blind re-guessing.
  - **Verifier checklist explicitly flags the smoke test as THE BAR.** Compile / import / exports / ToolDef-shape are necessary but not sufficient. The new header in `ADAPTATION_TODO.md` warns the model not to celebrate after the syntactic checks — most real bugs are type-shape mismatches that only the smoke test catches.

- May 09, 2026 (**v0.2.23**): **Auto-adapter teaches new plugins to declare TmuxOffload-worthy tools**
  - **The adapter prompt now requires** the model to estimate per-tool runtime. Any tool that typically takes more than ~15 seconds (sherlock, holehe, OSINT crawls, video downloads, full-repo analysis, etc.) must end its `description` with the literal marker `[long-running — wrap in TmuxOffload]`.
  - **System prompt now honors that marker** at runtime. When the agent sees a tool with that suffix, it wraps the call in TmuxOffload automatically instead of blocking the REPL. No more 90-second sherlock freezes pretending to be productive.
  - **Why this matters.** New plugins adopted via `/plugin adapt` now self-declare their UX hints. The agent picks them up the moment they install — zero manual config, zero "oh I should have offloaded that" regrets.

- May 09, 2026 (**v0.2.22**): **`/bg start` — one detached daemon for CLI + Web + Telegram**
  - **One command, three doors.** `/bg start` spawns a detached Dulus daemon that simultaneously listens on `127.0.0.1:5151` (IPC for `dulus "..."` from any shell), `127.0.0.1:5000` (WebChat in your browser), and the Telegram bridge if configured. All three entry points hit the SAME live session — same history, memory, plugins, tool state.
  - **`/bg status` / `stop` / `attach`** — small surface, big convenience. `attach` prints how to reach the running daemon (CLI, browser, tail the log).
  - **WebChat is loopback-only by default.** Previously the webchat server bound to `0.0.0.0` and was visible on the LAN out of the box. Now it binds to `127.0.0.1` unless you opt in with `/webchat lan on` (or `webchat_lan: true` in config). Anyone on the wifi can no longer poke your agent by accident.

- May 09, 2026 (**v0.2.21**): **System prompt clarifies SleepTimer scope** — added an explicit hint that SleepTimer is ONLY for user-facing reminders/notifications, NEVER for inter-tool waits (those freeze the console). When a pause is needed mid-pipeline, models should use `sleep N` inside the Bash command itself. Fixes a recurring console-freeze when models reflexively reached for SleepTimer between commands.

- May 09, 2026 (**v0.2.20**): **IPC port-collision fix on Windows** — `SO_REUSEADDR` on Windows lets two sockets share the same port, which would let a second Dulus instance silently "steal" the IPC listener. Switched to `SO_EXCLUSIVEADDRUSE` so the second instance correctly backs off and acts as a client. Verified end-to-end with the new test harness.

- May 09, 2026 (**v0.2.19**): **Shared sessions via tiny TCP socket — daemon workaround supremo**
  - **One Dulus, many shells.** When a Dulus REPL or `--daemon` is running, it now listens on `127.0.0.1:5151`. Any subsequent `dulus "do X"` from another shell forwards the prompt to that live session over the socket and prints back the reply — same history, same memory, same plugins, same tool state. No session manager, no IPC framework, no systemd unit. 80 lines of plain TCP.
  - **Falls back gracefully.** If no listener is up, the CLI behaves exactly as before (spawns its own `--print` process). Daemon/gui/`--cmd`/`--run-tool` modes intentionally bypass the IPC dispatch — they need their own process.
  - **Why this matters.** The competition wires up `multiprocessing.Manager`/grpc/zmq/dbus + a daemon CLI + config files + service installers to do the same thing. Dulus does it with `socket.bind` and a thread.

- May 09, 2026 (**v0.2.18**): **Add `beautifulsoup4` as default dep** — needed by web scraping / harvest flows and several plugins. Tiny dep, ships by default.

- May 09, 2026 (**v0.2.17**): **Mega-release — Composio bundled, awesome skills live, lite mode fixed, English prompt**
  - **Composio plugin shipped in the wheel.** `pip install dulus` now bundles the Composio Tool Router plugin (no MCP needed) and copies it into `~/.dulus/plugins/composio/` on first launch. The composio Python SDK (~1MB) is now a default dep — Slack, Gmail, GitHub, Notion, Asana, ClickUp, Linear, etc. all available via `composio_create_session`.
  - **`/skill list` interactive picker.** Calling `/skill list` without args opens a menu: awesome (~235 skills via GitHub), composio (1000+ toolkits via API), local (Anthropic marketplace on disk), installed, or all. Catalogs are cached 24h in `~/.dulus/cache/`.
  - **Awesome skills, no Claude Code needed.** Skills from `alirezarezvani/claude-skills` are now fetched live via the GitHub API (1 tree call) + raw.githubusercontent.com (no rate limit). Users without `~/.claude/plugins/` see the full ~235 catalog.
  - **Lite mode actually works.** `/lite` previously toggled a config flag that nothing consumed. Now it strips platform_hints, git_info, DULUS.md, batch/thinking/plan/tmux hints, project memory index — saves ~45% of the system prompt for cheap models.
  - **System prompt converted Chinese → English.** Internal prompt was condensed in Chinese for token density (~30% denser). Now in compact English: maintainable by humans, friendlier to small models, and matches the Spanish-first branding instead of fighting it. Net cost ~+125 tokens/turn — worth it.
  - **`dulus` CLI hint baked into the prompt.** The model is now told that `dulus -c "..."` works after `pip install dulus` (no need for `python dulus.py`). Less path-juggling in agent flows.
  - **VERSION auto-syncs from pyproject.** The `dulus.py` `VERSION` constant now reads `importlib.metadata.version("dulus")` so `/status`, `--version`, and the startup banner stop drifting from the released version.

- May 09, 2026 (**v0.2.16**): **MemPalace per-session dedup — game changer**
  - **No more re-injecting the same memory every turn.** MemPalace now keeps a per-session set of content-hashes (`_mp_injected_keys`) — once a memory is injected in this conversation, it won't be re-injected on later turns even if it scores high on a new query. The next-best non-duplicate hit is used instead, so each turn brings *new* context to the model rather than the same 3 hits over and over.
  - **Within-turn dedup too.** When mempalace's hybrid retriever returns the same chunk twice in a single query (it happens), the duplicate is dropped before injection.
  - **Why it's a game changer.** In a 20-turn conversation that previously kept re-injecting the same 3 memories, you save ≈8K tokens of duplicates AND the model gets ~30 distinct memories of palace coverage instead of just 3. Compounding context, no extra cost.
  - **`/mem_palace reset`** — new sub-command to clear the dedup cache mid-session if you want already-seen memories to be re-injectable on the next match.

- May 09, 2026 (**v0.2.15**): Banner image hosted locally so PyPI renders it correctly.

- May 09, 2026 (**v0.2.14**): **Multi-user Telegram bridge**
  - **Telegram bridge now supports multiple authorized chat_ids.** Configure with `/telegram <token> <id1>,<id2>,<id3>` or set `telegram_chat_ids` in `config.json` as a comma-separated string (trailing commas like `717151713,787615162,,` are ignored). Each authorized chat gets its own replies — Dulus tracks who sent each message via `_active_tg_chat_id` and routes the response back to the right user. Welcome message is broadcast to all configured users on bridge start. The legacy single-int `telegram_chat_id` still works for backwards-compat.
  - **Why this matters.** One Dulus instance, multiple humans poking it from their phones — useful for teams sharing a long-running agent, or for paired-up users running the same MemPalace from different devices.

- Apr 09, 2026 (**v1.01.20**): **Automated Plugin Adapter System, Premium UI, and Hot-Reloading**
  - **Automated Plugin Adapter (`plugin/autoadapter.py`)** — Dulus can now intelligently onboard any Python repository without a manual manifest. Using AST-based static analysis and AI-driven generation, it creates `plugin.json` and `plugin_tool.py` on the fly, handling complex dependencies and constructor arguments.
  - **Intelligent Library Handling** — The AI generation pipeline now includes specialized instructions for terminal-based libraries (e.g., `asciimatics`), ensuring correct usage of patterns like `Screen.wrapper` to prevent runtime errors.
  - **Hot-Reloading** — Newly adapted plugins are automatically registered and available for use in the current session immediately after installation, with no restart required.
  - **Premium Branding & UI** — Replaced the startup ASCII logo with a high-resolution Dulus design. Added real-time "Thinking" spinners and progress feedback during the adaptation process.

- Apr 06, 2026 (**v3.05.53**): **Telegram interactive menus, `/img` alias, `/voice device`, OpenAI/Gemini vision support**
  - **Telegram interactive menus fixed** — slash commands with interactive input (e.g. `/ollama`, `/permission`, `/checkpoint`) were blocking the Telegram poll loop, making it impossible to respond to the menu prompts. Slash commands now run in a daemon thread (like regular queries), keeping the poll loop free. All interactive menus (`ask_input_interactive`) work correctly over Telegram.
  - **`/img` alias** — `/img` is now an alias for `/image`, for faster clipboard-image workflows.
  - **`/voice device`** — new subcommand to list all available input microphones and select one interactively. The selected device index is persisted in the session config and shown in `/voice status`. Useful on systems with multiple audio interfaces (e.g. USB headset + built-in mic).
  - **Vision support for OpenAI / Gemini models** — `/img` (and `/image`) now sends images in the OpenAI multipart `image_url` format to cloud vision models (GPT-4o, Gemini 2.0 Flash, etc.), in addition to the existing Ollama native format. No configuration change needed — the correct format is selected automatically based on the active provider.
  - **Bug fix: threading race condition** — `_in_telegram_turn` is now tracked via `threading.local()` per-slash-runner thread instead of a shared config key, eliminating a race condition that could corrupt the flag when a regular message arrived while an interactive slash command was waiting for input.

- Apr 06, 2026 (**v3.05.52**): **Checkpoint system, plan mode, compact, and utility commands, support MiniMax Models, fix telegram bugs** 
  - **Checkpoint system** (`checkpoint/` package): auto-snapshots conversation state and file changes after every turn. `/checkpoint` lists all snapshots; `/checkpoint <id>` rewinds both files and conversation history to any previous state; `/checkpoint clear` removes all snapshots for the session. `/rewind` is an alias. 100-snapshot sliding window; initial snapshot captured at session start. Throttling: skips when nothing changed. File backups use copy-on-write; snapshots capture post-edit state.
  - **Plan mode**: `/plan <desc>` enters a read-only analysis mode — Claude may only read the codebase and write to a dedicated plan file (`.nano_claude/plans/<session_id>.md`). All other writes are silently blocked with a helpful message. `/plan` shows the current plan; `/plan done` exits plan mode and restores original permissions; `/plan status` reports whether plan mode is active. Two new agent tools — `EnterPlanMode` and `ExitPlanMode` — let Claude autonomously enter and exit plan mode for complex multi-file tasks; both are auto-approved in all permission modes.
  - **`/compact [focus]`**: manually trigger conversation compaction at any time. An optional focus string guides the LLM summarizer on what context to preserve. Auto-compact and manual compact both restore plan file context after compaction.
  - **Utility commands**: `/init` creates a `CLAUDE.md` template in the current directory; `/export [filename]` exports the conversation as Markdown (default) or JSON; `/copy` copies the last assistant response to the clipboard (Windows/macOS/Linux); `/status` shows version, model, provider, permissions, session ID, token usage, and context %; `/doctor` diagnoses installation health (Python version, git, API key + live connectivity test, optional deps, CLAUDE.md presence, checkpoint disk usage, permission mode).

- Apr 06, 2026 (**v3.05.51**): **Project renamed from Nano Claude Code to CheetahClaws**
  - The project has been rebranded from **Nano Claude Code** to **CheetahClaws** — a more distinctive name that captures the spirit of the tool: a sharp, agile coding assistant. The `Cl` in CheetahClaws is a subtle nod to Claude.
  - CLI command: `nano_claude` → `cheetahclaws`
  - PyPI package: `nano-claude-code` → `cheetahclaws`
  - Config directory: `~/.nano_claude/` → `~/.clawnest/` → `~/.cheetahclaws/`
  - Main entry point: `nano_claude.py` → `cheetahclaws.py`
  - All documentation, GitHub URLs, and internal references updated accordingly.
  - Added **CheetahClaws vs OpenClaw** comparison section to README.

- 00.29 PM, Apr 06, 2026 (**v3.05.5**): **SSJ Developer Mode, Telegram Bridge, Worker Command, and UX improvements** 
  - **`/ssj` — SSJ Developer Mode**: Interactive power menu with 10 workflow options: Brainstorm, TODO viewer, Worker, Expert Debate, Propose Improvements, Code Review, README generator, Commit helper, Git Diff Scan, and Idea-to-Tasks Promotion. Menu stays open between actions and supports `/command` passthrough (e.g. `/exit` works from inside SSJ).
  - **`/worker` command**: Auto-implements pending tasks from `brainstorm_outputs/todo_list.txt` one by one. Supports selecting specific tasks with comma-separated numbers (e.g. `1,4,6`), a custom todo file path (`--path /other/todo.md`), and a worker count limit (`--workers 3`). If you accidentally pass a brainstorm `.md` output file, Worker detects it and offers to redirect to `todo_list.txt` — or to generate it first from the brainstorm file and then run Worker automatically. Each task gets a dedicated prompt that reads code, implements the change, and marks it done.
  - **`/telegram` — Telegram Bot Bridge**: Receives messages via Telegram Bot API and routes them through the model, sending responses back to the chat. Auto-starts on launch if configured. Only responds to the authorized `chat_id`. Supports slash command passthrough (`/cost`, `/model`, etc.), shows a typing indicator while the model processes, and can be stopped remotely by sending `/stop` in Telegram.
  - **Brainstorm → TODO pipeline**: After brainstorm synthesis, automatically generates `brainstorm_outputs/todo_list.txt` with prioritized checkbox tasks. TODO viewer (SSJ option 2) shows only pending tasks as numbered (completed tasks shown with ✓ without a number).
  - **Expert Debate improvements**: SSJ option 4 now prompts for the number of debate agents (default 2, minimum 2); rounds are auto-calculated as `(agents × 2 − 1)`. The debate result is saved to the same directory as the debated file (`<stem>_debate_HHMMSS.md`). An animated per-round per-expert spinner (`⚔️ Round 2/3 — Expert 1 thinking...`) keeps the terminal lively throughout the debate.
  - **Brainstorm spinner**: Animated spinner with random phrases while brainstorm agents are thinking.
  - **Force quit**: 3× Ctrl+C within 2 seconds triggers `os._exit(1)` — kills the process immediately regardless of blocking I/O.
  - **Interactive Ollama Model Picker** — when a request fails with 404 (model not found), cheetahclaws queries the local Ollama API (`/api/tags`) and presents a numbered model selector to switch models and retry without restarting. Cancelling aborts gracefully without crashing the REPL.
  - **Windows file handling** — `_read`, `_write`, and `_edit` in `tools.py` now force UTF-8 encoding and `newline=""`. `_edit` detects pure-CRLF files (every `\n` is part of `\r\n`) and restores line endings after edit; mixed-line-ending files are left as-is to avoid corruption.
  - **/brainstorm command** — `/brainstorm [topic]` runs a multi-persona AI debate. The model first generates N expert personas tailored to the topic (geopolitics → analysts & diplomats; software → architects & engineers; etc.). Agent count is chosen interactively at runtime (2–100, default 5). Results are saved to `brainstorm_outputs/` and synthesized by the main agent. 
  - **Rich Live SSH fix** — Rich's in-place Live streaming is now automatically disabled in SSH sessions (`SSH_CLIENT`/`SSH_TTY` detected) where ANSI cursor-up breaks and causes repeated output lines. Override with `/config rich_live=true/false`.
  - **`threading.RLock`** — replaced `threading.Lock` with `RLock` to support re-entrant calls from brainstorm synthesis and Ollama retry paths.

- 05:39 PM, Apr 05, 2026 (**v3.05.4**): **Reasoning, Rendering, and Packaging Improvements, Enhanced Memory System, Native vision support for local Ollama models, Bracketed Paste Mode, Rich Tab Completion**
  - **Bracketed Paste Mode** — replaced the old timing-based multi-line paste detection with the standard terminal Bracketed Paste Mode protocol. Pasted text of any length (code blocks, long prompts, multi-paragraph instructions) is now collected as a single turn with zero latency and no blank-line artifacts. Falls back to a 60 ms timing window for terminals that don't support BPM. Bracketed paste mode is cleanly disabled on REPL exit.
  - **Rich Tab Completion with descriptions** — pressing Tab after `/` now shows every command with a one-line description and a hint of its subcommands. Typing `/plugin ` then Tab lists all subcommands (`install`, `uninstall`, `enable`, …). Auto-completes to the unique match when only one command matches the prefix. Subcommands supported for `/mcp`, `/plugin`, `/tasks`, `/cloudsave`, `/voice`, `/permissions`, `/proactive`, and `/memory`.
  - **Model name bug fix** — `--model ollama/qwen3.5:35b` no longer gets corrupted to `ollama/qwen3.5/35b`. The startup colon-to-slash conversion now only fires when the left side of `:` is a known provider name and no `/` is already present, preserving Ollama's `model:tag` format.
  - **Native vision support for local Ollama models** (`llava`, `gemma4`, `llama3.2-vision`): new `/image [prompt]` command captures the current clipboard image, encodes it to Base64, and attaches it to the next prompt. Install Pillow with `pip install cheetahclaws[vision]`; Linux users also need `xclip` (`sudo apt install xclip`).
  - **Enhanced Memory System** — added `confidence` / `source` / `last_used_at` / `conflict_group` metadata to every memory entry; conflict detection on `MemorySave` warns before overwriting; `MemorySearch` re-ranks results by `confidence × recency` (30-day decay) and updates `last_used_at` on hits; new `/memory consolidate` command runs a lightweight AI analysis of the current session and auto-saves up to 3 long-term insights (user preferences, feedback corrections, project decisions) at 0.8 confidence — never overwrites higher-confidence user memories.
  - **Post-merge fixes** — removed a debug `debug_payload.json` file write that was firing on every OpenAI-compatible API call (left over from PR #11 development). Also fixed ANSI dim color not being reset after the thinking block ends, which caused subsequent text to appear dim in non-Rich terminals. Bumped `pyproject.toml` version to `3.05.4`, and moved `sounddevice` to the optional `voice` extra (`pip install cheetahclaws[voice]`).
  - **Native Ollama reasoning + terminal rendering fix** — local reasoning models (`deepseek-r1`, `qwen3`, `gemma4`) now stream their `<think>` blocks to the terminal. Ollama exposes thoughts in `msg["thinking"]`, but cheetahclaws was previously dropping them; this is now fixed by yielding `ThinkingChunk` from the Ollama adapter. Also fixed a Windows CMD/PowerShell rendering issue where token-by-token ANSI dim resets caused thoughts to print vertically, and corrected `flush_response()` so it runs once at the end instead of on every thinking token. Enable with `/verbose` and `/thinking`.
  - **uv support** — added `pyproject.toml`; install with `uv tool install .` to make the `cheetahclaws` command available globally from anywhere in an isolated environment, without manual PATH setup.
- 00:41 PM, Apr 05, 2026: **v3.05.3 add structured session history** — Structured session history: on every exit, sessions are saved to `daily/YYYY-MM-DD/` (capped at `session_daily_limit`, default 5 per day) and appended to a master `history.json` (capped at `session_history_limit`, default 100). Each session file now includes `session_id` and `saved_at` metadata. `/load` groups sessions by date with time, ID, and turn-count display; supports multi-select (`1,2,3`) to merge sessions and `H` to load the full history with token-count confirmation. Both limits are configurable via `/config`.
- 00:41 PM, Apr 05, 2026: **v3.05.3 fix session** — Structured session history: on every exit, sessions are saved to `daily/YYYY-MM-DD/` (capped at `session_daily_limit`, default 5 per day) and appended to a master `history.json` (capped at `session_history_limit`, default 100). Each session file now includes `session_id` and `saved_at` metadata. `/load` groups sessions by date with time, ID, and turn-count display; supports multi-select (`1,2,3`) to merge sessions and `H` to load the full history with token-count confirmation. Both limits are configurable via `/config`.
- 09:34 AM, Apr 05, 2026: **v3.05.3** — Added GitHub Gist cloud sync: `/cloudsave setup <token>` to configure, `/cloudsave` to upload the current session to a private Gist, `/cloudsave auto on` to sync automatically on `/exit`, `/cloudsave list` to browse cloud sessions, and `/cloudsave load <id>` to restore from the cloud. Uses stdlib `urllib` — no new dependencies. Also added version number (e.g., `v3.05.2`) in the startup banner: The startup banner now displays the current version number (v3.05.2) in green, making it easy to identify which version is running at a glance.
- 08:58 AM, Apr 05, 2026: **v3.05.2** — Introduced `/proactive [duration]` command: a background daemon thread watches for user inactivity and automatically wakes the agent up after the specified interval (e.g. `/proactive 5m`), enabling continuous monitoring loops without user intervention. `/proactive` with no args now shows current status; `/proactive off` disables it explicitly. Proactive polling state is stored in `config` (no module-level globals). Watcher exceptions are logged via `traceback` instead of silently swallowed. Also fixed duplicated output in Rich-enabled terminals by buffering text during streaming and rendering Markdown once via `rich.live.Live` — updates happen in-place for a true streaming Markdown experience. 
- 10:51 PM, Apr 04, 2026: **v3.05_fix04** — Fixed a crash on `/model` and config save commands caused by the newly introduced `_run_query_callback` being serialized to JSON; also added `SleepTimer` usage    
  guidance to the system prompt so the agent knows when to invoke background timers proactively.
- 10:28 PM, Apr 04, 2026: **v3.05_fix03** — Added a native `SleepTimer` tool that lets the agent schedule background timers and autonomously wake itself up after a delay — no user prompt required. Paired with a `threading.Lock` to prevent output collisions when background and foreground calls overlap. Also includes cross-platform fixes: Windows ANSI color support, CRLF-aware Edit tool matching, an interactive numbered menu for `/load`, native Ollama streaming via `/api/chat`, and auto-capping `max_tokens` per provider to prevent API errors. 
- 08:31 PM, Apr 04, 2026: **v3.05_fix** — Autosave + `/resume`: session is automatically saved to `mr_sessions/session_latest.json` on `/exit`, `/quit`, `Ctrl+C`, and `Ctrl+D`. Run `/resume` to restore the last session instantly, or `/resume <file>` to load a specific file from `mr_sessions/`, and better support for api and local Ollama models (specifically gemma4), along with Windows compatibility enhancements, session management UX improvements, and cross-platform reliability fixes for the Edit tool.
- 00:41 AM, Apr 04, 2026: **v3.05** — Voice input (`voice/` package): `sounddevice` → `arecord` → SoX recording backends, `faster-whisper` → `openai-whisper` → OpenAI API STT backends. Smart keyterm extraction from git branch + project name + recent files passed as Whisper `initial_prompt` for coding-domain accuracy. `/voice`, `/voice status`, `/voice lang <code>` REPL commands. Works fully offline with no API key. 29 new tests (**~11.6K** lines of Python).
- 10:29 PM, Apr 03, 2026: **v3.04** — Expanded tool coverage: `NotebookEdit` (edit Jupyter `.ipynb` cells — replace/insert/delete with full JSON round-trip) and `GetDiagnostics` (LSP-style diagnostics via pyright/mypy/flake8/tsc/shellcheck). Also fixed a pre-existing schema-index bug in `_register_builtins` by switching to name-based lookup (**~10.5K** lines of Python).
- 06:00 PM, Apr 03, 2026: **v3.03** — Task management system (`task/` package): `TaskCreate` / `TaskUpdate` / `TaskGet` / `TaskList` tools with sequential IDs, dependency edges (blocks/blocked_by), metadata, persistence to `.cheetahclaws/tasks.json`, thread-safe store, `/tasks` REPL command, 37 new tests (**~9500** lines of Python).
- 02:50 PM, Apr 03, 2026: **v3.02** — Plugin system (`plugin/` package): install/uninstall/enable/disable/update via `/plugin` CLI, recommendation engine (keyword+tag matching), multi-scope (user/project), git-based marketplace. `AskUserQuestion` tool: interactive mid-task user prompts with numbered options and free-text input (**~8500** lines of Python).
- 10:00 AM, Apr 03, 2026: **v3.01** — MCP (Model Context Protocol) support: `mcp/` package, stdio + SSE + HTTP transports, auto tool discovery, `/mcp` command, 34 new tests (**~7000** lines of Python).
- 12:20 PM, Apr 02, 2026: **v3.0** — Multi-agent packages (`multi_agent/`), memory package (`memory/`), skill package (`skill/`) with built-in skills, argument substitution, fork/inline execution, AI memory search, git worktree isolation, agent type definitions (**~5000** lines of Python), see [update](https://github.com/SafeRL-Lab/clawspring/blob/main/docs/update_readme_v3.0.md).
- 10:00 AM, Apr 02, 2026: **v2.0** — Context compression, memory, sub-agents, skills, diff view, tool plugin system (**~3400** lines of Python Code).
- 01:47 PM, Apr 01, 2026: Support VLLM inference (**~2000** lines of Python Code).
- 11:30 AM, Apr 01, 2026: Support more **closed-source** models and **open-source models**: Claude, GPT, Gemini, Kimi, Qwen, Zhipu, DeepSeek, and local open-source models via Ollama or any OpenAI-compatible endpoint. (**~1700** lines of Python Code).
- 09:50 AM, Apr 01, 2026: Support more **closed-source** models: Claude, GPT, Gemini. (**~1300** lines of Python Code).
- 08:23 AM, Apr 01, 2026: Release the initial version of CheetahClaws (**~900 lines** of Python Code).
