@circuitwall/jarela
v1.9.3
Published
Jarela — local chat interface for LangGraph agents (multi-provider, single-process, SQLite-backed).
Downloads
4,505
Maintainers
Readme
Quick start
Get to a working local agent in under 10 minutes:
- Install using one channel:
- npm:
npm install -g @circuitwall/jarela - Docker:
docker run -d -p 127.0.0.1:4312:4312 -v jarela-data:/data andrewgewu/jarela - portable archive: download from Releases
- Start Jarela:
- npm install: run
jarela - source checkout: run
npm run dev(dev) ornpm run build && npm start(prod)
- Open the app:
- dev mode:
http://localhost:3000 - installed/prod mode:
http://127.0.0.1:4312
- Complete first-run setup:
- add one provider key (Models/setup screen), then create your first agent
- enable tool categories that match your workflow (Mail, Calendar, Files, Web, etc.)
- Optionally connect integrations from Connections:
- Gmail/Calendar, Outlook/Calendar, GitHub, Atlassian, MCP servers, WhatsApp bridge
For platform-specific install, service/autostart, and operations detail, jump to Installation and runtime details.
Configuration guide (home + work)
This section gives opinionated starter configs and tool chains you can adapt. Pattern that works well: one agent per lane (home, work), each with a narrow tool policy and clear trigger source.
Home setup
Recommended baseline:
- Agent name:
Home assistant - Model: low-cost chat model for routine tasks
- Tool categories:
Mail,Calendar,Web,Memory,Schedule - Bridges: WhatsApp route for family group in
silent_modefor monitoring, and one direct route with replies enabled for active planning
Popular goals and tool chains:
- Daily family agenda brief
- Chain:
calendar_list_events->gmail_search/outlook_search->memory_write - Result: one morning summary with events + high-priority emails
- Trip planning helper
- Chain:
web_search->calendar_create_event->gmail_create_draft/outlook_create_draft - Result: compares options, blocks time, drafts confirmation mail
- Household reminders
- Chain:
schedule_task->memory_read->calendar_create_event - Result: recurring reminders with memory-backed context
Work setup
Recommended baseline:
- Agent name:
Work operator - Model: stronger reasoning model for cross-system workflows
- Tool categories:
Work(GitHub + Atlassian),Mail,Calendar,Files,Documents,Schedule - Connections: GitHub PAT, Atlassian token, one mail/calendar stack (or both)
- Safety: keep destructive operations behind approvals and draft-first mail policy
Popular goals and tool chains:
- Standup prep in 5 minutes
- Chain:
jira_search_issues->github_list_pulls->documents_search->memory_write - Result: compact status grouped by in-progress, blocked, and review-ready
- Incident follow-up workflow
- Chain:
github_get_issue/jira_get_issue->file_write(draft runbook notes) ->outlook_create_draft/gmail_create_draft - Result: structured summary plus stakeholder update draft without auto-send
- Weekly planning and meeting slots
- Chain:
calendar_list_events->outlook_calendar_create_event/calendar_create_event->jira_update_issue - Result: reserves focus blocks and updates ticket state in one pass
Suggested operating pattern
- Keep
Home assistantandWork operatorseparate. - Use
silent_modeon noisy bridge/group channels so the agent reports important events without replying publicly. - Prefer watcher/scheduled
scriptreactions for deterministic automations; useagent_promptwhen judgment is required. - Store recurring context in memory namespaces (
home/*,work/*) so prompts stay compact. - Start with minimal tool categories, then expand only where needed.
What is Jarela?
Jarela is a desktop-grade chat UI for LangGraph agents that runs as a
single Next.js process on your own machine. Everything — agent definitions,
model API keys, memory, tool whitelists, scheduled tasks, OAuth tokens,
checkpointed conversation state — lives in SQLite under the default data dir
(~/.jarela on macOS/Linux, %LOCALAPPDATA%\Jarela on Windows). Talk to it
from any browser, or install it as a PWA / Windows scheduled task and forget
it's there.
It does not depend on any hosted backend. The only outbound traffic is to the LLM / MCP / GitHub providers you explicitly configure.
Runs on Windows, macOS, and Linux. Everything is plain Node.js + SQLite — no native installer, no platform-locked dependencies. Windows ships with a per-user Scheduled Task supervisor; macOS and Linux run under launchd / systemd (or any process supervisor you prefer).
Dual productivity-stack support. Jarela treats the Google (Gmail + Calendar) and Microsoft (Outlook + Calendar) ecosystems as first-class, parity peers — same in-UI OAuth, same tool surface, same safety policy. Pick one, or connect both and let your agents bridge them.
Features
Agent runtime
- LangGraph state machine per agent — message history, tool calls, and proposals checkpointed to SQLite, so conversations resume across restarts.
- Streaming with reasoning blocks (Anthropic extended thinking, OpenAI o-series reasoning) over Server-Sent Events. One POST submits a turn, one EventSource subscribes to the chunk stream — see ADR-0008. The same port serves UI, API, and stream; no WebSocket sidecar.
- Tool-policy gating per agent — agents can be locked to a subset of
tool categories (
Memory,Files,Shell,Web,Mail,Calendar, …) rather than toggling tools one by one. - Human-in-the-loop approvals — high-risk operations can be routed
through a
propose_config_changemechanism that surfaces an in-UI banner the user must approve before the agent proceeds. - Adaptive persona — per-agent MBTI preset (Architect, Campaigner, …) combined with empathy / expressiveness / verbosity sliders, mixed with a per-turn signal detector (frustrated / rushed / positive) to shape the system prompt deterministically. Off by default.
- Voice — per-agent push-to-talk via Gemini STT, plus a
generate_voicetool that produces audio clips (rendered inline in the chat). Requires a Google API key; chat works without it. See ADR-0017.
Persistence & memory
- Single SQLite store at
~/.jarela/jarela.dbfor settings, agents, models, MCP server configs, profile, memory, threads, scheduled tasks, proposals, OAuth tokens. - Separate LangGraph checkpoint DB at
~/.jarela/checkpoints.db(via@langchain/langgraph-checkpoint-sqlite). - Namespaced key/value memory that the agent reads and writes through
memory_read/memory_write/memory_list. - Vector embeddings support via a configured embedding provider for semantic memory recall.
Background work
- Scheduler (
lib/scheduler/) runs cron-driven jobs, so an agent can be asked to do something hourly/daily without a user in the loop. - Bridges (
lib/bridges/) let external transports inject messages into agent threads. Built-in: WhatsApp via Baileys, with a router that maps JIDs to specific agents.
Productivity stacks (Google + Microsoft, at parity)
Jarela ships matched tool sets for both major consumer/enterprise productivity ecosystems. Connect one or both — the agent surface and safety policy are identical.
| | Google | Microsoft |
| --- | --- | --- |
| Mail | Gmail (gmail_* tools) | Outlook (outlook_* tools) |
| Calendar | Google Calendar (calendar_* tools) | Outlook Calendar (outlook_calendar_* tools) |
| Meetings | Auto-attach Google Meet links | Auto-attach Microsoft Teams links |
| Auth | In-app Google OAuth | In-app Microsoft OAuth (Azure app registration) |
| Account types | Personal + Workspace | Personal (@outlook.com) + work/school (Entra ID) |
| Mail policy | Read / search / draft only — no auto-send | Read / search / draft only — no auto-send |
| Calendar policy | Full read / write / delete | Full read / write / delete |
| Setup | In-app Connect Gmail button | In-app Connect Outlook button |
Both flows store refresh tokens encrypted at rest under ~/.jarela. There
is no preferred stack — an agent with both connected can search Gmail and
create an Outlook Calendar invite in the same turn.
UI
- PWA install (
@serwist/next) — taskbar / home-screen install with the full Jarela icon set. - iOS-aware layout — safe-area insets, Dynamic Island padding, location sharing, push notification status indicator.
- Panels for Agents, Models, Connections, Tools, Memory, Documents, Profile, Bridges, Scheduled tasks, and Pending approvals.
- Browser extension (
browser-extension/) — Chrome MV3, click an element on any page and POST it (with a cropped PNG of the picked element) to your local Jarela as a new user message (ADR-0018). The screenshot is rendered inline in the chat bubble and forwarded to vision-capable agents on the silent observer turn that fires immediately after the capture. Loopback only; toolbar icon greys out when Jarela isn't running.
Operational
- Cross-platform — single Node.js process, runs on Windows, macOS, and Linux. Per-OS supervisor recipes below.
- Runs as a per-user Windows scheduled task via
scripts/install-to-system.ps1 — auto-starts at logon, logs to
%LOCALAPPDATA%\Jarela\logs\app.log, no admin required. - On macOS runs cleanly under
launchd(LaunchAgent plist); on Linux undersystemd --user. Sample units below. - Respects standard proxy env vars (
HTTP_PROXY/HTTPS_PROXY/NO_PROXY) throughundici'sEnvHttpProxyAgent, so it works inside corporate networks. On macOS, set proxy mode to System in Settings → Credentials → Network & environment auto-discovers the proxy (viascutil --proxy) and trust store (viasecurity find-certificateagainst System + login keychains, written to~/.jarela/system-ca.pem) — zero shell-config or manual CA paste required when the corporate roots are already in the system keychain (ADR-0020).
Extending Jarela
Seven extension surfaces, each documented in
docs/EXTENDING.md:
- LLM providers — built-in (
lib/providers/<name>.ts) or external (~/.jarela/providers/*.cjs). - Tools — drop a file under
lib/tools/<name>.ts, register withregisterLangChainPackage({ category, tools: { read|write|execute: [...] } }). External.cjsplugins also live under~/.jarela/tools/. - LangChain tool packages — drop a JSON manifest under
~/.jarela/packages/manifests/pointing at any npm package that exports aStructuredTool-shaped class. The operator managesnpm installinside~/.jarela/packages/; Jarela dynamic-imports, constructs, and registers the tool on first agent call. - MCP servers — stdio or http, configured via UI or programmatic
upsertMcpServer. Tools auto-merge into the agent's pool. - Agent harnesses — built-in presets in
lib/agents/harness/presets.ts, custom ones via the agent'spropose_config_changetool. - Integration manifests — agent-led setup recipes for external services (Atlassian, Gmail, GitHub, …).
- Brand overlays — env-var driven (
NEXT_PUBLIC_APP_NAMEand friends). - HTTP API — see
docs/api.mdfor the stable contract; the agent introspection tools (list_tools,list_providers,list_mcp_servers,describe_extension_surfaces) cover the same surfaces from inside an agent run.
The package's public type surface is locked via
package.json#exports and the deprecation policy in
CONTRIBUTING.md → Public API surface.
Reusable packages
Some of Jarela's internals are also published as standalone npm packages so
you can reuse them in your own LangChain / LangGraph projects without
running the Jarela app. They live under
packages/ in this repo as npm workspaces.
| Package | Description | Install |
|---|---|---|
| @circuitwall/atlassian-langchain | 64 LangChain tool() definitions for Atlassian Jira and Confluence Cloud (direct REST, no MCP, proxy-aware). | npm install @circuitwall/atlassian-langchain @langchain/core zod |
| @circuitwall/jira-align-langchain | 22 LangChain tool() definitions for Atlassian Jira Align (portfolio / SAFe). | npm install @circuitwall/jira-align-langchain @langchain/core zod |
| @circuitwall/github-langchain | 22 LangChain tool() definitions for the GitHub REST API (issues, PRs, repo content, code search). | npm install @circuitwall/github-langchain @langchain/core zod |
Each package exposes a setAuthResolver() API for pluggable credential
resolution (env, vault, UI form, …) and a low-level *Fetch() escape
hatch for endpoints not yet wrapped as tools. See each package's README
for the full quick start.
Installation and runtime details
Works on Windows 10/11, macOS 12+, and Linux (any modern glibc distro). See Supported platforms for the per-OS file layout.
Install (no source checkout needed)
Three pre-built release channels — pick the one that matches your host.
All three end at the same place: a local Next.js process on
http://127.0.0.1:4312, all state under the default host data dir
(~/.jarela on macOS/Linux, %LOCALAPPDATA%\Jarela on Windows) or a named
Docker volume. See INSTALL.md for first-launch warnings
and uninstall steps, and
ADR-0011
for why multiple paths exist.
| Channel | Best for | Install command | Updates |
|---|---|---|---|
| GitHub Releases (portable archive) | Air-gapped boxes, no Node toolchain | Download jarela-<ver>-<os>.{tar.gz,zip} from Releases, extract, run the install script with --skip-build | re-download next tag |
| npm (@circuitwall/jarela) | Any host with Node ≥ 22 — global CLI, OS-native autostart | npm install -g @circuitwall/jarela | npm update -g @circuitwall/jarela |
| Docker Hub (andrewgewu/jarela) | Linux server, NAS, container host | docker run -d -p 127.0.0.1:4312:4312 -v jarela-data:/data andrewgewu/jarela | docker pull andrewgewu/jarela |
A. Download a release archive (no Node required to install — bundle is self-contained):
| OS | Asset |
|---------|--------------------------------------|
| macOS | jarela-<version>-darwin.tar.gz |
| Windows | jarela-<version>-win.zip |
| Linux | jarela-<version>-linux.tar.gz |
Extract, then run the install script with --skip-build / -SkipBuild —
the bundle ships a pre-built .next/standalone/ tree.
B. From npm (Node ≥ 22 required):
npm install -g @circuitwall/jarela
jarela # starts the bundled standalone server immediately
jarela install-service # optional: register OS-native autostart (per-user, no admin)Releases on npm are published from GitHub Actions via
npm Trusted Publishing (OIDC) and ship
with provenance attestations
signed to the sigstore transparency log — no long-lived NPM_TOKEN ever
touches a release.
C. From Docker Hub (Linux host with Docker ≥ 20.10):
docker run -d --name jarela \
-p 127.0.0.1:4312:4312 \
-v jarela-data:/data \
-e JARELA_DB_DIR=/data \
andrewgewu/jarelaOr with docker compose:
curl -fsSL https://raw.githubusercontent.com/CircuitWall/jarela/main/docker-compose.yml -o docker-compose.yml
docker compose up -dTags: latest, 0, 0.1, 0.1.3, … (semver-stepped). See
INSTALL.md → Path 3 for
the full Docker recipe.
Or from source (developers)
Prerequisites: Node.js ≥ 22.6 (Node 25 recommended), npm, git.
git clone https://github.com/CircuitWall/jarela.git
cd jarela
npm installFirst launch — one key, then chat
The very first time the app starts with no model_configs row, it routes
to a single setup screen asking for one LLM provider key. Validate,
save, done — every subsequent integration (Gmail, Outlook, Atlassian, …)
is driven by chatting with the agent, which proposes the credential
collection through the existing approval rail. Secrets are typed into the
approval modal directly; the agent never sees them. See
ADR-0010.
Then pick one launch mode:
Dev (hot reload)
npm run dev
# http://localhost:3000Production one-shot
npm run build # next build (output=standalone) + hydrate static/public
npm start # node .next/standalone/server.js with PORT=4312
# http://localhost:4312npm run build writes a self-contained bundle to .next/standalone/ —
copy that directory anywhere you want to run Jarela without the source
checkout. npm start is just a thin wrapper around node server.js with
PORT/HOSTNAME defaults applied.
Configuration
All runtime knobs are resolved once at startup via lib/env/config.ts.
Set them in .env.local (see .env.example) or export
them in your shell / service unit:
| Var | Default | Purpose |
| ------------------------- | ---------------------- | ------------------------------------------------- |
| JARELA_PORT / PORT | 4312 | TCP port |
| JARELA_HOSTNAME / HOSTNAME | 127.0.0.1 | Bind address |
| JARELA_DB_DIR | ~/.jarela (Win: %LOCALAPPDATA%\Jarela) | SQLite + generated files dir |
| JARELA_TOOLS_DIR | $JARELA_DB_DIR/tools | External tool plugins (CJS/TS) loaded at startup |
| JARELA_PACKAGES_DIR | $JARELA_DB_DIR/packages | npm install root for hot-loaded vanilla LangChain tool packages (POST /api/v1/packages/install). Manifests live under $JARELA_PACKAGES_DIR/manifests/. |
| JARELA_PACKAGE_ALLOWLIST | (unset) | +-separated list of publisher prefixes (@my-org/, my-tool) allowed to install without an approval step. Defaults (@langchain/, @circuitwall/, langchain) are always allowed; this list extends them. Same naming/semantics as ENV_ALLOWLIST. |
| JARELA_RECURSION_LIMIT | 200 | Max LangGraph steps per agent run |
| JARELA_VOICE_TIMEOUT_MS | 60000 | Gemini voice (TTS/STT) request timeout |
| JARELA_IMAGE_TIMEOUT_MS | 60000 | Gemini image-generation request timeout |
| JARELA_UPDATE_CHANNEL | stable | stable (npm latest) or main (GitHub main, experimental) |
| JARELA_DISABLE_UPDATE_CHECK | (unset) | Set to 1 to skip the daily update probe |
| JARELA_ENABLE_MOCK_PROVIDER | (unset) | Set to 1 to register the mock LLM provider (tests / offline dev) |
| JARELA_TOOL_SAFETY | mostly_safe | Built-in exec + file_* guard tier. safe = read-only inspection commands, no filesystem writes. mostly_safe = blocks the obvious destructive patterns (rm -rf /, shutdown, fork bomb, credential dirs, ~/.jarela). bypass = every guard off (trusted local use only). |
| JARELA_ALLOW_SENSITIVE_FILES | (unset) | Set to 1 to override the credential-path denylist under mostly_safe. |
| NEXT_PUBLIC_APP_NAME | Jarela | User-visible app name (browser tab, sidebar, agent system prompt). Forks override to rebrand without source patches. |
| NEXT_PUBLIC_APP_DESCRIPTION | Jarela — local chat interface for LangGraph agents | <meta name="description"> for the HTML head. |
| NEXT_PUBLIC_APP_ISSUE_URL | https://github.com/CircuitWall/jarela/issues/new | "Report a bug" target. Forks point this at their own tracker. |
JARELA_* takes precedence over the legacy PORT / HOSTNAME names.
Update channels
On startup and via GET /api/v1/update, Jarela checks once a day whether
newer code is available. Two tracks:
stable(default) — polls npm for the latest published version of@circuitwall/jarela.jarela updaterunsnpm i -g @circuitwall/jarela@latest.main(experimental) — polls the tip commit of themainbranch on GitHub. For source checkouts the comparison is by commit SHA andjarela updatedoesgit pull --ff-only && npm i && npm run build. For npm installs it falls back to comparing themainbranch'spackage.jsonversion and installs vianpm i -g github:CircuitWall/jarela#main.
Set JARELA_DISABLE_UPDATE_CHECK=1 to silence the probe entirely.
Mock LLM provider (testing / offline dev)
Set JARELA_ENABLE_MOCK_PROVIDER=1 to register a deterministic mock
provider in the registry. With the flag set, configure an agent with
provider: "mock" (any model id works) and Jarela will reply locally
without making network calls. Behaviour is scripted via directives in the
last user message:
| Directive | Effect |
|---|---|
| MOCK:reply=<text> | stream back exactly <text> |
| MOCK:echo | echo the user message verbatim |
| MOCK:tool=<name>:<json-args> | emit one tool call (e.g. MOCK:tool=web_search:{"q":"foo"}) |
| MOCK:think=<text> | emit a thinking delta before the text |
| MOCK:slow=<ms> | sleep <ms> between word chunks |
| MOCK:error=<msg> | throw <msg> |
| MOCK:stop=<reason> | override stop_reason (stop / tool_use / length) |
Embeddings are deterministic 384-dim L2-normalised vectors seeded by
SHA-256 of the input, so semantic-recall tests have stable rankings. The
flag is opt-in so production deployments never expose mock as a
selectable backend.
Install as a background service
Jarela is a single long-running Node process. Use whatever supervisor your OS provides; the repo ships a turn-key installer for Windows, and the recipes below cover macOS + Linux.
Windows — per-user scheduled task (turn-key)
Auto-starts at logon, no admin required:
powershell -ExecutionPolicy Bypass -File scripts\install-to-system.ps1
# manage
Stop-ScheduledTask -TaskName Jarela
Start-ScheduledTask -TaskName Jarela
Get-Content "$env:LOCALAPPDATA\Jarela\logs\app.log" -Tail 30 -Wait
# uninstall
powershell -ExecutionPolicy Bypass -File scripts\uninstall-from-system.ps1macOS — LaunchAgent
Drop a plist at ~/Library/LaunchAgents/com.jarela.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.jarela</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/path/to/jarela/.next/standalone/server.js</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PORT</key><string>4312</string>
<key>HOSTNAME</key><string>127.0.0.1</string>
</dict>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>/tmp/jarela.log</string>
<key>StandardErrorPath</key><string>/tmp/jarela.log</string>
</dict>
</plist>Load it:
launchctl load -w ~/Library/LaunchAgents/com.jarela.plist
launchctl list | grep jarelaLinux — systemd user unit
Drop a unit at ~/.config/systemd/user/jarela.service:
[Unit]
Description=Jarela
After=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/jarela
ExecStart=/usr/bin/node %h/jarela/.next/standalone/server.js
Environment=PORT=4312 HOSTNAME=127.0.0.1
Restart=always
RestartSec=3
[Install]
WantedBy=default.targetEnable it:
systemctl --user daemon-reload
systemctl --user enable --now jarela.service
journalctl --user -u jarela -fSupported platforms
| OS | Status | Data dir (default) | Supervisor |
| --- | --- | --- | --- |
| Windows 10/11 | first-class | %LOCALAPPDATA%\Jarela (ADR-0006, OneDrive-safe) | Scheduled Task (scripts\install-to-system.ps1) |
| macOS 12+ (Intel + Apple Silicon) | supported | ~/.jarela | launchd LaunchAgent (sample plist above) |
| Linux (Ubuntu / Debian / Fedora / Arch) | supported | ~/.jarela | systemd --user unit (sample above) |
| WSL2 | supported | ~/.jarela inside the distro | systemd --user if the distro has it, else any supervisor |
Any OS: override the data directory with JARELA_DB_DIR=/some/path.
Nothing in the codebase shells out to platform-specific binaries at
runtime — file paths, process spawning, and SQLite all use Node's
cross-platform APIs.
First-run configuration
API keys can be set in two places:
.env.localat the repo root (copy from.env.example).- The Models panel in the UI — persisted to
~/.jarela, takes precedence over env vars.
The dev repo and the installed task share the same ~/.jarela, so you can
configure once and switch between them freely.
Built-in toolbelt
Tools are LangChain StructuredTools registered in
lib/tools/index.ts. Every agent has a tool policy
that whitelists which categories are usable.
| Category | Tools | Notes |
| --- | --- | --- |
| Memory | memory_read, memory_write, memory_list | Namespaced KV in SQLite |
| Documents | documents_search, documents_list_sources | Semantic recall over folders the user added under the Documents tab. Cosine over chunked text; substring fallback when no embedding provider is configured. See ADR-0024. |
| Files | file_read, file_write, file_edit, file_move, file_copy, file_delete, file_list, file_mkdir, file_stat | Workspace-relative paths under ~/.jarela/files/ |
| Shell | local_exec, shell_exec | Timeouts + deny-list of obviously-destructive patterns |
| Web | web_search (Tavily), web_fetch | HTML-to-text extraction in web_fetch |
| Images | generate_image | Routed through the configured image provider |
| Schedule | schedule_task, list_scheduled_tasks, cancel_scheduled_task, schedule_watcher, list_watchers, cancel_watcher | Cron + ISO scheduling, event-driven watchers (ADR-0027) |
| Work › Atlassian | Jira issues: search, get, create (incl. bulk), update, transition, link, comments CRUD, worklogs, attachments (upload/download/delete), changelog, delete.Jira agile: list/get boards, list/get/create/update/delete sprints (with state-machine validation), move issues to sprint or backlog, rank.Jira project metadata: list projects, get project (with versions/components/issue-types expand), CRUD versions, CRUD components, list issue-types/priorities/statuses/resolutions.Confluence: search, page CRUD + delete + move, comments (list/add/update/delete, footer + inline), attachments (upload/list/download/delete), labels (add/remove), spaces. | Direct REST; works through corporate proxies. Sprint operations cover the full ceremony (create → start → complete). Destructive ops (delete sprint, purge page, purge attachment) require a confirm arg matching the id. jira_get_issue/jira_update_issue accept custom_fields by display name or customfield_* id. See ADR-0035 for the full inventory and rationale. |
| Work › Jira Align | Work items: get, search, list children, create, update, transition, delete (per-type routing for epic/capability/feature/story/theme/task/defect/objective).Hierarchy: list / get programs, teams, releases, sprints (PIs), portfolios, value streams. | Direct REST against *.jiraalign.com. Bearer-token auth. Hierarchy tools resolve human names → ids without leaving the agent. See ADR-0019, ADR-0021, ADR-0035. |
| Work › GitHub | github_search_issues, github_get_issue, github_create_issue, github_add_comment, github_list_pulls, github_get_pull, github_get_repo | Direct REST against api.github.com; PAT auth via env or Credentials (ADR-0015). |
| Mail | Google: gmail_search, gmail_get_message, gmail_list_labels, gmail_modify_message, gmail_create_draft, gmail_trash_messageMicrosoft: outlook_search, outlook_get_message, outlook_list_folders, outlook_modify_message, outlook_create_draft, outlook_trash_message | Read/search/draft only on both providers — no auto-send. Gmail via Google OAuth, Outlook via Microsoft Graph. |
| Calendar | Google: calendar_list_calendars, calendar_list_events, calendar_get_event, calendar_create_event, calendar_update_event, calendar_delete_eventMicrosoft: outlook_calendar_list_calendars, outlook_calendar_list_events, outlook_calendar_get_event, outlook_calendar_create_event, outlook_calendar_update_event, outlook_calendar_delete_event | Full read/write on both. Outlook variant can provision Teams meetings; Google variant can provision Google Meet. |
| Location | get_user_location | Browser geolocation forwarded by the PWA |
| Config | propose_config_change, check_proposal | Human-in-the-loop approval flow |
Any tool exposed by a connected MCP server is mounted under the MCP
category automatically.
Providers
Built-in model providers in lib/providers/:
| Provider | Streaming | Tool calling | Notes |
| --- | --- | --- | --- |
| Anthropic | yes | yes | Extended thinking blocks |
| OpenAI | yes | yes | Reasoning models |
| Google GenAI (Gemini) | yes | yes | |
| DeepSeek | yes | yes | OpenAI-compatible endpoint |
| GitHub Copilot | yes | yes | OAuth device flow (github-copilot-auth.ts) |
| LangChain bridge | yes | yes | Generic adapter for anything langchain can chat with |
Connections
Jarela ships first-class support for both the Google and Microsoft productivity stacks — each with parity coverage for mail + calendar via the same in-UI OAuth flow:
| Integration | Where | How |
| --- | --- | --- |
| MCP servers | Connections → MCP servers | stdio / SSE via @langchain/mcp-adapters. Search the official MCP Registry or paste a custom command. |
| GitHub Copilot (model provider) | Profile panel | OAuth device flow |
| GitHub (issues + PRs) | Credentials | Personal Access Token (repo + optional read:org); used by github_* tools (ADR-0015) |
| Atlassian (Jira + Confluence) | Credentials | API token + email |
| Google (Gmail + Calendar) | Credentials | In-app Google OAuth — click Connect Gmail, approve, done. Scopes: gmail.modify (drafts only, no send) + calendar.events. |
| Microsoft (Outlook + Calendar) | Credentials | In-app Microsoft OAuth via Azure app registration — click Connect Outlook, approve, done. Scopes: Mail.ReadWrite + Calendars.ReadWrite + offline_access. Works with personal (@outlook.com) and work/school accounts. |
| WhatsApp | Bridges panel | Baileys; pairs a phone, routes JIDs to agents |
Each first-class integration ships a machine-readable manifest at
lib/integrations/<id>/manifest.ts (prerequisites + ordered steps +
troubleshooting). The agent reads these via the list_integrations /
get_integration_setup tools and walks the user through enabling them
without anyone touching the Connections tab by hand. New integrations
must include a manifest — npm run lint enforces it.
Extension points
External providers and tools are hot-loaded: drop a .cjs file in the
right directory, save, and the next chat picks it up — no restart. Loaded
extensions and any validation errors show up under the Extensions tab in
the menu (also at GET /api/v1/extensions). Contract is pinned in
ADR-0013.
Add an external model provider
Copy lib/providers/template-external.cjs.example
to ~/.jarela/providers/<name>.cjs (or $JARELA_DB_DIR/providers/<name>.cjs).
Must module.exports an object matching
lib/providers/types.ts#ModelProvider — at minimum:
// ~/.jarela/providers/my-provider.cjs
module.exports = {
name: "my-provider",
async chat(model_id, messages, params) {
// return { stream: AsyncIterable<string> }
},
};Optional advanced hooks for fuller native exposure:
invoke(model_id, messages, params, tools)for non-stream tool callsstreamInvoke(model_id, messages, params, tools)for token/tool/thinking streamsembed(model_id, inputs, params)for semantic recalllistModels(params)so the Models UI can show this provider's catalog
chat() now receives multipart content too (string | ContentPart[]), so
external providers can implement native vision/files paths without using
invoke() first.
.js, .cjs, and .ts are accepted (.ts uses Node ≥ 22.6 type-stripping).
ESM .mjs files are not — use CommonJS exports. Built-in provider names cannot
be overridden.
Add an external tool
Copy lib/tools/template-external.cjs.example
to ~/.jarela/tools/<name>.cjs. No langchain or zod imports — Jarela wraps
your plain object:
// ~/.jarela/tools/weather.cjs
module.exports = {
name: "weather",
description: "Get weather for a city.",
category: "Web",
// Optional: declare secret slots editable in the Extensions panel.
// Values are AES-256-GCM encrypted at rest (ADR-0023) and read at
// runtime via ctx.getSecret(). Each tool sees only its own slots.
secrets: [
{ key: "api_key", label: "OpenWeather API key", required: true },
],
schema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
async run({ city }, ctx) {
const apiKey = ctx.getSecret("api_key");
if (!apiKey) throw new Error("Configure 'api_key' under Extensions → weather.");
const r = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`);
return await r.json();
},
};Names that collide with built-in tools are rejected (built-in wins). Throw an
Error to signal failure — the agent receives the message.
Add a built-in tool (in-tree)
- Copy lib/tools/template.ts to
lib/tools/<name>.ts. - Implement with
tool(...)from@langchain/core/tools+ a Zod schema. - Call
registerLangChainPackage({ category: "<Category>", tools: { read | write | execute: [yourTool, ...] } })at the bottom of the file. Thetoolsobject accepts any combination of capability buckets, so a single call covers mixed-capability files (see ADR-0038). - Add
import "./<name>";to lib/tools/builtins.ts. - If it calls a network or external resource, document the env vars and gate it behind a category the user can toggle off.
Add a vanilla LangChain tool package (hot-load, no rebuild)
Any npm package that exports a StructuredTool (the standard
@langchain/core/tools shape) can be loaded into a running Jarela
without touching the source tree.
Easiest path — UI (since 1.9.1): open Tools → LangChain
packages. The panel covers the whole flow — install from npm,
approve pending publishers, add / remove manifests, and reload — all
without leaving the browser. Everything below is the equivalent HTTP
surface for scripted or remote setups.
- Pick a package — anything under
@langchain/community/tools/*,@circuitwall/*, orlangchainis allow-listed out of the box. Other publishers require an explicit approval (see below) or an addition toJARELA_PACKAGE_ALLOWLIST(+separated list, same pattern asENV_ALLOWLIST). POST /api/v1/packages/install { "spec": "<npm-spec>" }— Jarela runsnpm installinside$JARELA_PACKAGES_DIR(default~/.jarela/packages/), then introspects everyStructuredToolInterfaceexport it finds and returns them. For an untrusted publisher this returns 202 + a pending approval id; callPOST /api/v1/packages/install/:idto approve orDELETEto deny.POST /api/v1/packages/manifests { name, package, export?, category, capability?, args?, requiredEnv? }— register the tool. The manifest is written to$JARELA_PACKAGES_DIR/manifests/<name>.jsonand the loader reloads in-place. The agent picks the tool up on its next turn — no restart.GET /api/v1/packagesshows what's currently loaded;DELETE /api/v1/packages/manifests/:nameremoves a manifest and reloads.
See docs/EXTENDING.md → Hot-loading a vanilla LangChain tool package for the full endpoint reference and trust model.
Add an MCP server
Use Connections → MCP servers in the UI. The picker searches the official
MCP Registry live (ADR-0014);
results are translated into install templates with prompts for any required
env vars or tokens. The config is stored in ~/.jarela/jarela.db and
reconnected on startup. Tools exposed by the server appear automatically
under the MCP category for any agent's tool policy.
Add a bridge
Implement a transport in lib/bridges/<name>.ts that produces normalized
InboundMessages and consumes outbound replies, then wire it into
lib/bridges/dispatcher.ts. The WhatsApp/Baileys
implementation in lib/bridges/whatsapp.ts is a
working reference.
Trust model for external code
External providers and tools run in-process with the same Node privileges as the rest of the app — same trust level as a locally-spawned MCP server. Only drop in code you wrote or trust. There is no sandbox.
Where your data lives
| Path | Contents |
| --- | --- |
| ~/.jarela/jarela.db | Settings, agents, models, memory, threads, scheduled tasks, proposals, OAuth tokens |
| ~/.jarela/checkpoints.db | LangGraph state checkpoints |
| ~/.jarela/files/ | Files written by the file_* tools |
| ~/.jarela/baileys/ | WhatsApp Baileys auth state |
| ~/.jarela/providers/ | External provider plugins, hot-loaded (optional) |
| ~/.jarela/tools/ | External tool plugins, hot-loaded (optional) |
| ~/.jarela/packages/ | npm install root for vanilla LangChain packages (POST /api/v1/packages/install); manifests under packages/manifests/ |
| %LOCALAPPDATA%\Jarela\logs\app.log | Installed-task stdout/stderr |
Override the location with JARELA_DB_DIR=/path/to/dir. On first launch
against a populated ~/.langgui (legacy LangGUI install), Jarela renames it
to ~/.jarela automatically — see ADR-0005.
Default ports
| Mode | URL |
| --- | --- |
| npm run dev | http://localhost:3000 |
| npm start / installed task | http://localhost:4312 |
Remote access via Tailscale
Jarela is a single-port HTTPS-or-HTTP app — no WebSocket sidecar,
no separate streaming port (see
ADR-0008). The
recommended way to reach an installed Jarela from your phone or
another machine is tailscale serve, which terminates HTTPS at
your tailnet name and forwards to plain HTTP on loopback.
Helper scripts (Windows):
# Configure tailscale serve for the installed Jarela on :4312
powershell -ExecutionPolicy Bypass -File scripts\install-tailscale-serve.ps1
# Clear it again
powershell -ExecutionPolicy Bypass -File scripts\uninstall-tailscale-serve.ps1scripts\install-to-system.ps1 invokes the install helper automatically
at the end of an install; pass -SkipTailscale to opt out.
scripts\uninstall-from-system.ps1 resets the serve config on uninstall;
pass -KeepTailscale to leave it in place.
Live status, the exact recipe, and a copy button are also surfaced in Settings → You → Tailscale serve in the UI.
Remote tailnet users still need to be whitelisted under
Settings → You → Tailscale access whitelist — local loopback is
always allowed, but anything reaching the node through the
Tailscale-User-Login header must match an entry.
Architecture (C4 context)
C4Context
title System Context — Jarela
Person(user, "Developer", "Drives agents from the browser/PWA")
System(jarela, "Jarela", "Next.js app: UI + API + agent runtime")
System_Ext(anthropic, "Anthropic API", "Claude models")
System_Ext(openai, "OpenAI API", "GPT models")
System_Ext(google, "Google GenAI", "Gemini models + STT/TTS for voice (ADR-0017)")
System_Ext(cohere, "Cohere API", "Embeddings / models")
System_Ext(mcp, "MCP Servers", "Tool providers via @langchain/mcp-adapters")
System_Ext(mcpreg, "MCP Registry", "registry.modelcontextprotocol.io — discovery only (ADR-0014)")
System_Ext(github, "GitHub API", "Repo / PR integrations")
System_Ext(atlassian, "Atlassian Cloud", "Jira + Confluence — tools + document-RAG ingest (ADR-0024, ADR-0026)")
SystemDb_Ext(sqlite, "SQLite (~/.jarela)", "LangGraph checkpoints, memory, settings, document chunks")
SystemDb_Ext(extdir, "~/.jarela/{providers,tools,packages}/", "Drop-in .cjs extension files + hot-loaded LangChain npm packages")
System_Ext(browserext, "Jarela Browser Extension", "MV3 — element picker → loopback POST (ADR-0018)")
Rel(user, jarela, "HTTPS / SSE")
Rel(user, browserext, "Pick an element on any page")
Rel(browserext, jarela, "POST /api/v1/page-capture (loopback only)")
Rel(jarela, anthropic, "HTTPS")
Rel(jarela, openai, "HTTPS")
Rel(jarela, google, "HTTPS")
Rel(jarela, cohere, "HTTPS")
Rel(jarela, mcp, "stdio / SSE")
Rel(jarela, mcpreg, "HTTPS (picker search)")
Rel(jarela, github, "HTTPS")
Rel(jarela, atlassian, "HTTPS (tools + RAG indexer)")
Rel(jarela, sqlite, "reads/writes")
Rel(jarela, extdir, "scans per request")See ARCHITECTURE.md for container, component, and sequence diagrams.
Development
npm run dev # hot-reload dev server on :3000
npm run build # production build (standalone output)
npm start # serve the standalone build on :4312
npm run lint # eslint
npm run test:live # live integration smoke tests
npm run test:live:full # extended live test suite
node scripts/gen-logo.mjs # regenerate the icon set from public/logo-source.pngTask runner
A make <target> wrapper is provided for both platforms:
- macOS / Linux: Makefile — uses GNU make (preinstalled on
macOS). System install uses launchd (
~/Library/LaunchAgents/com.jarela.app.plist). - Windows: make.ps1 (with a
make.cmdshim somake <target>works from cmd / PowerShell without GNU Make). System install uses Task Scheduler.
Targets are the same on both:
make help # list targets
make install # npm install
make dev # dev server
make build # production build
make start # serve standalone build
make lint
make test # live smoke tests
make icons # regenerate logo / icon set
make install-task # register auto-start (LaunchAgent on mac, Scheduled Task on Windows)
make start-task # / stop-task / restart-task
make logs # tail the installed-task log
make status # task state + listener on :4312 + data dir
make push # git push current branch -> jarela remote
make clean # remove .next + cachesInstalled-app paths differ by platform:
| Platform | Install dir | Log file |
|----------|----------------------------------------------|----------------------------------------|
| macOS | ~/Library/Application Support/Jarela | ~/Library/Logs/Jarela/app.log |
| Windows | %LOCALAPPDATA%\Programs\Jarela | %LOCALAPPDATA%\Jarela\logs\app.log |
Data dir (~/.jarela, configurable via JARELA_DB_DIR) is the same on both
and is shared between the dev repo and the installed copy.
Testing
The integration suite in scripts/live-test.mjs
boots the API and walks every public surface — agents, models, threads,
memory, scheduled tasks, bridges, MCP servers, connections, profile,
events / SSE, providers, pending actions, health — against a real running
server. LLM-dependent tests are gated behind --llm and skipped by default.
npm run test:live # no LLM keys required
npm run test:live -- --llm # also exercise model adapters end-to-end
npm run test:live:full # everything, including embedding round-tripThe orchestrator scripts/live-test-isolated.mjs
spawns its own next dev against a temp JARELA_DB_DIR so the suite
never touches your real ~/.jarela.
GitHub Actions runs .github/workflows/ci.yml
on every push and PR: lint + tsc --noEmit + next build, then the same
live integration suite against the production server output. The build
badge at the top of this README links straight to the latest run.
Recording a promo video
scripts/promo-record.mjs drives your real
local install (default http://localhost:4312) inside a 540×960
vertical (9:16) PWA viewport and records a .webm of a five-scene tour
in dark theme: a simulated PIN unlock, agent picker, a human-paced chat
turn, every side panel, and a closing pose.
npm run dev # in one terminal
npm run promo:record # in another — output lands in ./promo/The first run opens a headed Chromium so you can manually unlock the
install if needed; the resulting auth state is saved to
promo/.storage.json and reused on every subsequent run. Override the
target with JARELA_PROMO_URL, the chat line with JARELA_PROMO_MSG,
or skip the actual send with JARELA_PROMO_SKIP_CHAT=1.
Security
- CSRF / origin guard (lib/auth/access.ts)
rejects mutating requests whose
Origin/Sec-Fetch-Siteindicates a cross-origin caller. Defends against DNS-rebinding too. - Health redaction —
/api/v1/healthexposes liveness and acrypto.source(keychainorkeyfile), never the master key itself or any provider credential. - Secrets at rest — sensitive memory namespaces and OAuth tokens are
encrypted with an AES-GCM envelope keyed off a master key in the OS
keychain (or a fall-back
.secret-keyfile). See ADR-0005. - Local secret scanner (scripts/scan-secrets.mjs) refuses to push commits that contain obvious API-key shapes.
Documentation
All long-form docs live under docs/:
- docs/INSTALL.md — first-launch warnings, portable vs npm install paths, uninstall steps.
- docs/ARCHITECTURE.md — C4 container + component diagrams and the main request / streaming / tool-call sequence flows.
- docs/adr/ — Architecture Decision Records. Start at ADR-0001; recent ones worth skimming: ADR-0010 (agent-led setup), ADR-0015 (native GitHub tools), ADR-0017 (voice).
- docs/journal/ — narrative dev notes that don't rise to an ADR. See docs/journal/README.md for the conventions and the ADR-vs-journal split.
Decisions
Open a new ADR before adding a model provider, changing the persistence schema, or introducing a second process. Template lives at docs/adr/0000-template.md.
License
Licensed under the Apache License, Version 2.0. Derivative works and refactors must preserve the copyright/attribution notices and mark any modified files as changed (Apache-2.0 §4(b)–(c)).
