A self-hosted, pure-Node.js AI agent platform — chat, tasks, coding, workflows, persistent memory and a file-first plugin system, all behind one web UI, REST API and WebSocket stream. No cloud lock-in: run it fully local against LM Studio, Ollama, OpenRouter or OpenAI.
🌐 Landing Page & Documentation · ⚡ Installation · 🧩 Plugin System · 👤 Author
- Truly self-hosted & private — everything runs on your machine; point it at a local LLM (LM Studio / Ollama) and no data leaves your network.
- One platform, many jobs — an assistant for chat, an executor for tasks & Kanban, a coding agent, and a workflow engine, sharing one memory and skill layer.
- Pure Node.js, lightweight core — no heavyweight runtime, no framework sprawl. Easy to read, easy to extend.
- Extend without touching the core — a file-first plugin system adds tools, API connectors (OAuth), settings, and full UI pages (sidebar apps, dashboard widgets) as drop-in bundles.
- Provider-agnostic — LM Studio, Ollama, OpenRouter and OpenAI, switchable at runtime from
/settings. - Reach it from anywhere — messaging gateways (Discord, Telegram, Slack, Signal, custom webhooks) with optional local speech-to-text.
- Built in the heart of Germany — Fulda, near the Rhön. This agent is for me and for all — contributions, issues and feedback are very welcome.
| Area | Highlights |
|---|---|
| 🤖 Multi-agent core | Parallel chat / task / websocket runs without global lock contention; live metrics on /agents |
| 🧠 Persistent memory | Self-updating agent & user memory with profiles, approvals and curation flows |
| 🧩 Plugin system | File-first bundles: agent tools, OAuth connectors, encrypted settings/secrets, and typed UI pages — see below |
| 🛠️ Rich tooling | Filesystem, HTTP, shell, git, browser automation, workflow, cronjob and memory tools — enable per tool |
| 🕸️ Workflow engine | Build, run and resume graph workflows from the UI and from tools |
| 📚 Skills system | Slash-skill loading, automatic skill selection, pin/enable management, markdown editor |
| 📖 LLM-Wiki | Ingest shared-workspace files into searchable, moderated knowledge wired into memory |
| ⏰ CronJobs | Schedule prompts, tasks and skills to run at a specific time |
| 💬 Messaging gateways | Discord, Telegram, Slack, Signal & custom webhooks — with optional local speech-to-text |
| 🔌 MCP integration | Register MCP servers, discover and call remote tools (one-shot or streamed) via /mcp |
| 🖥️ Desktop apps | Two Tauri apps for Windows (frontend + tray-based backend server) |
| 🦆 Desk Pet | A draggable companion that reacts to agent events, fully configurable |
📘 Full documentation and guides live on the landing page.
pnpm install
cp .env.example .env
pnpm devpnpm install
Copy-Item .env.example .env
pnpm devAfter startup:
- Web UI:
http://localhost:5173 - API:
http://localhost:3001 - Health check:
http://localhost:3001/health
Common first steps:
- Open
/settingsand set provider/model. - Open
/skillsand enable only required skills. - Use
/chatfor iterative work with tools. - Use
/workflowfor graph-based execution. - Monitor
/agentsfor currently running agents. - Open
/pluginsto enable extensions and their settings/frontend pages.
DucKI can be extended without touching the core through file-first plugins. A plugin is a
self-contained bundle in plugins/<name>/ with a plugin.json manifest — no npm dependency,
no core code change, no database row required. Drop a folder in, enable it on /plugins, and it
hot-reloads without interrupting running agents.
What a plugin can provide
- Agent tools in three flavors:
- Data-source tools — declarative JSON that turns a public/keyed API into a first-class tool.
- Script tools — small sandboxed scripts (no network/filesystem) that transform data.
- Module tools — full Node.js ESM modules (
trust: "node") with a host-guardedfetch, logger and access to the plugin's settings/secrets.
- Its own SQLite database (
storage.sqlite) — isolated per plugin, never bloating the main DB. Every DB-backed plugin automatically gains a<name>_storagetool the agent can query directly. - Encrypted settings & secrets — declared in the manifest and edited from the UI. Secrets (API tokens, OAuth tokens) are AES-256-GCM encrypted at rest and never returned in clear text.
- OAuth2 connectors — a declarative
*.oauth.jsonruns the full authorization-code flow and stores the token as an encrypted plugin secret, so an authenticated tool just reads it. - Typed UI pages, rendered as sandboxed same-origin iframes:
settingsPage— a pure configuration page on the plugin's card.frontendPage— a full mini-app that gets its own sidebar link (withicon+category) and opens in the content area like a built-in route.widgetPage— a small tile rendered inline in the sidebar and/or on the dashboard.
Manage plugins from the /plugins page or the /api/plugins API: list, enable/disable
(hot-reloaded), install bundles, and read/write per-plugin settings.
Bundled example plugins
| Plugin | Demonstrates |
|---|---|
exchange-rates |
Declarative data-source tool (no code) |
notes |
Own SQLite DB, auto notes_storage tool, and a frontend page in the sidebar |
clock |
A widget (time / date / weekday) shown in the sidebar and on the dashboard |
github-connector |
Node module tool + OAuth2 connector + encrypted secrets + settings page |
By default:
- Frontend (Web UI):
http://localhost:5173 - Backend API:
http://localhost:3001
To use different ports, set environment variables in .env:
# Frontend port (defaults to 5173, auto-increments if already in use)
VITE_PORT=5173
# Backend port (defaults to 3001)
PORT=3001Note: If a port is already in use, the frontend automatically tries the next available port (5174, 5175, etc.).
If you get Port 5173 is already in use:
- Quick fix: The dev server will automatically use the next available port (5174, 5175, etc.)
- Manual fix: Kill the old process:
# Windows: Find and kill the process using port 5173 netstat -ano | findstr :5173 taskkill /PID <PID> /F # Linux/macOS: lsof -ti:5173 | xargs kill -9
- Custom port: Set
VITE_PORTin.envbefore runningpnpm dev
apps/
server/ Express + Socket.IO API
web/ React + Vite dashboard
cli/ CLI entrypoint
packages/
agent/ Core agent loop, guards, memory integration
tools/ Tool executors (filesystem, http, shell, git, skills)
providers/ LLM providers (LM Studio, OpenAI, OpenRouter, Ollama)
database/ SQLite service + schema
logger/ Logging helpers
shared/ Shared types and API helpers
skills/ User and system skill folders (SKILL.md per skill)
plugins/ File-first plugin bundles (plugin.json per plugin)
storage/ Runtime storage (DB, logs)
DucKI includes two separate Tauri apps for Windows:
- Standalone web UI application
- Connects to backend (local or remote)
- Settings: Local/Remote toggle in
/settings - Starts with:
pnpm tauri:dev - Build:
pnpm tauri:build
- Separate backend application with system tray integration
- Manages Node.js server process
- Auto-detects available port (3001-3010 fallback)
- Windows Autostart support
- Starts with:
pnpm tauri:server:dev - Build:
pnpm tauri:server:build
Start everything (web server + both Tauri apps) with one command:
pnpm tauri:all:devThis will automatically start in parallel:
pnpm dev→ Vite dev server (5173) + Node API server (3001)pnpm tauri:server:dev→ Backend Wrapper (system tray)pnpm tauri:dev→ Frontend (waits for Vite to be ready)
All three start together - just use one terminal!
If you only need the frontend (connecting to remote server):
pnpm tauri:devIf you only need the backend server:
pnpm tauri:server:devtauri-server implements smart port detection:
- Tries port 3001 first
- Falls back to 3002-3010 if ports are in use
- Displays actual port in system tray tooltip
- Logs port on startup
The frontend automatically detects the backend port, or you can set it manually in Settings.
When tauri-server starts, it creates a system tray icon that shows:
- 🟢 Status indicator (green = running)
- Tooltip with port information:
DucKI Server - Running on localhost:3001 - Hover to see full details and port number
The tray icon persists in the Windows system tray even when the app window is closed, allowing the server to run in the background.
┌─────────────────────────────────────────┐
│ tauri-desktop (Frontend App) │
│ - Windows desktop wrapper for web UI │
│ - Vite dev server on :5173 │
│ - Settings: Local/Remote backend │
└─────────────────────────────────────────┘
↓ connects to ↓
┌─────────────────────────────────────────┐
│ tauri-server (Backend Server App) │
│ - System tray with port indicator │
│ - Manages Node.js server process │
│ - Auto-detects free port (3001+) │
│ - Windows Autostart support │
│ - tsx compiles TypeScript on-the-fly │
└─────────────────────────────────────────┘
↓ runs ↓
┌─────────────────────────────────────────┐
│ Node.js Server (apps/server/) │
│ - Express API on detected port │
│ - Socket.IO WebSocket events │
│ - Agent/Memory/Skill systems │
└─────────────────────────────────────────┘
"Waiting for your frontend dev server to start..."
- The frontend needs Vite dev server on :5173
- This is normal; wait or check that Vite is running
- Vite auto-increments port if 5173 is in use
Backend not starting
- Check if ports 3001-3010 are available
- Run
pnpm build:serverto ensure Node server is built - Check console for port binding errors
Frontend can't connect to backend
- Verify tauri-server is running (check system tray)
- Check Settings: Local/Remote toggle
- If using Remote: verify URL and port are correct
- Frontend will auto-detect localhost port if using Local
# Development
pnpm dev # Start web + server (Vite + Node + shared watch)
pnpm tauri:dev # Start tauri-desktop only
pnpm tauri:server:dev # Start tauri-server only
pnpm tauri:all:dev # Start everything (web + server + both tauri apps)
pnpm tauri:dev:no-server # Start tauri apps without dev server (uses built web)
# Building
pnpm tauri:build # Build tauri-desktop .exe
pnpm tauri:server:build # Build tauri-server .exe
pnpm build # Build all packages
pnpm build:web # Build web frontend only
pnpm build:server # Build Node server only
# Utilities
pnpm typecheck
pnpm test
pnpm lintCLI examples:
pnpm --filter @ducki/cli dev chat
pnpm --filter @ducki/cli dev run "implement health endpoint"
pnpm --filter @ducki/cli dev toolsSettings are stored in DB via /api/settings and can be edited from /settings.
Important agent controls:
AGENT_MAX_ITERATIONSAGENT_TIMEOUT_MSAGENT_MAX_TOOL_FAILURESAGENT_MAX_REPEATED_TOOL_CALLAGENT_AUTO_MEMORYAGENT_AUTO_SKILL_SELECTIONAGENT_SKILL_BEHAVIOR(automaticoractive)AGENT_AUTO_SKILL_FALLBACK_NONEAGENT_AUTO_SKILL_THRESHOLDAGENT_AUTO_SKILL_MARGINAGENT_AUTO_SKILL_MIN_INPUT_LENAGENT_AUTO_SKILL_MIN_OVERLAP
LLM-Wiki controls:
WIKI_ENABLED(hard on/off switch; whenfalse, no ingest and no reindex)WIKI_SHARED_SOURCE_PATH(default:llm-wikiunder shared workspace root)WIKI_SHARED_SOURCE_AUTO_MEMORY(write approved wiki chunks into semantic memory)WIKI_AUTO_APPROVE(iftrue, new chunks becomeapproved; otherwisecandidate)WIKI_SHARED_SOURCE_MAX_FILE_SIZE_KBWIKI_INGEST_INTERVAL_MSWIKI_CHUNK_SIZE_CHARSWIKI_CHUNK_OVERLAP_CHARS
Skill behavior notes:
AGENT_SKILL_BEHAVIOR=automatic: Agent evaluates relevance and auto-loads only needed skills fromENABLED_SKILLSallowlist.AGENT_SKILL_BEHAVIOR=active: Agent loads all skills listed inENABLED_SKILLS.AGENT_AUTO_SKILL_FALLBACK_NONE=true: Inautomaticmode, if no skill matches, no skill is loaded.
Tool modularity notes:
Every built-in tool has a tools/<name>/TOOL.md manifest (frontmatter: name,
description, core) alongside its TypeScript implementation. All built-in
tools are always registered (so they're always listed on the Tools settings
page, including ones nobody has enabled yet), but whether a tool can actually
run depends on its core flag: tools marked core: true always run; all
others are optional and disabled by default until enabled via
ENABLED_OPTIONAL_TOOLS (same JSON-array-of-names shape as ENABLED_SKILLS),
settable from the Tools page in the web UI.
- Core (always usable):
project,task,memory,skill_manage,tool_factory,filesystem. - Optional (off by default):
http,git,browser,shell,mcp,workflow,cronjob,history,gateway. ENABLED_OPTIONAL_TOOLS=["shell", "http"]: enables those two optional tools; all other optional tools stay disabled.- Disabling a tool hides it from the agent's system prompt and rejects any call to it with a clear "disabled" error - enforced fresh on every call, both for the interactive chat agent and for workflow/cronjob
tool_calldispatch, so a setting change takes effect immediately without a restart. - Tools loaded from an external URL (tool packages) are not yet supported - only skills currently support
POST /api/skills/import. This is a deliberate follow-up, not an oversight.
Script-backed tools (no TypeScript required):
A tools/<name>/TOOL.md can also declare an executable script - the same
convention skills already use (frontmatter script: pointing to a file, a
sibling script.js, or an inline <script>...</script> block), plus a
required sibling parameters.json (the tool's JSON-schema parameters, since
there's no TypeScript file to declare them in). This turns the manifest into
a real, independently callable tool with no hand-written code at all:
tools/weather_summary/TOOL.md # frontmatter: script, optional subagent: true
tools/weather_summary/script.js # sees toolInput/toolContext only - no fetch/require/fs
tools/weather_summary/parameters.json
- The script runs in the exact same sandbox as skill scripts
(
runScriptInSandbox, 1500ms timeout, no network/filesystem access) - it can only transform data the calling agent already gathered (e.g. viahttp/filesystem), not fetch new data itself. - Set
subagent: truein the frontmatter to have a second, lightweight LLM call interpret the script's raw result (+ console logs) before it's returned to the calling agent - useful when the raw output needs summarizing/formatting rather than being returned as-is. The frontmatter body (everything after the closing---) becomes that subagent's directive. This is a genuinely separate, billed LLM call per tool invocation - the Tools settings page badges any tool withsubagent: truefor this reason, since an unattended cronjob could trigger it repeatedly. - If the script throws, the tool call fails immediately with no subagent call
(no extra cost on a hard failure). If the subagent call itself fails or
times out, the tool call still succeeds with the raw script result plus a
subagentFailed: truemarker - a bad interpretation step never discards a script that ran successfully. AGENT_SCRIPT_SUBAGENT_TIMEOUT_MS(default20000): timeout for the subagent's LLM call.- A tool name reserved by a built-in (
filesystem,shell,task, ...) or missing/invalidparameters.jsonis skipped with awarnlog rather than registered - check server logs if a script tool doesn't show up. - New/changed script tools reach the interactive chat agent on the next
request (no restart). They only reach workflow/cronjob
tool_calldispatch after a server restart, same asMCP_SERVERS.
Provider controls:
DEFAULT_PROVIDER*_MODEL,*_BASE_URL,*_API_KEY
Messaging gateways and local STT controls:
DucKI can receive and reply to messages across Discord, Telegram, Slack, Signal and custom
webhooks. Configure endpoints on the /gateway page (each with its own portal, token/webhook
and channel hints); inbound messages run the agent and the reply is sent back on the same channel.
Discord additionally supports voice notes via local speech-to-text using the controls below.
DISCORD_GATEWAY_ENABLEDDISCORD_BOT_TOKEN(or gatewayauthTokenin/gatewayconfig)DISCORD_GUILD_ID(optional)DISCORD_ALLOWED_USER_ID(optional)DISCORD_VOICE_STT_PROVIDER(local,nodejs-whisper,silero,ollama,openai)DISCORD_VOICE_STT_COMMAND/DISCORD_VOICE_STT_ARGS(for providerlocal)LOCAL_STT_COMMAND/LOCAL_STT_ARGS/LOCAL_STT_TIMEOUT_MSDEFAULT_SPEECH_TO_TEXT_PROVIDERNODEJS_WHISPER_MODEL_NAME/NODEJS_WHISPER_MODEL_ROOT_PATHNODEJS_WHISPER_AUTO_DOWNLOAD/NODEJS_WHISPER_USE_CUDANODEJS_WHISPER_LANGUAGE/NODEJS_WHISPER_TIMEOUT_MS
Example for local STT binary (whisper.cpp style):
DISCORD_VOICE_STT_PROVIDER=local
LOCAL_STT_COMMAND=whisper-cli
LOCAL_STT_ARGS=-m C:/models/ggml-base.en.bin -f {input}Supported placeholders for local STT args:
{input}temporary input audio file path{output}temporary transcript output file path{outputBase}temporary output file base path{language}language hint (if provided)
Recommended setup for Discord voice transcription without external cloud STT:
- Install
nodejs-whisperin the workspace (already included in this project). - Install CMake:
winget install -e --id Kitware.CMake
- Ensure Visual C++ Build Tools are available (required by
cmake --build). - In Settings (
/settings), set:
DISCORD_VOICE_STT_PROVIDER=nodejs-whisper
DEFAULT_SPEECH_TO_TEXT_PROVIDER=nodejs-whisper
NODEJS_WHISPER_MODEL_NAME=base
NODEJS_WHISPER_AUTO_DOWNLOAD=true
NODEJS_WHISPER_LANGUAGE=auto
NODEJS_WHISPER_TIMEOUT_MS=180000Optional local command fallback (used if configured):
LOCAL_STT_COMMAND=C:/tools/whispercpp/whisper-cli.exe
LOCAL_STT_ARGS=-m C:/tools/whispercpp/models/ggml-base.bin -f {input} -otxt -of {outputBase} -l de
LOCAL_STT_INPUT_EXT=ogg
LOCAL_STT_TIMEOUT_MS=180000Notes:
- On first run,
nodejs-whispermay download a model and buildwhisper.cpp. - Build output executable is typically:
node_modules/.pnpm/nodejs-whisper@0.3.0/node_modules/nodejs-whisper/cpp/whisper.cpp/build/bin/Release/whisper-cli.exe
- If Discord voice message processing is rate-limited by LLM provider (
429), the system still returns the raw transcript when available.
Core endpoints:
POST /api/chatGET /api/chat/conversationsGET /api/chat/conversations/:id/messagesGET /api/workflowsPOST /api/workflowsPOST /api/workflows/:id/runGET /api/memoryPOST /api/memory/actionsGET /api/skillsGET /api/wiki/statusGET /api/wiki/entriesGET /api/wiki/searchPOST /api/wiki/reindexPUT /api/wiki/configPOST /api/wiki/entries/:id/approvePOST /api/wiki/entries/:id/rejectGET /api/shared/filesGET /api/agents/liveGET /api/logs
Client -> server:
chat:messagechat:stopagent:status
Server -> client:
chat:startchat:chunkchat:eventchat:completechat:erroragent:statusagent:metrics
- Skills live in
skills/<slug>/SKILL.md. - Enable/disable skills in
/skills. - Agent can auto-select relevant skills when enabled.
- Memory supports add/replace/remove/batch/approval flows.
A small companion lives on top of the whole web UI: it walks along the window
floor (or flies freely), reacts to what the agent is doing, and can be picked up
and thrown anywhere. Everything is configured in /settings -> Character tab.
Interaction:
- Drag & drop the pet anywhere; releasing it mid-air throws it with real momentum (ground pets fall, bounce and walk on from where they land).
- Click for a reaction + speech bubble, right-click for the pet menu (wave, jump, sleep, reset position, settings, hide).
- It gets startled by fast pointer moves; chasing the cursor is off by default and can be switched on in the settings.
- It falls asleep after a minute without interaction and wakes up when the cursor comes close.
Agent reactions (toggleable):
| Event | Reaction |
|---|---|
| Agent run starts | Working animation + "working on it" bubble |
| Run finished | Happy jump + "done" bubble |
agentStatus = error |
Shake / fail animation |
| WebSocket disconnect / reconnect | Sad state, waves again when back online |
Settings (/settings -> Character -> Desk Pet):
- On/off, pet gallery, size, speed, opacity, ground offset.
- Movement mode:
automatic(use the pet's own), forcedwalkingor forcedflying. - Toggles for cursor following, agent-event reactions and speech bubbles.
- State preview (idle, walk, run, fly, jump, wave, work, fail, sleep, drag) plus trigger buttons to test wave/jump/reset position on the live pet.
Built-in pets: Ducki (duck, ground), Pixel Cat (ground), Ghost (air),
Helper Bot (air) - all drawn as SVG + CSS, no image assets required.
Custom sprite-sheet pets:
Charactertab ->Import sprite sheet, choose a PNG/GIF/WebP sheet.- Enter the grid (columns x rows) - the frame size is derived from the image.
- Map sheet rows to engine states (
row,frames,loop) and check them in the state viewer, then save. - Unmapped states fall back to related ones (
run->walk->idle), so a partially mapped sheet still animates everywhere.
Storage note: pet settings, last position and imported sheets live in the
browser's localStorage (ducki.pet), not in the server DB - they are per
device and per browser. Sprite sheets are stored as data URLs, so keep them
small (a few hundred KB).
Implementation lives in apps/web/src/components/pet/ (engine, renderer, overlay,
importer) with the settings UI in apps/web/src/components/settings/PetSettingsPanel.tsx.
LLM-Wiki turns files in shared workspace into searchable, moderated knowledge for the agent.
Source folder:
shared-workspace/llm-wiki
What happens during ingest:
- Scanner reads supported text files (
.md,.txt,.json) from the wiki folder. - Files are chunked (
WIKI_CHUNK_SIZE_CHARS+WIKI_CHUNK_OVERLAP_CHARS). - Chunks are stored as wiki entries with moderation status (
candidate,approved,rejected,error). - If auto-memory is enabled, approved chunks are mirrored into semantic memory.
Moderation flow:
- New chunks are
candidateby default. - Approve to promote reliable chunks into active knowledge.
- Reject to exclude incorrect chunks from retrieval.
Search behavior:
/api/wiki/searchranks by token relevance + recency + moderation weight.approvedentries are prioritized.rejectedanderrorentries are excluded.candidateentries are only included when explicitly requested.
UI controls:
- Open
/memory->LLM Wikitab. - Toggle
WIKI_ENABLEDandWIKI_SHARED_SOURCE_AUTO_MEMORY. - Trigger
Reindexmanually. - Filter/search entries and approve/reject candidates.
Disable guarantee:
- With
WIKI_ENABLED=false, wiki ingest does not run and reindex requests are rejected.
The browser tool (in packages/tools/src/browser.ts) supports browser automation via Puppeteer Core and runs in an isolated worker process.
Implemented actions:
detectlaunchlist_pagesgotoclicktypepresswaitevaluatescreenshotcookies_getcookies_setcookies_clearform_fillloginpdfdownloadclose
Operational notes:
- Browser execution is isolated in a child process (IPC). Puppeteer runtime failures return tool errors and should not terminate the agent process.
- On Windows, browser detection checks env vars and common install paths for Edge/Chrome/Chromium.
- For
download, usesaveDirfor deterministic storage and verify resulting files in that directory.
Minimal flow example:
detectlaunch(optionalurl)- Interact with
goto/click/type/form_fill/login - Capture artifacts with
screenshotorpdf close
The project includes MCP runtime integration with server registry, reconnect handling, streaming, and a dedicated UI page (/mcp).
Core capabilities:
- Configure MCP servers (
id,name,url,enabled) and persist settings. - Automatic runtime sync/reload of configured servers.
- Reconnect tracking (
connected,reconnectAttempts) and discovered tool counts. - List discovered remote tools across connected MCP servers.
- Execute remote MCP tools via one-shot calls.
- Execute remote MCP tools via SSE stream with live output.
- Stop active streams from UI.
- Inspect streamed chunks with per-chunk timestamps in UI.
Server API endpoints:
GET /api/mcp/serversPUT /api/mcp/serversPOST /api/mcp/servers/reloadGET /api/mcp/toolsPOST /api/mcp/tools/callPOST /api/mcp/tools/stream
UI flow (/mcp):
- Add or update MCP servers.
- Click reload to sync runtime.
- Verify connected status and tool discovery.
- Call tools directly or start stream mode.
- Stop stream if needed and review chunk timeline.
- Workspace uses
pnpmwith TypeScript project references. - Route-heavy pages are lazy-loaded in web app.
- Server logs requests/errors to DB and exposes
/api/logs. - Shared workspace APIs are under
/api/shared/*. - Sidebar
Live Agentencard shows Discord gateway runtime state (green/red) with tooltip (lastErrorwhen inactive). - Discord inbound lifecycle can set reactions on source messages:
👀on receive,✅on success,⚠️on failure.
If server fails to start:
- Ensure port
3001is free. - Run
pnpm --filter @ducki/server run start. - Check
/api/logsand console output.
If Discord gateway is inactive (red indicator):
- Check
/api/agents/live->gateway.discord.lastError. - Verify bot token source (
DISCORD_BOT_TOKENor/gatewayconfigauthToken). - Confirm Discord bot has permissions: View Channel, Read Message History, Add Reactions, Send Messages.
If Discord reactions are missing on inbound messages:
- Confirm inbound payload includes
sourceMessageId(WS bridge sends this automatically). - Verify channel permissions for reactions.
- Inspect
/api/logsforreaction_set/reaction_errorgateway events.
If local voice transcription fails:
- Ensure
LOCAL_STT_COMMANDpoints to an installed local binary. - Check
LOCAL_STT_ARGSplaceholders and quoting for Windows paths. - Validate command manually with a local audio file before running
pnpm dev.
If nodejs-whisper fails with cmake or whisper-cli errors:
- Verify CMake is installed and reachable:
cmake --version
- If CMake was just installed, restart terminal/dev server.
- Confirm Build Tools are installed (Visual Studio Build Tools with C++ workload).
- If the log shows
whisper-cli executable not found, run one clean rebuild:
$root = Join-Path (Resolve-Path .) "node_modules/.pnpm/nodejs-whisper@0.3.0/node_modules/nodejs-whisper/cpp/whisper.cpp"
& "C:\Program Files\CMake\bin\cmake.exe" -S $root -B (Join-Path $root "build")
& "C:\Program Files\CMake\bin\cmake.exe" --build (Join-Path $root "build") --config ReleaseIf skills are not visible:
- Verify files exist under
skills/<slug>/SKILL.md. - Confirm
SKILLS_PATH(or fallback resolution) points to workspaceskills. - Restart server after path/config changes.
Contributions, issues and feedback are very welcome — new features, connectors, plugins and fixes.
- Create a feature branch.
- Run
pnpm typecheckand relevant tests. - Open a PR with a short change summary and validation steps.
If DucKI helps you, a donation supports its development into a powerful assistant for everyone — thank you!
- PayPal: https://www.paypal.me/davidduckwitz
- Bitcoin:
1AinLLwLGvh2Y51a53PAYi5PdPBsLwpU1G
- Landing Page & Documentation: https://ducki-ai-agent.davidduckwitz.de/
- Author: https://www.davidduckwitz.de/
MIT (see LICENSE if present).