Website · Releases · Contributing
Souz is a Kotlin Multiplatform AI assistant focused on safe, observable, user-approved automation. It combines a Compose Desktop app, an Android chat-agent entry point, a reusable graph-based agent runtime, shared backend-safe tools, local and cloud LLM providers, sandbox-aware file/process access, and an HTTP backend for web/API integrations.
The project is designed around one core idea: an AI agent should be useful enough to operate your desktop and data, but transparent and constrained enough that users can trust what it is doing.
- Kotlin Multiplatform app surfaces built with Compose for Desktop plus an Android chat-agent entry point.
- GraphBasedAgent powered by an explicit graph runtime with classification, MCP tool injection, prompt enrichment, LLM execution, tool loops, summarization, retries, tracing, and cancellation.
- Shared runtime layer used by desktop and backend for LLM clients, settings/config, sandbox-aware filesystem access, and backend-safe tools, plus an Android-safe LLM runtime surface for the Android chat-agent host.
- Sandbox abstraction for filesystem and command execution, with local mode by default and opt-in Docker-backed execution.
- HTTP backend with trusted-proxy auth, per-user settings/provider keys, chat lifecycle, message execution, Telegram bot chat bindings, cancellation, option continuation, event replay, WebSocket streaming, and memory/filesystem/Postgres storage.
- Rich desktop tool catalog for files, browser, web search/research, config, notes, applications, data analytics, calendar, mail, text replacement, Telegram, desktop capture, presentations, and calculator.
- SafeMode confirmations for tool permission prompts, destructive Telegram operations, ambiguous contact/chat selection, and deferred file-modification review.
- Multi-provider LLM support for GigaChat, Qwen, AiTunnel, Anthropic Claude, OpenAI, and local llama.cpp models.
- Local inference through a packaged native bridge with Qwen/Gemma chat profiles, EmbeddingGemma embeddings, prompt-family rendering, strict JSON tool output handling, model downloads, preload/warmup, and cancellation.
- MCP integration over stdio/http with OAuth discovery and token refresh support.
- Voice and desktop interaction with audio capture/playback, speech recognition, global hotkeys, native media keys, screenshots, screen recording, and macOS integrations.
- ClawHub/OpenClaw skill support with bundle parsing, canonical hashing, desktop-first registry storage, backend user-scoped storage support, safe loading, LLM-backed selection, structural/static/LLM validation, validation caching, activation, and context injection.
brew tap D00mch/tap
brew install --cask souz-aiOr download the latest build from GitHub Releases.
.
├── agent/ # Shared agent contracts, GraphBasedAgent, skill activation, sessions
├── graph-engine/ # Framework-free typed graph DSL/runtime
├── llms/ # Shared LLM DTOs, provider enums, model profiles, token logging
├── native/ # llama.cpp bridge and local model runtime
├── sharedLogic/ # Shared JVM runtime plus Android-safe LLM/agent support variant
├── sharedUI/ # Shared Compose presentation plus desktop UI, view models, host ports, UI adapters, UI resources
├── desktopApp/ # Runnable desktop host, DI composition root, OS integrations, packaging
├── androidApp/ # Android chat-agent host over sharedUI, sharedLogic, and GraphBasedAgent
├── backend/ # Ktor HTTP backend over the shared agent runtime
├── scripts/ # Build, release, and packaging helper scripts
├── docs/ # Project documentation
└── gradle/ # Version catalog and wrapper configuration
Gradle modules included by the build:
:agent
:graph-engine
:llms
:native
:sharedLogic
:sharedUI
:desktopApp
:androidApp
:backend
Module docs:
sharedLogic/README.mdcovers the shared JVM runtime layer, sandbox modes, tools, and Docker sandbox image setup.
flowchart LR
userNode["User"] --> desktopApp[":desktopApp\nDesktop entry + packaging"]
desktopApp --> sharedUi[":sharedUI\nCompose UI + UI adapters"]
userNode --> androidApp[":androidApp\nAndroid chat entry"]
androidApp --> sharedUi
androidApp --> agentNode
androidApp --> runtimeNode
sharedUi --> agentNode[":agent\nGraphBasedAgent"]
backendApi[":backend\nHTTP API"] --> agentNode
agentNode --> graphEngine[":graph-engine\nTyped graph runtime"]
agentNode --> runtimeNode[":sharedLogic\nShared JVM runtime"]
agentNode --> llmsNode[":llms\nLLM contracts"]
runtimeNode --> llmsNode
runtimeNode --> nativeRuntime[":native\nLocal llama.cpp runtime"]
backendApi --> runtimeNode
desktopApp --> runtimeNode
sharedUi --> runtimeNode
sharedUi --> nativeRuntime
runtimeNode --> sandboxNode["RuntimeSandbox\nfilesystem + commands"]
runtimeNode --> toolsNode["Tool catalog"]
llmsNode --> providersNode["Cloud + local providers"]
:desktopApp owns the runnable desktop entry point, app composition root, OS integrations, desktop-only services/tools, and Compose Desktop packaging. It depends on :sharedLogic and :sharedUI.
:sharedUI owns shared presentation surfaces and the desktop experience:
- Android-capable shared chat/settings presentation surface for the Android chat-agent entry point.
- Compose screens, ViewModels, app theme, reusable UI components, and setup/settings flows for desktop.
- Chat UI with model/context selectors, attachments, send/mic controls, streaming state, speech output, and graph/thinking visualization.
- Tool-management UI and permission/selection approval flows.
- Settings UI for models, provider keys, general behavior, security, folders, Telegram, sessions, visualization, and support logs.
- Host-port interfaces plus UI adapters for permission/selection flows and macOS window effects. Non-UI desktop services and OS-bound tools live in
:desktopApp.
UI code should stay presentation-only. Business logic belongs in ViewModels or use cases.
Souz keeps platform-specific logic at the edges:
:llmscontains provider-agnostic contracts and shared model/profile definitions.:graph-enginecontains no LLM/tool/agent knowledge; it only runs typed suspendable graph nodes.:agentimplements agent behavior on top of the graph engine.:sharedLogiccontains JVM-shared runtime services, backend-safe tools, sandbox/skills infrastructure, provider clients, shared contracts/models, and a minimal Android-safe LLM runtime surface for the Android agent host. SeesharedLogic/README.md.:nativecontains local model support used by desktop and backend-capable runtime wiring.:sharedUIcontains shared Compose presentation, Desktop/KMP UI, view models, UI adapters, and desktop test coverage.:desktopAppcontains the runnable desktop entry points, DI composition root, OS integrations, desktop-only tools/services, and packaging resources.:androidAppcontains the Android entry point, Android storage/settings adapters, and the Android bridge from shared chat UI events toGraphBasedAgent.:backendexposes the same runtime over HTTP without starting the desktop app.
GraphBasedAgent is the standard tool-calling agent. Its graph is explicit and traceable:
flowchart TD
input["User input"] --> history["Append input to history"]
history --> classify["Classify request / narrow tool categories"]
classify --> mcp["Inject MCP tools"]
mcp --> enrich["Append additional context"]
enrich --> llm["LLM chat node"]
llm --> decision{"LLM result"}
decision -->|tool call| tool["Execute tool"]
tool --> llm
decision -->|final answer| summary["Summarize / save point"]
decision -->|error| errorNode["Map error to user-facing output"]
summary --> finish["Finish"]
errorNode --> finish
Key behavior:
- Classification narrows tool exposure before the LLM call.
- MCP tools are injected dynamically.
- Tool calls loop back into the LLM until the model returns a final answer.
- Session history and graph steps can be persisted for replay/inspection.
- The execution delegate supports active-job cancellation and trace callbacks.
- Errors are routed through a dedicated user-facing error node.
:graph-engine is a small framework-free runtime for composing typed suspendable Kotlin nodes.
It provides:
Node<IN, OUT>as the unit of work.Graph<IN, OUT>as a node-compatible executable graph.- Static and dynamic transitions.
- Nested graphs.
- FIFO traversal.
- Retry policies.
- Step tracing through
onStep. - Cancellation handling that preserves the last context.
maxStepsprotection against accidental loops.
Run the graph engine README example:
./gradlew :graph-engine:test --tests ru.souz.graph.GraphReadmeExampleTestSouz separates tool behavior from the execution environment through RuntimeSandbox.
RuntimeSandbox
├── mode: LOCAL | DOCKER
├── scope: SandboxScope
├── runtimePaths: home, workspace, state, sessions, vector index, logs, models, native libs, skills
├── fileSystem: SandboxFileSystem
└── commandExecutor: SandboxCommandExecutor
The current implementations are LocalRuntimeSandbox and DockerRuntimeSandbox. Local mode is the default. Docker mode is opt-in through SOUZ_SANDBOX_MODE=docker and requires the souz-runtime-sandbox:latest image to exist locally. Build it with ./gradlew :sharedLogic:buildRuntimeSandboxImage. Tools plus skill loading, storage, and validation depend on sandbox abstractions instead of directly assuming host access. See sharedLogic/README.md for setup details.
Default state layout is under:
~/.local/state/souz/
├── sessions/
├── vector-index/
├── logs/
├── models/
├── native/
├── skills/
└── skill-validations/
Safety mechanisms include:
- SafeMode permission prompts before sensitive tool execution.
- User approval UI for pending tool requests.
- Deferred review flow for file modifications.
- Confirmation requirement for destructive Telegram operations.
- Ambiguity dialogs for Telegram contact/chat selection.
- Backend tool restriction to backend-safe categories.
- Trusted-proxy identity only for backend
/v1/**routes. - Durable tool-call audit rows in the backend with redacted/truncated previews.
- Opt-in Docker runtime sandbox mode for local app runs and integration tests.
Souz has two tool catalogs:
- Desktop catalog in
:desktopApp, composed with shared runtime tools and surfaced through:sharedUIapproval flows. - Runtime/backend-safe catalog in
:sharedLogic, reusable by:backendwithout instantiating desktop-only services.
| Category | Tools |
|---|---|
| Files | List files, find text in files, create file, delete file, modify file, move file, extract text, find files by name, read PDF pages, open file/path, find folders |
| Browser | Create new browser tab, Safari info, browser hotkeys, focus tab, Chrome info, open default browser |
| Web search | Quick internet search, multi-step internet research, web image search, web page text extraction |
| Config | Sound config, sound config diff, instruction store |
| Notes | Open note, create note, delete note, list notes, search notes |
| Applications | Show installed apps, open app/file/path |
| Data analytics | Create plot from CSV, upload file, download file, read Excel, generate Excel report |
| Calendar | Create event, delete event, list calendars, list events |
| Count unread messages, list messages, read message, reply, send new message, search mail | |
| Text / clipboard | Get clipboard, replace selected text, read selected text |
| Calculator | Calculator |
| Telegram | Read inbox, get chat history, set chat state, send message/attachment, forward message, search Telegram, save to Saved Messages |
| Desktop | Take screenshot, start screen recording |
| Presentation | Create presentation, read presentation, list/find files for presentation workflows |
The backend-safe catalog avoids desktop-only APIs and includes:
| Category | Tools |
|---|---|
| Files | List/find/create/delete/modify/move files, extract text, find files, read PDF pages, find folders |
| Web search | Internet search, internet research, optional web image search, web page text |
| Config | Sound config, sound config diff |
| Data analytics | CSV plotting, Excel read, Excel report |
| Calculator | Calculator |
The backend intentionally excludes desktop automation, browser control, Mail, Calendar, Notes, desktop Telegram tools, presentation UI integrations, and other OS-bound tools. It separately supports Telegram bot chat bindings for text ingress into existing backend chats.
Souz treats tool execution as an interactive workflow instead of a hidden side effect.
sequenceDiagram
participant Agent
participant Broker as ToolPermissionBroker
participant UI as Compose approval UI
participant Tool
Agent->>Broker: requestPermission(description, params)
Broker->>UI: emit ToolPermissionRequest
UI->>Broker: approve / reject
Broker->>Agent: Ok / No
Agent->>Tool: invoke only when approved
Confirmation-related flows:
ToolPermissionBrokerserializes SafeMode permission prompts and waits for the user decision.PermissionsUseCaselistens to generic tool permission requests and selection approval sources.ToolModifyReviewUseCasemanages deferred file-modification review/approval inside chat messages.- Telegram tools use selection brokers for ambiguous fuzzy contact/chat matches.
- Destructive Telegram operations require explicit confirmation before continuing.
:backend is a JVM Ktor server that exposes the shared agent runtime over HTTP.
| Route | Purpose |
|---|---|
GET /health |
Process and selected-model status |
GET /v1/bootstrap |
Features, storage mode, visible models/tools, effective trusted-user settings |
GET /v1/me/settings |
Read public user settings |
PATCH /v1/me/settings |
Persist public user settings |
GET /v1/me/provider-keys |
List configured provider-key state |
PUT /v1/me/provider-keys/{provider} |
Store encrypted provider key |
DELETE /v1/me/provider-keys/{provider} |
Delete provider key |
GET /v1/chats |
List owned chats |
POST /v1/chats |
Create chat |
PATCH /v1/chats/{chatId}/title |
Rename chat |
POST /v1/chats/{chatId}/archive |
Archive chat |
POST /v1/chats/{chatId}/unarchive |
Unarchive chat |
GET /v1/chats/{chatId}/messages |
List visible product messages |
POST /v1/chats/{chatId}/messages |
Create user message and start/complete agent execution |
GET /v1/chats/{chatId}/telegram-bot |
Read Telegram bot binding state for an owned chat |
PUT /v1/chats/{chatId}/telegram-bot |
Validate and upsert a Telegram bot binding for an owned chat |
DELETE /v1/chats/{chatId}/telegram-bot |
Remove the Telegram bot binding from an owned chat |
GET /v1/chats/{chatId}/events |
Replay durable chat events |
WS /v1/chats/{chatId}/ws |
Replay and subscribe to live chat events |
POST /v1/options/{optionId}/answer |
Resume execution after a pending option is answered |
POST /v1/chats/{chatId}/cancel-active |
Cancel active execution |
POST /v1/chats/{chatId}/executions/{executionId}/cancel |
Cancel a specific execution |
/v1/**trusts identity only from proxy-managed headers:X-User-IdX-Souz-Proxy-Auth
X-User-Idis treated as opaque and provisioned throughUserRepository.ensureUser(userId).- Request bodies are never trusted for user identity.
- Each chat, execution, option, and setting is scoped to the trusted user.
- Backend host adapters replace desktop-only services with no-op implementations.
- The backend uses the same shared agent execution kernel as desktop.
| Mode | Description |
|---|---|
memory |
Bounded in-process repositories, useful for local/dev execution |
filesystem |
Per-user files under SOUZ_BACKEND_DATA_DIR / souz.backend.dataDir |
postgres |
JDBC + HikariCP + Flyway-backed durable storage |
Postgres storage supports durable event replay, per-chat message/event sequence numbers, one active execution per chat, optimistic locking for agent_conversation_state, and durable tool-call audit rows.
Telegram bot bindings are available in all backend storage modes. Bot tokens are encrypted at rest via TELEGRAM_TOKEN_ENCRYPTION_KEY, pending links use one-time /start <secret> commands with only the secret hash stored server-side, and binding setup drops pending Telegram updates before long polling starts.
# Server
SOUZ_BACKEND_HOST=127.0.0.1
SOUZ_BACKEND_PORT=8080
# Feature flags
SOUZ_FEATURE_WS_EVENTS=true
SOUZ_FEATURE_STREAMING_MESSAGES=true
SOUZ_FEATURE_TOOL_EVENTS=true
SOUZ_FEATURE_OPTIONS=true
SOUZ_FEATURE_DURABLE_EVENT_REPLAY=false
# Storage
SOUZ_STORAGE_MODE=filesystem
SOUZ_BACKEND_DATA_DIR=data
# Postgres
SOUZ_BACKEND_DB_HOST=127.0.0.1
SOUZ_BACKEND_DB_PORT=5432
SOUZ_BACKEND_DB_NAME=souz
SOUZ_BACKEND_DB_USER=souz
SOUZ_BACKEND_DB_PASSWORD=...
SOUZ_BACKEND_DB_SCHEMA=public
SOUZ_BACKEND_DB_MAX_POOL_SIZE=10
SOUZ_BACKEND_DB_CONNECTION_TIMEOUT_MS=30000Run the backend:
./gradlew :backend:runBy default it binds to 127.0.0.1:8080.
Souz supports standalone ClawHub/OpenClaw-style skill bundles across :agent and :sharedLogic.
Skill pipeline:
flowchart LR
skillList["List user skills"] --> skillSelect["Select skills\nmetadata + LLM"]
skillSelect --> skillLoad["Load selected bundle"]
skillLoad --> skillHash["Canonical hash"]
skillHash --> validationCache["Validation cache lookup"]
validationCache --> structuralValidation["Structural validation"]
structuralValidation --> staticValidation["Static validation"]
staticValidation --> llmValidation["LLM validation"]
llmValidation --> skillActivate["Activate skill"]
skillActivate --> skillInject["Inject skill context"]
Skill safety and storage:
- Bundles are loaded through safe filesystem access.
- Desktop/local skills are persisted under
~/.local/state/souz/skills/{skillId}/, with immutable bundles inbundles/{bundleHash}/and metadata instored-skill.json. - Desktop/local validation records are persisted separately under
~/.local/state/souz/skill-validations/{skillId}/policies/{policy}/. - Backend storage keeps the user-scoped scope available under
skills/users/{encodedUserId}/skills/{skillId}/andskill-validations/users/{encodedUserId}/skills/{skillId}/. - Validation cache keys include user id, skill id, bundle hash, and policy version.
- Stale validations are invalidated when the active bundle hash changes.
- Selected skills are activated only after structural, static, and LLM validation pass.
Souz supports:
- GigaChat REST and voice APIs.
- Qwen.
- AiTunnel.
- Anthropic Claude.
- OpenAI.
- Local llama.cpp models through
:native.
Provider/model selection is key-aware: chat, embeddings, and voice-recognition model lists are filtered by configured provider keys, and invalid saved selections are normalized to available providers.
:native provides local model execution through a JNA bridge into a packaged llama.cpp-based native library.
Features:
- macOS arm64 and x64 packaged bridge binaries.
- Qwen and Gemma chat profiles.
- Linked EmbeddingGemma GGUF asset for embeddings.
- Model storage under
~/.local/state/souz/models/. - Native bridge extraction under
~/.local/state/souz/native/. - Background preload/warmup when selecting a local chat model.
- Settings-driven context windows capped by model limits.
- Prompt rendering for Qwen ChatML and Gemma 4 turn formats.
- Strict JSON tool-call contract and output recovery/parsing.
- Prompt-prefix/KV reuse in the native runtime.
- Local generation and embeddings cancellation support.
Rebuild packaged bridge binaries:
desktopApp/src/main/resources/scripts/build-llama-bridge.shSouz can connect to external tools through Model Context Protocol:
- stdio transport.
- HTTP transport.
- OAuth discovery.
- Token refresh.
- Dynamic MCP tool injection into the agent graph.
Souz has two web modes:
- Internet search for quick factual answers.
- Internet research for multi-step synthesis with LLM-built strategy, broader source coverage, citations, and automatic Markdown export for oversized reports.
Recommended IntelliJ IDEA plugins:
- Kotlin Multiplatform
- Compose Multiplatform
- Compose Multiplatform desktop support, optional
Run the desktop app:
./gradlew :desktopApp:runRun desktop tests:
./gradlew :sharedUI:cleanJvmTest :sharedUI:jvmTestRun agent integration scenarios:
export SOUZ_AGENT_INTEGRATION_TESTS_ON=true
./gradlew :sharedUI:cleanJvmTest :sharedUI:jvmTest --tests "agent.GraphAgentComplexScenarios"Run backend tests:
./gradlew :backend:testRun all checks:
./gradlew checkUseful release scripts:
# Prepare universal macOS app bundle
scripts/kmp-build-macos-universal.sh
# Build notarized arch-specific DMGs and export to dest/homebrew/<version>/
scripts/kmp-build-macos-dev.sh
# Generate Homebrew cask from exported DMGs
scripts/prepare-homebrew-release.shSee JetBrains Compose Multiplatform release docs for signing and notarization details.
- Prefer composition over inheritance.
- Keep UI free of business logic and IO.
- Coordinate UI logic from ViewModels and delegate domain work to use cases.
- Avoid mixing coroutines with low-level JVM concurrency primitives unless there is a clear boundary.
- Use open/closed design for tools, providers, and runtime adapters.
- Keep Compose/UI dependencies out of
:sharedLogic; backend wiring should avoid desktop-only service/tool implementations. - Read the nearest
AGENTS.mdbefore editing a module or nested package.
Copyright © 2026 Artur Dumchev and Shamil Khizriev
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.