joyczl-agent
Health Pass
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 35 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Joy — a local-first personal assistant. Rust core (loop / memory / tools / graph / evals), TypeScript gateways + dashboard, Python thin SDK.
English | 简体中文
Joyczl-agent
A local-first personal assistant: Rust owns logic and state, TypeScript
covers the browser and chat gateways, Python stays a thin SDK. The state
store, reasoning, and tools all run on the user's own machine. Codename
Joy — the assistant's persona is Joy too.
Design principles
- Rust owns logic and state. The agent loop, memory, tools, and
state.db live in Rust; crates are split by responsibility (target <500
lines per module), with nojoy-coremega-crate. - The protocol has one definition.
joyczl-protocolis the single
source of truth for the cross-language contract: types generate the
TypeScript and Python bindings, and the artifacts are checked in. - Clients only translate. The CLI, dashboard, chat gateways, and Python
SDK are all clients ofjoy app-server(JSON-RPC over stdio); state.db is
opened by exactly one process.
Quick start
cd joy-rs && cargo build
JOY_PROVIDER=deepseek DEEPSEEK_API_KEY=… ./target/debug/joy # terminal REPL
JOY_PROVIDER=deepseek DEEPSEEK_API_KEY=… ./target/debug/joy dashboard # → http://localhost:7777
A turn over raw protocol:
ANTHROPIC_API_KEY=sk-… JOY_HOME=/tmp/joy-demo ./target/debug/joy app-server <<'JSONRPC'
{"jsonrpc":"2.0","id":1,"method":"turn/start","params":{"message":"Remember that Alex likes morning meetings"}}
JSONRPC
The response follows a notification stream: turnStarted → gateDecided → textDelta* → toolStarted → toolCompleted* → turnCompleted. turnCompleted.meta
records the gate ruling, tool timing, which model answered, and token usage —
persisted with the conversation.
What Joy can do
- Converse on the terminal, in the dashboard, or through chat gateways
(Telegram, Discord, WeChat, Lark — see docs/gateways.md) - Remember: semantic facts (FTS5, CJK-capable), dated episodes, and
procedural Skills — with a retrieval gate so memory is consulted only when
relevant (docs/architecture.md) - Act: 11 built-in tools plus MCP servers
(docs/configuration.md) - Plan as a graph: the triage front door answers small talk cheaply; the
gather workflow writes a morning brief
(docs/architecture.md) - Be examined: deterministic evals gate every release; a judge grades
answer quality (docs/testing.md) - Be observed: every turn lands a trace line and a usage row
(docs/architecture.md)
Built-in tools
| Tool | Purpose |
|---|---|
save_note / forget_note / search_memory / list_memory / manage_memory |
memory CRUD |
create_event / list_events |
calendar (idempotent, ICS + optional Apple Calendar sync) |
send_message |
message drafts into the outbox — never actually sends |
search_web |
DuckDuckGo HTML, or Tavily with TAVILY_API_KEY |
read_file |
read a text file: paginated (2000 lines / 50KB, never mid-line), line numbers, a continuation protocol, and a re-read short-circuit; on by default |
list_dir |
list a directory: directories first, files with sizes, 500-entry cap; on by default |
fetch_url |
fetch a page's text (HTML→plain, 50k cap); SSRF guard + external-content-is-data declaration |
Twelve LLM providers are pre-adapted in code (joyczl-provider), switchable at will: /model lists configured profiles, /model add ds deepseek deepseek-chat --env DEEPSEEK_API_KEY adds one, /model use ds switches — no need to retype the key, effective immediately, no restart. The dashboard's "Model" panel is the same implementation. Keys go to the OS keychain first, falling back to <home>/secrets.json (0600, plaintext) when there is none — the save response says which one it was, honestly. The old JOY_API_KEY / JOY_BASE_URL env vars are no longer read — configuration has exactly one path: models.json.
| fetch_url | fetch a page's text (HTML→plain, 50k cap); SSRF guard (blocks private/metadata targets, re-checks every redirect) + an external-content-is-data declaration |
| create_skill | persist an agreed workflow as procedural memory |
| job_output / job_list / job_kill | read, list and stop background jobs (started with run_command's background=true) |
| todo_write | keep a per-session todo list, re-injected every turn (whole-table replace; one in_progress at a time) |
| current_time | local time with weekday and timezone |
| run_command | shell commands — sandboxed, allowlisted, off unless JOY_EXEC=1 (SECURITY.md) |
| delegate_task | hand one self-contained job to a subagent — off unless JOY_DELEGATE=1, cannot delegate again; pass result_schema for a structured answer (retried once, then prose) |
Tool failures return to the model as text — the loop never crashes on one.
Documentation
| Document | Content |
|---|---|
| docs/architecture.md | principles, crate dependency graph, one turn's data flow, key mechanisms, design decisions |
| docs/protocol.md | the 13 JSON-RPC methods, notification sequence, error codes, clients |
| docs/configuration.md | every JOY_* variable, gateway variables, mcp.json, settings.json |
| docs/gateways.md | Telegram / Discord / WeChat / Lark setup, quirks, and security notes |
| docs/testing.md | the four-layer quality system and the latest test report |
| docs/operations.md | running every shape, backups, troubleshooting |
| docs/limitations.md | every known boundary — deliberate or not — with the file to open next |
| docs/internals/ | the tutorial: every crate's algorithms, data flow and invariants, chapter by chapter |
| docs/skills.md | skill authoring: format, triggers, install and export |
| CONTRIBUTING.md | engineering discipline and commit rules |
| SECURITY.md | the security model: credentials, capability boundaries, OAuth |
| CHANGELOG.md | changelog |
Roadmap
| Phase | Content |
|---|---|
| P0 ✅ | protocol layer + generation pipeline (63 types) |
| P1 ✅ | state (SQLite+FTS5), app-server (JSON-RPC over stdio), joy CLI |
| P2 ✅ | config / provider (12, incl. Ollama) / tools / loop / memory (gate + consolidation) |
| P2b ✅ | provider streaming, MCP, graph + triage front door |
| P3 ✅ | dashboard, Telegram / Discord / WeChat / Lark gateways (the latter three never connected to real platforms) |
| P4 ✅ | terminal REPL, turn/interrupt, config/write + model/list, full tool set, Skills, trace/usage, gather, MCP OAuth, evals + gate + judge |
| P5 ✅ | local inference (Ollama, no key), joy mcp serve (memory as an MCP server), Homebrew formula + Dockerfile |
| P6 ✅ | sandboxed execution behind JOY_EXEC (deny list + allowlist + seatbelt/bwrap), context compaction (rolling summaries), declarative schedules (joy schedule) |
| P7 ✅ | hybrid retrieval (JOY_EMBEDDINGS, RRF), temporary-statement filter, skill updates (joy skill update) |
| P8 ✅ | startup config validation, tool-argument schema checks, a stall guard, token budgets, sandboxed-and-offline execution, visible retries, output spilling, subagents, interactive approval, skill policy fields, memory kinds |
| P9 ✅ | tool-result budgets (line-aware spilling + observed-prefill calibration), subagent report scanning and result_schema, MCP circuit breaking and name de-duplication, memory write de-duplication, twelve lifecycle hooks (JOY_HOOKS), a todo list, background jobs, and a goal loop (/goal) |
| Next | distribution: PyPI wheel + npm shim; both unpublished |
Naming
| Purpose | Name |
|---|---|
| Repo / PyPI / crates.io | joyczl-agent |
| Rust crate prefix | joyczl- |
| CLI binary | joy |
| Persona | Joy |
| Env vars | JOY_* |
| State directory | .joy/ |
English | 简体中文 · Code is MIT.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found