ChatGPT-style chat UI with a coding agent behind it. Client-side: it calls your own LLM endpoints and runs the agent's commands on your machine.
Entoli (εντολή) is the Greek word for a command; both the one you give and the one the agent runs.
Entoli ships for Windows, Linux, macOS, Android, and the web.
Windows: download entoli-installer.msi from the
GitHub releases and run
it; its welcome page carries the licence and the Install button commits.
It installs to Program Files and puts Entoli in the Start menu. The build
is unsigned for now, so Smartscreen's More info, then Run anyway, is how the
installer's first run goes.
Linux: download entoli-x86_64.AppImage from the
GitHub releases, make it
executable, and run it.
macOS: download entoli-arm64.dmg from the
GitHub releases, open it,
and drag the app to /Applications.
Android: the app is distributed through Google Play closed testing.
- Join the Entoli Google group.
- Join the closed test.
- Get the app from the Google Play Store.
Web: the latest release is served at yiannis128.github.io/agent-chat.
Entoli is a Flutter app targeting Windows, Linux, macOS, Android, and the web. Beyond the Flutter SDK, each platform needs a few native dev libraries; the lists below are the complete set, copy-pasteable into whichever package manager you use.
tool/prepare.shFetches the packages and generates the sources that freezed and
json_serializable produce, which are not in the repository — so no Dart
target compiles until this has run. Run it again after changing a @freezed
or @JsonSerializable type. The first run is about a minute; later ones are a
few seconds.
GTK 3 and GStreamer dev headers (the latter come from audioplayers_linux),
plus the C/C++ build toolchain.
- Fedora:
clang cmake ninja-build pkg-config gtk3-devel gstreamer1-develgstreamer1-plugins-base-devel - Ubuntu:
clang cmake ninja-build pkg-config libgtk-3-dev libgstreamer1.0-devlibgstreamer-plugins-base1.0-dev
Xcode and CocoaPods (brew install cocoapods). flutter build macos builds
the app; tool/macos/make-dmg.sh packages it — see macos/AGENTS.md for the
entitlements decision and what signing adds.
JDK 17 and the Android SDK with command-line tools. Gradle pulls the NDK it
needs; minSdk is 26.
The APK ships a series of apps that are used for the agentic-dev. During
compilation, the compiler bundles everything in android/app/src/main/jniLibs.
This needs to be built, or else the agent will only have access to the default
shell utils included in Android (essentially Toybox).
Entoli ships the following builtin packages:
prootllama-server(llama.cpp), which is what the On-device settings page runs
git used to be here, cross-compiled against bionic. It is not any more: the
agent gets git from apk inside the Alpine rootfs the Terminal settings page
installs, where a distribution patches it for a CVE rather than us. So on a
device with no rootfs installed there is no git.
Build them once per ABI, then stage them:
ANDROID_NDK_ROOT=$HOME/.android/ndk/<version> tool/android/build.sh arm64-v8a
tool/android/stage.sharm64-v8ais what a phone wants; addarmeabi-v7aandx86_64for emulators or a universal APK.- The NDK has to be named explicitly; Gradle's own is not on
PATH. Use$HOMErather than~: as an argument toenvor inside quotes a tilde is not expanded, and the build fails on a path with a literal~in it. - llama.cpp needs
cmakeandninja, and is built forarm64-v8aonly —LLAMA_ABISoverrides that. About 55 MB an ABI is why — the Vulkan backend'slibggml-vulkan.sois 36 MB of it — and a 32-bit phone cannot map a model anyway, x86_64 Android being an emulator. - The NPU wants Qualcomm's Hexagon SDK, and a build without it is a build
without the NPU.
tool/android/setup.shfetches it intotool/android/toolchains/, checked against the SHA-256 pinned inversions.sh, andbuild.shpicks it up from there. Only the NPU is gated on it: the build always carries the CPU and the Vulkan GPU backend, so skipping the fetch still accelerates most phones, and the settings page says the device has no accelerator only when it has neither an NPU nor a Vulkan-1.1 GPU. - CI runs all three steps from
.github/actions/bundled-tools, the fetch included, so a release APK carries every backend. A release build whose arm64 APK has no NPU libraries in it fails there: it is the only place a build that quietly lost them would be noticed.
Skipping this leaves an APK whose On-device page says it carries no model server, and which cannot run a Linux at all.
- Pin the source in
tool/android/versions.sh; version, URL and SHA-256. That file and the recipe are what the CI cache builds from. - Add a step to
tool/android/build.shthat builds it against the NDK toolchain the script has already exported (CC,AR,STRIP, and aCFLAGScarrying API 26 and 16 KB page alignment), and strip the result into$out/lib<name>.so. Anything it links has to be built here too and linked statically: the guest isbionic, so glibc is not there, and the only libraries a device is guaranteed to have arelibc,libdlandlibz. The build checks this and fails naming what it finds, since otherwise the first thing to say so is the phone. - Then
stage.sh. It copies every*.soit finds per ABI.
The name matters twice. The installer extracts only lib*.so out of an APK, and
the native library directory is the one place an app may execute a file from, so
a binary named anything else is either not unpacked or not runnable. That is
also why useLegacyPackaging has to stay on in android/app/build.gradle.kts:
without it the file is mapped from inside the APK and there is nothing on disk
to exec.
Then tell the app. BundledTools.install in lib/data/local/bundled_tools.dart
symlinks the binary into its prefix under its real name; the link is what gives
it a name without a lib prefix, since the file itself may not be renamed; and
adds it to Toolset.names, which is what the system prompt tells the model is
there. Laying one out without naming it means a tool the model never reaches
for.
The cost is its size in every ABI the APK carries. And nothing on the device
updates any of this, so a CVE in a bundled tool is a version bump in
versions.sh and a release that every user has to install — which is the
reason git is no longer one of them, and worth weighing before adding another.
On Android the agent can run its commands inside a downloaded rootfs under
proot, which is where a package manager comes from; nothing downloaded may be
executed directly, so the bundled tools are otherwise the whole of what a phone
can run. A distribution Entoli offers is a const entry in
lib/domain/rootfs.dart and a name in rootfsCatalog; nothing else has to be
told, because the Terminal settings category lists the catalog, downloads what
is picked, and the next command runs in it.
An entry names the distribution, its version, its package manager, and one image
per Android ABI. Each image carries the architecture under the guest's own name
for it (aarch64 where Android says arm64-v8a), the URL, the exact byte count
and the SHA-256:
curl -fLO <url>
stat -c %s <file>; sha256sum <file>
Both numbers are load-bearing. The size is what the progress bar reads before
the first byte arrives, and the checksum is checked before anything is unpacked
— everything in that tarball becomes a program this device will run, which is
the same bargain versions.sh makes for what is built into the APK. An ABI left
out of images is one the settings page says has no build for it, rather than
offering a download that cannot work.
The tarball has to be .tar.gz: the system tar on a phone cannot pipe .xz
and Dart decodes it at a crawl. Prefer a small official rootfs with a package
manager working from the first command; packageManager is named in the system
prompt, so the model knows how to install what it needs. id is the directory
the rootfs installs into under the cache directory, so renaming one orphans what
a user already downloaded.
Only the Flutter SDK. No native dependencies; sherpa_onnx's WebAssembly is
stubbed out via dependency_overrides, so it never enters the web bundle.
tool/linux/make-appimage.sh packages a flutter build linux bundle and
downloads appimagetool itself locally (pinned to a release, SHA-256 checked):
tool/linux/make-appimage.sh build/linux/x64/release/bundle dist/entoli.AppImage
