Compare commits

..
971 Commits
Author SHA1 Message Date
github-actions[bot] a97c64c67b chore: update clone traffic data [skip ci] 2026-08-14 07:19:51 +00:00
Jon Saad-Falcon 64333651d1 Delete tools/pearl-reference-oracle directory 2026-08-13 19:27:37 -07:00
Elliot Slusky 9bee016c82 fix(server): preserve grounded agent stream content (#736)
* fix(server): preserve grounded agent stream content

* test(server): cover active grounded stream path

* fix(server): retain agent stream bridge
2026-08-13 18:15:36 -07:00
Elliot Slusky c9942961ad fix(evals): make TauBench dependency explicit (#739)
* fix(evals): make TauBench dependency explicit

* fix(evals): verify TauBench install provenance
2026-08-13 18:14:55 -07:00
Elliot Slusky c3a7ffebff fix(memory): recall auto-captured facts across sessions (#740)
* fix(memory): recall auto-captured facts

* fix(memory): harden recalled context injection

* fix(memory): preserve mixed context history

* fix(agents): preserve caller system context
2026-08-13 17:07:10 -07:00
Elliot Slusky b3c57468ae fix(cli): honor enabled tools when serving (#737)
* fix(cli): honor enabled tools when serving

* fix(server): preserve tools for streaming agents
2026-08-13 17:03:51 -07:00
Elliot Slusky ff69797135 fix(web): normalize tool call arguments (#738)
* fix(web): normalize tool call arguments

* fix(web): preserve chats when repair writeback fails
2026-08-13 17:03:10 -07:00
github-actions[bot] 465dba4b3f chore: update clone traffic data [skip ci] 2026-08-13 07:22:13 +00:00
github-actions[bot] 4f857b0abb chore: update clone traffic data [skip ci] 2026-08-12 07:20:09 +00:00
Jon Saad-FalconandClaude Opus 5 20aa08ef04 ci(desktop): preflight Apple notarization credentials before build (#724)
Notarization is the last thing tauri-action does, so any credential or
account-state fault surfaced ~10 minutes into the macOS job -- after the
Rust toolchain, npm install, two Ollama sidecar downloads and a universal
cargo build -- as one opaque line:

  failed to bundle project: failed codesign application: failed to
  notarize app: Error: HTTP status code: 403. ...

That message conflates three unrelated causes, and the signing step
succeeds in all of them, so the log actively misleads: the certificate is
clearly valid right up until the failure.

Add a read-only `notarytool history` call immediately after checkout. It
submits nothing and exercises the identical auth path, so all three
failures reach us in ~2s with the specific cause and fix named:

  401 invalid credentials  -> APPLE_PASSWORD is not an app-specific
                              password, or was minted under a different
                              Apple ID than APPLE_ID
  403 inaccessible team    -> APPLE_ID is not a member of APPLE_TEAM_ID
  403 required agreement   -> the Program License Agreement lapsed; only
                              the Account Holder can accept it

xcrun is preinstalled on macOS runners, hence placement before the
toolchain steps rather than beside "Configure Apple signing".

Skips cleanly when APPLE_CERTIFICATE is unset (unsigned builds never
notarize), mirroring the existing signing step, and errors when a
certificate is present but notarization secrets are missing -- previously
that combination signed successfully and then failed at the very end.
Transient network faults retry 3x; credential errors are deterministic
and exit on the first definitive answer.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:49:52 -07:00
github-actions[bot] 9a63561db8 chore: update clone traffic data [skip ci] 2026-08-11 07:02:06 +00:00
Elliot Slusky 7959285ad8 fix(cli): preserve extras during self-update (#718)
* fix(cli): preserve extras during self-update

* style(cli): format install detection
2026-08-10 17:22:29 -07:00
goatoush 26e1741059 fix(desktop): keep background streams out of active chat (#654) 2026-08-10 17:15:33 -07:00
kelliott-cloudandElliot Slusky 2ed885eb11 fix: never auto-select embed-only models for chat (#659)
* fix: never auto-select embed-only models for chat

Ollama lists nomic-embed-text alongside chat models. Auto-picking
models[0] / recommending the only available id selected the embedder
and every generation failed with HTTP 400 "does not support chat".

- Filter embed-only ids out of GET /v1/models (chat picker)
- Exclude them from /v1/recommended-model; return empty when none left
- Frontend setModels prefers chat models and clears a bad embed selection
- Regression tests for mixed, embed-only, and classifier cases

* fix: harden chat model capability filtering

---------

Co-authored-by: Elliot Slusky <elliot@slusky.com>
2026-08-10 17:06:48 -07:00
07fcf35276 fix: use a signal-free liveness probe for the daemon on Windows (#681)
* fix: use a signal-free liveness probe for the daemon on Windows

`_read_pid()` probed the recorded pid with `os.kill(pid, 0)`. That is a
POSIX idiom: on Windows signal 0 is `CTRL_C_EVENT`, so the call routes to
`GenerateConsoleCtrlEvent` rather than testing for existence, and raises
`OSError` (WinError 87, "The parameter is incorrect") for any pid that is
not a live console process-group leader — which includes both dead pids
and the detached server `jarvis start` creates.

That single call produced three symptoms. `jarvis status` propagated the
error and crashed. `_read_pid`'s `except OSError` swallowed it for a
running server, so `status` and `stop` reported "not running" and deleted
a live pid file. And because the probe *sends* a console control event
rather than merely asking, running `status` against the daemon could
terminate it.

Add `_pid_alive()`, which opens a process handle and checks it on Windows
and keeps the signal-0 probe on POSIX, and use it for both liveness
checks. `SIGKILL` in the stop path is now reached on Windows for the
first time, so guard it — it is POSIX-only, and `SIGTERM` already maps to
`TerminateProcess` there.

The existing round-trip test mocked `os.kill` to succeed, which is why
this passed CI on Linux while failing on every Windows run. Point it at
the new seam and add `TestPidLiveness`, which exercises real pids so the
platform behaviour is actually covered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* style: format daemon tests with CI Ruff

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Elliot Slusky <elliot@slusky.com>
2026-08-10 16:56:53 -07:00
Haotian Zheng 4efdb07dae fix(cli): use UTF-8 for config files 2026-08-10 16:44:17 -07:00
Elliot Slusky 7feda3dad3 fix(cloud): isolate Gemini stream metadata 2026-08-10 16:32:14 -07:00
Elliot Slusky f08ad574d3 fix(cloud): handle parallel Gemini tool calls 2026-08-10 16:32:14 -07:00
Dustin Zander 1bfc25a860 fix: preserve Gemini tool calls while streaming 2026-08-10 16:32:14 -07:00
Elliot Slusky 063dd8ea75 fix: unify managed-agent tool resolution (#705)
* fix: unify managed-agent tool resolution

* fix: harden managed-agent tool lifecycle
2026-08-10 15:09:41 -07:00
Elliot Slusky 6af9317556 fix: route configured LiteLLM models by engine ownership (#714) 2026-08-10 12:51:16 -07:00
Elliot Slusky 410562409d fix: make websocket bridge race cancellation-safe 2026-08-10 11:04:12 -07:00
Ari 9498adc7c4 fix: close ws_bridge send loop on client disconnect
Previously the ws_bridge send loop blocked forever on queue.get(),
never learning the client left. When the server service stopped,
uvicorn waited for open WebSocket tasks until systemd SIGKILLed
after TimeoutStopSec=90s. Now each iteration races recv+send; a
completed receive means the client disconnected => break the loop.
2026-08-10 11:04:12 -07:00
github-actions[bot] ebf370595d chore: update clone traffic data [skip ci] 2026-08-10 07:26:44 +00:00
Elliot Slusky bcdbf13d02 test(tools): cover eager deep-research registration 2026-08-09 22:59:45 -07:00
Ari 3dc621618f fix(tools): eager-import scan_chunks and knowledge_sql at package load
Every other built-in tool is imported here specifically to fire its
@ToolRegistry.register() decorator at package-load time; these two
were missing, so their test_registered tests only passed when some
unrelated test (via agent_manager_routes.py, channels_cmd.py, or
deep_research_setup_cmd.py) happened to import the module first in
the same process. Under pytest-xdist that's worker-distribution
dependent, so adding an unrelated test file could flip either test
from pass to fail.
2026-08-09 22:59:45 -07:00
github-actions[bot] fd0b60fefc chore: update clone traffic data [skip ci] 2026-08-09 06:51:50 +00:00
github-actions[bot] 95a9857984 chore: update clone traffic data [skip ci] 2026-08-08 06:46:50 +00:00
github-actions[bot] f9c89308fc chore: update clone traffic data [skip ci] 2026-08-07 07:10:58 +00:00
Elliot Slusky 65d08e9d94 Fix proactive cron reconciliation and notifications 2026-08-06 13:40:00 -07:00
Loma 45717780fa Fix proactive agent cron duplicating on every server restart
register_cron() ran unconditionally on every 'jarvis serve' startup and
create_task() persists to scheduler.db, so each restart added another
copy of the daily proactive cron. On a real install 68 duplicates
accumulated; when due they fired back-to-back and monopolized the
single-slot local inference queue, stalling interactive chat.

register_cron() is now idempotent: an existing active task is reused and
surplus duplicates are cancelled. Also stop the notification channel
from calling connect() — a second getUpdates poll loop on the same bot
token makes Telegram return Conflict and kills the main listener.
2026-08-06 13:40:00 -07:00
goatoush 9da7c30880 Fixing text wrap for agent creator tool selector (#655) 2026-08-06 11:23:27 -07:00
github-actions[bot] 98e791f258 chore: update clone traffic data [skip ci] 2026-08-06 08:31:13 +00:00
Elliot Slusky b9e0928aef fix(telemetry): record cloud inference cost (#704) 2026-08-05 20:51:06 -07:00
Elliot Slusky 652a522e50 fix(config): deserialize skill source tables (#703) 2026-08-05 20:50:42 -07:00
github-actions[bot] ce1a9ce133 chore: update clone traffic data [skip ci] 2026-08-05 08:31:05 +00:00
github-actions[bot] ae45a4f67c chore: update clone traffic data [skip ci] 2026-08-04 08:31:51 +00:00
github-actions[bot] 697eed23d4 chore: update clone traffic data [skip ci] 2026-08-03 09:38:25 +00:00
github-actions[bot] 100595f8aa chore: update clone traffic data [skip ci] 2026-08-02 08:20:07 +00:00
github-actions[bot] dd03a55028 chore: update clone traffic data [skip ci] 2026-08-01 08:15:52 +00:00
github-actions[bot] a72218f99f chore: update clone traffic data [skip ci] 2026-07-31 08:45:27 +00:00
github-actions[bot] eaa76032d5 chore: update clone traffic data [skip ci] 2026-07-30 08:21:39 +00:00
CurryrajandClaude Opus 5 ed01ab8c8d fix: proxy WebSocket upgrades to the API in dev (#692)
The Vite dev proxy forwards `/v1` as plain HTTP with no `ws: true`, so the
WebSocket upgrade for `/v1/agents/events` is never proxied. The socket does not
open, does not error, and does not close — it just sits silent — so every live
agent view is empty under `npm run dev` while working in a production build,
where the frontend is served from the same origin as the API.

That covers the per-agent live trace on the Agents page, which subscribes via
`useAgentEvents` (`frontend/src/lib/useAgentEvents.ts`).

The silence is what makes it costly: with no error to see, it reads as "no
events are being emitted" rather than "the transport never connected", so the
search starts on the server side.

Verified on Windows 11 with `jarvis start` running: before, a
`new WebSocket('ws://localhost:5173/v1/agents/events')` from the dev page never
fired open, error or close within 6s. After, it opens, and a real agent tick
delivers 8 events (agent_tick_start, inference_start/end, tool_call_start/end,
agent_tick_end).

`changeOrigin` is set alongside so the upgrade request carries the target's
host, which some setups require when the API is not on localhost.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 12:04:41 -07:00
CurryrajandClaude Opus 5 a65f663d2e fix: stop SessionStore treating ":memory:" as a file path (#684)
``SessionStore.__init__`` passed its ``db_path`` straight to
``secure_create()``, which touches the path and chmods it. ``:memory:`` is
a SQLite sentinel, not a filename, so this tried to create a file literally
named ``:memory:``.

On Windows ``:`` is illegal in a filename, so construction raised
``OSError: [Errno 22] Invalid argument: ':memory:'`` and the two
``tests/server/test_channel_bridge_deep_research.py`` tests failed there.
Elsewhere it succeeds and is merely wrong: it leaves a stray ``:memory:``
file in the working directory, and because ``Path(":memory:").parent`` is
``.``, ``secure_mkdir`` chmods the working directory itself to 0o700.

``KnowledgeStore``, ``TelemetryStore`` and ``TraceStore`` already guard this
exact case; ``SessionStore`` was the one store missing the check. Apply the
same guard, with the same comment.

Adds two regression tests: one that an in-memory store is usable, one that
constructing it creates no file. The second fails on every platform without
the fix, so the bug cannot silently return on Linux or macOS.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 10:44:18 -07:00
Elliot Slusky c6382f2473 fix web search setup in browser quickstart (#691) 2026-07-29 08:38:04 -07:00
github-actions[bot] 2922a2b154 chore: update clone traffic data [skip ci] 2026-07-29 08:35:42 +00:00
Elliot Slusky 403dec8e98 Merge pull request #687 from Curryraj/fix/windows-daemon-detach
fix: detach the daemon from its console on Windows
2026-07-28 23:34:01 -07:00
Elliot Slusky 2c7cf6118c test: make Windows daemon test cross-platform 2026-07-28 23:27:25 -07:00
CurryrajandClaude Opus 5 2bdd860b54 test: tolerate a 503 memory backend in the route-wiring tests (#685)
``TestMemoryRoutes.test_search`` and ``test_stats`` assert the status code
is in ``(200, 500)``. That list dates from the initial commit; #527 later
made the memory routes raise 503 when the native ``openjarvis_rust``
extension is missing, so both tests now fail on any checkout where the
extension has not been built — which is every contributor who has not run
``maturin develop``.

The failure is spurious: these two tests only check that the routes are
wired up, and their own comment ("May fail if SQLite not set up, that's
ok") says an unavailable backend is tolerated. 503 is exactly that case,
and it is already asserted deliberately in ``TestMemoryRustMissing``
directly below.

Add 503 to the tolerated set via a named constant, so the reason is stated
once rather than repeated as a bare literal.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:17:25 -07:00
Robby Manihani 81f1ffbb4f docs: document how to give an agent OS-level access (#689)
Someone asked in Discord how to give Jarvis access to their whole
machine and hit a confusing failure. With no ~/.openjarvis/config.toml,
tools.enabled and agent.tools both come back empty, and SystemBuilder
builds the agent with no tools at all. It reads like a permissions
problem but it's just missing config, and nothing in the docs points
you anywhere useful.

Adds docs/user-guide/system-access.md, covering the empty tool list as
the usual cause, what shell_exec and the file tools actually reach,
which entry points prompt for confirmation and which quietly
auto-approve, Full Disk Access on macOS and which process needs it, and
the fact that there's no computer use at all, so Accessibility and
Screen Recording grants buy you nothing on their own.

Also adds a full-system-access.toml example to copy from.

Fixes two config tables that describe security.enforce_tool_confirmation
as requiring confirmation before tools run. The loader accepts the key
but nothing on the execution path reads it, so anyone setting it gets
assurance they don't actually have.
2026-07-28 20:11:27 -07:00
github-actions[bot] 93fc7b9e77 chore: update clone traffic data [skip ci] 2026-07-28 08:29:18 +00:00
Jaiydaan RajandClaude Opus 5 9fc5b875d1 fix: detach the daemon from its console on Windows
``jarvis start`` spawned the server with ``start_new_session=True``. That is
POSIX-only — CPython's Windows ``_execute_child`` names the parameter
``unused_start_new_session`` and ignores it — so on Windows the server
inherited the launching console instead of detaching from it.

Closing that console, or logging off, therefore delivered CTRL_CLOSE_EVENT
to the server. Observed in the wild as the daemon dying overnight, with

    forrtl: error (200): program aborting due to window-CLOSE event

in server.log (the Fortran runtime under NumPy handles the event and
aborts). ``jarvis start`` looked like it worked: it printed a PID, wrote the
pid file and exited 0, and the server ran for as long as the console stayed
open. Registered as a log-on scheduled task, this means the machine comes
back up with no backend.

Pass DETACHED_PROCESS on Windows so the child gets no console at all, plus
CREATE_NEW_PROCESS_GROUP so a Ctrl-C in the parent console cannot reach it.
POSIX keeps start_new_session.

Verified by attaching to each spawned process with AttachConsole():
start_new_session=True attaches successfully (the child shares a console);
DETACHED_PROCESS fails with ERROR_INVALID_HANDLE (no console exists).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 13:17:06 +08:00
github-actions[bot] 08279e6b99 chore: update clone traffic data [skip ci] 2026-07-27 09:46:21 +00:00
github-actions[bot] a7c31e89b4 chore: update clone traffic data [skip ci] 2026-07-26 08:22:36 +00:00
github-actions[bot] 04014c658a chore: update clone traffic data [skip ci] 2026-07-25 08:02:21 +00:00
github-actions[bot] c1238d3e7e chore: update clone traffic data [skip ci] 2026-07-24 08:23:24 +00:00
github-actions[bot] 687e80a55a chore: update clone traffic data [skip ci] 2026-07-23 08:25:07 +00:00
github-actions[bot] b90fd01af2 chore: update clone traffic data [skip ci] 2026-07-22 08:24:05 +00:00
github-actions[bot] bbe7df7d33 chore: update clone traffic data [skip ci] 2026-07-21 08:23:44 +00:00
Arush WadhawanandElliot Slusky 9685b9b78f fix(telemetry): enable WAL and batch writes in TelemetryStore to avoid SQLITE_BUSY under concurrency (#597)
TelemetryStore opened SQLite without WAL, so concurrent readers (server, aggregator, dashboard) hitting the database under inference load raised SQLITE_BUSY, and every insert committed immediately, paying fsync on each record.

- Enable PRAGMA journal_mode=WAL with synchronous=NORMAL and busy_timeout=5000, matching TraceStore.
- Batch inserts in memory under a lock and flush via executemany() when a batch reaches batch_size (default 50), when a batch goes stale, on any read through the store, and on close().
- Run a background flusher thread (default 5s interval) so a partial batch written just before traffic stops still becomes visible to other connections; close() stops the thread with an ordering that prevents touching a closed connection.
- Tests cover batching deferral, read-triggered flushes, stale-batch flushes, and close() behavior.

Fixes #560

Co-authored-by: Elliot Slusky <elliot@slusky.com>
2026-07-20 14:11:10 -07:00
goatoush aa2d127de4 fix(frontend): remove jitter when scrolling up during chat autoscroll (#646)
The chat area re-armed autoscroll whenever the user was within 100px of the bottom, so scrolling up during a streaming response fought the incoming content ticks and produced jitter.

Autoscroll now disengages on any upward scroll (direction-based, no distance threshold), re-engages when scrolled back within 2px of the bottom (tolerating sub-pixel rounding at fractional zoom levels, where the at-bottom residual can reach 1px), and ignores sub-1px upward movement so macOS elastic-bounce settling does not disengage it. Sending a message pins the view to the bottom even if the user had scrolled up to read earlier messages.
2026-07-20 13:45:12 -07:00
github-actions[bot] 87f6238338 chore: update clone traffic data [skip ci] 2026-07-20 08:53:13 +00:00
github-actions[bot] 452bcc38cf chore: update clone traffic data [skip ci] 2026-07-19 08:12:04 +00:00
goatoush b35a4c8113 fix(desktop): preserve active chat when switching models (#648)
Switching models from the command palette called createConversation() on every change, creating a persisted empty "New chat" entry and pulling the user out of their active conversation. Because updateLastAssistant writes the visible messages array without checking the active conversation, a mid-stream switch could also clobber the new chat's view with the old conversation's messages.

Remove the conversation-creation side effect. Model switching now preserves the active chat (matching the pull-completion and delete-fallback paths, which already switched silently); the next request uses the newly selected model with the current conversation context. Preloading, loading state, and logging are unchanged.
2026-07-18 12:51:38 -07:00
github-actions[bot] f001e3b0ca chore: update clone traffic data [skip ci] 2026-07-18 07:44:44 +00:00
Cesar Schneider b6dba93ae5 fix: make pytest suite hermetic against local dev-machine state (#647)
Two test-isolation fixes: (1) an autouse conftest fixture sets OPENJARVIS_NO_UPDATE_CHECK=1 so the CLI's PyPI update-check banner (stderr, merged into CliRunner output) can never pollute JSON/CSV-parsing CLI tests on local runs; CI was already covered by CI=true. (2) test_dense.py's Ollama skip-guard now queries /api/tags and requires nomic-embed-text to be pulled instead of a bare TCP connect, so machines running Ollama without the embed model skip instead of erroring. The probe normalizes all documented OLLAMA_HOST forms (full URL, host:port, bare host) and the Ollama-backed tests construct DenseMemory against that same endpoint rather than the embedder's hard-coded localhost default, with unit tests covering the probe. Related: #645.
2026-07-17 13:21:36 -07:00
github-actions[bot] 3000116d18 chore: update clone traffic data [skip ci] 2026-07-17 08:05:34 +00:00
Elliot Slusky 9db21d37ef style: apply Ruff formatting to recently merged tests (#644)
Fix the Ruff formatter check on main by formatting tests changed in #639 and #640 with the repository's pinned Ruff 0.15.1. No behavior change.
2026-07-16 18:13:36 -07:00
Elliot Slusky 95480363b7 style(skills): wrap importer manifest parse call (#643)
Fix the Ruff E501 failure on main introduced during the #639 fix-up. The call was 89 characters against the repository's 88-character limit. No behavior change.
2026-07-16 18:07:44 -07:00
CurryrajandElliot Slusky 4419b76412 fix: catch ImportError in git tools, fall back to CLI when Rust ext missing (#636)
get_rust_module() was called outside the try block in GitStatusTool/GitDiffTool/GitLogTool.execute(), so on installs without the compiled openjarvis-rust extension (e.g. plain pip installs, where openjarvis-rust is a uv-only group since #624) the ImportError escaped uncaught instead of degrading. Move the call inside try and fall back to the git CLI via the existing _run_git helper on ImportError, matching the fallback git_log already had. Adds regression tests covering the fallback path for all three tools.

Co-authored-by: Elliot Slusky <elliot@slusky.com>
2026-07-16 17:59:25 -07:00
Jon Saad-FalconandClaude Opus 4.8 99bbc2054a Add arXiv badge to README (#642)
Add a red arXiv badge linking to the OpenJarvis paper (2605.17172) as
the first item in the header badge row, matching the style used on the
Intelligence-Per-Watt repo.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:42:20 -07:00
Jon Saad-FalconandClaude Opus 4.8 d5d8fddc94 Fix leaderboard savings lookup after provider-key rename (#635)
PR #634 renamed the Anthropic cost-comparison provider key
`claude-opus-4.6` -> `claude-fable-5` but missed one consumer:
App.tsx looks up the Anthropic entry by that key to compute the
`dollar_savings` value submitted to the leaderboard. After the rename
`per_provider.find(p => p.provider === 'claude-opus-4.6')` returned
undefined, so this path silently submitted dollar_savings = 0.

Point the lookup at the new key.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:33:38 -07:00
jaaaaorr06 cadb3e2ae6 feat(skills): enforce capability/trust-tier checks at install and run time (#639)
Wires the previously-dead skills/security.py checks into two places. SkillImporter.import_skill() classifies trust tier before writing to disk, refuses unreviewed skills requesting dangerous capabilities unless confirmed (confirm_dangerous=True, or --yes-dangerous on skill install/sync), and persists tier/capabilities to the .source sidecar. SkillExecutor.run() gains opt-in capability enforcement: allowed_capabilities=None (the default, used by all existing call sites) means no policy; passing a set blocks skills whose required_capabilities are not covered before any step runs. Bulk sync reports refused skills instead of silently skipping them. Both enforcement points are covered by tests.
2026-07-16 17:23:27 -07:00
Trevor Willard 7dc904c1b2 fix: wire persona files into default chat endpoint, fix FTS5 apostrophe crash (#637)
Two fixes: (1) /v1/chat/completions now builds its injected system prompt via SystemPromptBuilder, so SOUL.md/MEMORY.md/USER.md persona files apply to the OpenAI-compatible endpoint exactly as they do on the managed-agent path; injection still only happens when the client omits a system message. (2) FTS5 query tokens are now split on any non-alphanumeric character, so apostrophes (user's) and quotes can no longer reach the MATCH string unescaped; all-punctuation queries return empty instead of erroring.
2026-07-16 17:10:51 -07:00
ScottyAmr d9725fbb6a fix(speech): close temp file before transcribing to fix Windows EACCES in faster-whisper backend (#638)
faster-whisper's transcribe() was handed the path of a still-open NamedTemporaryFile; on Windows the open handle is exclusive, so PyAV's reopen failed with EACCES and local STT was broken. Switch to delete=False, close before transcribing, and unlink in a finally. The write is wrapped in the file's context manager so the handle closes even if write() raises, and unlink failures are logged at debug.
2026-07-16 17:09:45 -07:00
Syed Osama Ali ShahandElliot Slusky 23f04264f9 fix(knowledge_sql): match write keywords on word boundaries (allow valid SELECTs) (#640)
* fix(knowledge_sql): match write keywords on word boundaries

The read-only guard rejected a query if any of DROP/DELETE/INSERT/UPDATE/
ALTER/CREATE/ATTACH appeared as a bare substring of the uppercased text. That
wrongly blocks valid SELECTs whose column/alias/literal merely contains one --
e.g. "deleted_at" (DELETE), "created_at" (CREATE), "updated_content" (UPDATE).
The knowledge_chunks table actually has deleted_at/created_at columns and the
store's own retrieval filters on "WHERE deleted_at IS NULL", so realistic
read queries were refused. Match on word boundaries with a compiled regex,
mirroring the sibling tool db_query.py. Add a regression test.

* fix(knowledge_sql): ignore string literals in keyword scan, broaden error handling

- Strip single-quoted literals before the forbidden-keyword scan so
  SELECTs whose data merely mentions a write keyword (e.g. LIKE
  '%delete%') are not rejected.
- Catch sqlite3.Error instead of only OperationalError so multi-
  statement strings return a failed ToolResult instead of raising.
- Document created_at/deleted_at in the tool's schema description.

---------

Co-authored-by: Elliot Slusky <elliot@slusky.com>
2026-07-16 16:36:31 -07:00
github-actions[bot] 8b59eb87e0 chore: update clone traffic data [skip ci] 2026-07-16 08:08:14 +00:00
github-actions[bot] 2e68e227b7 chore: update clone traffic data [skip ci] 2026-07-15 08:04:26 +00:00
github-actions[bot] fc98614437 chore: update clone traffic data [skip ci] 2026-07-14 07:59:08 +00:00
github-actions[bot] b1c5aba6fd chore: update clone traffic data [skip ci] 2026-07-13 09:20:55 +00:00
Jon Saad-FalconandClaude Opus 4.8 6240c59ca3 Update cost-comparison models: GPT-5.6 Sol, Claude Fable 5 (#634)
Refresh the cost-comparison / savings surfaces to current frontier cloud
pricing (per 1M tokens):

- OpenAI:    GPT-5.3 ($2/$10)        -> GPT-5.6 Sol ($5/$30)
- Anthropic: Claude Opus 4.6 ($5/$25) -> Claude Fable 5 ($10/$50)
- Google:    Gemini 3.1 Pro ($2/$12)  -> unchanged

Internal provider keys are renamed in lockstep (gpt-5.3 -> gpt-5.6-sol,
claude-opus-4.6 -> claude-fable-5) across the canonical CLOUD_PRICING
dict, the two server-rendered HTML pages, and the frontend color/label
maps so backend, dashboard, and UI stay consistent. Energy/FLOPs
metadata is carried over unchanged. Model catalog and eval configs are
untouched (real model/benchmark entries, not the cost comparison).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:48:22 -07:00
github-actions[bot] 9f3c7fd086 chore: update clone traffic data [skip ci] 2026-07-12 08:12:51 +00:00
github-actions[bot] 657c8dd26b chore: update clone traffic data [skip ci] 2026-07-11 07:51:15 +00:00
github-actions[bot] 4ef296e9d0 chore: update clone traffic data [skip ci] 2026-07-10 09:30:29 +00:00
github-actions[bot] d5d06ca0e5 chore: update clone traffic data [skip ci] 2026-07-09 09:39:27 +00:00
github-actions[bot] 213ee4ff7e chore: update clone traffic data [skip ci] 2026-07-08 08:23:57 +00:00
Jon Saad-FalconandClaude Opus 4.8 215ab76e5f docs: point the Project Site link to openjarvis.stanford.edu (#628)
The project's canonical site moved from the Scaling Intelligence Lab blog
(scalingintelligence.stanford.edu/blogs/openjarvis/) to
https://openjarvis.stanford.edu/. Update every reference to that URL:

- README: the "Project" badge and the "Project Site" link
- docs/index.md: the research write-up link
- desktop Settings: the "Project site" link (SettingsPage.tsx)
- the Twitter-bot operator prompt

The bare Scaling Intelligence Lab homepage links (the lab itself, not the
project site) are intentionally left unchanged, as are the github.io
documentation and installer URLs.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 13:27:32 -07:00
github-actions[bot] 0812ee0701 chore: update clone traffic data [skip ci] 2026-07-07 09:36:44 +00:00
talyaseenandClaude Opus 4.8 0b140110d8 Make OpenAI-compat and Ollama engine streaming truly async (#626)
The OpenAI-compat and Ollama engines exposed stream()/stream_full() as async def but iterated a synchronous httpx.Client.iter_lines() internally, blocking the single event loop on every inter-token read (serializing concurrent chats; one wedged upstream read froze the whole API). Convert both to a shared AsyncHTTPEngineMixin using httpx.AsyncClient + aiter_lines() with a per-event-loop pooled client and the configured timeout applied; map mid-stream transport errors (RemoteProtocolError/ReadError) to EngineConnectionError via a deliberately narrow set that keeps CancelledError/GeneratorExit propagating; handle non-2xx explicitly (incl. 3xx and a typed EngineContextLengthError for context-window overflow 400s); switch litellm streaming to acompletion; and offload the blocking non-streaming handlers and websocket generate() to asyncio.to_thread. No public API change. Strong MockTransport-based tests, including a pin that the async path never touches the sync client. Complements #618.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:10:22 -07:00
github-actions[bot] 2623f9e0f4 chore: update clone traffic data [skip ci] 2026-07-06 10:14:22 +00:00
github-actions[bot] d454c41500 chore: update clone traffic data [skip ci] 2026-07-05 08:49:16 +00:00
github-actions[bot] e3fb816d12 chore: update clone traffic data [skip ci] 2026-07-04 08:33:11 +00:00
github-actions[bot] 3486f27357 chore: update clone traffic data [skip ci] 2026-07-03 08:58:35 +00:00
Elliot SluskyandClaude Opus 4.8 928776a71c ci: enforce ruff format in CI, add Makefile matching the CI test lane (#625)
CI's lint job ran ruff check but never ruff format --check, letting format drift land silently (79 files had drifted from the pinned ruff 0.15.1). Add the ruff format --check step to ci.yml, reformat the 79 drifted files with the pinned ruff (mechanical only — verified AST-identical to before across all files, no logic changes), and add a Makefile whose test target mirrors the actual CI lane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 14:49:18 -07:00
github-actions[bot] 2c2a4b6ae4 chore: update clone traffic data [skip ci] 2026-07-02 07:10:21 +00:00
26bc7efb09 fix(packaging): make openjarvis-rust a uv-only group to unblock pip install openjarvis[desktop] (#624)
* fix(packaging): make openjarvis-rust a uv-only dependency group (unblock pip[desktop])

#615 added openjarvis-rust to the published `desktop` extra so
`uv sync --extra desktop` builds the native PyO3 extension for the desktop
app. But openjarvis-rust is not on PyPI and `[tool.uv.sources]` is stripped
from published wheel metadata, so `pip install openjarvis[desktop]` from PyPI
failed at install trying to resolve openjarvis-rust from PyPI (#584).

Move openjarvis-rust into a uv `desktop-native` dependency group (PEP 735 —
excluded from wheel metadata) and sync it in the desktop app via
`uv sync --group desktop-native`. The extension is still built from the local
path source; only the published metadata changes.

Verified: the built wheel no longer lists openjarvis-rust in any Requires-Dist
(nowhere in the metadata), and uv.lock still resolves it from the local path
source under the group. Adds tests/deployment/test_packaging.py to guard the
split (not in the published extra, present in the group, path source, and the
desktop app syncs the group).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(packaging): sync native group in install paths

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Elliot Slusky <elliot@slusky.com>
2026-07-01 13:02:13 -07:00
github-actions[bot] f3954e087a chore: update clone traffic data [skip ci] 2026-07-01 07:33:10 +00:00
Elliot SluskyandClaude Opus 4.8 d865b4bed4 Fix blocking async server handlers (#618)
Closes #219. Replace synchronous httpx calls in async SendBlue and model-management handlers with awaited httpx.AsyncClient (context-managed close); run Whisper transcription and engine.list_models via asyncio.to_thread so they don't block the event loop; and harden TelemetryStore/aggregator SQLite for concurrency (WAL, synchronous=NORMAL, busy_timeout=5000, plus a write-serializing lock on the shared connection). Adds async-usage assertions and a real 8-thread concurrent-write test. Related: #570 (async httpx, different issue #559).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 17:37:20 -07:00
Elliot SluskyandClaude Opus 4.8 c686517cc7 Fix upcoming Google Calendar event retrieval (#617)
Closes #388. Parse Google Calendar all-day events from start.date instead of stamping them with the current time; treat generic next/upcoming calendar-event queries as gcalendar timeline requests that return nearest-future events first (UTC-normalized, instant-aware comparison that handles tz offsets and all-day events); and update the research planner guidance to route such queries with sources=[gcalendar] + a today-onward time_range. Real in-memory KnowledgeStore integration tests cover ordering, tz normalization, all-day inclusion, and source narrowing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:18:09 -07:00
Elliot SluskyandClaude Opus 4.8 904133cb25 fix(research): respect configured engine for Deep Research (#616)
Fixes #575. Web Deep Research was hardcoded to OllamaEngine + DEFAULT_PLANNER_MODEL, ignoring the user's configured/active engine and model. Resolve the planner from [deep_research] override -> live app chat engine + selected model -> config defaults -> legacy Ollama, pass the chat picker's model from the frontend into /api/research, record the actual planner engine in telemetry, and refuse to silently fall back to a different engine (raise an actionable error instead). Adds config support and focused tests for resolution and the route. Related: #576 (duplicate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 13:46:18 -07:00
Elliot SluskyandClaude Opus 4.8 299dee1f40 fix(desktop): verify Rust extension before server startup (#615)
Fixes #505. Make the desktop backend resilient to a missing/unbuilt openjarvis_rust extension: declare openjarvis-rust as a uv-managed desktop path dependency so 'uv sync --extra desktop' owns the PyO3 build (instead of pruning an undeclared package); add ~/.cargo/bin to the subprocess PATH and fail early with Rust / Windows Build Tools guidance when the toolchain is missing; verify 'import openjarvis_rust' before starting jarvis serve; and add a TCP bind preflight for port 8000 to catch non-HTTP listeners the /health probe can't classify. Includes the uv.lock entry for the new path dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 13:24:24 -07:00
github-actions[bot] 44ff286005 chore: update clone traffic data [skip ci] 2026-06-30 07:21:15 +00:00
Gilbert Barajas be51eb8684 docs(user-guide): document SOUL/MEMORY/USER.md persona files (#604) (#610)
* docs(user-guide): document SOUL/MEMORY/USER.md persona files (#604)

The persistent-memory showcase links to the User Guide: Agents page for how
SOUL.md / MEMORY.md / USER.md are loaded at conversation start, but that page
never covered them (site search for the filenames returns nothing).

Add a "Persistent Persona" section to user-guide/agents.md: the three files and
what each holds, where they live (config dir + [memory_files]), how they load
(after the agent template, cached per conversation, per-section truncation),
named personas (--persona / personas/<name>/), and editing by hand or via the
memory_manage / user_profile_manage tools. Cross-links the distinct retrieval
memory backend to resolve the reporter's confusion.

Closes #604

* docs(user-guide): clarify memory_manage/user_profile_manage target the default MEMORY.md/USER.md
2026-06-29 17:38:22 -07:00
Elliot Slusky 420908401c fix(engine): count tool call payloads in token estimates (#614)
Closes #608. Make Message.content officially Optional (str | None) with a Message.text accessor that treats None as empty, and count tool-call IDs/names/arguments, tool-result IDs, and reasoning/thinking metadata in estimate_prompt_tokens — all are replayed into later prompt turns, so they belong in the estimate. Extends estimator and message-type regression tests.
2026-06-29 17:34:10 -07:00
Elliot Slusky b70be55681 fix(openhands): handle none content in token estimates (#612)
Closes #607. Assistant tool-call turns can carry content=None, which crashed token estimation (len(m.content)) and think-tag stripping. Normalize with 'content or ""', route native OpenHands truncation through the shared estimate_prompt_tokens, and add tests for the estimator, the truncation helper, and an end-to-end tool-call run with None content.
2026-06-29 16:51:52 -07:00
Elliot Slusky a0187e40e6 Fix desktop startup fallback to installed Ollama models (#611)
Addresses #605. Prefer an already-installed Ollama model before attempting a startup download (matching the requested tag, else a preferred non-embedding installed model); fall back through installed -> FALLBACK_MODEL -> error, reusing installed models at each failure point; persist the resolved model only for first-run/default so an explicit user choice is never overwritten. Refactors the model logic into testable helpers with unit coverage.
2026-06-29 16:51:49 -07:00
Jon Saad-FalconandClaude Opus 4.8 d32f20f9b3 fix(desktop): align @tauri-apps npm packages with the 2.11 Rust crate (#613)
Desktop release builds failed on all platforms with 'Found version mismatched Tauri packages' because @tauri-apps/api and @tauri-apps/cli were pinned at 2.10.1 while the tauri Rust crate resolved to 2.11.3. Bump both npm packages to the 2.11 line (api 2.11.1, cli 2.11.4) so they share the crate's major.minor. Plugins were already aligned.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 16:51:46 -07:00
github-actions[bot] 0b552cbcb5 chore: update clone traffic data [skip ci] 2026-06-29 07:46:59 +00:00
github-actions[bot] 19fd3c8d2b chore: update clone traffic data [skip ci] 2026-06-28 07:24:04 +00:00
Jon Saad-FalconandClaude Opus 4.8 1fa80d8ecd fix(docs): wire the savings leaderboard's Supabase anon key into the docs build (#596)
The docs-site leaderboard (docs/javascripts/leaderboard.js) reads the public
Supabase anon key from window.OPENJARVIS_SUPABASE_ANON_KEY, but nothing set it,
so the published leaderboard always rendered "Leaderboard not configured yet".

Add a generated config file (leaderboard-config.js) loaded before
leaderboard.js that supplies the global, and inject its value at docs-build
time from the existing VITE_SUPABASE_ANON_KEY repo secret. The committed
default is empty, so local `mkdocs build` and fork PRs (no secret) degrade
gracefully. The anon key is public by design (Supabase RLS protects the data).

- docs/javascripts/leaderboard-config.js: empty-default global declaration.
- mkdocs.yml: load leaderboard-config.js before leaderboard.js.
- docs.yml: write the config from the secret (read via env, JSON-encoded into a
  JS string literal to avoid injection) before `mkdocs build`.
- tests/deployment/test_docs_leaderboard.py: guard the wiring + load order.

Verified with a local `mkdocs build`: the generated config ships in site/ and
loads before leaderboard.js.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 15:49:18 -07:00
github-actions[bot] b3f90691bf chore: update clone traffic data [skip ci] 2026-06-27 07:07:41 +00:00
github-actions[bot] 4ebf0839e7 chore: update clone traffic data [skip ci] 2026-06-26 07:21:03 +00:00
github-actions[bot] b1e93d4ed0 chore: update clone traffic data [skip ci] 2026-06-25 07:14:52 +00:00
Elliot SluskyandClaude Opus 4.8 eb2b612c7c fix(memory): address service follow-ups (#591)
Closes #582. Route fact-store construction through a new FactStoreRegistry (local backend registered by default); align the default facts path with get_config_dir(); wire completed chat exchanges (streamed and non-streamed) through the EventBus so the memory service captures them consistently; reload the local fact store from disk before operations so external clears don't resurrect stale facts; make the affected config/persona/memory/CLI/route tests hermetic; and refresh uv.lock with the current resolver (locks pytest-xdist + transitive deps, drops py3.14 artifacts since the project constrains Python <3.14).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:58:49 -07:00
Elliot SluskyandClaude Opus 4.8 560ec860df fix(docker): build native Rust extension into images (#590)
Build and install the mandatory openjarvis_rust wheel in the CPU, NVIDIA, ROCm, and sandbox Docker images. Rust 1.88 (matching the workspace MSRV / rust-toolchain.toml) and maturin are installed only in the builder stage, the module's import is verified during the build, and maturin is removed before the runtime artifacts are copied so build tooling never ships. The frontend leaderboard anon key is an optional empty-by-default build arg (post-#589), so default images cleanly disable the leaderboard. Adds static deployment coverage for the native build path. Closes #584.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:27:17 -07:00
Jon Saad-FalconandClaude Opus 4.8 e7c46c1985 fix(frontend): make Supabase anon key optional to unblock PyPI publishing (#589)
PyPI publishing had been broken since v1.0.3.dev851: #587 made VITE_SUPABASE_ANON_KEY a hard build-time requirement, but no such secret exists, so the frontend build aborted every publish run before the PyPI upload. Decouple package buildability from the leaderboard credential: a missing anon key now disables the savings leaderboard at runtime instead of failing the build, and auto-enables when the secret is provided. Verified: npm run build with the key unset succeeds; tsc + vitest pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:15:06 -07:00
github-actions[bot] 00d1e39b6d chore: update clone traffic data [skip ci] 2026-06-24 07:14:44 +00:00
Elliot SluskyandClaude Opus 4.8 843375d6ef Fix Supabase frontend build env for release builds (#588)
Follow-up to #587. Pass VITE_SUPABASE_ANON_KEY into the frontend builds of both release paths: the PyPI publish workflow (wheel-bundled frontend) and the desktop tauri-action build (npm run build:tauri -> vite build). Kept strict: a missing/empty secret fails the release by design rather than shipping a placeholder key. Requires the VITE_SUPABASE_ANON_KEY repo secret to be set for releases to succeed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:32:13 -07:00
Elliot SluskyandClaude Opus 4.8 8d33cb58fa Fix secure cloud key storage and Supabase key config (#587)
Route desktop cloud-key saves/status through the OS credential store (keyring with per-platform native backends: apple-native / windows-native / sync-secret-service), migrate the legacy plaintext ~/.openjarvis/cloud-keys.env into it, remove browser localStorage persistence of provider keys, and push key updates to the running server via /v1/cloud/reload (legacy env-file fallback retained). Remove hardcoded Supabase anon JWTs from frontend/docs source and make VITE_SUPABASE_ANON_KEY a required build var. Adds libdbus-1-dev to the Linux desktop build and a CI build var. Closes #220.

NOTE (post-merge follow-ups, not covered by CI): add the VITE_SUPABASE_ANON_KEY repo secret with the rotated key (release/docs builds otherwise use a placeholder), rotate the previously-committed Supabase anon key, and run a desktop save->restart->read smoke test to confirm keychain persistence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:11:32 -07:00
github-actions[bot] e4c4bcbae3 chore: update clone traffic data [skip ci] 2026-06-23 07:18:13 +00:00
Jon Saad-Falcon 993c24c8b9 test(server): make TestTraceRecording hermetic (env-independent) (#583)
TestTraceRecording relied on the ambient ~/.openjarvis/config.toml leaving traces.enabled at its default, so it failed on any machine with traces disabled locally (passing in CI only because the runner has no config file). Pass an explicit traces-enabled config with a tmp db_path so the tests are environment-independent and parallel-safe under pytest -n auto. Relates to #582.
2026-06-22 19:12:10 -07:00
Elliot Slusky 5bc8d3a2f6 Harden Docker and systemd deployment configs (#581)
Pin all base images and ollama to fixed versions + @sha256 digests (no floating :latest), run Docker images as an unprivileged openjarvis user (uid 10001), replace the curl|bash NodeSource install with a digest-pinned multi-stage copy, install from the committed uv.lock via uv export --frozen --no-dev (hash-verified, --no-deps), and add systemd sandboxing (NoNewPrivileges, ProtectSystem=strict, PrivateTmp, kernel/SUID protections). Closes #228, #563, #564, #565, #566, #567.
2026-06-22 19:12:07 -07:00
Elliot Slusky 433d10db5e feat(memory): native persistent memory service integrated into core (#579)
Adds the openjarvis.memory package (LocalFactStore, FactExtractor, background MemoryService), starts/stops it in the jarvis serve and jarvis chat lifecycle, feeds completed non-streaming exchanges to it, adds [memory] config support, and adds jarvis memory list/clear CLI commands. Extraction runs on a background thread and degrades to a no-op on any failure (BrokenPipe, timeouts, unparseable output) so it can never block a reply or crash the host. Disabled by default. Closes #393, #571, #572, #573.
2026-06-22 13:59:02 -07:00
Elliot Slusky 9b7b3681f6 ci: parallelize the test suite and cut install/coverage overhead (#580)
Run pytest with -n auto (pytest-xdist) and COVERAGE_CORE=sysmon, enable the uv cache, and switch test output to -q. Cuts the test job from ~40min to ~4min without changing what's tested or the 60% coverage gate.
2026-06-22 13:58:46 -07:00
Jon Saad-FalconandClaude Opus 4.8 6dbe5461bb fix(engine): drop Qwen3 control-token tool calls from Ollama responses (#578)
Qwen3 treats /think and /no_think as soft-switch control tokens. On small
models a multi-line prompt makes the model emit one as the sole tool argument
(e.g. {"command": "/no_think"}); OpenJarvis forwards Ollama's native tool_calls
verbatim, so the operative agent executes garbage. Filter control-token-only
tool calls in both the non-streaming generate() and streaming _run_stream()
paths, keeping legitimate calls like {"command": "date"}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 11:40:25 -07:00
github-actions[bot] a65592fecb chore: update clone traffic data [skip ci] 2026-06-22 08:05:29 +00:00
github-actions[bot] 0513fbdb84 chore: update clone traffic data [skip ci] 2026-06-21 07:41:30 +00:00
Elliot Slusky d4eb6308b1 Fix desktop speech dependency setup (#574) 2026-06-20 17:08:24 -07:00
github-actions[bot] 2853a0001d chore: update clone traffic data [skip ci] 2026-06-20 07:23:14 +00:00
github-actions[bot] 3c99481975 chore: update clone traffic data [skip ci] 2026-06-19 07:55:20 +00:00
Elliot Slusky 4bf39af9bd Add provider-aware search support to hybrid orchestration agents (#558) 2026-06-18 13:40:06 -07:00
github-actions[bot] 0a3e812751 chore: update clone traffic data [skip ci] 2026-06-18 07:44:28 +00:00
github-actions[bot] eb46febad5 chore: update clone traffic data [skip ci] 2026-06-17 07:54:02 +00:00
github-actions[bot] 81482b45d4 chore: update clone traffic data [skip ci] 2026-06-16 08:00:49 +00:00
Jon Saad-FalconandClaude Opus 4.8 3e2f4bcdb4 feat(core): consolidate all state under a single env-aware home directory (#462) (#549)
Previously `core/config.py` defined `DEFAULT_CONFIG_DIR = Path.home() /
".openjarvis"` as a by-value module constant imported into ~45 modules, and 34
modules hardcoded `Path.home() / ".openjarvis"` directly. The installer honored
`OPENJARVIS_HOME` but the Python runtime ignored it, producing a split-brain
layout (some modules honored the override, the core config dir did not). Eval
dataset caches also scattered into `~/.cache/<benchmark>`.

This introduces a single env-aware resolver in `openjarvis/core/paths.py` and
routes every state/config/cache path through it. OpenJarvis now keeps ALL of
its state under ONE root, resolved in priority order:

  1. $OPENJARVIS_HOME
  2. $XDG_DATA_HOME/openjarvis   (single nested dir, when XDG_DATA_HOME is set)
  3. ~/.openjarvis               (default — unchanged, so existing installs are
                                  untouched and no data migration is required)

Implementation:
- New `core/paths.py`: get_config_dir / get_config_path / get_data_dir /
  get_cache_dir, with a source-tree rejection guard (fails loudly per
  REVIEW.md if the root resolves inside the repo).
- `core/config.py`: DEFAULT_CONFIG_DIR / DEFAULT_CONFIG_PATH are now resolved
  via the env-aware resolver at import (real attributes, so existing
  monkeypatch.setattr-based tests keep working). All dataclass field defaults
  that pointed at ~/.openjarvis converted to default_factory so they honor the
  override at instantiation.
- Routed all 34 hardcoders plus several string-literal escapees the original
  audit missed: prompt_loader / description_loader (were OPENJARVIS_HOME-only,
  no XDG), swebench_harness cache, tools/{memory,skill,user_profile}_manage
  defaults, server trace.db fallbacks, doctor_cmd hints.
- spec_search storage/paths now delegates to the unified resolver (gains XDG);
  its ConfigurationError is aliased to the core one.
- Eval dataset caches moved from ~/.cache/<name> to <root>/cache/<name>
  (~/.cache/huggingface left alone — it is HF's own cache).
- Docs + installer comment + `jarvis config path` to show resolved dirs.

Read-only macOS connectors and OS service files (LaunchAgents/systemd) are
intentionally left untouched.

Fixes #462

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 09:32:02 -07:00
github-actions[bot] a35b21195f chore: update clone traffic data [skip ci] 2026-06-15 08:03:30 +00:00
Jon Saad-FalconandClaude Opus 4.8 f9d1bc8c27 fix(connectors): complete Google OAuth and register Drive in Data Sources (fixes #512) (#548)
Pasting a Google Client ID / Secret never completed OAuth: Drive (and its
Google siblings) accepted the credentials, showed no error, opened no browser,
and never appeared in Data Sources. Root cause is three coupled defects, all
reproduced at the unit level against main with a FastAPI TestClient (no Google
creds, network-free):

(A/B) POST /connect routed a `client_id:client_secret` pair into the
  connector's handle_callback, which spawned a daemon thread that popped a
  browser and ran its own localhost:8789 callback server. That thread fails
  silently in the bundled desktop context (`except Exception: pass`), so the
  connector never gained an access_token; /connect returned status "pending"
  and the UI's 20x2s poll timed out with no error.
  Fix: in POST /connect, an OAuth `client_id:client_secret` pair now persists
  the client credentials to every Google credential file and returns an
  `oauth_required` directive pointing at the in-process server flow, instead of
  the silent background thread. The Google connectors' handle_callback no longer
  spawns the browser thread for the pair case — it only persists the creds; the
  server's /oauth/start -> /oauth/callback owns the consent round-trip.

(C) The would-be-correct server flow was itself broken: under
  `from __future__ import annotations` plus a `Request` import local to the
  router factory, FastAPI could not resolve the stringized `request: Request`
  annotation. /oauth/start returned HTTP 422 (request mis-bound as a query
  param) and /oauth/callback injected None -> AttributeError on
  `request.base_url`. Fix: import `Request` at module scope and make the
  callback's `request` a required injected dependency.

A malformed/blank client pair now raises HTTP 400 with the provider setup URL
instead of a perpetual silent "pending" (REVIEW.md silent-failure discipline).

Frontend: DataSourcesPage now opens the server OAuth window when /connect
returns `oauth_required`, then polls until connected; connect errors surface the
backend detail; the Drive setup steps document the "Web application" OAuth
client + server-callback redirect URI the in-process flow requires.

Tests (run on the main venv, hermetic — no ~/.openjarvis pollution):
- test_oauth_flow.py: the three handle_callback tests now assert NO browser is
  opened and only client creds are persisted (was: assert background flow ran).
- test_connectors_router_oauth.py (new): reproduces + fixes all three defects via
  TestClient with mocked token exchange; parametrized over gdrive/gcalendar/
  gcontacts/gmail/google_tasks to prove the shared OAuth path is fixed for every
  sibling and that a single consent writes the access_token to all six Google
  credential files and flips is_connected() to True.
Full tests/connectors suite: 355 passed.

Relationship to PR #510: #510 rewrites all of these files (account-scoped
retrieval) but still carries all three defects. This fix is intentionally scoped
to the OAuth path and does not modify oauth.py, to minimize collision. A
maintainer can either merge this and rebase #510 on top, or port these changes
into #510. See PR body for details.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 19:55:22 -07:00
Aditya MalikandJon Saad-Falcon dfa908c358 Adopt hatch-vcs dynamic versioning (reworks autotag, pypi-publish, desktop) (#538)
* build: adopt hatch-vcs dynamic versioning (#526)

Replace the static `version = "1.0.2"` with `dynamic = ["version"]` and
derive the version from git tags via hatch-vcs, so source and editable
checkouts report their true git describe version (e.g.
1.0.3.dev110+g<sha>) instead of a stale constant.

Config notes:
- Exclude .dev/.rc/desktop-* tags from derivation. setuptools_scm cannot
  bump custom .devN tags, so the base is taken from the latest plain
  release tag (vX.Y.Z) and the dev distance from commit count.
- Add fallback_version so builds without a git checkout (shallow CI
  clones, Docker COPY src/, source-zip installs) resolve to a sentinel
  instead of hard-failing. CI release builds inject the exact version via
  SETUPTOOLS_SCM_PRETEND_VERSION.

* ci(autotag): derive dev base from the latest release tag (#526)

pyproject no longer carries a static version, so read the base from the
latest plain release tag (vX.Y.Z) reachable from HEAD instead of grepping
pyproject. .dev/.rc/desktop-* tags are excluded so they cannot be mistaken
for the release base. The computed tag (vX.Y.Z.devN) is unchanged.

* ci(pypi-publish): pin build version from tag, drop sed injection (#526)

With dynamic versioning there is no static line to sed. Pin the exact
build version from the pushed tag via SETUPTOOLS_SCM_PRETEND_VERSION so
the published version equals the tag. This is required, not cosmetic: a
naive hatch-vcs build emits 1.0.3.devN+g<sha>, and PyPI rejects local
version segments on upload.

Also add a dry_run input that targets TestPyPI instead of PyPI, for
validating the release path without a production upload.

* ci(desktop): derive dispatch-fallback version from release tag (#526)

The workflow_dispatch fallback grepped the now-removed static pyproject
version. Derive its base from the latest release tag instead (matching
autotag), and give the build-and-release checkout full history and tags
so the derivation works.

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
2026-06-14 19:35:17 -07:00
Jon Saad-FalconandClaude Opus 4.8 8ef1ab1928 fix(ci): gate secret-bearing Claude workflows by author association (fixes #218) (#547)
The Claude automation workflows (claude-issues.yml, claude-review.yml)
trigger on public, attacker-controllable events (issues, issue_comment,
pull_request_review_comment) and grant the job secrets.ANTHROPIC_API_KEY
plus a write-scoped GITHUB_TOKEN with NO author-association gate.

Because issues / issue_comment / pull_request_review_comment always run in
the base-repo context with full secret access (unlike fork pull_request,
from which GitHub withholds secrets), any external GitHub user could fire
these jobs — draining the API budget and, via contents:write +
pull-requests:write, creating branches/PRs.

Fix:
- Add an author-association gate to every human-triggered, secret-bearing
  if: clause, restricting to OWNER / MEMBER / COLLABORATOR. Uses the correct
  event payload field per trigger: github.event.issue.author_association for
  the `issues` event, github.event.comment.author_association for
  issue_comment and pull_request_review_comment. workflow_dispatch stays
  trusted (requires repo write to invoke).
- Drop unused id-token: write from both workflows (claude-code-action@v1 is
  passed github_token directly, so OIDC is unused).
- Reduce claude-issues.yml timeout-minutes 60 -> 15.

desktop.yml and take-assign.yml are intentionally NOT touched: independently
verified as not exploitable for ANTHROPIC_API_KEY (desktop.yml's only
pull_request job uses no secrets and the trigger is plain pull_request, not
pull_request_target; take-assign.yml uses only GITHUB_TOKEN with issues:write
and no checkout/no Anthropic key). claude-review.yml's stale pull_request
auto-trigger was already removed in 3f2f46e4.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 19:31:51 -07:00
Jon Saad-FalconandClaude Opus 4.8 28e75cb513 fix(server): inject OpenJarvis identity system prompt on the desktop chat path (fixes #540) (#546)
The OpenAI-compatible POST /v1/chat/completions endpoint — the desktop UI's
chat backend — never injected OpenJarvis's agent.default_system_prompt when the
client omits a system message. The frontend (Chat/InputArea.tsx) posts only
user/assistant turns, so the model answered from its training identity
("I'm Claude", "I am Qwen", ...). The CLI paths ground identity via
SystemPromptBuilder / BaseAgent; the engine-direct server handlers did not.

Fix:
- Add _ensure_identity_prompt(messages, app_config) in server/routes.py: returns
  messages unchanged when any has role==SYSTEM, else prepends a SYSTEM message
  with the resolved identity prompt (app.state.config.agent.default_system_prompt,
  else load_config()), wrapped in try/except that debug-logs on failure (no crash,
  no silent swallow per REVIEW.md).
- Apply it after _to_messages() in all three engine-direct handlers:
  _handle_stream, _handle_stream_tools, and _handle_direct; thread app.state.config
  through. _handle_agent is left untouched (BaseAgent already injects the default).
- Harden AgentConfig.default_system_prompt so distilled models stop claiming to be
  Claude/ChatGPT/Gemini and self-identify as OpenJarvis.

Tests (tests/server/test_routes.py, tests/core/test_config.py): identity prompt IS
prepended when no system message is present (stream / direct / tools paths) and is
NOT duplicated when the client supplies one; config wording anchors "OpenJarvis"
and "not Claude". Verified fail-on-unfixed against main (3 inject tests + config
wording test fail there).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 18:38:05 -07:00
4b9948250b feat(engine): add DeepSeek as a first-class cloud provider + fix #335 over-permissive cloud fallback (#545)
* feat(engine): add DeepSeek as a first-class cloud provider

Adds DEEPSEEK_API_KEY support to the cloud engine, wiring DeepSeek's
OpenAI-compatible API (api.deepseek.com/v1) alongside the existing
MiniMax, OpenRouter, Anthropic, and Google providers.

- Add _DEEPSEEK_MODELS list (deepseek-v4-flash, deepseek-v4-pro)
- Add _is_deepseek_model() routing predicate
- Init self._deepseek_client from DEEPSEEK_API_KEY in _init_clients()
- Add _generate_deepseek() and _stream_deepseek() methods
- Wire DeepSeek into generate(), stream(), _stream_full_openai(),
  list_models(), and health()
- Add approximate pricing entries for both models

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(engine): strict cloud model routing + deepseek can_serve branch

Builds on the DeepSeek provider (PR #504) with two routing-correctness
fixes to CloudEngine._client_for_model:

1. Add the missing DeepSeek branch so can_serve('deepseek-*') agrees with
   list_models()/health() when only DEEPSEEK_API_KEY is set (mirrors the
   minimax branch). Without it the engine advertised deepseek models via
   list_models() but refused to serve them (the #532 can_serve contract).

2. Fix #335: _client_for_model previously fell through to the OpenAI client
   for ANY unrecognized model name, so an OpenAI key (even a dummy
   sk-dummy... one) made can_serve('qwen3.5:0.8b') return True. With the
   local engine transiently down (classic post-Windows-restart Ollama not
   yet up), model-aware get_engine then mis-selected the cloud engine for a
   local model and died with "OpenAI client not available". Add a positive
   _is_openai_model predicate (gpt-/chatgpt-/o1/o3/o4 + _OPENAI_MODELS) and
   return None for unrecognized names, so can_serve declines them. generate()
   and stream() keep their OpenAI fall-through, preserving loud failure for an
   explicitly-requested unknown cloud model.

Tests: DeepSeek detection/pricing/health/list_models/generate-routing/
can_serve and a #335 regression (can_serve rejects local names with an
OpenAI key; unknown model not served even with all clients set; end-to-end
get_engine does not misroute a local model with a dummy OpenAI key).

Fixes #335

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Jen Huls <me@jenhuls.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 18:21:56 -07:00
79e23719d4 feat(vision): add image + screen capture input for vision models (#486)
OpenJarvis can run vision-capable local models (gemma3, qwen2.5-vl), but the
CLI had no way to send them a picture -- the Ollama engine only serialized
text. This adds end-to-end image input.

What's new
- `jarvis ask -i/--image <file>` attaches one or more images to the query.
- `jarvis ask -S/--screen` captures the primary monitor (dependency-free on
  Windows via .NET; mss/Pillow fallback elsewhere).
- Vision auto-routes to direct-to-engine mode; with an explicit --agent it
  warns rather than silently dropping the image.
- Privacy guard: warns before sending an image to a non-local engine,
  keeping OpenJarvis local-first by default.
- Context-window default raised 8k -> 16k (JARVIS_NUM_CTX) so an image plus
  a conversation fit.

Implementation
- Message.images carries base64 data; messages_to_dicts() forwards it to
  Ollama's /api/chat "images" field. Text-only messages are unchanged.
- GuardrailsEngine preserves images when it rewrites a flagged message.

Tests (tests/test_vision.py, 6/6 pass, ruff-clean)
- payload forwarding, text path untouched, num_ctx override, guardrail
  image preservation.

Verified on AMD RX 9070 XT (Ollama/Vulkan, 100% GPU) with gemma3:4b:
solid-color image, file image, and live screen capture all described.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
2026-06-14 18:19:00 -07:00
SANJAYandsanjayravit 7ba334b5f0 fix(gui): inject bearer token into streaming chat/research routes (#499)
Ensures that the desktop GUI correctly sends the Authorization header
when an API key is configured. This resolves the 'Failed to get response'
bug on Windows systems with enabled authentication.

Ref: #266

Co-authored-by: sanjayravit <sanjay@example.com>
2026-06-14 17:32:43 -07:00
github-actions[bot] 48a2627c9a chore: update clone traffic data [skip ci] 2026-06-14 07:38:47 +00:00
github-actions[bot] 8625f4f95f chore: update clone traffic data [skip ci] 2026-06-13 07:21:38 +00:00
github-actions[bot] cf08f164c0 chore: update clone traffic data [skip ci] 2026-06-12 07:41:03 +00:00
Jon Saad-FalconandClaude Opus 4.8 b21463aab6 fix(evals): harden terminalbench-native harness against tmux death and setup hangs (#536)
Two failure classes hit by a downstream team:

1) tmux/task-env death (TerminalBenchTaskEnv.__enter__, terminalbench_env.py):
   create_session ran inside the spin_up_terminal generator-CM with no
   exception safety — a tmux failure leaked the docker compose project
   (down deferred to GC, never if the env was retained) and surfaced as an
   opaque mid-run death. Now: exception-safe __enter__ with an idempotent
   _teardown(), a tmux/asciinema preflight in the container BEFORE the
   agent loop (TaskEnvironmentError naming the task image + remedy), and
   fail-that-task-cleanly semantics — the failure is recorded as a harness
   error (QueryTrace.error_kind="harness_error"), the container is downed,
   and the run continues.

2) OpenHands in-container SETUP hang misattributed as a model result:
   harness.run() had no bound (terminal-bench runs installed-agent setup
   with max_timeout_sec=inf inside the per-trial agent budget), and the
   summary conversion read the nonexistent results.trial_results attr and
   hardcoded errors=0, folding zero-model-request trials into resolve-rate
   as model misses. Now: global_agent_timeout_sec / global_timeout_multiplier
   are threaded config -> backend -> Harness kwargs (default 1800 s bound on
   SETUP+RUN; configurable per [run]/[[benchmarks]] TOML), and
   summarize_benchmark_results() classifies harness/infra failures out of
   the accuracy denominator keyed on token usage (zero/missing tokens +
   unresolved = the agent never contacted the model), NOT failure_mode —
   terminal-bench 0.2.18 leaves failure_mode UNSET on success AND on
   genuine misses, so a failure_mode-based check would misflag every real
   model miss. Genuine misses (tokens>0, is_resolved=false) stay in the
   denominator.

QueryTrace gains error/error_kind (wired through to_dict/from_dict so the
fields actually reach traces.jsonl; backward-compatible loads), the
AgenticRunner flags zero-model-contact traces and TaskEnvironmentError as
harness errors, and export/summary/console output exclude harness errors
from resolve-rate while reporting them loudly.

terminal-bench stays an undeclared dep on purpose: it requires Python
>=3.12 while this project supports >=3.10,<3.14, so an unmarked extra
would break uv lock. pyproject/uv.lock untouched.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 14:30:10 -07:00
Jon Saad-FalconandClaude Opus 4.8 0cac61d3bb docs(evals): rewrite evaluations guide to match the real CLI surface; add openjarvis-eval alias (#534)
docs/user-guide/evaluations.md documented a standalone "openjarvis-evals"
package, a "uv sync --extra eval" install, and an "openjarvis-eval" console
script — a layout from commit bd493832 that was never an ancestor of main.
Rewrite the page against the real surface (jarvis eval / python -m
openjarvis.evals), document all 40 registered benchmark keys and 4 backends,
fix the judge-model default, correct run-all semantics, and split the option
reference into the jarvis-eval subset and the module CLI's research-only
options.

Add the one-line [project.scripts] alias
openjarvis-eval = "openjarvis.evals.cli:main" (the click group the module
CLI already dispatches to) so the long-documented command name works again.

The page was a complete orphan: add it (and the equally orphaned
benchmarks.md) to the mkdocs nav and link it from docs/index.md.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:50:17 -07:00
Jon Saad-FalconandClaude Opus 4.8 50993dfa4d fix(evals): honor --base-url/--api-key for first-party eval backends (#535)
`jarvis eval run --base-url ... --api-key ...` was silently dropped for
jarvis-direct/jarvis-agent (_build_backend only forwarded the flags to
hermes/openclaw) and ignored by terminalbench-native, which hardcoded
api_base="http://localhost:8000/v1". Worse, with --base-url set the
engine-discovery fallback silently substituted ANY healthy local engine
(observed: requested vllm + healthy endpoint at --base-url, got
OllamaEngine@localhost:11434 — the requested URL was never contacted).

Changes:
- _OpenAICompatibleEngine gains an api_key param (Bearer Authorization
  header on the httpx client; {ENGINE_ID}_API_KEY env fallback with
  hyphen-sanitized names; no header when unset).
- New non-registered OpenAICompatEngine + normalize_openai_base_url()
  (strips a single literal trailing "/v1" so request paths don't double).
- SystemBuilder.engine_instance() injects a pre-built engine; build()
  health-checks it and fails loudly naming the host instead of falling
  back to discovery. Discovery substitution after an explicit -e key now
  logs a warning.
- JarvisDirectBackend/JarvisAgentBackend accept base_url/api_key; on
  base_url they pin an OpenAICompatEngine to that endpoint with a
  fail-fast pre-flight (actionable error naming the URL and probe).
- _build_backend forwards base_url/api_key to first-party backends on
  the CLI path; _run_terminalbench_native receives --base-url as
  api_base (single /v1 suffix) and exports OPENAI_API_KEY around the
  in-process harness run (terminus-2 routes via LiteLLM).
- Suite TOML [backend.external] stays scoped to hermes/openclaw
  (suite_mode=True in the suite drivers) — first-party suite semantics
  are explicitly deferred. The config-host path is untouched.
- Help text updated on both CLI surfaces; KNOWN_BACKENDS now lists
  hermes/openclaw/terminalbench-native.

Fixes the eval-CLI endpoint gap reported by the downstream team.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:49:37 -07:00
github-actions[bot] 527f84f960 chore: update clone traffic data [skip ci] 2026-06-11 07:44:18 +00:00
8eaeb3a754 fix(windows): desktop backend spawn + model-aware engine selection (#533)
* fix(windows): desktop backend spawn (#531) + model-aware engine selection (#532)

Two runtime bugs found during end-to-end testing on a clean Windows 11
24H2 Azure VM.

#531 - Desktop "Failed to get response": run_jarvis_command spawned the
backend with .output(), which waits for the process to exit. `jarvis
serve` never exits, so the Tauri command hung forever (the Start button
never resolved); and it ran `uv run jarvis` with no cwd, so in a packaged
install -- where the cwd isn't the checkout -- `jarvis` wasn't found and
the server never started. Now: run from find_project_root(), and for
`serve` spawn detached (.spawn()), drain stderr, and poll /health for
readiness (mirrors start_backend); short commands keep .output().

The server layer itself was verified healthy on Windows (/health and
/v1/chat/completions both 200, localhost included) -- the fault was the
Tauri spawn path.

#532 - "OpenAI client not available" after reboot: when the local engine
is down, get_engine's fallback selected CloudEngine because health() is
True if ANY provider client exists -- without checking the resolved
model's provider has a client. A user with e.g. OPENROUTER_API_KEY and a
gpt-* model then hit the OpenAI path with no client. Add
CloudEngine.can_serve(model) (checks the specific provider client via the
same routing generate()/stream() use) + a default can_serve->True on the
base engine, and make get_engine model-aware so it skips an engine that
can't serve the model -- the user falls through to the helpful "no engine
available / start ollama" message instead.

Tests: engine discovery/cloud/model-matrix + cli serve/ask suites pass
(the one ask_e2e failure is a pre-existing version-banner flake, fails
identically on main). The Tauri crate couldn't be compiled locally (no
GTK/webkit sys-libs in this env); relies on CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(engine): cover model-aware engine selection + CloudEngine.can_serve (#532)

#533 added a `model` arg to get_engine and a can_serve() gate but shipped no
tests. Add them:
- get_engine skips a healthy engine that can't serve the requested model
  (the cloud-fallback-for-unservable-model case behind #532),
- model=None preserves the legacy model-agnostic selection,
- CloudEngine.can_serve gates on the per-provider client (gpt->OpenAI,
  claude->Anthropic, ...), verified empirically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
2026-06-10 19:52:36 -07:00
Robby ManihaniandClaude Opus 4.8 90b7d0cb9b fix(windows): installer encoding + Ollama readiness loop (#523)
Two bugs found during end-to-end testing on a clean Windows 11 24H2
Azure VM (closes #522). Both are dodged by the canonical `irm | iex`
one-liner but hit by the documented `-OutFile` fallback and any
non-interactive run.

1. Encoding. install.ps1 was UTF-8 without a BOM and contained em-dashes
   plus a box-drawing banner. Windows PowerShell 5.1 decodes BOM-less
   files with the legacy ANSI/OEM code page, mis-decoding the multi-byte
   sequences and desyncing the parser into cascading here-string parse
   errors. Converted the file to pure ASCII (em-dashes -> hyphens, banner
   -> ASCII art) so it parses no matter how it's read.

2. Ollama readiness loop. With $ErrorActionPreference='Stop', the probe
   `& $ollamaExe list 2>&1 | Out-Null` turned the daemon-not-up stderr
   into a terminating NativeCommandError, aborting the install on the
   first iteration and making the loop's own Start-Process serve retry +
   Write-Warn2 fallback dead code. Wrapped the probe in try/catch so it
   falls through to the self-start path as intended.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 19:15:57 -07:00
d7053c35d5 security: harden network-exposed surface (#509)
* security: harden network-exposed surface

Hardening for the network-reachable attack surface, prioritizing fixes
that are strong but do not change working local/loopback defaults.

- auth_middleware: constant-time API key comparison (secrets.compare_digest)
  for the HTTP path, and gate /metrics behind auth so operational counters
  are not readable unauthenticated. /health stays open.
- webhook_routes: fail closed when a channel's secret/token is unset. Twilio,
  BlueBubbles, WhatsApp (verify + inbound), and SendBlue now reject (403)
  instead of processing unsigned/unauthenticated input. Constant-time
  comparisons for BlueBubbles/SendBlue/WhatsApp verify token.
- http_request: follow redirects manually and re-run the SSRF check on every
  hop (capped at 5) so an allowed public URL cannot 30x-redirect to an
  internal/metadata address.
- api_routes /v1/memory/index: restrict indexing to OPENJARVIS_WORKSPACE roots
  when configured and refuse sensitive files (.env, keys, credentials).
- config.toml: default [server] host to 127.0.0.1 (loopback) with a comment
  on how to safely expose to a LAN (0.0.0.0 + API key).

Tests: new fail-closed webhook tests, /metrics auth tests, and SSRF
redirect block/follow tests; updated SendBlue tests for the new
secret-required behavior. Affected suites pass (95 tests), ruff clean.

* fix(http): keep SSRF redirect-following patchable via httpx.request

The manual redirect-following loop used a private httpx.Client, which
bypassed the `http_request.httpx.request` mock seam that consumers' tests
rely on (e.g. the twitter-bot GitHub-issue tests escaped to the real
network and 401'd). Issue each hop via module-level httpx.request with
follow_redirects=False instead — same per-hop SSRF re-check, restored
testability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 15:32:28 -07:00
03c5ec3e40 fix(chat): wire SystemPromptBuilder so persona files load in jarvis chat (fixes #458) (#497)
* fix(chat): wire SystemPromptBuilder so persona files load in jarvis chat (fixes #458)

* fix(chat): make `--persona none` actually disable persona files

This PR exposes `--persona none`, but SystemPromptBuilder._load_file read
empty paths as "." (Path("") -> ".") and raised IsADirectoryError, so the
documented opt-out crashed. Guard empty path_str so the "none" opt-out
(which _resolve_persona maps to empty file paths) cleanly injects no
persona. Adds an end-to-end regression test (building with persona
"none" must not raise). Also merges current main (branch was stale).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:10:13 -07:00
Jon Saad-FalconandClaude Opus 4.8 4218258486 perf(serve): build the system once — drop duplicate SystemBuilder.build() (#263) (#529)
`jarvis serve` constructed every heavy component inline (engine discovery +
instrumentation, telemetry, memory, agent manager, per-agent tools) and then
called `SystemBuilder(config).build()` a second time inside the scheduler
block purely to feed `AgentExecutor.set_system()`. That second build
re-discovered and re-connected the engine, re-instrumented it, re-resolved
tools, re-opened the configured channel and re-created the agent manager —
~30-40s of fully redundant startup work (the headline remaining cost in #263
after engine probes were parallelised and the version check moved off the hot
path in #470).

Fix: assemble the executor's `JarvisSystem` from the components already built
inline instead of rebuilding from scratch. `AgentExecutor` only reads
`engine`, `model`, `config`, `memory_backend`, `tool_executor`,
`session_store` and `channel_backend` off the system; all are wired here. The
memory backend is now constructed just before the scheduler block (it was
built later) so the executor's system can reference it, and the primary
agent's resolved tool list is reused to build the scheduler's `ToolExecutor`
(preserving the MCP-discovered-tool pool the executor reads via
`tool_executor._tools`). `skill_manager` / the learning orchestrator are only
consumed by the orchestrator's `system.ask()` path, which the executor never
invokes, so they are intentionally omitted.

Tests: new `tests/cli/test_serve_single_build.py` patches
`SystemBuilder.build` and asserts it is never called during `jarvis serve`
startup, and that the executor still receives a system exposing
`tool_executor` / `session_store` / `memory_backend` (plus engine/model/config).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:10:10 -07:00
Jon Saad-FalconandClaude Opus 4.8 176ed3029b fix(channels): unify send() destination/reply contract (Discord #515/#516) (#528)
The channel `send()` contract was inconsistent across adapters. Almost
every adapter (Discord, Slack, email, WhatsApp, ...) treats the first
positional `channel` arg as the real DESTINATION id and `conversation_id`
as an optional reply/thread reference. Telegram alone treated
`conversation_id` as the destination (`chat_id = conversation_id or
channel`).

`JarvisSystem._on_channel_message` hard-coded the Telegram-shaped mapping
for ALL channels: `send(cm.channel, reply, conversation_id=cm.conversation_id)`.
Since inbound `ChannelMessage`s carry the channel TYPE label in `.channel`
("discord") and the real destination id in `.conversation_id`, this sent
the literal "discord" as the Discord channel id (HTTP 400
NUMBER_TYPE_COERCE, #515) and passed the channel id as a Discord
`message_reference` (MESSAGE_REFERENCE_UNKNOWN_MESSAGE, #516).

Fix: define and document ONE canonical contract on `BaseChannel.send` —
positional `channel` = destination id, `conversation_id` = inbound message
id used as a reply reference — and dispatch it from `_on_channel_message`
as `send(cm.conversation_id, reply, conversation_id=cm.message_id)`,
matching the already-fixed `ChannelAgent` path (#495/#459). Telegram's
`send()` is brought into line (destination = `channel`, with a
`reply_to_message_id` reply ref and a legacy `conversation_id`-only
fallback) so it keeps working unchanged.

The DiscordChannel `_gateway_loop` ChannelMessage shape locked by #495 is
untouched; `tests/agents/test_channel_agent.py` passes unchanged. The
stale `test_serve_channel_wiring.py` assertions (which encoded the old
buggy mapping from #94) are updated, and regression tests are added for
Discord (real channel id + correct message_reference), Telegram (chat id
+ reply ref + legacy fallback), and per-channel dispatch in
`_on_channel_message`.

Fixes #515
Fixes #516

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:10:06 -07:00
Jon Saad-FalconandClaude Opus 4.8 590addae4c test(engine): expect EngineConnectionError for vllm 404 (fix main CI) (#530)
#463 made the OpenAI-compatible engine wrap upstream HTTP errors (incl.
404) in EngineConnectionError with an actionable message, but
test_invalid_model_404 still asserted the raw httpx.HTTPStatusError, so it
broke on main once #463 landed (#463 was a stale fork PR with no CI, so it
wasn't caught pre-merge). Expect EngineConnectionError now, asserting the
httpx.HTTPStatusError is preserved as the chained cause. Whole tests/engine
suite is green again.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:09:37 -07:00
Jon Saad-FalconandClaude Opus 4.8 bc9fa6b3c8 fix(memory): surface clear error when openjarvis_rust missing instead of silent no-op (#527)
Memory tools degraded silently and misleadingly when the mandatory
`openjarvis_rust` extension was absent from the *serving* venv:

- `POST /v1/memory/store` returned HTTP 200 `{"status":"stored","note":
  "no backend available"}` and stored nothing (silent data loss).
- `POST /v1/memory/index` returned a generic "No memory backend available",
  and the desktop frontend discarded the server `detail` and threw a blanket
  "Failed to index path", blaming the path instead of the real cause.
- `GET /v1/memory/config` reported `backend_type: sqlite` even though no
  backend could be constructed.

Root cause: `SQLiteMemory.__init__` calls `get_rust_module()` (which raises
ImportError by design — the Rust ext is mandatory, no Python fallback), and
`_get_memory_backend` swallowed that ImportError and returned `None`,
conflating "native extension missing" (a hard install error) with "memory
intentionally disabled" (benign). A chunking floor also silently dropped whole
short documents, and the installer never verified the extension imported from
the serving venv before writing its success marker.

Fix (no fake Python fallback — the Rust ext stays mandatory by design):

- Add `MemoryBackendUnavailable` + `RUST_MISSING_HINT` in tools/storage/_stubs.
  `SQLiteMemory.__init__` translates the bridge ImportError into this clear,
  actionable error ("run `uv run maturin develop ...`").
- `_get_memory_backend` distinguishes the two cases: a missing native ext
  raises HTTP 503 with the actionable hint; a benign unconfigured backend
  still returns `None` (graceful path preserved for search/stats).
- `/store` now returns 503 instead of a 200 silent no-op.
- `/config` reports `available: false` + `detail` instead of falsely claiming
  a healthy `backend_type`.
- `/index` adds a `note` when `chunks_indexed == 0` so "indexed" never
  silently means "stored nothing".
- chunk_text no longer drops an entire short document below `min_chunk_size`
  (the floor only discards tiny *trailing* fragments now).
- Frontend `storeMemory`/`indexMemoryPath` surface the server `detail` instead
  of blanket strings; `MemoryConfig` gains optional `available`/`detail`.
  (Left the pre-existing `backend` vs `backend_type` mismatch untouched.)
- build-extension.sh verifies `import openjarvis_rust` succeeds in the serving
  venv before writing the `extension-built` marker.

Regression tests: tests/server/test_api_routes.py::TestMemoryRustMissing mocks
`get_rust_module` to raise ImportError and asserts /store (503, not 200 no-op),
/index (actionable detail, not "Failed to index path"), and /config
(available:false) all surface the clear error; tests/memory/test_chunking.py
asserts short-only docs are kept while tiny trailing fragments are still
filtered.

Fixes #502

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 13:25:10 -07:00
Jon Saad-FalconandClaude Opus 4.8 18d9897317 fix(version): restore version to 1.0.2 (undo erroneous revert to 0.1.1) (#525)
The package version was reverted from 1.0.2 to 0.1.1, which:
- makes `jarvis --version` / installed metadata report 0.1.1 (the root of
  #478 "version unchanged after update" and the version half of #520), and
- drives autotag.yml to compute `0.1.2.dev<count>` — BELOW the real 1.0.x
  line — so every push to main now auto-tags and publishes sub-1.0.2 dev
  releases to PyPI (latest stable is 1.0.2; dev line was 1.0.3.dev*).

Restore version to 1.0.2 (the last released stable). autotag resumes
`1.0.3.dev<count>`, back on the 1.0.x line, and reported versions are
correct again. Pipeline-compatible (pypi-publish.yml still seds the
tag version). Full hatch-vcs dynamic versioning is a separate follow-up.

Fixes #478. Refs #520.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 13:03:05 -07:00
Lakshmi narayana .UandLakshmi narayana U a61d3c09e1 Improve local engine model resolution (#463)
Co-authored-by: Lakshmi narayana U <ln-mini@Lakshmis-Mac-mini.local>
2026-06-10 12:33:01 -07:00
c76c80d6b4 fix: forward tools through OpenRouter engine (#511)
* fix: forward tools through OpenRouter engine

The OpenRouter chat completion path built the request from only
`model`, `messages`, `max_tokens`, and `temperature`. `tools` and
`tool_choice` passed via `kwargs` were silently dropped, so
function definitions never reached the model. Symptom on a
managed deep_research agent: the model answered every query from
its own prior knowledge and never invoked `knowledge_search`,
`knowledge_sql`, etc.

The same path also discarded `choice.message.tool_calls` from the
response — when a model did return a tool call (verified directly
against OpenRouter with `google/gemma-4-31b-it:free` and
`nvidia/nemotron-3-ultra-550b-a55b:free`), the agent loop never
saw it.

This patch:
- forwards `tools` and `tool_choice` into the OpenAI-compatible
  request in both `_generate_openrouter` (sync) and
  `_stream_openrouter` (async stream),
- extracts `tool_calls` from the response in `_generate_openrouter`
  in the same shape used by the OpenAI / Anthropic paths.

Verified end-to-end against a `deep_research` managed agent using
an OpenRouter preset with Gemma 4 31B + Nemotron 3 Ultra fallback:
before, the agent stated "I don't have access to your vault";
after, it calls `knowledge_search`, cites results, and produces a
structured answer.

* test(engine): regression test for OpenRouter tool forwarding

Asserts the OpenRouter path forwards tools/tool_choice to the
OpenAI-compatible API and parses tool_calls back into the result (#511).
Verified: passes on the fix, fails (KeyError 'tools') against pre-fix main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:30:22 -07:00
d908372eb5 fix(chat): honor model config in managed agent chat (#477) (#514)
* fix(chat): honor model config in managed agent chat (#477)

* test(server): regression test for managed-agent engine resolution

_make_lightweight_system must resolve the user's configured engine
(intelligence.preferred_engine, else engine.default) via get_engine,
not a hardcoded OllamaEngine (#477/#514). Asserts the key passed to
get_engine (captured before the system is built); runs under the server
extra (fastapi). Verified: passes on the fix, fails (KeyError) against the
pre-fix hardcoded-Ollama code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:15:37 -07:00
1b2b0a06e1 fix(learning): exclude padding tokens from SFT loss (#521)
* fix(learning): exclude padding tokens from SFT loss

* test(learning): add regression tests for SFT padding-loss masking

Cover the fix in both trainers (#521):
- OrchestratorSFTDataset.__getitem__: labels are -100 at padded positions,
  equal to input_ids elsewhere, and input_ids is not mutated.
- LoRATrainer._train_step: the labels passed to the model are masked at
  padded positions (captured via an injected model), with input_ids intact.

Both are torch-gated (pytest.importorskip / skipif HAS_TORCH), matching the
project's existing torch test gating, so they skip cleanly in the default CI
env. Verified locally with CPU torch: both PASS against the fix and FAIL
against the pre-fix code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 11:48:57 -07:00
github-actions[bot] 9e4504cce4 chore: update clone traffic data [skip ci] 2026-06-10 07:32:27 +00:00
Jon Saad-Falcon d8d4985eb5 Update README.md 2026-06-09 13:54:06 -07:00
github-actions[bot] 3359629456 chore: update clone traffic data [skip ci] 2026-06-09 07:17:33 +00:00
Robby Manihani 726445433b fix: wire TraceCollector into server chat endpoints (#513) 2026-06-08 18:04:14 -07:00
github-actions[bot] b30bf43557 chore: update clone traffic data [skip ci] 2026-06-08 07:46:18 +00:00
github-actions[bot] a99ee7c473 chore: update clone traffic data [skip ci] 2026-06-07 07:26:47 +00:00
Jon Saad-FalconandClaude Opus 4.8 4523715bff fix(ci): gate live HuggingFace Hub download tests behind hub marker (#507)
The two eval-dataset suites that download real corpora from the
HuggingFace Hub at runtime were running in the default CI lane. When the
Hub was unreachable or rate-limited they failed and reddened `main` even
though no code changed — confirmed by #506 (docs-only) failing on merge
while its own PR run passed an hour earlier. They also dominated CI
wall-time (~33 min of downloads + retry backoff on the failing run).

Add a `hub` pytest marker, apply it to both suites via module-level
`pytestmark`, and exclude it from the default CI lane
(`-m "not live and not cloud and not hub"`). The tests stay runnable on
demand with `pytest -m hub`.

The ADP provider swallows per-config download errors and returns 0
records on a network failure, so a Hub outage surfaced there as
`assert 1 <= 0` (not an exception) — it could not be made non-flaky by
exception handling alone, only by gating.

Coverage holds: removing these from CI drops total from 60.92% to
~60.73% (paranoid worst case 60.18%), still above the 60% gate.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 16:31:50 -07:00
Jon Saad-Falcon f7c948fe12 Merge pull request #506 from open-jarvis/update-discord-link
Update Discord invite link
2026-06-06 12:09:19 -07:00
Jon Saad-FalconandClaude Opus 4.8 30a014faf4 Update Discord invite link to discord.gg/CMVBmDQ5Fj
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 11:07:07 -07:00
github-actions[bot] bb90480430 chore: update clone traffic data [skip ci] 2026-06-06 07:07:04 +00:00
github-actions[bot] 3ab7764e06 chore: update clone traffic data [skip ci] 2026-06-05 07:33:00 +00:00
68b27654a8 docs(showcase): add outcome-first gallery tier above Tutorials (#500)
Addresses feedback from our Discord admin curating #config-showcase: the
existing docs land non-technical users straight into Tutorials, which
are script-first and TOML-heavy ("standalone script you can run
immediately, a TOML recipe, a detailed walkthrough"). For a curious-
but-non-technical reader trying to decide whether OpenJarvis is worth
their weekend, that's the wrong first contact — they bounce before they
ever see what the framework can do for them.

This PR inserts a new Showcase tier *above* Tutorials in the docs
information architecture. Each entry is outcome-first: hook sentence,
hero screenshot, 2-3 short paragraphs of personal context, then a
"How I set this up →" link that lands on the relevant Tutorial /
User Guide. The Showcase is the funnel; Tutorials are the build steps.

Five inaugural entries — drafted to be paste-ready for #config-showcase:

- showcase/morning-brief.md          — Slack/email/GitHub overnight digest
- showcase/persistent-memory.md      — SOUL.md/MEMORY.md/USER.md story
- showcase/cost-savings.md           — the public leaderboard as motivation
- showcase/discord-companion.md      — DM Jarvis from anywhere
- showcase/coding-assistant.md       — code review on an airplane

Plus the contributor template and an assets directory:

- showcase/CONTRIBUTING.md           — format skeleton + editorial conventions
                                       (screenshot specs, what to redact, tone)
- assets/showcase/README.md          — asset directory conventions
- assets/showcase/*.png              — placeholder hero screenshots (1600x1000,
                                       6 KB each, dark gradient) so the gallery
                                       renders cleanly before community
                                       submissions populate real screenshots

Information-architecture changes:

- mkdocs.yml — insert "Showcase" tier between Getting Started and
  Tutorials. Funnel order is now: land → "what's possible?" → "build it."
- docs/index.md — new hero card directly under the tagline, pointing to
  the Showcase. The research-framework framing stays, but no longer
  occupies the first scroll-fold.

CSS:

- docs/stylesheets/extra.css — `.showcase-screenshot` class adds rounded
  corners + subtle border so hero images (placeholder or real) read as
  intentional rather than as broken-image artifacts.

Validation:

- `uv run mkdocs build` (CI mode) succeeds.
- `uv run mkdocs build --strict` produces zero showcase-specific
  warnings. The 18 remaining strict-mode warnings are all pre-existing
  on main (`desktop-auto-update.md`, `telemetry.md`, griffe parser
  warnings on existing source, mkdocs_autorefs cross-reference issues).

Explicit non-goals (deferred to follow-up PRs in the showcase-tier
roadmap):

- `jarvis showcase` CLI for personal recaps (PR #2)
- Showcase-aligned recipes in `src/openjarvis/recipes/data/` so
  "How I set this up →" links into 2-command installs (PR #2)
- Automated screenshot regeneration via Playwright on release tags (PR #3)
- Replacing placeholder PNGs with real screenshots — that happens
  organically as community contributors and team members submit their
  own setups (see CONTRIBUTING.md for the format)

Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 17:42:23 -07:00
Jon Saad-FalconandClaude Opus 4.7 1827c1578b fix(leaderboard): filter quarantined methodology_version=0 rows (#501)
Pairs with the Supabase migration that added `methodology_version` to
`savings_entries` and demoted 43 corrupt rows (pre-fix telemetry from
March 2026) to version 0. Adds `methodology_version=gte.1` to the
public leaderboard fetch URL so the quarantined rows hide at the query
layer — fewer bytes over the wire than client-side outlier filtering,
and forward-compatible: new clients write `methodology_version >= 1`
and remain visible.

The leaderboard's existing client-side outlier thresholds stay in place
as a second line of defence in case any future v >= 1 row slips past
the bounds.

Smoke-tested live: anon-key fetch against
`mtbtgpwzrbostweaanpr.supabase.co` returns 291 rows (was 334), top
result is the largest-savings user with v=1. A separate `eq.0` probe
confirms quarantined rows are skipped only by the filter (no RLS
policy in place yet — flag for follow-up if you want hard isolation).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 17:42:19 -07:00
28ef745384 fix(leaderboard): correct telemetry pipeline and outlier handling (#498)
* fix(leaderboard): correct telemetry pipeline and outlier handling

The public leaderboard at /leaderboard showed a clear bimodal Wh/token
distribution: most users at ~3-5 J/token, ~30% inflated by 1000-4000×.
A 5-agent investigation workflow + 4-agent verification pass traced the
inflation to a cluster of related bugs across telemetry, server, and
display layers. This PR fixes the in-repo half. Backfilling existing
Supabase data is a separate follow-up.

Bug 1 — Dual telemetry recording (`server/routes.py`)
=====================================================

`_handle_direct` wrapped the engine with `instrumented_generate`
unconditionally. When `app.state.engine` was already an
`InstrumentedEngine` (the common case when telemetry is wired in),
BOTH layers published `TELEMETRY_RECORD` — once from the inner
`InstrumentedEngine.generate`, once from the outer wrapper. Every
chat-completion request was counted twice in the leaderboard pipeline.

Fix: detect the InstrumentedEngine and unwrap to `._inner` before
passing to the wrapper so only one layer fires.

Bug 2 — KV-cache fallback over-counts multi-turn (`server/savings.py`)
=====================================================================

`compute_savings` falls back from `prompt_tokens_evaluated` to
`prompt_tokens` when the KV-cache-aware count is missing. But routes.py
aggregates by summing each turn's full prompt — which counts the system
prompt N times for an N-turn conversation. The fallback inflated FLOPs
and energy by N×.

Fix: use 0 (conservative under-count) when the evaluated count is
missing rather than falling back to the inflated `prompt_tokens` sum.

Bug 3 — TelemetryRecord lacked methodology versioning
=====================================================

There was no per-record version tag, so legacy (pre-fix) and current
records were silently aggregated together in the public leaderboard
even though they used different methodologies.

Fix: add `token_counting_version: Optional[int]` field to the
`TelemetryRecord` dataclass + a nullable column to the SQLite schema
(with idempotent migration); the constant moves from `server.savings`
to `core.types` to avoid the server→telemetry layering. New records
write the current version; pre-fix rows remain NULL. The aggregator
gains a `current_methodology_only=True` flag that filters NULL rows out
of leaderboard sums — local dashboards leave it off so historical
aggregates still render.

Bug 4 — Leaderboard JS displayed missing telemetry as legit zeros
=================================================================

Rows with significant token counts but `energy_wh_saved = 0` and
`flops_saved = 0` rendered as `0.00 Wh / 0 FLOPs` — visually identical
to a user who genuinely did almost nothing. The headline totals also
included these rows.

Fix: new `isMissingTelemetry()` detector renders missing-telemetry
energy/FLOPs cells as `—` (with a tooltip explaining why); a new
`.lb-missing` CSS class differentiates the placeholder visually.

Bug 5 — Outlier filter was too generous
=======================================

`MAX_ENERGY_WH_PER_TOKEN = 10` left a 10,000× margin that admitted
every Group B row even though those values are physically impossible
(would imply a space-heater per token). Same for the FLOPs cap at
`1e17`.

Fix: tighten to `0.5` Wh/token (still 500× over a typical consumer GPU)
and `1e15` FLOPs/token (still 10,000× over typical). This removes
existing pre-fix corrupt rows from the public view without touching
Supabase.

Tests
=====

New regression tests (all pass, ruff clean):

- `tests/server/test_routes.py::test_instrumented_engine_unwrapped_to_avoid_dual_telemetry`
  — pins the bug 1 fix; asserts exactly ONE TELEMETRY_RECORD event per
  request when the engine is already an InstrumentedEngine.
- `tests/server/test_savings.py` (NEW file, 3 tests) — pins the bug 2
  fix (FLOPs not inflated via fallback) and the cost-side invariant
  (dollar savings still use full prompt_tokens because cloud providers
  bill per input token even when local KV cache hit).
- `tests/telemetry/test_aggregator.py::TestMethodologyFilter` (3 tests)
  — default behaviour includes legacy rows (local dashboard parity);
  `current_methodology_only=True` excludes them; the summary surface
  honors the same filter.

Unrelated test note
===================

`tests/telemetry/test_energy_wiring.py::TestTelemetryStatsEnergy::test_export_includes_energy_fields`
and `::TestEndToEndPipeline::test_ask_to_export_with_energy` are flaky
on this branch but ALSO flaky on `main` (confirmed via stash + the
banner-only run). Root cause: `_version_check.py:132-135` prints the
"new version of OpenJarvis is available" banner to stdout outside the
throttle window, contaminating `json.loads(result.output)` in those two
tests. Reproducible with `OPENJARVIS_NO_UPDATE_CHECK=1` → all pass.
That's a separate bug (the banner shouldn't write to the same stream
as machine-readable output); not addressing it here to keep this PR
focused on the leaderboard pipeline.

What this PR explicitly does NOT do
====================================

- Backfill ~30% of Supabase rows that already shipped with inflated
  values from pre-fix clients. That requires Supabase write access and
  is the natural follow-up — see PR comments for proposed dry-run audit
  query and the additive `methodology_version` column migration.
- Distinguish which past submissions came from buggy vs correct clients
  retroactively (no `app_version` tag on existing submissions).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(evals): fix test_energy_scales_linearly under leaderboard fix

CI on #498 failed in the slow `test` job:

    tests/evals/test_use_case_benchmarks.py::TestSavings::
    test_energy_scales_linearly — ZeroDivisionError: float division by zero

Root cause: the test (and `test_energy_wh_matches_direct_formula`)
called `compute_savings(N, 0)` without `prompt_tokens_evaluated`. Under
the OLD buggy fallback, an unset evaluated count silently became N,
energy scaled linearly with N, and the test passed by accident. Under
this branch's conservative fix the fallback is 0, FLOPs collapse to 0,
and the `p10.energy_wh / p1.energy_wh` ratio divides 0 by 0.

The test's own docstring says "evaluated tokens (KV-cache model)" — it
was always meant to exercise the explicit-evaluated path, the API
call just didn't match. Pass `prompt_tokens_evaluated` explicitly so
the test now expresses the invariant it claims to.

Same one-line fix for `test_energy_wh_matches_direct_formula` plus an
explicit `flops > 0` sanity assertion — that test was passing trivially
with `0 == 0` after the conservative fallback fix, masking whether the
formula was actually being exercised.

No production code change in this commit. Verified locally: all 6
TestSavings tests pass; the 5 new regression tests added on this branch
still pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 16:59:24 -07:00
github-actions[bot] 50780a6a1d chore: update clone traffic data [skip ci] 2026-06-04 07:41:09 +00:00
Jon Saad-FalconandClaude Opus 4.7 f72abadf68 fix(desktop): AppImage env-strip + attach to existing healthy server (#496)
Closes #455. @wishwanto on Linux Mint hit two distinct bugs in the desktop launcher that ship together because they share the same boot_backend code path.

Bug A — AppImage hang on "starting server":
When the desktop is shipped as an AppImage on Linux, the AppImage runtime sets LD_LIBRARY_PATH to its temp-extracted lib dir. Children we spawn (uv, ollama) inherit that env, then Python's numpy/cryptography extensions dlopen the AppImage's mismatched libstdc++/libssl versions and python dies silently. Stderr drainer sees immediate EOF → GUI hangs forever.

Fix: new helper prepare_subprocess_for_appimage strips LD_LIBRARY_PATH, LD_PRELOAD, APPIMAGE, APPIMAGE_UUID, APPDIR, ARGV0 when $APPIMAGE is set. Linux-only via #[cfg(target_os = "linux")] — true no-op on macOS/Windows. Called at all 3 spawn sites: ollama sidecar, uv sync, uv run jarvis serve.

Bug B — Desktop force-kills the user's already-running `jarvis serve`:
Old code (post #437) ran fuser -k 8000/tcp / taskkill /PID /F on ANY HTTP response from :8000/health — including 200 OK from a healthy user-launched serve.

Fix: replace the indiscriminate kill with a health-aware decision tree:
  * 2xx /health → confirm with a 500ms-apart second probe, then attach (set server/model/ollama ready, return without spawning)
  * 503 → user-facing error "wait for engine or stop it"
  * other 4xx/5xx → user-facing error with lsof -i :8000 (unix) / netstat (windows) hint
  * Err → fall through to normal spawn (unchanged)

Adversarial review caught and fixed 5 real issues pre-commit:
  - HIGH: parallel cargo-test env mutation race → APPIMAGE_ENV_LOCK Mutex
  - HIGH: 2xx attach left model_ready/ollama_ready false → set both true before return
  - MEDIUM: #[allow(unused_variables)] suppressed lint on Linux too → cfg_attr
  - MEDIUM: single-probe attach trusted a 2s snapshot → confirmatory second probe
  - MEDIUM: /bin/true would silently mislead on Windows → HARMLESS_BIN const per platform

2 new unit tests; CI green on all gates including rust.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 19:54:06 -07:00
Jon Saad-FalconandClaude Opus 4.7 945dabbce7 fix(channels): use conversation_id (not channel type) as Discord reply destination (#495)
Closes #459. Reported by @jasonftl with a precise smoking gun: incoming ChannelMessage has channel="discord" (TYPE label) and conversation_id=<numeric channel id>, but the reply code was using cm.channel as the destination — Discord saw /channels/discord/messages and 404'd silently, blackholing every reply.

The bug was two field-mappings in channel_agent.py:_process_message (the happy path + the exception path):

  self._channel.send(
      msg.channel,                          # WRONG — TYPE label
      reply,
      conversation_id=msg.conversation_id,  # WRONG — channel id used as msg-ref-id
  )

The existing DiscordChannel.send() contract — proved by the existing test_send_with_conversation_id test — is:
  - first positional `channel` = native destination ID (Discord channel id)
  - `conversation_id` kwarg = native message ID for reply threading

Fix: swap both fields to the correct ones from ChannelMessage:

  self._channel.send(
      msg.conversation_id,                  # Discord channel id
      reply,
      conversation_id=msg.message_id,       # message id for threading
  )

Plus a defensive guard in discord_channel.py: when channel is empty, refuse fast with a clear warning instead of POSTing to /channels//messages and silently 404'ing.

3 new tests, 38 affected pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 19:54:03 -07:00
Jon Saad-FalconandClaude Opus 4.7 7506bcc0e4 fix(mcp): send Authorization: Bearer; auto-load MCP tools in ask/serve (#494)
Closes #461. Reported and empirically validated by @swilliams76360.

Two bugs prevented authenticated MCP servers (e.g. Home Assistant) from working with OpenJarvis:

1. StreamableHTTPTransport never sent Authorization: Bearer <token> — constructor didn't accept a token kwarg and _build_headers() never set the header. Authenticated MCP servers always returned 401.

2. jarvis ask and jarvis serve never iterated config.tools.mcp.servers — only loaded tools from ToolRegistry. MCP tools were silently dropped on every CLI invocation.

The reporter's 3-file fix was correct; the workflow investigation surfaced a 4th file (agent_manager_routes.py:695, identical broken code) and an adversarial-review catch (MCP clients in _build_tools would be GC'd on function return, closing transports mid-request — fixed by stashing on agent._mcp_clients).

Edits:

- transport.py: token kwarg + Authorization header (skips on empty/None — avoids malformed "Bearer " that triggers confusing 400s).
- mcp/loader.py (NEW): shared load_mcp_tools_from_config helper returning (tools, clients). Caller MUST hold the clients reference.
- builder.py + agent_manager_routes.py: extract cfg.get("token"), forward to transport.
- cli/ask.py: _run_agent calls the loader, dedupes by spec.name (registry wins), stashes clients on agent._mcp_clients.
- cli/serve.py: same pattern in main-agent AND channel-agent paths; mcp_clients initialised before the accepts_tools branch so the post-instantiation reference is always valid.

22 new tests (transport + loader + discovery updates), 179 total cli/server/mcp tests pass on this branch.

Adversarial review interrogated 10 angles — slotted-class attr safety, MCPConfig duck-typing, config.tools.mcp AttributeError risk, dedup precedence, token leak via str(exc), logger scope in serve.py, _mcp_clients shadowing, _channel_mcp_clients lifetime, empty-token future-compat, lazy-import cost shift. Nine non-issues; the tenth (theoretical token leak via httpx exception str()) assessed as low actual risk because the token is a header value, not URL-embedded.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 18:27:00 -07:00
Gilbert BarajasandClaude Opus 4.8 739bff417c feat(prompt): per-invocation persona scope (#380) (#493)
Adds --persona NAME / --persona none and a [memory_files].persona_name config field, resolving ~/.openjarvis/personas/<name>/{SOUL,MEMORY,USER}.md. Default (empty) preserves today's global-persona behavior exactly. Includes a path-traversal guard on persona names. Resolution lives in SystemPromptBuilder._resolve_persona so all callers benefit. Squad-derived: Ada (Qwen3.6-27B) authored the spec independently from the issue+code; Lucy (Qwen3-Coder-Next, 80B-A3B / ~3B active) implemented it; cross-function param threading completed in the test phase. 21 existing tests pass; +8 new persona-scope tests.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:59:47 -07:00
de3e86c544 fix(install): use requires-python range; bootstrap shims are Python-free (#476, #484) (#492)
Consolidates two reports that overlap in scope:

- #476 (@senki): install.sh hardcoded `--python 3.11` but
  pyproject.toml declares `requires-python = ">=3.10,<3.14"`. The
  installer should track the project's allowed range, not pin a
  conservative-three-years-ago version.
- #484 (@sanjayravit): install.sh crashed on hosts with no
  python3/python on PATH because `mark_done`, `beacon`, and
  `get_anon_id` all called $PY_CMD via inline Python heredocs.

This PR closes both gaps with five surgical edits to install.sh
(all behavior-preserving for the existing happy path; the bats
tests prove it):

1. get_anon_id — replace `python3 -c uuid` with POSIX
   `/dev/urandom + od` + bash substring expansion. Same UUID v4
   shape; works on Python-less hosts.

2. beacon — replace the 40-line Python heredoc with curl + a
   shell-built JSON payload. All inputs are from controlled
   sources (event ∈ fixed vocabulary, stage from stage_label(),
   numeric ids/codes from validated arithmetic, anon_id from a
   fresh UUID) so no general-purpose JSON escaping is needed.
   `|| true` is load-bearing — PostHog 5xx must never abort an
   install via the ERR trap.

3. mark_done — replace `python3 -c json.load+update+dump` with
   awk that regenerates the file from scratch. Idempotent: if
   the key is already marked, return early. Robust against
   prior format drift. wsl key is always rewritten last so a
   later FORCE_WSL=1 re-run correctly updates it.

4. parse_requires_python — new helper. Greps the project's
   requires-python field, handles inclusive (`<=3.13`) and
   exclusive (`<3.14`) upper bounds correctly, falls back to
   3.11 if pyproject can't be parsed (the previous hardcoded
   value — safe under the existing 3.10-3.13 range).

5. create_venv — call parse_requires_python instead of
   hardcoding 3.11. The existing uv-managed-Python fallback
   (from #444) still kicks in if the host doesn't have the
   target version installed.

Adversarial review caught two real bugs before commit:

- HIGH: parse_requires_python's exclusive-bound regex would
  also match the digits after `<=` (inclusive bound) and then
  incorrectly subtract 1 — producing 3.12 from `<=3.13`. Fixed
  by checking the inclusive form first.
- MEDIUM: the new "no Python on PATH" bats test silently skips
  symlinking `pgrep` on hosts where it isn't at /usr/bin or
  /bin (some minimal BusyBox configurations). Added an explicit
  "no matching process" fallback so start_ollama's check works.

New bats coverage:

- `install succeeds with no system Python on PATH (#484)` —
  builds a PATH that excludes python3/python and exercises the
  full install. Asserts state file is written and contains
  greppable step keys.
- `mark_done is idempotent — second mark of same key doesn't
  duplicate` — re-runs the install and verifies install_uv
  appears exactly once in install-state.json.
- `create_venv picks newest in requires-python range, not
  hardcoded 3.11 (#476)` — asserts uv was called with
  `--python 3.13` (the upper minor of `>=3.10,<3.14`).

The git stub now includes `requires-python = ">=3.10,<3.14"`
in its fake pyproject.toml so create_venv has something
realistic to parse.

@sanjayravit — your PR #484 motivated this consolidation;
closing that one as superseded with credit.

Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 16:59:29 -07:00
github-actions[bot] 58e822ff91 chore: update clone traffic data [skip ci] 2026-06-03 07:45:26 +00:00
Jon Saad-FalconandClaude Opus 4.8 b3cfa398b4 fix(config): honor system_prompt.prefix from config.toml (#482)
A [system_prompt] prefix set in config.toml was silently ignored: (1)
load_config()'s section allowlist dropped the [system_prompt],
[memory_files], [compression], and [skills] blocks entirely; (2)
SystemPromptConfig had no prefix field; (3) the builder never prepended
one.

Add the four blocks to the allowlist, add prefix: str = "" to
SystemPromptConfig, and prepend a "prefix" PromptSection at the front of
SystemPromptBuilder's frozen sections so it leads build() output and is
exposed via sections() (#457). Empty prefix emits no section — existing
configs are byte-for-byte unchanged.

Rebuilt on top of #457 (which refactored the builder to PromptSection
objects); the original #452 by @SoulSniper-V2 patched the pre-#457
method. Credit to @SoulSniper-V2. Adds the regression tests the original
PR lacked: config parse + prefix-prepended + empty-prefix-unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 20:14:42 -07:00
53fb42104e feat(rlm): expose real tool calls inside the REPL (#481)
* rlm: expose real tool calls inside the repl

* rlm: wrap long TypeError message in repl

* style(rlm): collapse over-wrapped TypeError to satisfy ruff format

The cherry-picked RLM tool-call work left a `raise TypeError(\n message\n)`
that `ruff format --check` rejects (the PR's own lint-fix commit broke
format). Collapse to `raise TypeError(message)`. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Eddie Richter <eddie.richter@amd.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 19:31:08 -07:00
Jon Saad-FalconandClaude Opus 4.8 f922811cba fix(tools): return labeled page content from web_search (#480)
web_search returned thin, unlabeled results (Tavily default search_depth,
**title**/url/content blob), so small models in the native_react loop
tended to echo URLs instead of synthesizing content (#390). Query Tavily
with search_depth="advanced" and format each result as a labeled block:

    ### {title}
    Source: {url}
    Summary: {content or snippet}

joined by `---` separators. DuckDuckGo fallback uses the same labeled
shape. Falls back to a result's `snippet` when `content` is absent.

Extracted from #448 (the web_search portion only; that PR also bundled
an unrelated install.sh rewrite, left out of scope here). Credit to
@sanjayravit for the original fix. Adds tests asserting the labeled
format, the snippet fallback, and search_depth="advanced"; updates the
max_results test for the new call signature.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 19:31:05 -07:00
seilk 097795567b feat(prompt): expose SystemPromptBuilder.sections() inspection API (#457) 2026-06-02 19:31:02 -07:00
github-actions[bot] 98b79eca72 chore: update clone traffic data [skip ci] 2026-06-02 07:41:56 +00:00
Jon Saad-FalconandClaude Opus 4.8 58f7f717ae docs(test): use canonical github.io install URL in run-as-root case (#473)
The run-as-root install case doc still showed the retry command using
the community-operated openjarvis.ai domain, whose TLS is broken and
which the project does not control (#337). Point it at the canonical,
project-controlled GitHub Pages URL, matching README and the install
docs. Documentation only — the bats test it describes asserts exit
code + "root" in stderr and has no URL dependency.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:51:16 -07:00
Jon Saad-FalconandClaude Opus 4.8 4b4fd587b2 fix(frontend): send local API key as Bearer on /v1 + /api requests (#471)
When `jarvis serve` runs with an API key configured, AuthMiddleware 401s
every /v1 and /api request that lacks a Bearer token. The frontend never
sent one, so telemetry, managed-agents, savings, etc. all failed (#266).

- Add getApiKey() (reads settings.apiKey, with optional
  VITE_OPENJARVIS_API_KEY build-time override) and authHeaders() to
  api.ts, plus an apiFetch() wrapper that prepends getBase() and injects
  the Bearer header on every local-server call. Route all /v1 + /api
  fetches through it so none can omit auth.
- Add `apiKey` to the Settings model (store.ts) and a password field in
  Settings → Connection so users can enter it.

Keyless local servers are unaffected: with no key, no Authorization
header is sent (byte-for-byte unchanged). The Supabase savings path keeps
its own anon key — not conflated with the local key.

Bootstraps vitest (no prior frontend test runner) + a `test` script, and
adds api.auth.test.ts covering getApiKey/authHeaders. Verified: tsc
--noEmit clean, vitest 6/6, vite build succeeds.

Deferred (not in scope): WebSocket auth (browsers can't set WS headers)
and Tauri auto-injecting a generated key.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:25:14 -07:00
Jon Saad-FalconandClaude Opus 4.8 690b95e050 perf(serve): parallelize engine discovery + async version check (#470)
`jarvis serve` startup was slow on two counts addressed here:

1. discover_engines() probed each registered engine's health() serially,
   and every probe is a blocking network check with its own ~2s timeout —
   so N dead/slow localhost ports cost N*2s. Run the probes concurrently
   in a ThreadPoolExecutor; the existing healthy.sort() normalizes order,
   so the result is identical to the serial version. health() impls are
   read-only on per-instance HTTP clients with no shared mutable state.

2. The PyPI update check ran a blocking urlopen (up to 3s on a cache
   miss) inline before dispatch, delaying every command. Move it to a
   daemon thread — it's best-effort and never raises.

Together these remove ~10-30s from cold startup. Adds a regression test
asserting discovery probes overlap (concurrency), not just that output
is unchanged (covered by existing tests).

Note: the larger ~30-40s win — the duplicate SystemBuilder.build() in
serve.py — is NOT addressed here; it's not redundant (the second build
wires a JarvisSystem the inline path never constructs) and needs a
design pass. Deferred.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:17:38 -07:00
Jon Saad-FalconandClaude Opus 4.8 d1ad3316b6 build(rust): pin MSRV to 1.88 + add rust-toolchain.toml (#469)
The Rust workspace fails to build on stable 1.86/1.87 with cryptic E0658
errors deep in dependencies: rig-core uses let-chains and
openjarvis-skills uses `is_multiple_of`, both stabilized in 1.88 (#252).

Add a rust-toolchain.toml pinning channel 1.88 (rustup then auto-selects
a working toolchain instead of erroring mid-build) and declare
rust-version = "1.88" in [workspace.package] to self-document it.

Because the toolchain pin makes CI's `cargo clippy -D warnings` run under
1.88 — whose clippy enables `uninlined_format_args` — also apply the
mechanical `format!("{}", x)` -> `format!("{x}")` rewrites across the
workspace (via `clippy --fix`; string output is identical, no logic
change). Verified clippy + fmt + `cargo test --workspace` clean on BOTH
1.88 and current stable.

Verified locally: cargo +1.86 and +1.87 fail (E0658), +1.88 builds and
tests cleanly. Supporting true 1.86 is infeasible without downgrading
rig-core below the versions exposing the token-usage symbols we use —
deferred.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:17:33 -07:00
2b14315f3c fix(security): detect Rust at import time + SSRF Python fallback (#467)
* fix(security): detect Rust at import time + SSRF Python fallback (#225)

RUST_AVAILABLE was hardcoded to True, so the exported flag never
reflected reality and the pure-Python fallbacks it was meant to gate
were unreachable.

- _rust_bridge: compute RUST_AVAILABLE dynamically by probing the
  compiled extension once at import time.
- security.ssrf: check_ssrf now falls back to the existing
  _check_ssrf_python implementation when the Rust extension is not
  built, instead of raising ImportError. The SSRF guard is
  security-critical and must never be silently skipped or crash just
  because Rust was not compiled.
- tools.browser: drop the `except ImportError: pass` around the SSRF
  check, which previously disabled SSRF protection entirely on installs
  without the compiled backend (internal/metadata endpoints reachable).
- tests: cover the Python fallback path (metadata IP, private IP, and
  public URL) with RUST_AVAILABLE patched False.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(security): make test_no_hostname backend-agnostic

The cherry-picked #451 fix adds a pure-Python SSRF fallback, but
test_no_hostname asserted the Rust-specific message "Invalid URL". The
Python fallback returns "No hostname in URL" for the same input, so the
SSRF suite failed on exactly the uncompiled-install path #451 targets
(in CI the Rust extension is built, masking it).

Assert the security behavior (URL blocked, non-None reason) and accept
either backend's wording, so the suite passes on both the Rust and
Python paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(tools): update browser SSRF test for non-bypassable check

The #225 fix removes the `except ImportError: pass` that silently
disabled the SSRF check in browser navigation. The existing
test_execute_ssrf_module_missing asserted that very anti-pattern ("skip
check and proceed"), so it failed once the swallow was removed.

Replace it with tests that assert the SECURE behavior: the SSRF check
runs unconditionally and is honored (a private-IP URL is blocked even
when navigation would otherwise succeed), and a public URL still
navigates. check_ssrf's pure-Python fallback means the import never
fails anymore, so the old skip path no longer exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Rahul <therahulll56@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 11:57:44 -07:00
09b19193fe fix(server): load SOUL.md / USER.md context in streaming chat (#449)
* fix(server): load SOUL.md / USER.md context in streaming chat

* refactor+test: extract _build_managed_system_prompt + cover #431

The streaming persona fix was inline and untestable without a live
engine. Extract it into _build_managed_system_prompt (matching this
module's extract-and-unit-test pattern for the streaming helpers) and
add regression tests:
- SOUL.md persona is injected into the streaming system prompt (#431),
- the agent's own template is preserved,
- output matches a directly-constructed SystemPromptBuilder (parity with
  the CLI/ask path — the whole point of the fix).

Behavior unchanged from the original PR; this only makes it testable and
locks in CLI parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:31:19 -07:00
Jon Saad-FalconandClaude Opus 4.8 b5926400e2 fix(engine): retry without temperature on unsupported_value 400 (#466)
Some OpenAI models (e.g. gpt-5, the default for a fresh cloud install)
reject a non-default temperature with HTTP 400 "Unsupported value:
'temperature' does not support 0.7 ... Only the default (1) value is
supported." — so the user's very first prompt fails (#426).

Detect that specific 400 (param=temperature + unsupported_value/"only the
default"/"does not support") and retry the create() once without
temperature, mirroring the tools-400 retry in the Ollama and
OpenAI-compat engines. Unrelated 400s are re-raised unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 10:26:17 -07:00
Jon Saad-FalconandClaude Opus 4.8 3398e0c10b fix(cli): escape exception text in agents-list error handler (#465)
`jarvis agents list` crashed with a secondary Rich MarkupError when the
underlying exception message contained markup metacharacters like
`[...]`: the error handler did `console.print(f"[red]Error: {exc}[/red]")`,
and Rich re-parsed the interpolated message as markup (#297).

Escape the dynamic message with rich.markup.escape and keep only the
static "Error:" label styled, so the original error surfaces cleanly
instead of a traceback. Adds a regression test that reproduces the
MarkupError via an exception message with brackets.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 10:26:11 -07:00
Jon Saad-FalconandClaude Opus 4.8 bd1ab115ec fix(engine): convert apple_fm_shim cumulative snapshots to deltas (#464)
Apple FM's stream_response yields cumulative text snapshots, but
OpenAI-compatible clients concatenate delta.content — so streamed
responses were duplicated/stuttered (#378). Diff each snapshot against
the last and emit only the incremental suffix; fall back to the full
snapshot if the model revises earlier text, and skip empty deltas.

Rebased on #377 (apple_fm_sdk migration): uses the options= streaming
API. Adds a stubbed-SDK regression test asserting deltas are
incremental, not cumulative.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 10:11:53 -07:00
5270149ea5 fix(engine): modernize apple_fm_shim for the public apple-fm-sdk (#377)
* fix(engine): modernize apple_fm_shim for the public apple-fm-sdk

Apple released the official Foundation Models Python SDK as
`apple/python-apple-fm-sdk` (import name `apple_fm_sdk`, distribution
name `apple-fm-sdk`). The shim's `import apple_fm` predates the public
SDK and the per-call API has since moved as well; running the shim
against current `apple-fm-sdk` v0.1.1 fails on import, then on the
`/health` call shape, and again on every `respond` / `stream_response`
keyword.

This change brings the shim up to date with the real SDK without
altering its external OpenAI-compatible contract:

- Import `apple_fm_sdk` (the public name). Update the missing-package
  error to point at the GitHub repo since the SDK isn't on PyPI;
  installation is `uv pip install -e <clone>`.
- `SystemLanguageModel.is_available()` is an instance method and now
  returns `(bool, SystemLanguageModelUnavailableReason | None)`. The
  health endpoint instantiates the model and unpacks the tuple, and
  surfaces the reason string when the model is unavailable.
- `LanguageModelSession.respond` and `.stream_response` no longer
  accept `max_tokens` / `temperature` positionally; they take a
  `GenerationOptions` instance via the `options` kwarg. The shim now
  builds a `GenerationOptions(temperature=..., maximum_response_tokens=...)`
  from each `ChatRequest` and threads it through both paths.
  `temperature` is now actually honored (the previous code dropped it
  silently).
- Add `response_model=None` to the `/v1/chat/completions` decorator so
  FastAPI doesn't try to build a Pydantic field for the
  `JSONResponse | StreamingResponse` union return type — that fails
  with the FastAPI version pinned in the `server` extra.
- Update the module docstring to reference macOS 26 + Apple
  Intelligence (the SDK's actual minimum), not macOS 15.

## How was this tested?

- `uv pip install -e ./python-apple-fm-sdk` against a local clone of
  Apple's repo on macOS 26 / M5 Max with Apple Intelligence enabled.
- `uv sync --extra dev --extra server` then `uv run uvicorn
  openjarvis.engine.apple_fm_shim:app --host 127.0.0.1 --port 8079`.
- `GET /health` → `{"status": "ok"}` (200).
- `GET /v1/models` → lists `apple-fm`.
- `POST /v1/chat/completions` with messages + temperature +
  max_tokens → returns a real Apple Intelligence completion.
- End-to-end through OpenJarvis: add `[engine.apple_fm]
  host = "http://localhost:8079"` to `~/.openjarvis/config.toml`,
  then `jarvis ask --engine apple_fm --model apple-fm "..."` returns
  the same Apple FM response routed through the OpenAI-compatible
  engine wrapper.
- `uv run ruff check src/openjarvis/engine/apple_fm_shim.py` and
  `uv run ruff format --check src/openjarvis/engine/apple_fm_shim.py`
  both pass.

* test(engine): add stubbed-SDK tests for apple_fm_shim migration

Covers the apple_fm -> apple_fm_sdk migration: GenerationOptions carries
temperature + max_tokens and is passed via options= to respond() and
stream_response(), and /health unpacks the (available, reason) tuple
from SystemLanguageModel().is_available().

The real apple-fm-sdk is not installable in CI (not on PyPI; macOS 26 +
Apple Intelligence only), so the tests inject a stub SDK into
sys.modules. They verify the shim's OpenAI-compat wiring, not Apple's
real SDK behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:23:43 -07:00
a13d8a909d fix: copy package includes in Docker builds, incl. GPU (#450)
* fix: copy package includes in Docker build

* fix: copy package includes in GPU Docker builds too

PR #450 fixed the CPU Dockerfile but Dockerfile.gpu and
Dockerfile.gpu.rocm have the identical bug: they COPY src/ then
`uv pip install ".[server]"` without copying the non-src
force-include paths (scripts/install, deploy/windows), so hatchling's
wheel build fails the same way on GPU images (#447).

Adds the two COPY lines to both GPU Dockerfiles and generalizes the
regression test to guard every wheel-building Dockerfile (CPU + both
GPU variants) instead of only the CPU one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:23:22 -07:00
github-actions[bot] 0fcee8236e chore: update clone traffic data [skip ci] 2026-06-01 07:50:11 +00:00
Jon Saad-FalconandClaude Opus 4.8 a08282c3e9 fix(server): stream tool_calls instead of agent filler on tool requests (#460)
When a client streams (stream:true) with explicit `tools`, the server
routed to the agent stream bridge, which ignored request_body.tools, ran
the agent's own tool loop, and word-split filler content into fake token
deltas — dropping the caller's tool_calls. This is the streaming analog
of #414 (whose non-streaming fix was #454).

Now stream+tools bypasses the agent and streams the model's raw
function-calling decision via engine.stream_full(), emitting OpenAI-shape
tool_calls deltas and a tool_calls finish_reason. Adds tool_calls to
DeltaMessage and removes the now-dead _handle_agent_stream.

Verified end-to-end on Ollama (qwen3.5:4b) plus a unit regression test.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:09:36 -07:00
Jon Saad-FalconandClaude Opus 4.7 d13006263e fix(server): bypass agent on /v1/chat/completions when caller passes tools (#454)
Closes #414.

Root cause: routes.py:151 unconditionally routed non-streaming /v1/chat/completions through _handle_agent when an agent was registered. _handle_agent calls agent.run(input_text) which IGNORES request_body.tools entirely, runs the agent's own internal tool loop with its own (different) tool spec, and returns only result.content — never result.tool_calls. The "Understood. If you have another request..." filler is not hardcoded anywhere in OpenJarvis (the cloud_router.py:126 "Understood." is a different Gemini-only injection). It's the model's actual generic response when the agent re-prompts it without the user's intended tools.

Fix: one conditional. Skip _handle_agent when request_body.tools is present — the client is asking for raw OpenAI-compat function-calling, so route to _handle_direct which preserves tool_calls. Plus a forward-looking comment documenting this as an intentional trade-off so a future maintainer doesn't naively remove the guard.

Streaming path left intact (its asymmetry — "use agent_stream WHEN tools present" — is intentional per the existing comment at lines 143-145; reporter's repro is non-streaming).

Two regression tests:
- test_with_tools_bypasses_agent: mocks engine+agent, asserts tool_calls survives, agent.run is NOT called.
- test_without_tools_still_uses_agent: pins existing behavior for the no-tools path.

Reported by @gilbert-barajas — the side-by-side curl repro made the triage tractable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 14:12:19 -07:00
github-actions[bot] 6fc644e209 chore: update clone traffic data [skip ci] 2026-05-31 07:21:34 +00:00
Jon Saad-Falcon 21f1becb4d feat(desktop): single default model + custom OpenAI-compatible endpoints (#453) 2026-05-30 19:34:51 -07:00
Jon Saad-Falcon d9af1eca45 fix(desktop): stop auto-pulling the entire Qwen3.5 model ladder (#446) 2026-05-30 19:31:22 -07:00
Jon Saad-FalconandClaude Opus 4.7 a901fcc12a fix(install-ps1): make install.ps1 actually zero-friction on a fresh Windows (#445)
PR B of the post-cluster install-hardening pair (PR A: install.sh #444 — merged). The audit found native Windows was the worst path at 2/10 zero-friction: the installer refused on every missing prereq (Python / git / Ollama), didn't pull a model, and gave the user no `jarvis` command after install. This closes those gaps.

Changes in deploy/windows/install.ps1:

1. Auto-install Python via winget when missing (was: hard refuse).
2. Auto-install git via the same Install-WithWinget helper.
3. Auto-install Ollama via the official OllamaSetup.exe (NSIS /S flag, $ProgressPreference SilentlyContinue for the 150 MB download).
4. Wait for Ollama daemon health before pulling the model (60s poll on `ollama list`).
5. Pull qwen3.5:2b foreground (~1.5 GB) — banner is honest if pull failed.
6. jarvis.cmd shim at %LOCALAPPDATA%\OpenJarvis\bin\ added to User PATH (deduped against expanded form so re-runs don't append). Shim uses %~dp0..\src to self-locate and uv from PATH so a future uv update can't break it.
7. Pre-check admin for the scheduled-task path; refuse fast in -Service branch if not elevated, default to skip-with-explanation in the interactive branch.
8. Final banner tells the truth: 'jarvis' if all good, 'jarvis doctor' if model missing, 'open a new PowerShell' if User PATH was just updated.

Adversarial review caught 5 real bugs before commit:
- CRITICAL: GetEnvironmentVariable returns REG_EXPAND_SZ raw → %LOCALAPPDATA% wasn't expanded → just-installed Python invisible. Wrap in ExpandEnvironmentVariables.
- HIGH: Ollama NSIS silent flag is /S not /silent (wrong flag opens GUI, hangs install).
- MEDIUM: PS 5.1 progress bar makes Invoke-WebRequest 30x slower.
- LOW: -Service branch missed isAdmin precheck.
- LOW: PATH dedup missed unexpanded %VAR% entries → duplicate every re-run.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 19:20:49 -07:00
github-actions[bot] f833193454 chore: update clone traffic data [skip ci] 2026-05-30 07:03:34 +00:00
Jon Saad-FalconandClaude Opus 4.7 0591a80448 fix(install): make install.sh actually zero-friction on a fresh laptop (#444)
PR A of the post-cluster install-hardening pair. A multi-agent audit (4 install paths × 2 agents each) showed the README's "copy this one-liner and chat" claim broke on every fresh laptop because of missing git, missing Python 3.11, a racing Ollama daemon, and silently-swallowed model-pull failures. This PR closes the macOS / Linux / WSL2 gaps.

Changes in scripts/install/install.sh:

1. Auto-install missing tools (was: hard refuse):
   - macOS: xcode-select --install + 10-min poll. Refuses fast under SSH (no display for the dialog).
   - Linux: detects apt-get / dnf / yum / pacman / zypper / apk. Pre-checks `sudo -n true` and refuses fast with actionable guidance if sudo would prompt (stdin is the curl pipe — any prompt silently hangs under `set -euo pipefail`). Uses `;` not `&&` between apt-get update and install. Skips sudo entirely if already root.

2. Auto-install Python 3.11 via uv when missing. Captures real errors to $STATE_DIR/venv-create.err so disk-full / permission-denied isn't hidden behind "downloading managed Python".

3. Replace `sleep 1` after `ollama serve` with a 60-second poll on `ollama list`. Caller guards with `|| true` so timeout surfaces as a warning in the final banner instead of aborting under `set -e`.

4. Track MODEL_PULL_OK + PATH_MODIFIED. The completion banner now tells the truth: if the model pull failed, point at `jarvis doctor` (not bare `jarvis` which would crash). If PATH was just written to ~/.bashrc / ~/.zshrc, print the exact `source <rc> && jarvis` so it works in the same shell.

Adversarial review caught 5 real bugs before commit:
- SSH/headless macOS xcode-select hang → pre-detect SSH session
- sudo no-TTY silent abort → `sudo -n true` precheck
- wait_for_ollama timeout tripping ERR trap → `|| true` at call sites
- swallowed venv stderr hiding diagnostics → capture to log + surface on fallback failure
- contradictory "PATH new + model missing" banner → conditional NEXT_CMD

All 26 install bats tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 20:06:15 -07:00
Jon Saad-FalconandClaude Opus 4.7 d8a8cd8df7 docs(readme): switch demo reel border from dark grey to light grey (#443)
Re-encode WebP with #cccccc (4px) replacing #3a3a3a.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 18:26:50 -07:00
Jon Saad-FalconandClaude Opus 4.7 c48784e304 docs(readme): scale demo reel to 75% width, add dark grey frame (#442)
- Resize embed: width="100%" → width="75%".
- Re-encode WebP with a 4px #3a3a3a border baked in (GitHub's README
  sanitizer doesn't reliably honor inline CSS borders on <img>, so the
  frame is part of the asset).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 18:24:02 -07:00
Jon Saad-FalconandClaude Opus 4.7 e29ed9986e docs(readme): replace stripped <video> with animated WebP demo reel (#441)
GitHub's README sanitizer strips <video> tags sourced from release
assets, so the previous embed never rendered. Swap for an animated
WebP (no audio anyway) at assets/openjarvis_demo_reel.webp — renders
inline on every Markdown viewer with auto-loop.

960px wide, 15fps, lossy q=60, 4.5MB.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 18:05:40 -07:00
Jon Saad-FalconandClaude Opus 4.7 a7b9e09305 docs(readme): tighten Install + Quick Start; add platform-guides hub (#440)
Tidies the README after the Windows cluster (#432-#438) landed. The Install section had grown three nested Windows sub-bullets, and Quick Start + Starter Configs both re-listed the same presets. -49 net lines, no content lost.

- `## Installation` is now a 3-row table (macOS·Linux·WSL2 / Native Windows / Desktop GUI), one one-liner each. Per-platform detail moves to the docs.
- `## Quick Start` absorbs Starter Configs into one preset table + one example + a row of per-preset deep-dive links.
- New "Platform-specific guides" hub at the top of `docs/getting-started/install.md` links to the existing `macos.md` / `linux.md` / `wsl2.md` / `windows-native.md` siblings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 17:46:58 -07:00
Jon Saad-FalconandClaude Opus 4.7 efa64beda8 docs(readme): embed demo reel video in hero section (#439)
* docs(readme): embed demo reel video in hero section

Adds a centered video block between the badges and the Documentation
links, framed by horizontal rules. URL is a placeholder pending the
user-attachments upload.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(readme): point demo reel at readme-media release asset

Replaces the placeholder with a <video> tag sourced from the dedicated
readme-media prerelease.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 16:48:52 -07:00
Jon Saad-FalconandClaude Opus 4.7 a79cd6f5e9 feat(windows): Phase-1 native install + scheduled-task service (#438)
Closes Phase-1 of #298 (Native Windows Support RFC).

Reverses the long-standing "native Windows is not supported" stance. PowerShell installer at `deploy/windows/install.ps1` (published to https://open-jarvis.github.io/OpenJarvis/install.ps1) plus a `jarvis-service.ps1` scheduled-task helper that mirrors `deploy/systemd/openjarvis.service` and `deploy/launchd/com.openjarvis.plist`. Loopback default (127.0.0.1, no API key) — same as launchd.

One-liner install:
    irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex

Adversarial review caught and fixed two real bugs pre-commit:
1. `irm | iex` drops `param()` flags — added env-var fallbacks (OPENJARVIS_SKIP_SERVICE / OPENJARVIS_SERVICE / OPENJARVIS_FORCE).
2. Scheduled tasks don't inherit the registering session's env — the LAN-exposed `OPENJARVIS_API_KEY` path now persists the key to User scope so the task's logon environment can read it.

Supersedes #434 (the guidance-only "use WSL2" install.ps1).

Massive thanks to @SeCuReDmE-main-dev for the careful RFC #298 — the three-phase decomposition (install / service / shared-memory bridge) is exactly the right framing. This PR ships Phase-1 and Phase-2 of the RFC fused into one release; Phase-3 (shared memory bridge) remains future work.

Thanks also to @KadenBordeaux for raising #334 ("'bash' is not recognized as the name of a cmdlet"). That report is what made this whole Windows-support cluster a priority — without your bug report the unsupported stance would still be in the README. The friction you hit motivated #432 (numpy/python cap), #433 (CLI startup resilience), #436 (python discovery helpers), #437 (desktop launcher fix), and this PR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:44:10 -07:00
Jon Saad-FalconandClaude Opus 4.7 b6a8280d68 fix(desktop): detect early child exit + drain stderr to avoid pipe stall (#437)
Closes #309.

Three root-cause fixes in `frontend/src-tauri/src/lib.rs`:

1. **Background stderr drainer.** `jarvis serve` was spawned with `stderr(Stdio::piped())` but its 4 KB Windows pipe buffer would fill from the startup log volume before the child could bind its HTTP port — hanging the process mid-startup. Now a tokio task drains stderr from the moment of spawn into a rolling 16 KB tail buffer; the pipe never fills.
2. **`try_wait()` early-exit detection.** The old `wait_for_url` was blind to child crashes — uv-not-on-PATH or extension-import failures would silently waste the full 10-minute timeout. The new `wait_for_jarvis_health` checks the child's exit status each iteration and surfaces the stderr tail with the exit code.
3. **HTTP 503 distinguished from connection-refused.** A 503 means the server bound but the inference engine failed to load (terminal); we now surface the body text immediately rather than polling for 10 minutes.

Adversarially reviewed before commit — caught and fixed a stderr-pipe back-pressure regression on the Ready path before push.

Huge thanks to @xoomarx for the careful #309 repro — without that exact symptom signature ("stuck on Starting api server" on Windows) the pipe-buffer deadlock would have been very hard to identify from the user-visible behavior alone.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:36:49 -07:00
caa3adbbce fix(windows): cross-platform python discovery + browser open helpers (#436)
Reimplements the useful parts of #385 cleanly.

Adds two small cross-platform helpers under `openjarvis.core.utils`:
- `get_python_executable()` — prefers `python3`, falls back to `python` for Windows / minimal distros that only ship the unversioned name.
- `open_browser(url)` — `webbrowser.open` by default; on Windows uses `cmd /c start "" <url>` to avoid console-host edge cases.

Swapped at every hardcoded `python3` / `webbrowser.open` site: `connectors/oauth.py`, `evals/scorers/livecodebench.py`, `scripts/oauth_all.py`, `scripts/install/install.sh` (adds `PY_CMD` detection block), `scripts/quickstart.sh` (adds `MINGW*|MSYS*|CYGWIN*) cmd /c start` case), and the two affected test files. Test files wrap `get_python_executable()` in `shlex.quote()` before interpolating into `shell=True` strings — Windows interpreter paths often contain spaces.

Deliberately different from #385: `openjarvis.core.__init__` does NOT re-export `DEFAULT_CONFIG_DIR` (would have raised ImportError because it's in `openjarvis.core.config`, not the package `__init__`; re-exporting would also force eager import of the heavy config module at every `import openjarvis.core`). `oauth.py` keeps `from openjarvis.core.config import DEFAULT_CONFIG_DIR` alongside the new `from openjarvis.core import open_browser`.

Original API surface and call-site sweep by @sanjayravit in #385 — huge thanks for the careful Windows-compatibility audit. This PR preserves your design while fixing the ImportError edge cases caught during review.

Co-Authored-By: sanjayravit <sanjayravit@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:28:45 -07:00
Jon Saad-FalconandClaude Opus 4.7 ad7c86495f fix(cli): don't let a broken numpy crash CLI/server startup on Windows (#433)
Addresses #404 (unable to launch / fully download) and contributes to #309 (stuck on starting api server) by removing the eager numpy import paths that fail hard when a Windows host has a partially-installed or cp314-incompatible numpy.

What changed:

- `src/openjarvis/connectors/embeddings.py` / `hybrid_search.py`: numpy imports are now lazy (inside the method that needs them) with a `TYPE_CHECKING` guard for annotations. Default-argument evaluation no longer touches numpy at module import.
- `src/openjarvis/cli/__init__.py`: the `deep_research_setup` command import is now guarded behind a `try/except Exception` so an OverflowError or ImportError during its module load doesn't crash the entire CLI.
- New regression test in `tests/cli/test_cli.py`: `test_importing_cli_does_not_import_numpy` spawns a subprocess and asserts `numpy` is not in `sys.modules` after `import openjarvis.cli`. Guards against future eager-numpy regressions on Windows.

Reported by @Tentacle39 in #404 and seen alongside @xoomarx's #309. Thanks to both — the Windows-only crash signature made this hard to diagnose without your repros.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:18:47 -07:00
Jon Saad-FalconandClaude Opus 4.7 f1b0df6b6b fix(packaging): cap requires-python to <3.14 for Windows numpy wheels (#432)
Fixes #350 (API server fails to start on Windows because numpy has no cp314 wheels).

Caps `requires-python` to `>=3.10,<3.14` in pyproject.toml so uv resolves a Python that has working numpy wheels on Windows. Extends the `test-windows` CI matrix to run on both Python 3.12 and 3.13 to keep the cap honest.

Reported by @RizaldyMongi in #350. Thanks for the careful repro — the Windows-only fallout was tricky to reproduce on Linux/macOS and the issue gave us the exact symptom signature to triage from.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:10:08 -07:00
Jon Saad-FalconandClaude Opus 4.7 c8970cc387 chore: restore green ruff lint on main (#435)
Auto-fixes 13 inherited ruff errors in agents/hybrid/skillorchestra/* and evals/scorers/swebench_harness.py (I001/F401/W291/W292/E703), strips trailing whitespace in evals/eval_orchestrator.py, wraps a long signature in speech/cartesia_tts.py, and extends per-file-ignores for `agents/hybrid/**` and `agents/research_loop.py` (research code with long prompt strings — same rationale as the existing evals relaxation).

Unblocks lint CI for the rest of the Windows-fix cluster (#432, #433, #436, #437, #438).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 16:03:53 -07:00
Andrew Park 48dcb40be4 fix(desktop): surface backend error frames in Deep Research stream (#428) 2026-05-29 09:48:59 -07:00
Andrew Park 94c515bba5 hybrid: six local+cloud paradigm agents (advisors, conductor, minions, archon, skillorchestra, toolorchestra) (#423) 2026-05-29 09:48:27 -07:00
diken97 c9290aa77f feat(tts): make Cartesia language configurable (#420) 2026-05-29 09:45:24 -07:00
github-actions[bot] cf29ef710e chore: update clone traffic data [skip ci] 2026-05-29 07:23:59 +00:00
Jon Saad-Falcon 3aa24b2709 Merge pull request #429 from open-jarvis/feat/opencode-agent
feat(agents): add OpenCodeAgent — run opencode on a local engine
2026-05-28 20:38:46 -07:00
krypticmouseandClaude Opus 4.7 c6f172cbdc docs(agents): add eval-backed model-capability guidance for OpenCodeAgent
A 27B local model (Qwen3.5-27B via vLLM) passed a 7-task coding suite cleanly
(create/edit/bug-fix/implement-to-pass-tests/multi-file, verified by running
code + pytest); an 8B model was unreliable. Document so users pick a capable
model for real coding work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 02:59:35 +00:00
krypticmouseandClaude Opus 4.7 00b85b71dd fix(agents): unwrap wrapped engines to derive opencode provider URL (+ clear guard)
The real `jarvis ask --agent opencode` path passes an InstrumentedEngine
(telemetry wrapper) whose underlying engine — and its `_host` — lives at
`_inner`. The base-URL derivation only checked the top object, so it returned
"", no provider was registered, and opencode 500'd on `openjarvis/<model>`.
Direct construction with a raw engine masked this; only the CLI path exposed
it.

- _derive_openai_base_url now unwraps up to 6 wrapper layers
  (`_inner`/`_engine`/`_wrapped`) to find `_host`/`base_url`.
- run() resolves the provider/model spec up front and, when it genuinely
  can't (no base URL + bare model name), returns a clear actionable error
  instead of letting opencode 500.

Tests: wrapper-unwrap derivation + unresolvable-provider guard. 20 passed,
ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 01:10:25 +00:00
krypticmouseandClaude Opus 4.7 6a011b4abb fix(agents): deterministic opencode permissions + no workspace pollution
Two hardening fixes for headless use, found while verifying real runs:

- Permissions: opencode's interactive default *asks* before some actions (and
  `plan` asks before bash), which would block forever with no TTY. The agent
  now writes an explicit permission policy: `build` allows edit+bash, `plan`
  denies them (read-only), overridable via a `permission` kwarg. Verified: a
  bash task completes without hanging and plan mode refuses to create files.
- Config no longer written into the user's workspace. We now write provider +
  permission config to a private temp file referenced via `OPENCODE_CONFIG`
  (confirmed honored by opencode), keeping the workspace clean while opencode
  still operates there as cwd.

Tests updated to cover `_build_config` (provider presence, per-mode
permission, custom override, no-pollution). 18 passed, ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 01:07:00 +00:00
krypticmouseandClaude Opus 4.7 20f90f3ca0 fix(agents): recover opencode tool-results from the full turn, not just the final message
A real-model E2E (Ollama qwen3:8b) exposed what unit tests with synthetic
parts missed: `POST /session/{id}/message` returns only the final assistant
message (text/reasoning), while ToolParts live in intermediate assistant
messages. The parser was reading the wrong message, so tool_results was always
empty even when opencode actually edited files.

- run(): after the prompt POST, GET /session/{id}/message and collect parts
  across the whole turn for tool extraction (content still comes from the
  final message). Verified against a live opencode session.
- _extract_tool_results: success now keys on opencode's state.status ==
  "completed" (states: completed | error | running | pending).
- Test now feeds the real full-turn message shape and asserts the write tool
  is recovered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 00:56:36 +00:00
krypticmouseandClaude Opus 4.7 4fa9163ea9 feat(agents): add OpenCodeAgent — run the opencode coding agent on a local engine
Adds an `OpenCodeAgent` (registry key `opencode`) that delegates coding tasks
to opencode (https://opencode.ai, MIT) while keeping inference local-first:
OpenJarvis's engine backs opencode via an OpenAI-compatible provider.

How it works:
- Derives an OpenAI-compatible base URL from the engine (e.g. Ollama/vLLM at
  `<host>/v1`) and writes an `opencode.json` registering it as an
  `@ai-sdk/openai-compatible` provider (`openjarvis/<model>`).
- Spawns a headless `opencode serve` (loopback, random port), waits for
  `/global/health`, then drives a session: `POST /session` →
  `POST /session/{id}/message` with `model={providerID,modelID}` + agent
  (`build`/`plan`) → parses message `parts` (text → content, tool → tool_results)
  into an `AgentResult`. `close()` disposes the server.
- opencode is an external binary (not bundled); `run()` returns a clear,
  actionable error when it's missing, mirroring ClaudeCodeAgent's degradation.

Verified end-to-end against the real opencode binary wired to a stub
OpenAI-compatible engine: opencode called the local endpoint and the agent
parsed the response (content/finish/model) correctly. Unit tests cover part
parsing, base-URL derivation, provider-config writing (incl. merge), binary
detection, graceful degradation, and run() parsing with a mocked client — 15
passed, ruff clean. Registered via the standard try/except import in
agents/__init__.py; documented in docs/user-guide/agents.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 00:41:22 +00:00
Robby Manihani 23ed17fe12 Refine CLI branding: readable wordmark, new tagline, research footer (#424) 2026-05-28 12:07:50 -07:00
Robby Manihani 26285e56fa fix(agents): wire continuous-agent Interact tab end-to-end + CLI workflow polish (#425) 2026-05-28 12:07:03 -07:00
Andrew Park c6448f7cf2 fix(desktop): preselect model in onboarding so first chat doesn't 400 (#427) 2026-05-28 11:19:52 -07:00
Jon Saad-Falcon 6a27094658 Update README.md 2026-05-28 10:30:12 -07:00
github-actions[bot] c14c7c24c0 chore: update clone traffic data [skip ci] 2026-05-28 07:24:03 +00:00
Jon Saad-Falcon 75d26d23ea Merge pull request #422 from open-jarvis/fix/persona-files-persistent-agents
fix(agents): persistent agents honor SOUL.md / MEMORY.md / USER.md persona files (#376)
2026-05-27 10:57:12 -07:00
krypticmouseandClaude Opus 4.7 4ece9a933e fix(agents): persistent agents honor SOUL.md / MEMORY.md / USER.md persona files (#376)
SystemPromptBuilder (which loads the SOUL/MEMORY/USER persona files) was wired
only into the one-shot `jarvis ask` path, so persistent agents run through
AgentExecutor ignored persona entirely. The naive "pass prompt_builder" fix is
insufficient: `prompt_builder` was dropped at every __init__ hop
(ToolUsingAgent never accepted/forwarded it), and monitor_operative/operative
assemble their own system prompt and never consult `_prompt_builder` — so they
would silently ignore it even if it arrived.

Fix:
- `SystemPromptBuilder.persona_sections()` returns just the SOUL/MEMORY/USER
  sections (no agent template), for agents that build their own prompt and want
  to *append* persona rather than have it replace their instructions.
- `BaseAgent._apply_persona()` appends persona to a self-assembled prompt
  (no-op without a builder or persona files).
- Thread `prompt_builder` through the __init__ chain: ToolUsingAgent now
  accepts and forwards it to BaseAgent; monitor_operative and operative forward
  it and call `_apply_persona()` on their assembled system prompt.
- AgentExecutor constructs a SystemPromptBuilder from config and passes it to
  any agent whose __init__ accepts it (same gating as session_store /
  memory_backend). Agents that override __init__ without forwarding (e.g.
  orchestrator) opt out automatically and keep their own machinery.

Specialized prompts are preserved — persona is appended, not substituted. The
one-shot path is unchanged.

Verified: persona_sections() excludes the template but build() still includes
both; monitor_operative/operative receive the builder through the chain and
their assembled prompt includes the SOUL content. New tests in
tests/agents/test_persona_persistent.py (11 passed; ruff clean).

Closes #376

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 17:21:43 +00:00
Jon Saad-Falcon 00065cd960 Merge pull request #418 from open-jarvis/fix/managed-agent-streaming-parity
fix(server): managed-agent streaming parity — tool_calls replay, sampler params, tool DI (#382, #386, #395)
2026-05-27 10:16:17 -07:00
Tanvir Bhathal 0702f2793c (bug): telemetry fixes (#421) 2026-05-27 09:36:26 -07:00
github-actions[bot] f6156f480b chore: update clone traffic data [skip ci] 2026-05-27 07:30:01 +00:00
github-actions[bot] eb08987dae chore: update clone traffic data [skip ci] 2026-05-26 07:15:55 +00:00
krypticmouseandClaude Opus 4.7 5acc86d7cc fix(server): managed-agent streaming parity — tool_calls replay, sampler params, tool DI (#382, #386, #395)
`_stream_managed_agent` had diverged from the canonical cli/ask.py path and
lost three behaviours. All three are fixed via small extracted, unit-tested
helpers:

- #382: cross-request history replay dropped stored `tool_calls`, so the model
  never saw its own prior tool use and fabricated tool output on turn 2+.
  `_replay_history_messages` now reconstructs the assistant tool-use message
  plus matching tool-result messages (synthesised, consistent tool_call_ids).
- #386: only temperature/max_tokens reached the engine. `_sampler_kwargs`
  forwards repetition_penalty / top_p / top_k / min_p / frequency_penalty /
  presence_penalty when set in the agent config (opt-in; default agents send
  nothing extra). Fixes degenerate repetition loops on local models with no
  repetition_penalty.
- #395: tools were built with a bare `tool_cls()`, so memory_* / channel_* /
  llm tools loaded with no backend and failed on every call.
  `_instantiate_managed_tool` injects backend / channel / engine the same way
  cli/ask.py::_build_tools does.

Verified empirically: replay emits user → assistant(tool_calls) → tool(result)
with matching ids; sampler extraction forwards only set keys; DI gives memory
tools a backend and llm the engine/model. New tests in
tests/server/test_managed_agent_streaming.py (helpers are pure, so verifiable
without a live engine). 38 passed locally incl. existing route tests.

Closes #382
Closes #386
Closes #395

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 03:22:14 +00:00
Jon Saad-Falcon f827a8165d Merge pull request #417 from open-jarvis/security/default-deploy-auth
security(deploy): require auth (or loopback) in default deployments (#221)
2026-05-25 19:07:17 -07:00
Jon Saad-Falcon 0d3cc6db51 Merge pull request #416 from open-jarvis/security/websocket-a2a-auth
security(server): authenticate WebSocket handshakes + A2A requests (#217)
2026-05-25 19:07:05 -07:00
Jon Saad-Falcon b0f133a42d Merge pull request #415 from open-jarvis/security/template-loader-rce
security(tools): fix RCE in template loader (eval + shell=True) (#216)
2026-05-25 19:06:54 -07:00
krypticmouseandClaude Opus 4.7 9cd760ad1e security(deploy): stop default deployments shipping an open, unauthenticated server (#221)
All three deployment methods bound 0.0.0.0:8000 with no API key, so following
the README produced a server reachable from any device on the network with no
auth. `check_bind_safety` already refuses to start a non-loopback bind without
a key (so these configs actually failed to start) — this wires the key in so
the documented path yields a *working, authenticated* server.

- docker-compose.yml: require `OPENJARVIS_API_KEY` via `${VAR:?...}` so
  `docker compose up` fails fast when unset; added `deploy/docker/.env.example`
  (un-ignored in .gitignore).
- systemd: add `EnvironmentFile=/etc/openjarvis/env` (no `-` prefix, so a
  missing key file blocks startup rather than exposing an open server).
- launchd: bind `127.0.0.1` by default (the personal-device default — no
  network exposure, no key needed) with a documented, commented opt-in to
  0.0.0.0 + `OPENJARVIS_API_KEY`. Avoids shipping a usable default credential.
- Docs (docker/systemd/launchd) updated with the key-setup step.
- Tests assert each config can't reintroduce an open server, plus
  `check_bind_safety` behavior across loopback/public × key/no-key.

Closes #221

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 23:56:55 +00:00
krypticmouseandClaude Opus 4.7 87e978ef20 security(server): authenticate WebSocket handshakes and A2A requests (#217)
`AuthMiddleware` is a BaseHTTPMiddleware and never intercepts WebSocket
upgrade requests, so `/v1/chat/stream` and `/v1/agents/events` accepted any
connection — leaking all agent events/message content and allowing
unauthenticated inference even when an API key was configured for HTTP. The
A2A JSON-RPC server likewise dispatched every request without auth.

- Add `websocket_authorized(websocket, expected_key)` (constant-time compare)
  and check it in both WS handlers BEFORE `accept()`, closing with code 1008
  on failure. Token is read from `?token=` (browsers can't set WS headers) or
  an `Authorization: Bearer` header. `create_app` now exposes the key via
  `app.state.api_key`; when empty, auth is disabled, matching the HTTP
  middleware's local-default behavior (so loopback dev is unchanged).
- A2AServer gains an optional `auth_token`: `handle_request(token=...)`
  rejects with JSON-RPC -32001 before dispatch when configured, advertises
  `{"schemes": ["bearer"]}` on the agent card, and stays open when unset.
  Added `A2AConfig.auth_token`.

Verified empirically against the real mounted endpoints via TestClient: no
token / wrong token are rejected at the handshake (WebSocketDisconnect),
correct token streams normally, and no-key configs still connect freely.

Closes #217

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 23:51:29 +00:00
krypticmouseandClaude Opus 4.7 47ca09f1a3 security(tools): eliminate RCE in template loader eval() and shell=True (#216)
`python`-action tool templates evaluated expressions with `eval()` under a
restricted `__builtins__`. That sandbox is escapable via attribute walks like
`str.__class__.__mro__[-1].__subclasses__()`, reaching `object.__subclasses__()`
and arbitrary code. `shell`-action templates interpolated parameters into a
string and ran it with `shell=True`, so a value like `; rm -rf ~` or
`$(curl evil)` executed in the host shell.

Fixes:
- Replace `eval()` with a small AST interpreter (`safe_eval_expr`) that
  implements an explicit node allowlist — literals, names, arithmetic/boolean/
  comparison ops, ternaries, subscripts, container literals, and calls to a
  fixed set of builtins only. Attribute access, lambdas, comprehensions, and
  dunder names have no implementation and raise `ValueError`, so the escape
  vectors are unreachable by construction. No `eval`/`exec` remains.
- Shell action: tokenize the FIXED template with `shlex.split` first, then
  substitute params into individual argv elements and run with `shell=False`.
  Injected metacharacters become inert literal arguments.

All shipped builtin templates (`str(float(value))`, `str(input) if input
else ...`, etc.) continue to work. Verified empirically: every known escape
payload is rejected and a `; touch <marker>` / `$(touch <marker>)` /
backtick injection never creates the marker file.

Closes #216

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 23:45:59 +00:00
Jon Saad-Falcon 7e8db280e4 Merge pull request #413 from open-jarvis/ci/desktop-stable-updater-channel
ci(desktop): stable (desktop-latest) + edge (desktop-edge) updater channels
2026-05-25 16:41:08 -07:00
krypticmouseandClaude Opus 4.7 e3f2b008d2 ci(desktop): split updater into stable (desktop-latest) + edge (desktop-edge) channels
The installed desktop app polls `desktop-latest/latest.json`. Previously
every push to `main` (autotag -> v*.devN -> desktop.yml dispatch) rebuilt
and republished `desktop-latest` as a DEV prerelease, so stable users were
auto-updated onto unvetted dev builds, and any manual stable mirror was
clobbered on the next merge.

Split the streams so the app's channel only ever serves vetted stable:

- Dev/rolling builds (v* autotag + manual workflow_dispatch) now publish to
  a new `desktop-edge` pre-release. The shipped app does not poll edge, so
  dev builds never auto-install onto stable users.
- Stable `desktop-v*` builds publish the user-facing release as before, then
  a new `refresh-stable-channel` job copies that release's signed
  `latest.json` into `desktop-latest` (mirror; URLs already point at the
  desktop-v* assets). Cut a `desktop-v*` tag to ship an update.
- `clean-release` now targets `desktop-edge`; `desktop-latest` is never
  wiped by CI.

No app/tauri.conf.json change — the updater endpoint stays `desktop-latest`.
Doc updated to describe the now-implemented stable/edge split.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 23:36:17 +00:00
Jon Saad-Falcon cdae426d48 docs: fix broken desktop download links → stable desktop-v1.0.2 release (#412)
Repoints all desktop download links (README, docs/index.md, downloads.md, getting-started/installation.md) from the removed desktop-latest prerelease (with wrong 0.1.0 filenames) to the stable desktop-v1.0.2 release. All 5 URLs verified live (200). macOS .dmg now included (addresses #356).
2026-05-25 16:20:33 -07:00
krypticmouseandClaude Opus 4.7 ae9727599b docs: point desktop download links at the stable desktop-v1.0.2 release
Every desktop download link in the docs was broken on two counts:
1. They pointed at the rolling `desktop-latest` prerelease, which had
   been removed (404 for all of README, docs/index.md,
   docs/downloads.md, docs/getting-started/installation.md).
2. They used the wrong version in the filenames (`OpenJarvis_0.1.0_*`)
   — the actual published assets were never `0.1.0`.

Repointed all four files at the new stable `Desktop desktop-v1.0.2`
release with the exact asset filenames it ships
(`OpenJarvis_1.0.1_*` — the Tauri bundle version is 1.0.1, distinct
from the 1.0.2 Python/CLI release). This release also includes a
macOS universal `.dmg`, which the prior desktop releases lacked
(addresses #356 "No Mac Download") — so the macOS rows now say
"Universal" (Apple Silicon + Intel) instead of "Apple Silicon".

All five download URLs verified live (HTTP 200) against the
desktop-v1.0.2 release before committing.

Note: `docs/desktop-auto-update.md` still references the
`desktop-latest` rolling channel — that's the auto-updater's endpoint,
a separate concern from the manual download links, and is left as-is.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 23:18:33 +00:00
Robby Manihani a86d022947 fix(agents): repair continuous monitor_operative agent (parrot loop, tool calling, traces) (#407) 2026-05-25 11:08:14 -07:00
Robby Manihani 8e6bc343d8 fix(connectors): validate credentials before persisting + populate Gmail URLs (#410) 2026-05-25 11:07:35 -07:00
Jon Saad-Falcon 56c9a59f8d release: v1.0.2 — fix broken PyPI wheel (#372) + pynvml/Windows/install fixes (#411)
Bumps 1.0.1 → 1.0.2 so the merged fixes (#372 wheel packaging, #389 pynvml, #373 Windows RAM, #331 desktop diagnostics, #337/#352 install URL) can ship to PyPI. See PR #411.
2026-05-25 10:51:56 -07:00
krypticmouseandClaude Opus 4.7 bed3524b62 release: v1.0.2
Patch release bundling the fixes merged since v1.0.1. The headline is
#372 — the v1.0.1 wheel on PyPI is missing `openjarvis/traces/`, so
every `pip install openjarvis==1.0.1` breaks at import. PyPI filenames
are immutable, so the fix has to ship under a new version number.

Bumps version 1.0.1 → 1.0.2 and adds the CHANGELOG entry covering
#372 (wheel packaging), #389 (pynvml warning), #373 (Windows RAM),
#331 (desktop uv-sync diagnostics), and #337/#352 (install URL → GitHub
Pages).

After this merges, cut the release:
  uv build
  unzip -l dist/openjarvis-1.0.2-*.whl | grep traces   # verify
  twine upload dist/openjarvis-1.0.2-*

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 17:49:40 +00:00
github-actions[bot] eb5d1e467f chore: update clone traffic data [skip ci] 2026-05-25 07:37:54 +00:00
Jon Saad-Falcon b43f4d2291 refactor(desktop): extract testable uv-sync error helpers + unit tests (#331) (#406)
Extracts the #331 uv-sync error-formatting logic into pure functions with 7 unit tests, now run via cargo test in desktop.yml's validate job. Verified: all 7 pass on the real Tauri crate in CI.
2026-05-24 08:31:54 -07:00
krypticmouseandClaude Opus 4.7 658effb964 refactor(desktop): extract testable uv-sync error helpers + unit tests (#331)
The uv-sync failure handling added in #398 was inline in the async
`boot_backend` GUI path, so its logic — exit-code rendering and the
stderr "tail" extraction — could only be verified by running the
desktop app. This extracts the pure logic into three free functions
and adds unit tests, so the message formatting is now covered by
`cargo test` with no GUI / webview runtime needed.

Extracted:
- `uv_sync_stderr_tail(stderr, max_chars)` — last N chars of stderr,
  trimmed, on char boundaries. The original inline version used
  `.rev().take(N).collect().chars().rev().collect()`; the new version
  is `skip(count - N)` which is clearer and equally UTF-8-safe (matters
  because Windows consoles emit non-ASCII cp9xx bytes — a byte slice
  could split a codepoint).
- `format_uv_sync_failure(root, exit_code, stderr)` — the non-zero-exit
  message. Now renders a missing exit code (signal-terminated process)
  as "unknown" instead of a misleading "-1".
- `format_uv_sync_spawn_error(root, uv_bin, err)` — the can't-spawn
  message.

`boot_backend` now calls these instead of formatting inline.

Tests (7, in `#[cfg(test)] mod tests`):
- tail returns whole string when shorter than the limit
- tail keeps the END (the actionable line), not the spinner-noise start
- tail trims surrounding whitespace
- tail never splits a multi-byte codepoint (500×"é", limit 100 → exactly
  100 chars, all "é")
- failure message includes exit code + stderr tail + the actionable
  "run uv sync manually" hint
- missing exit code renders as "exit unknown", never "exit -1"
- spawn-error names the uv binary path and repo root

Verified the logic standalone via `rustc --test` (7/7 pass). In CI they
run via `cargo test` in desktop.yml's `validate` job, which already
builds the Tauri crate with the webkit deps and runs on every PR that
touches `frontend/**` — so this changes the existing `cargo check` step
to `cargo test` (a superset: same build coverage, plus the tests).

This doesn't verify the GUI *behavior* (that still needs a human running
the app, or the windows-latest empirical path) — but the error-message
logic that was previously untestable now has automated coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:26:07 +00:00
Jon Saad-Falcon 0c34817ecd ci: add windows-latest job to empirically verify Windows code paths (#405)
Adds a windows-latest CI job that executes the real GlobalMemoryStatusEx RAM path (#373), the #293 stdout reconfigure test, and builds+imports the openjarvis_rust PyO3 extension on Windows. Verified green on a real Windows runner (4m1s, all steps success).
2026-05-24 08:08:49 -07:00
krypticmouseandClaude Opus 4.7 c06633e26f ci: add windows-latest job to empirically verify Windows code paths
The `test` job runs only on ubuntu-latest, so the Windows-specific
branches added for #373 (RAM detection via GlobalMemoryStatusEx) and
#293 (cp9xx → UTF-8 stdout reconfigure) were never *executed* in CI —
only unit-tested with mocks on Linux. `tests/hardware/test_hardware_profiles.py::test_total_ram_gb_windows`
has existed all along but is `skipif(sys.platform != "win32")`, so it
silently skipped on every run.

This adds a `test-windows` job that runs on a real Windows runner
(free for public repos) and:

1. Verifies `_total_ram_gb()` returns > 0 on actual Windows — executes
   the real `ctypes.windll.kernel32.GlobalMemoryStatusEx` path (#373).
   Runs as a pure-Python step BEFORE any Rust build, so a flaky
   toolchain install can't mask the result.
2. Runs `tests/hardware/test_hardware_profiles.py` (the now-unskipped
   Windows RAM test) and `tests/cli/test_cli.py` (the
   `test_windows_reconfigures_stdout_to_utf8` test from #293).
3. Builds + imports the `openjarvis_rust` PyO3 extension on Windows —
   the only CI job that does so. The extension is mandatory at runtime
   (`_rust_bridge.py` hard-errors without it), yet nothing else
   verified it compiles/imports on Windows. desktop.yml builds the
   Tauri app's Rust, not this extension.
4. Smoke-tests `jarvis --version`.

Scoped to the platform-relevant test files (not the full 6700-test
suite) so the job stays fast; the slow part is the Rust build, which
doubles as Windows-extension-build coverage.

All `run:` steps are static commands with no `github.event.*`
interpolation — no workflow-injection surface.

Note: #331 (desktop "did not become healthy" — uv sync error
surfacing) is GUI-triggered Tauri boot logic and isn't covered here;
its only automatable surface is string formatting, and testing it
needs the heavy webview build. Left as a possible follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:03:11 +00:00
github-actions[bot] deb0dd509c chore: update clone traffic data [skip ci] 2026-05-24 07:09:11 +00:00
Jon Saad-Falcon a76319841a fix(install): serve install.sh from GitHub Pages as canonical URL (#402)
Resolves the openjarvis.ai SSL failure (#337, #352) by serving the installer from the project-controlled GitHub Pages site. Adds uv prerequisite docs for the Windows desktop path. See PR #402 for the full breakdown and the openjarvis.ai CNAME migration note.
2026-05-23 12:10:00 -07:00
krypticmouseandClaude Opus 4.7 cf8d3e2cbe fix(install): serve install.sh from GitHub Pages, make it the canonical URL
The documented install command pointed at `https://openjarvis.ai/install.sh`,
but that domain is community-operated (not controlled by this project) and
its TLS config broke — every new user hit `sslv3 alert handshake failure`
(#337, #352). Since we can't fix a domain we don't control, this moves the
installer onto infrastructure we DO control: the project's GitHub Pages
docs site.

Changes:

- **`docs/gen_install_script.py`** (new) + **`mkdocs.yml`**: a `gen-files`
  hook copies `scripts/install/install.sh` verbatim into the built site at
  `install.sh` on every `mkdocs build`. Single source of truth — the script
  stays at `scripts/install/install.sh` (still bundled into the wheel as
  `_install_scripts/`); the published copy can't drift. Verified locally:
  `mkdocs build` emits `site/install.sh` byte-identical to the source.

  New canonical URL: `https://open-jarvis.github.io/OpenJarvis/install.sh`
  — HTTPS always valid (GitHub's cert), fully under project control.

- **`README.md`**: canonical command switched to the github.io URL for
  both the Installation and Quick Start blocks. Also addresses the uv
  discoverability gap — explicitly states the curl installer downloads uv
  for you (no prerequisite), and that the Windows **desktop .exe** expects
  uv to be installed first, with the exact PowerShell command.

- **`docs/getting-started/{install,wsl2,macos,linux}.md`**: canonical URL
  switched to github.io. `install.md` gains an "Install URL" info note
  explaining the github.io URL is canonical and that the older
  `openjarvis.ai` URL is community-operated with intermittent TLS issues.

- **`scripts/install/install.sh`** + **`jarvis-wrapper.sh`**: usage comment,
  the WSL re-run hint (added in #399), and the wrapper's re-install message
  all updated to the github.io URL.

Migration note for maintainers: ask whoever operates `openjarvis.ai` to
CNAME it to `open-jarvis.github.io`. Once they do, `openjarvis.ai/install.sh`
will serve this same GitHub Pages content with a valid GitHub-managed cert,
and the nicer brand URL can become canonical again with zero further code
changes. Until then, the github.io URL works and is under our control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 19:05:26 +00:00
Jon Saad-Falcon bced6cc274 fix(install): Windows discoverability + Git Bash early bail + concrete uv install command (#399)
Follow-up to PR #398. Three surface fixes for the Windows install confusion documented in two Discord support threads. See PR #399 for the full breakdown.
2026-05-23 08:54:10 -07:00
krypticmouseandClaude Opus 4.7 612d3e1f88 fix(install): make Windows install path discoverable + bail early on Git Bash
Addresses the second half of the Discord support thread on the
"Jarvis server did not become healthy in time" issue (PR #398 fixed
the diagnostic gap; this fixes discoverability of the right install
path so users don't end up there in the first place).

Three changes, all surface improvements:

1. **`README.md`** — explicit Windows section in the Installation
   block. Previously, the only Windows mention was a footnote
   ("Platforms: ... WSL2 on Windows") that came AFTER the `curl … |
   bash` install command. Users on PowerShell would copy/paste the
   command, get a syntax error, then try to debug bash on Windows.
   Now the README clearly says: bash installer is macOS/Linux only;
   Windows users have two paths (WSL2 with one-time `wsl --install`
   setup, or the desktop .exe from Releases). Both link to the
   relevant docs.

2. **`scripts/install/install.sh`** — early bail when running under
   Git Bash / MSYS2 / Cygwin (MINGW*, MSYS*, CYGWIN* per `uname -s`).
   These environments aren't WSL — `uv` and `git` will install to
   Windows-side paths that OpenJarvis can't reach, Ollama integration
   silently breaks, and the user gets to debug it 3 minutes into a
   doomed install. The bail message points at both the WSL2 setup
   command (`wsl --install -d Ubuntu-24.04`) and the desktop .exe
   download as alternatives.

   Verified the case-match doesn't fire on Linux (`uname -s` →
   `Linux`, matches the wildcard fall-through, not the MINGW patterns).

3. **`frontend/src-tauri/src/lib.rs`** — when `resolve_bin("uv")`
   can't find uv, the per-OS error message now contains the exact
   install command for the user's OS, ready to copy/paste. On Windows
   that's the `irm https://astral.sh/uv/install.ps1 | iex` command
   Marc kept reposting on the Discord support thread (5/12-5/14).
   On macOS/Linux it's the standard `curl | sh` installer.

   The previous generic "Install it from https://astral.sh/uv" left
   users guessing whether to use winget, scoop, pip, or the official
   installer — which is exactly the confusion the Discord thread
   captured.

None of these are root-cause code fixes (Discord users' uv installs
fail for environment-specific reasons we can't diagnose remotely),
but together they remove the three biggest friction sources we saw:
copy-paste install command that can't possibly work, doomed git-bash
installs that fail mysteriously, and missing exact install commands
when uv isn't found.

The Tauri change ships in the desktop binary; the README change is
visible immediately on the repo page; the install.sh change reaches
users via openjarvis.ai (when it's restored) and via the GitHub-raw
fallback from PR #398.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:48:13 +00:00
Jon Saad-Falcon 58f7b753bf fix: PyPI v1.0.2 cluster — wheel packaging + pynvml warning + install fallback + uv-sync diagnostics (#398)
Closes #372 #389 #337 #352 #331. Resolves #373 (already on main, awaiting release). See PR #398 for the full per-issue breakdown.

Credits: @gilbert-barajas (#372), @boeani05 (#389), @kumanday + @filactre (#337/#352), @Hris4oG + @NatanelBranski + @Robjes87 + @EmiDaDuck (#331).
2026-05-23 08:36:44 -07:00
krypticmouseandClaude Opus 4.7 158fc83feb fix(desktop): surface uv sync errors so users aren't stuck waiting for health check
Addresses #331 (multiple Windows users hit "Jarvis server did not
become healthy in time" with no actionable detail).

The boot sequence ran `uv sync` with **both** stdout AND stderr piped
to `/dev/null` and discarded the exit code (`let _ = …`). When
`uv sync` failed for any reason — Windows PATH/permission issues,
network problems, lockfile conflicts, stale `.venv/` — the user saw
nothing useful. The boot proceeded to `uv run jarvis serve` in an
under-provisioned venv, then waited the full 600-second health-check
window before showing a generic "did not start" message with no
hint of what actually went wrong.

Fix: capture stderr, check the exit status, and surface a useful
error to the user **before** the long server-start wait, including:
- The exit code
- The last ~800 chars of `uv sync` stderr (where the diagnostic
  message usually lives)
- A concrete next step (open a terminal and run `uv sync --extra server`
  manually for full output)

Also updated the status detail message to "Installing dependencies
(uv sync — may take 1-2 min on first boot)..." so users on slow
connections don't restart the app thinking it's stuck.

Discord support thread (5/12-5/14) shows the same pattern across
multiple Windows users (@ItsVoyage, @Mystic_irl, @doevud, @Sainthood):
all stuck on "Starting api server" / "did not become healthy in time"
for 4+ minutes, with the actual root cause turning out to be a uv
installation issue that the discarded stderr would have surfaced
immediately. The community workaround (uninstall, install Feb
pre-release, run a magic PowerShell `irm` command for uv) is the
right diagnosis applied without diagnostic output — this commit
makes that diagnostic output visible.

Note: this fix lands in the desktop binary's source (`frontend/src-tauri/src/lib.rs`).
Users running the v1.0.1 desktop binary won't see the improved
error message until the desktop release is re-cut from this branch.
Until then, the workaround documented in this PR's `openjarvis.ai`
fallback section (curl the install.sh from GitHub raw) gets new
users past the install step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:31:17 +00:00
krypticmouseandClaude Opus 4.7 52e830e1a4 docs(install): add GitHub-raw fallback for the openjarvis.ai install URL
Addresses #337 (also reported as #352).

`curl -fsSL https://openjarvis.ai/install.sh | bash` — the documented
one-liner — currently fails with:

    curl: (35) ... sslv3 alert handshake failure

Reproduced from this machine just now; @kumanday and @filactre both
report the same against different OpenSSL and LibreSSL versions. The
underlying SSL issue is on the openjarvis.ai server (cert / TLS
config) and needs an operational fix at the infra layer — not
something a code change in this repo can resolve.

What this commit *can* do is unblock users immediately:

- `README.md` (under "Installation"): callout pointing at issue #337
  and the GitHub-mirror fallback.
- `docs/getting-started/install.md`: same callout as a
  Material-for-MkDocs `!!! warning` admonition.

Both link to the canonical script at
`https://raw.githubusercontent.com/open-jarvis/OpenJarvis/main/scripts/install/install.sh`.
The script is identical content — it's the same `scripts/install/install.sh`
served from GitHub's CDN instead of openjarvis.ai. Once the
installer runs, it pulls everything else (`uv` from astral.sh, the
project source from `github.com/open-jarvis/OpenJarvis.git`, Ollama
from `ollama.com`) — none of which depend on `openjarvis.ai`. So
the rest of install proceeds normally.

When the openjarvis.ai SSL issue is fixed at the server / DNS layer,
both callouts can be removed and the canonical URL becomes the only
documented path again.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:25:08 +00:00
krypticmouseandClaude Opus 4.7 0483f5377d fix(telemetry): silence pynvml deprecation FutureWarning on startup
Fixes #389.

The legacy `pynvml` PyPI package (since version 13.x) registers a
meta-path-finder shim — `_pynvml_redirector.py` — that prints a
`FutureWarning("The pynvml package is deprecated. Please install
nvidia-ml-py instead.")` on every `import pynvml`, even when the
caller's project doesn't depend on pynvml directly. The warning was
firing on every `jarvis --version` / `jarvis ask` / any command that
touches the telemetry path.

Two-layer fix:

1. **pyproject.toml**: switch `pynvml>=13.0.1` → `nvidia-ml-py>=12.560.30`
   in the core deps, `gpu-metrics` extra, and `energy-all` extra.
   `nvidia-ml-py` is NVIDIA's official package and ships the same
   `pynvml` module name without the redirector shim, so the warning
   doesn't fire.

2. **Defensive filters** at all four `import pynvml` sites
   (`telemetry/gpu_monitor.py`, `telemetry/energy_nvidia.py`,
   `server/research_router.py`, `evals/backends/external/_subprocess_runner.py`):
   wrap the import in a narrowly-scoped `warnings.filterwarnings("ignore",
   message=r"The pynvml package is deprecated.*", category=FutureWarning)`.
   Belt-and-suspenders for the case where `pynvml` gets pulled in
   transitively by torch / vllm / etc. — the user's environment may
   still have it installed even if our deps don't pull it in.

Verified locally:
- `uv sync` swaps pynvml → nvidia-ml-py.
- `python -c "import warnings; warnings.simplefilter('error', FutureWarning); from openjarvis.telemetry import gpu_monitor"` → no warning fires (would raise if it did).
- `jarvis --version` → clean output, no FutureWarning preceding the version string.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:20:12 +00:00
krypticmouseandClaude Opus 4.7 2d6d6ed7bd fix(packaging): anchor traces/ gitignore pattern so hatch ships it in the wheel
Fixes #372.

The `.gitignore` had `traces/` (unanchored), which matches any directory
named `traces/` anywhere in the tree — including the runtime module at
`src/openjarvis/traces/`. hatchling honors `.gitignore` when building
the wheel, so it silently dropped the entire `openjarvis/traces/`
package.

Effect: every fresh `pip install openjarvis==1.0.1` failed at import
time with `ModuleNotFoundError: No module named 'openjarvis.traces'`
the moment the user touched `jarvis ask`, learning, or the server.
Confirmed by @gilbert-barajas with a clean repro on macOS Apple Silicon.

Reproduced locally by running `uv build --wheel` on a clean checkout
of `main`:
- Before this change: `openjarvis/traces/` absent from the wheel.
- After this change: all 4 expected files present
  (`__init__.py`, `analyzer.py`, `collector.py`, `store.py`).

Anchored the pattern to `/traces/` so it only matches a top-level
`traces/` directory (where ad-hoc trace dumps may live during
development), not any nested `traces/` subdir. Added a comment in the
gitignore explaining the gotcha so it doesn't recur.

The other unanchored directory patterns in the file (`results/`,
`logs/`, `htmlcov/`, `site/`, `build/`, `dist/`, `venv/`, `env/`)
were audited — none collide with any directory currently under
`src/openjarvis/`. Left them unanchored to keep the diff minimal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:17:12 +00:00
Robby Manihani 71c5b2bce1 feat(cli): arc-reactor startup banner for init/serve/ask (#397) 2026-05-23 07:57:32 -07:00
github-actions[bot] 440915c822 chore: update clone traffic data [skip ci] 2026-05-23 06:58:07 +00:00
Tanvir Bhathal c84f1fddee feat: proactive agent approval bell with approve/deny UI (#370) 2026-05-22 17:38:48 -07:00
github-actions[bot] fecbc51767 chore: update clone traffic data [skip ci] 2026-05-22 07:16:50 +00:00
Tanvir Bhathal 07b6cba6df remove (#371) 2026-05-21 10:46:13 -07:00
github-actions[bot] 7b6fe86c91 chore: update clone traffic data [skip ci] 2026-05-21 07:20:37 +00:00
Jon Saad-Falcon a6f18dff43 Consolidate contributor PRs #203 + #260 (#368)
Two clean contributor PRs landed with original authorship preserved. Two related PRs (#116, #119) excluded — both need rebase against significant main refactors (see PR #368 for details). Credits: @bootcrowns (#203), @samy19980109 (#260).
2026-05-20 18:56:20 -07:00
Samarth Agarwal 5a74fd82b8 adding TTS for morning digest 2026-05-21 01:47:13 +00:00
Abhinav Cherukuru 9d0bb25516 fix(lint): wrap long lines in collect_metrics available dict (E501) 2026-05-21 01:46:40 +00:00
Abhinav Cherukuru e963321114 feat(operators): wire metrics field and add collect_metrics() to OperatorManager
OperatorManifest has had a `metrics: List[str]` field since the initial
commit but OperatorManager never read it. This change wires that field
through the manager in three ways:

1. activate(): passes `metrics` into the scheduler task metadata so
   workers can introspect which metrics an operator cares about.

2. status(): includes `metrics` in the per-operator status dict returned
   to callers, making it visible alongside tools and schedule info.

3. collect_metrics(operator_id, *, since, until) [new method]: queries
   the system's TelemetryAggregator (system.telemetry) and returns only
   the summary fields explicitly declared in manifest.metrics. Unknown
   metric names are skipped with a DEBUG log so old manifests remain
   forward-compatible. Returns an empty dict gracefully when telemetry
   is not configured.
2026-05-21 01:46:39 +00:00
Robby Manihani 363bdbfa41 feat: Deep Research Granola integration + citation fixes (#366) 2026-05-20 15:56:42 -07:00
Jon Saad-Falcon 75cb70526e Consolidate contributor PRs #340 + #341 + #301 + #347 + #202 (#367)
Consolidates five contributor PRs into one bundle to avoid overlapping issues (notably #340/#341 both touching mcp/transport.py + agent_cmd.py + executor.py). Original authorship preserved on every commit. See PR #367 for full per-author breakdown.

Credits: @Dilligaf371 (#340, #341), @eddierichter-amd (#301), @kriptoburak (#347), @bootcrowns (#202).
2026-05-20 13:37:46 -07:00
krypticmouseandClaude Opus 4.7 e087773b36 style: ruff line-length + import fixes for #340 and #202
Follow-up on the cherry-picks from @Dilligaf371's #340 and
@bootcrowns's #202. Both fail ruff's E501 (88-char line limit) on
main:

- ``src/openjarvis/agents/executor.py:325`` (from #340) — the
  ``self._system is not None and getattr(..., "tool_executor", None)
  is not None`` guard was 101 chars. Wrapped the condition.
- ``src/openjarvis/telemetry/gpu_monitor.py:55-60`` (from #202) —
  five new GPU_SPECS entries (Jetson Orin NX 16GB/8GB, AGX Orin,
  Snapdragon X Elite/Plus) were 90-93 chars. Wrapped each
  GpuHardwareSpec constructor call onto its own block.
- ``tests/telemetry/test_gpu_monitor.py`` (from #202) — removed a
  spurious blank line between imports and the first ``#`` comment
  block, picked up by ``ruff check --fix`` (rule I001).

No behavior change — pure formatting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 20:22:44 +00:00
Abhinav Cherukuru 9c9c883f24 test: update test_all_specs_present with 7 new GPUs 2026-05-20 20:20:58 +00:00
Abhinav Cherukuru ef267abcc1 fix: remove extra whitespace in Arc B580 entry 2026-05-20 20:20:58 +00:00
Abhinav Cherukuru 5f685a2e3b feat(telemetry): expand GPU_SPECS with Intel Arc, Jetson Orin, and Snapdragon entries
Adds hardware specs for Intel Arc B580/B570, NVIDIA Jetson Orin NX/AGX Orin, and Qualcomm Snapdragon X Elite/Plus to the GPU_SPECS database in telemetry/gpu_monitor.py.

Specs sourced from official vendor datasheets:
- Intel Arc B580: 196 TFLOPS FP16, 456 GB/s, 190W TDP
- Intel Arc B570: 136 TFLOPS FP16, 380 GB/s, 150W TDP
- Jetson Orin NX 16GB: 50 TFLOPS FP16, 102 GB/s, 25W TDP
- Jetson Orin NX 8GB: 25 TFLOPS FP16, 68 GB/s, 15W TDP
- Jetson AGX Orin: 108 TFLOPS FP16, 204 GB/s, 60W TDP
- Snapdragon X Elite (Adreno X1-85): 4.6 TFLOPS FP16, 136 GB/s, 80W SoC TDP
- Snapdragon X Plus: 3.8 TFLOPS FP16, 136 GB/s, 80W SoC TDP

Closes Workstream 5 roadmap item: GPU specs database expansion.
2026-05-20 20:20:58 +00:00
kriptoburak 1d37528637 docs: add Hermes Tweet skill install example 2026-05-20 20:20:58 +00:00
Eddie Richter 86c42e4e4a tests: wrap lemonade host override signature 2026-05-20 20:20:53 +00:00
Eddie Richter a65cd12ea2 lemonade: update default host and recommended model 2026-05-20 20:20:53 +00:00
Gilles Ceyssat 36482b1a27 fix(mcp): bump default tool timeout 30s → 600s for MCP-discovered tools
MCPClient.list_tools constructs ToolSpec without a timeout_seconds
override, so MCP tools inherit the dataclass default of 30s. Most MCP
servers in practice wrap long-running tools (pentest scanners, build
runners, search agents, …) that comfortably take longer than 30s.

The symptom is misleading — the ToolExecutor reports the timeout, and
the LLM relays it as "command execution timed out", with no hint that
the cause is the OpenJarvis client side and not the MCP server's
actual policy. (CyberStrikeAI, for example, allows 30 *minutes* on its
side via tool_timeout_minutes.)

Bump the default to 600s (10 min) — still bounded, but enough for the
typical long-running tool. Individual MCP servers can shorten via
their own timeout policy if they care.
2026-05-20 20:20:49 +00:00
Gilles Ceyssat 333cb9a370 feat(cli): wire confirm_callback for jarvis agents ask (with --yes/--no-yes)
`jarvis agents ask` is a non-interactive CLI path, so the AgentExecutor's
ToolExecutor never had a confirm_callback wired. Tools whose ToolSpec
sets requires_confirmation=True (shell_exec, git_*, ...) returned
"requires confirmation but no confirmation callback is available" and
the agent relayed that back as natural language, never executing.

This adds a --yes/--no-yes flag (default --yes):
- --yes: auto-approve (lambda _prompt: True). Suited for CLI runs where
  the operator already authorized the engagement scope.
- --no-yes: prompt on TTY via click.confirm.

The callback is set on the AgentExecutor itself; executor.py:_invoke_agent
reads it and forwards it to the constructed agent through agent_kwargs
(both `interactive=True` and `confirm_callback=...`).
2026-05-20 20:20:42 +00:00
Gilles Ceyssat 4c37aa83df feat(executor): pull SystemBuilder MCP tools into agent's tool list
The AgentExecutor builds agents from a template's `tools` whitelist by
resolving each name against the static `ToolRegistry`. External MCP
tools (discovered at SystemBuilder.build() time via _discover_external_mcp)
were never picked up — they exist on system.tool_executor._tools but
the per-agent build path never read from there.

Result: any agent whose template declared MCP tool names by name got
0 of them at runtime, falling back to natives only. The agent's system
prompt could still mention the tools, but the model could not call them
(only shell_exec or other native fallbacks were available).

This adds a fallback after the ToolRegistry loop: for any tool name not
resolved from the registry, look it up in system.tool_executor._tools
and append the already-instantiated MCP adapter. Native tools still
take precedence (same name, the registry hit wins).

Verified by configuring a CyberStrikeAI stdio MCP server (78 tools).
Before this patch: agent built with 4 tools. After: 4 native + 78 MCP.
2026-05-20 20:20:42 +00:00
Jon Saad-Falcon c8f1b3c54a Consolidate top-priority contributor PRs (#235 + #339 + #293 + #294 + #295) (#362)
Lands 5 high-priority contributor PRs as one bundle, with original authorship preserved on every commit, plus follow-up commits from me to make each PR CI-green and non-regressive. See PR #362 for the full per-author commit breakdown and credits.

Credits: @tomaioo (#235), @Dilligaf371 (#339), @TX-Huang (#293, #294, #295).
2026-05-20 13:11:34 -07:00
Robby Manihani 855ed51780 feat: Deep Research Slack integration (#365) 2026-05-20 12:02:05 -07:00
Tanvir Bhathal 4652b6e8a8 [FEAT] Proactive Agents (#364) 2026-05-20 12:00:19 -07:00
github-actions[bot] 30f8d57398 chore: update clone traffic data [skip ci] 2026-05-20 07:16:55 +00:00
krypticmouseandClaude Opus 4.7 9a42e62172 style: wrap long line in test_ask_agent.py
Follow-up on @TX-Huang's PR #295. The new
test_soul_md_content_reaches_engine_in_simple_agent test has one
line at 92 chars that trips ruff's E501 (88 char limit). Wrap the
ternary onto multiple lines so the file passes ruff check on CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:37:18 +00:00
IsaacHandClaude Opus 4.7 90f009e906 feat(ask): wire SystemPromptBuilder so SOUL.md / MEMORY.md / USER.md actually load
`SystemPromptBuilder` is fully implemented and tested in
`openjarvis.prompt.builder`, and `BaseAgent.__init__` accepts a
`prompt_builder` kwarg. But no production code path ever instantiates
the builder, so the persona-files feature documented in
`MemoryFilesConfig` (SOUL.md / MEMORY.md / USER.md) had no effect.
Users could write a fully-customized `~/.openjarvis/SOUL.md` and the
file was never read.

This PR wires it up in `_run_agent` (called by `jarvis ask --agent
<name>` and the new fallback from #294):

```python
if "prompt_builder" in inspect.signature(agent_cls.__init__).parameters:
    agent_kwargs["prompt_builder"] = SystemPromptBuilder(
        agent_template=config.agent.default_system_prompt or "",
        memory_files_config=config.memory_files,
        system_prompt_config=config.system_prompt,
    )
```

The `inspect` guard means agents that override `__init__` without
forwarding `prompt_builder` (e.g. OrchestratorAgent, which has its
own tool-aware system prompt) opt out automatically and keep
working unchanged. SimpleAgent and any future agent that inherits
`BaseAgent.__init__` directly picks up the persona files.

Also fixes a latent bug in `SystemPromptBuilder._load_file`: it
called `path.read_text()` with no encoding, which on Windows falls
back to the system code page (cp950 / cp932 / cp949) and raises
`UnicodeDecodeError` on any non-ASCII persona content. Pin to UTF-8.

## Tests

- Add `test_soul_md_content_reaches_engine_in_simple_agent` — writes
  sentinel SOUL.md / MEMORY.md / USER.md to tmp_path, runs the
  command, and asserts each sentinel appears in the SYSTEM message
  passed to engine.generate.
- Add `test_orchestrator_keeps_its_own_system_prompt` — exercises
  the `inspect`-based opt-out so OrchestratorAgent doesn't crash
  on the unexpected kwarg.

Run: `pytest tests/cli/test_ask_agent.py tests/cli/test_ask_router.py
tests/cli/test_ask_e2e.py tests/agents/ tests/prompt/`

The 6 remaining failures (test_base_agent, test_loop_guard,
test_manager, test_native_openhands) are pre-existing on
origin/main and unrelated to this change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:37:18 +00:00
krypticmouseandClaude Opus 4.7 0217a901dd test(energy_wiring): opt out of #294 default-agent fallback in _energy_config
Follow-up on @TX-Huang's PR #294. The default-agent fallback now
routes `jarvis ask "..."` (no --agent) through `config.agent.default_agent`,
which dataclass-defaults to `"simple"`. The autouse `_clean_registries`
fixture in tests/conftest.py clears AgentRegistry between tests, so
the fallback then fails with `Unknown agent: simple` and every
CLI-driven test_energy_wiring test exits with code 1.

These tests exercise engine-level instrumentation, not agent dispatch
— ``test_engine_wrapped_with_instrumented``, the energy-monitor
lifecycle tests, and the end-to-end pipeline tests all care that the
engine gets wrapped and telemetry lands in SQLite, regardless of
whether the call goes through an agent. Set ``cfg.agent.default_agent
= ""`` in ``_energy_config`` to keep these tests on the direct-engine
path they were originally designed for. The dedicated
``test_agent_mode_uses_instrumented_engine`` (which explicitly passes
``--agent``) is unaffected.

Same pattern PR #294 already uses in tests/cli/test_ask_router.py
for the two MagicMock-config tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:37:09 +00:00
IsaacHandClaude Opus 4.7 c7f451fbc8 fix(ask): honor agent.default_agent from config when --agent omitted
`jarvis ask "..."` (no `--agent` flag) routed straight to
`engine.generate()` regardless of `agent.default_agent` in the user's
config. As a result the persona stack — `agent.default_system_prompt`,
SOUL.md, MEMORY.md, USER.md — was silently bypassed for the most
common command.

Behavior now:
- `--agent X`              → use agent X (unchanged)
- `--agent ""` (empty)     → explicit opt-out, direct-to-engine mode
- (omitted)                → fall back to `config.agent.default_agent`,
                             which dataclass-defaults to `"simple"`,
                             so persona settings finally take effect

Tests:
- Rename `test_no_agent_uses_direct_mode` to
  `test_no_agent_flag_falls_back_to_config_default_agent` and update
  its docstring to document the new behavior.
- Add `test_explicit_empty_agent_opts_out_of_agent_mode`.
- Add `test_no_agent_with_blank_config_default_uses_direct_mode` to
  cover the case where the user clears `default_agent`.
- Update `_patch_engine` (test_ask_router) and `_patch_ask`
  (test_ask_e2e) to re-register `SimpleAgent` after the autouse
  `_clean_registries` conftest fixture, since the agent path now
  runs in tests that previously short-circuited to direct mode.
- Add explicit `cfg.agent.default_agent = ""` to two
  `test_ask_router` tests that mock load_config with a MagicMock
  (so `cfg.agent.default_agent` doesn't auto-create as a truthy mock).

Note: `tests/cli/test_ask_context.py` has 2 unrelated pre-existing
failures on origin/main (memory backend returns None on Windows);
those are out of scope for this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:37:09 +00:00
IsaacHandClaude Opus 4.7 30ac635e83 fix: force UTF-8 stdout on Windows for CJK CLI output
On Windows the default Python stdout encoding follows the system
ANSI code page (cp950 for zh-TW, cp932 for ja, cp949 for ko).
`click.echo()` then raises `UnicodeEncodeError` whenever a CJK
character lands in CLI output — `jarvis ask` returning Chinese
crashes with `'cp950' codec can't encode character '义'`.

Reconfigure `sys.stdout` and `sys.stderr` to UTF-8 with
`errors='replace'` at the `main()` entry point. Scoped to
`win32` so other platforms are untouched. Two unit tests verify
the reconfigure happens on Windows and doesn't on Linux.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:36:55 +00:00
IsaacHandClaude Opus 4.7 31efd9e672 fix: detect total RAM on Windows via GlobalMemoryStatusEx
`_total_ram_gb()` had branches for Darwin (sysctl) and Linux
(/proc/meminfo) but no Windows path, so `jarvis init` reported
"0.0 GB RAM" on every Windows host. The downstream
`recommend_model()` then fell back to VRAM-only sizing, often
selecting a smaller tier than the system can actually run.

Add a Windows branch that calls `GlobalMemoryStatusEx` via
ctypes — no new dependency. Add a platform-skip-aware test for
each OS branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:36:55 +00:00
krypticmouseandClaude Opus 4.7 8b5a304222 test(mcp): regression test for StdioTransport.send_notification
Follow-up on @Dilligaf371's PR #339 fix. Adds a regression test that
spawns a subprocess which consumes stdin without ever writing to
stdout, then issues `send_notification` from a worker thread. If the
override is missing, the thread blocks forever on `proc.stdout.readline()`
and the 2-second join timeout fires.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:36:37 +00:00
Gilles Ceyssat df566d4186 fix(mcp): StdioTransport.send_notification must not read response
The base MCPTransport.send_notification falls back to self.send() which
writes a request AND reads a response. For stdio MCP servers this hangs
forever because notifications (per JSON-RPC 2.0 spec) have no response.

This affected every stdio MCP server attached to OpenJarvis: after
MCPClient.initialize() sent its `initialize` request and received the
server capabilities, it sent the spec-required `notifications/initialized`
notification, which then blocked indefinitely on proc.stdout.readline().

StreamableHTTPTransport already overrides send_notification correctly
(transport.py:213). This adds the symmetric fix for StdioTransport so
stdio MCP servers can complete the handshake and be discovered.

Reproduced with the CyberStrikeAI cmd/mcp-stdio server (78 tools): without
the fix, `_discover_external_mcp` hangs forever in `MCPClient.initialize`.
With the fix, all 78 tools are discovered cleanly.
2026-05-20 03:36:37 +00:00
krypticmouseandClaude Opus 4.7 4177b5c50e fix(skills): satisfy Clippy + reject non-ASCII hex + add regression tests
Follow-up on @tomaioo's signature-verification panic fix in PR #235.

1. Clippy on Rust 1.95+ flags `len() % 2 != 0` with the
   `manual_is_multiple_of` lint, which was failing the `rust` CI job
   on PR #235 and blocking merge. Switch to `is_multiple_of(2)`.

2. Add an explicit `is_ascii()` guard before slicing. With only the
   length check, a non-ASCII input (e.g. `"é"` — 2 bytes but 1 char)
   would survive the length check before `from_str_radix` caught it.
   The explicit guard makes the rejection intent clear and avoids
   relying on the post-slice error path.

3. Extract the hex-parsing into a private `parse_public_key_hex`
   helper. PyO3-bound `#[pymethods]` are awkward to unit-test from
   Rust (need a Python interpreter via `prepare_freethreaded_python`);
   a plain function is testable with no GIL boilerplate.

4. Add 5 unit tests covering the security boundary:
   - empty input -> Some(empty vec)
   - valid hex decodes to the right bytes
   - odd-length rejected without panic (the original bug)
   - non-hex chars rejected (previously silently filtered)
   - multi-byte UTF-8 rejected without panic

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 03:36:28 +00:00
tomaioo f21eec6c86 fix(security): panic on malformed hex input in signature verifica
`verify_signature` slices `public_key_hex[i..i + 2]` without validating that the input length is even. An odd-length or otherwise malformed string can trigger an out-of-bounds panic, which may crash the process (or at minimum terminate the request path), creating a denial-of-service vector if this method is reachable from untrusted input.

Signed-off-by: tomaioo <203048277+tomaioo@users.noreply.github.com>
2026-05-20 03:36:16 +00:00
Jon Saad-Falcon 4a7817509b docs(index): align homepage with canonical surfaces (#361) 2026-05-19 18:52:30 -07:00
Jon Saad-Falcon 2b0e6e7130 Update README.md 2026-05-19 18:18:26 -07:00
Jon Saad-Falconandkrypticmouse 289fcb00e2 fix(ci): desktop — translate PEP 440 .devN to SemVer for Tauri (#360)
Tauri's build script enforces strict SemVer
(`MAJOR.MINOR.PATCH[-pre][+build]`) on `tauri.conf.json > version`, but
the autotag scheme introduced in #358 emits PEP 440 dev releases like
`1.0.2.dev661` — PEP 440 separates dev with `.`, SemVer requires `-`.
First post-#358 desktop run failed with:

    tauri.conf.json > version must be a semver string

PyPI requires PEP 440; Tauri requires SemVer. They genuinely don't
agree on `.devN`. Translate just for the Tauri bundle:

  1.0.2.dev661  -> 1.0.2-dev.661   (valid SemVer prerelease)
  1.0.2          -> 1.0.2          (passthrough)
  1.0.0-rc.1     -> 1.0.0-rc.1     (passthrough)

Tag, git history, PyPI wheel, and the updater's `latest.json` all keep
the PEP 440 form. Only the embedded bundle version uses SemVer, and
the comparison the updater does is bundle-vs-latest.json (both SemVer
now) so the upgrade path stays consistent.

Failing run for reference: 26118619500

Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
2026-05-19 12:22:24 -07:00
Jon Saad-Falconandkrypticmouse 4668ea0b7f fix(ci): publish-to-pypi — match Vite's actual outDir (#359)
The frontend bundling step in pypi-publish.yml assumed Vite produced
`frontend/dist/`, but vite.config.ts is configured with
`outDir: '../src/openjarvis/server/static'` and `emptyOutDir: true` —
Vite writes straight to the package's static dir and clears stale
assets itself. The `rm -rf dist; cp -r dist/.` ceremony was dead code
that would have deleted the build output had the assertion not
short-circuited it first.

First post-#358 autotag run failed at this step with
`frontend/dist/index.html missing or empty after build` (build was
fine; the assertion checked the wrong path).

Fix: drop the rm/cp dance and assert the actual output location.

Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
2026-05-19 12:12:04 -07:00
Tanvir Bhathal a58d6b6c48 Auto Update (#358) 2026-05-19 11:57:10 -07:00
github-actions[bot] af21bc18ea chore: update clone traffic data [skip ci] 2026-05-19 07:16:45 +00:00
Jon Saad-Falcon fea8d3e872 release: v1.0.1 (#357) 2026-05-18 20:19:37 -07:00
Jon Saad-Falcon 5b847cd081 Update README.md 2026-05-18 18:41:48 -07:00
Jon Saad-Falcon a5029e297f Update README.md 2026-05-18 18:40:54 -07:00
Robby Manihani 9af0dfe336 feat: Deep Research — personal deep research over Gmail with hybrid retrieval and agentic synthesis (#354) 2026-05-18 17:46:12 -07:00
github-actions[bot] b863cbb07b chore: update clone traffic data [skip ci] 2026-05-18 07:27:15 +00:00
Tanvir Bhathal 7081be7bd3 [FEAT] Telemetry (#351) 2026-05-17 13:07:05 -07:00
github-actions[bot] df4332b0e8 chore: update clone traffic data [skip ci] 2026-05-17 07:00:19 +00:00
Jon Saad-Falcon e97088f199 release: v1.0.0 (#349) 2026-05-16 13:47:23 -07:00
github-actions[bot] 50b887ba25 chore: update clone traffic data [skip ci] 2026-05-16 06:49:33 +00:00
Jon Saad-Falcon 9540e85dfb ci: fix env-dependent test failures (gated datasets, server extra, router prefix) (#348) 2026-05-15 21:31:13 -07:00
Andrew Park a7b8597856 hybrid: evaluate six local+cloud paradigms (minions, conductor, archon, advisors, skillorchestra, toolorchestra) (#344) 2026-05-15 20:51:19 -07:00
Jon Saad-Falcon eacf34e500 spec_search: drop CLI stubs, rename, and ship paper-aligned tutorial + configs (#346) 2026-05-15 14:17:23 -07:00
github-actions[bot] 10b7ef3d6c chore: update clone traffic data [skip ci] 2026-05-15 07:10:17 +00:00
Jon Saad-Falcon 44815e8544 feat(evals): add TerminalBench V2.1 benchmark (#345) 2026-05-14 20:23:14 -07:00
Avanika Narayan b8c3b417ec feat(mining): validate Gemma E4B and Qwen 3.6 Pearl artifacts (#328) 2026-05-14 20:11:24 -07:00
github-actions[bot] da993eedf8 chore: update clone traffic data [skip ci] 2026-05-14 07:04:22 +00:00
github-actions[bot] eb097062b0 chore: update clone traffic data [skip ci] 2026-05-13 07:05:38 +00:00
github-actions[bot] d7888caf7a chore: update clone traffic data [skip ci] 2026-05-12 07:01:40 +00:00
Tanvir Bhathal 5044cf2bc8 Small gitignore for opt-in mining (#338) 2026-05-11 14:37:45 -07:00
github-actions[bot] 797ee6b761 chore: update clone traffic data [skip ci] 2026-05-11 07:16:28 +00:00
github-actions[bot] fec270eecf chore: update clone traffic data [skip ci] 2026-05-10 06:57:30 +00:00
github-actions[bot] d314f3ea8c chore: update clone traffic data [skip ci] 2026-05-09 06:46:02 +00:00
Jon Saad-Falcon 57151327b3 feat: LLM-guided spec search building blocks (split-aware sampling, external corpora, agent-trace adapter) (#332) 2026-05-08 20:18:40 -07:00
github-actions[bot] 37f4942b07 chore: update clone traffic data [skip ci] 2026-05-08 06:39:07 +00:00
Jon Saad-Falcon b8245136db fix(security): block IPv4-mapped IPv6 addresses in SSRF check (#327) 2026-05-07 14:55:52 -07:00
Jon Saad-Falcon ab46c89660 Delete CLAUDE.md 2026-05-07 12:14:45 -07:00
github-actions[bot] f705a46b39 chore: update clone traffic data [skip ci] 2026-05-07 08:31:07 +00:00
f0ab045cad fix(tools): correct Python-fallback bugs in calculator, file tools, and git tool (#236)
- calculator: add `ln` alias for `math.log`, replace `^` with `**` before
  AST parsing so caret-as-power works, convert SyntaxError → ValueError for
  consistent error handling, and return `math.inf` on division by zero
  (matching the documented meval behaviour) instead of raising an exception
- file_read / file_write: replace the hardcoded `str(path).startswith(str(d) + "/")
  guard with `Path.is_relative_to()` so allowed-directory checks work on
  Windows (and any OS whose separator is not `/`)
- git_tool: catch `NotADirectoryError` raised by `subprocess.run` on Windows
  when `cwd` points to a non-existent path, returning a proper error result

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Robby Manihani <manihani@stanford.edu>
2026-05-07 00:41:58 -07:00
Jack Hamilton 9e7366fc05 Close KnowledgeStore connections in connectors_router endpoints (#209)
* fix: close KnowledgeStore connections in connectors_router endpoints

KnowledgeStore opens a persistent SQLite connection in __init__ but was
never closed at the three instantiation sites in connectors_router.py,
leaking one file descriptor (plus its WAL handle) per call. The worst
offender is _connector_summary(), which is invoked per-connector on
every GET /connectors poll from the frontend — after ~100 polls the
backend hits the default macOS per-process FD limit (256) and asyncio's
accept() starts failing with OSError: [Errno 24] Too many open files.

Add __enter__/__exit__ to KnowledgeStore (close() already existed) and
wrap all three instantiation sites in `with` blocks so the connection
is released deterministically when the scope exits.

- connectors/store.py: add context manager protocol
- server/connectors_router.py: use `with KnowledgeStore() as store:`
  in _connector_summary, _ingest, and _run_sync

* test: cover KnowledgeStore context manager + connectors_router close leak

- test_store.py: add two tests for the new __enter__/__exit__ protocol
  verifying the connection closes on normal exit and on exceptions.
- test_connectors_router.py: add a regression test that monkeypatches
  KnowledgeStore.close to count invocations and asserts every store
  opened by GET /v1/connectors is paired with a close.

Also fix a pre-existing bug in the test_connectors_router fixture: the
router is created with prefix="/v1/connectors" internally (line 92 of
connectors_router.py), and the fixture was wrapping it again with
prefix="/v1", producing "/v1/v1/connectors". All 6 existing router
tests were failing with 404 before this fix. 5 of them now pass; the
remaining failure (test_trigger_sync) is an unrelated KeyError on a
missing response field and is out of scope for this PR.

* test: drop connectors_router regression test — skipped in CI anyway

The regression test added in the previous commit relies on FastAPI
being importable, but CI's dev-extra install does not include fastapi
(it lives in the `server` optional group). All tests in
test_connectors_router.py silently skip on CI with "fastapi not
installed", so the regression test would never actually execute there.

Revert test_connectors_router.py to the upstream main version. The
fixture bug I fixed (double prefix) and the connection-leak regression
test both deserve a separate PR scoped to test infrastructure — that
PR should either add fastapi to dev deps, split server tests into
their own CI job, or both.

The KnowledgeStore context manager tests in test_store.py are kept
because they have no fastapi dependency and will run in CI.
2026-05-06 23:44:43 -07:00
Avanika Narayanandkrypticmouse 4935053ac7 feat(mining): register ScalingIntelligence Pearl models (#324)
Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
2026-05-06 22:20:18 -07:00
Avanika Narayanandkrypticmouse 8fb9d5ece3 feat(mining): add Pearl model conversion workflow (#323)
* feat(mining): add Pearl model conversion workflow

* test(mining): tolerate missing Docker device request type

* docs(mining): record Gemma and Qwen conversion evidence

* docs(mining): record Qwen local validation evidence

---------

Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
2026-05-06 16:20:27 -07:00
Avanika Narayan daaa5577f0 feat(mining): inspect Pearl model artifacts before launch
Add jarvis mine inspect-model to fail fast on missing Pearl/Hugging Face artifact metadata before GPU startup.
2026-05-05 21:21:33 -07:00
Avanika Narayan d74fb68b68 docs(mining): record Gemma/Qwen Pearl validation blockers
Record H100 validation findings for planned Gemma/Qwen Pearl models and keep them planned until public artifacts pass runtime validation.
2026-05-05 20:29:55 -07:00
Avanika Narayanandkrypticmouse a689be0c69 fix(evals): clean up AI stack runtime harness (#320)
Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
2026-05-05 19:28:50 -07:00
Jon Saad-Falcon f41cf420be feat(mining): Pearl mining integration
Consolidates NVIDIA vLLM, Apple Silicon, CPU Pearl mining support, CLI/docs, and live H100 validation.
2026-05-05 19:11:15 -07:00
Avanika Narayan e79bc1e196 Add cold-start CLI installer (#313) 2026-05-05 14:42:48 -07:00
Avanika Narayan a3ba63d148 AI_stack_support: subprocess-based external framework harness (#311) 2026-05-05 13:37:33 -07:00
github-actions[bot] ac2deb9d9d chore: update clone traffic data [skip ci] 2026-05-05 08:06:09 +00:00
github-actions[bot] 5a97e1197c chore: update clone traffic data [skip ci] 2026-05-04 08:26:37 +00:00
Avanika Narayan d7624510bb Eval framework: silent-failure detection, surgical reruns, continuous-score reporting (#303) 2026-05-03 21:07:44 -07:00
github-actions[bot] aaaf00a951 chore: update clone traffic data [skip ci] 2026-05-03 07:59:20 +00:00
github-actions[bot] bd1e87373f chore: update clone traffic data [skip ci] 2026-05-02 07:41:02 +00:00
github-actions[bot] 5e0123ffd7 chore: update clone traffic data [skip ci] 2026-05-01 08:09:11 +00:00
github-actions[bot] 363cb637cd chore: update clone traffic data [skip ci] 2026-04-30 08:18:16 +00:00
Ali Shahkar 2d3e5ad8b6 fix: correct import path for build_routing_context in executor (#214)
routing.types appears to have been an in-progress module that was
consolidated into routing.router before it was created. The lazy
import inside the router policy block was silently caught by the
surrounding try/except, causing the policy to never activate and
the executor to always fall back to the configured model regardless
of any router_policy setting.

Corrects the import to openjarvis.learning.routing.router where
build_routing_context is defined and exported.
2026-04-29 14:24:48 -07:00
Robby Manihani 259f97b81c fix(server): allow Tauri 2 webview origins on every platform (#292)
The desktop app's chat-completions stream relies on a raw browser
fetch from the Tauri webview to the FastAPI backend, so it is
subject to CORS. The default allowlist only contained
`tauri://localhost`, which is the production webview origin on
macOS, Linux, and iOS. On Windows and Android, Tauri 2 serves the
app from `http://tauri.localhost` (or `https://tauri.localhost`
when `windows.useHttpsScheme` is enabled), so the preflight for
`POST /v1/chat/completions` was rejected and the streaming fetch
threw `TypeError: Failed to fetch` before any byte was read.

Symptom users reported: in the Logs tab the request appears to
succeed up to "Request sent", followed immediately by
"Stream error: Failed to fetch" and "Response: 22 chars" -- which
is exactly the length of the synthesized fallback string
`Error: Failed to fetch` written by InputArea.tsx when streamChat
throws.

Add `http://tauri.localhost` and `https://tauri.localhost` to both
default origin lists (`ServerConfig.cors_origins` and the
`create_app` fallback), and add a regression test that drives a
real preflight against `/v1/chat/completions` for each of the
three Tauri origin schemes.

Browser model loading kept working because it is routed through
the Rust `tauriInvoke('fetch_models')` command, which is a
server-to-server HTTP call not subject to browser CORS -- only
streaming chat goes through the webview's fetch.
2026-04-29 12:55:25 -07:00
github-actions[bot] cdb04cdecb chore: update clone traffic data [skip ci] 2026-04-29 08:13:18 +00:00
Jon Saad-Falcon f16bf734cf Merge branch 'main' of https://github.com/open-jarvis/OpenJarvis 2026-04-28 20:26:44 -07:00
Jon Saad-FalconandClaude Opus 4.7 3f2f46e406 ci: stop auto-running Claude PR review on every pull request
Removes the pull_request trigger so the workflow only runs on explicit
@claude mentions or manual workflow_dispatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:23:24 -07:00
Robby Manihani deabda0c4b Docs/onboarding feedback mac (#290) 2026-04-28 20:11:27 -07:00
github-actions[bot] 43b037a796 chore: update clone traffic data [skip ci] 2026-04-28 08:22:55 +00:00
github-actions[bot] 1403c7b843 chore: update clone traffic data [skip ci] 2026-04-27 08:21:17 +00:00
Robby Manihani f4d544d09e fix(server): replace CORS wildcard fallback with closed localhost list (#283)
server/app.py defaulted to allow_origins=["*"] combined with
allow_credentials=True when cors_origins was not passed. That
combination is invalid per the CORS spec — browsers reject it — and
when used loosely it signals that any cross-origin request with
credentials is allowed, exposing session cookies and auth headers.

The reference config at configs/openjarvis/config.toml binds to
0.0.0.0 without setting cors_origins, so this fallback fired in every
default deployment.

Default to a closed list (Vite dev origin + Tauri webview) instead of
the wildcard. Users who actually need a custom origin already pass it
via cors_origins.

Closes #222
2026-04-26 17:08:38 -07:00
Robby Manihani 65a340bb7a fix(server): allow 'unsafe-inline' 'unsafe-eval' in CSP so dashboard renders after Rust module compiles (#284)
The dashboard at /dashboard renders blank once the Rust security module
is compiled (uv run maturin develop ...). The middleware sets
"Content-Security-Policy: default-src 'self'", which blocks the inline
scripts and styles the dashboard HTML relies on.

Loosen the policy to allow 'unsafe-inline' and 'unsafe-eval' so the
dashboard works once the security middleware is active. The same string
is exported via SECURITY_HEADERS for tests, so update both call sites.

Closes #261
2026-04-26 17:08:29 -07:00
Robby Manihani a3a23125b4 fix(config): drop [security] enabled=false from reference config so security middleware ships on by default (#285)
configs/openjarvis/config.toml is the file users copy as their starting
configuration. It explicitly set [security] enabled = false (with a
comment about eval performance) — meaning users who follow the
quickstart unknowingly run with GuardrailsEngine, InjectionScanner,
CapabilityPolicy, and rate limiting all disabled.

The SecurityConfig.enabled default in code is True, so removing the
override here lets every install ship with security on. Eval workflows
that genuinely need it disabled can opt out via their own config.

Closes #224
2026-04-26 17:08:12 -07:00
Robby Manihani 7ba095ebf1 fix(memory): default context_min_score to 0.0 so FTS5 BM25 results aren't dropped on small corpora (#286)
SQLite FTS5 BM25 scores collapse toward zero on small corpora — when
every indexed document contains the query term, IDF approaches zero so
scores return ~1e-6. The default context_min_score = 0.1 silently
filters out every result before injection, so memory is stored but
never reaches the model on a fresh install.

Lowering both defaults to 0.0 lets retrieved context flow through;
users who want strict filtering can still set [memory]
context_min_score in config.toml.

Closes #262
2026-04-26 17:07:52 -07:00
Robby Manihani a26f7b2e48 chore(deps): bump mlx-lm to >=0.31.1 so Qwen3.5 architecture is supported (#282)
uv.lock pinned mlx-lm==0.29.1, which predates the qwen3_5 architecture.
Running mlx_lm.server with mlx-community/Qwen3.5-* models therefore
fails with 'Model type qwen3_5 not supported.' on every uv sync, even
when pyproject.toml's loose constraint (>=0.19) would otherwise allow
a newer release.

Tighten the inference-mlx extra to mlx-lm>=0.31.1 (the first release
with Qwen3.5 support) and regenerate uv.lock — resolves to mlx-lm
0.31.3.

The lockfile diff is large because uv produces a single cross-platform
lock and adds 'split' entries for a handful of transitive deps
(transformers, vllm, huggingface-hub, mistral-common, etc.). Every
split entry is marker-gated to:

    python_full_version >= '3.14' and sys_platform != 'linux'
    and sys_platform != 'win32'

i.e. macOS on Python 3.14+ — the very subset that actually pulls in
mlx-lm 0.31.x. Linux, Windows, and Python <= 3.13 paths keep the
existing pins (transformers 4.57.6, vllm 0.17.1, …).

Closes #191
2026-04-26 17:05:12 -07:00
Robby Manihani 83bcbb803c docs: align quickstart model with CPU-friendly recommendation (qwen3.5:4b) (#287)
Reported via twitter (issue #269): the macOS install guide and other
docs recommend a 4B model on CPU but the README and quickstart pull /
ask a much larger model (qwen3:8b) — confusing and slow for first-time
CPU-only users.

Switch the README quickstart and the docs/getting-started/quickstart.md
'Chat with Any Model' tile to qwen3.5:4b, with an inline note that GPU
users can scale up to qwen3.5:9b or larger. This matches the existing
chat-simple preset and the macOS guide's CPU recommendation.

Closes #269
2026-04-26 17:04:06 -07:00
Jon Saad-Falcon 484d0f090b Delete results (#280) 2026-04-26 05:43:06 -07:00
github-actions[bot] 5a5df909c1 chore: update clone traffic data [skip ci] 2026-04-26 07:26:14 +00:00
github-actions[bot] 02565fb787 chore: update clone traffic data [skip ci] 2026-04-25 07:12:41 +00:00
github-actions[bot] b4f208deb6 chore: update clone traffic data [skip ci] 2026-04-24 08:00:21 +00:00
github-actions[bot] adf06593b7 chore: update clone traffic data [skip ci] 2026-04-23 07:48:56 +00:00
github-actions[bot] 43f068830e chore: update clone traffic data [skip ci] 2026-04-22 07:43:35 +00:00
github-actions[bot] f391401f35 chore: update clone traffic data [skip ci] 2026-04-21 07:47:01 +00:00
Jon Saad-Falcon f5695845b7 feat(distillation): M1→M2→M3 spec-level distillation pipeline + hill-climb optimizer (#273) 2026-04-20 19:18:10 -07:00
Robby Manihani 8258295f51 Feature/twitter bot (#272) 2026-04-20 15:33:51 -07:00
github-actions[bot] 35a068637d chore: update clone traffic data [skip ci] 2026-04-20 08:03:52 +00:00
github-actions[bot] c4a7427358 chore: update clone traffic data [skip ci] 2026-04-19 07:16:31 +00:00
github-actions[bot] 36ddf9c26b chore: update clone traffic data [skip ci] 2026-04-18 07:04:42 +00:00
Robby Manihani ef03d98fc0 Feature/twitter bot (#259) 2026-04-17 15:22:17 -07:00
Andrew Park 64d0f7ab02 feat(frontend): cleaner UI pass — consistent layout, tokens, skeleton loading (#258) 2026-04-17 12:33:29 -07:00
Andrew Park 30d4d45a7d refactor(system): decompose JarvisSystem god-object into system/ package (#257) 2026-04-17 11:02:56 -07:00
Andrew Park 88a7ebcaa3 test: add live marker, CI coverage gate, rust bridge boundary tests (#256) 2026-04-17 11:02:21 -07:00
Andrew Park 28e913f706 fix(agents): bind built-in tools, live tool-call UI, Markdown streaming (#255) 2026-04-17 11:01:50 -07:00
Andrew Park 48c23d87df perf: replace agents polling with WebSocket, fix workbox prod mode (#254) 2026-04-17 11:00:50 -07:00
github-actions[bot] 7108be9aec chore: update clone traffic data [skip ci] 2026-04-17 07:43:25 +00:00
github-actions[bot] e868445b74 chore: update clone traffic data [skip ci] 2026-04-16 07:43:10 +00:00
github-actions[bot] 00cd93e472 chore: update clone traffic data [skip ci] 2026-04-15 07:28:35 +00:00
Andrew ParkandJon Saad-Falcon f339c1c2d6 feat: memory UI, settings, and API fixes (#247)
* refactor: merge desktop/ into frontend/, eliminate duplicate Tauri scaffolding

The project had two overlapping directories: desktop/ (Tauri Rust backend +
stale React components) and frontend/ (real React app + dead Tauri stub).
This consolidates everything under frontend/:

- Move desktop/src-tauri/ → frontend/src-tauri/ (the real 1,720-line Rust
  backend with Ollama sidecar, backend lifecycle, cloud keys, overlay, etc.)
- Preserve 9 old desktop React components in frontend/src/components/Desktop/
  (excluded from TS build — APIs have drifted, kept for future integration)
- Delete the old frontend/src-tauri/ stub (246 lines, never compiled)
- Delete desktop/ entirely
- Fix tauri.conf.json frontendDist path (../../frontend/dist → ../dist)
- Update CI workflow, bump script, .gitignore, and docs paths
- Rename setup/ → Setup/ for consistent PascalCase component directories

* feat: add memory UI, settings, and fix memory API routes

- Add Memory tab to Data Sources page with stats, search, index path,
  and manual store functionality
- Add Memory section to Settings page with backend picker, context
  injection toggle, and parameter sliders (top_k, min_score, max_tokens)
- Add memory API functions to frontend (getMemoryStats, searchMemory,
  storeMemory, indexMemoryPath, getMemoryConfig)
- Fix backend /v1/memory/* routes to use app-level memory backend
  instead of creating fresh SQLiteMemory instances per request
- Add GET /v1/memory/config and POST /v1/memory/index endpoints
- Gracefully handle missing Rust backend (return defaults instead of 500)
- Fix nested scroll in Agents Interact tab (use viewport-relative height)

* fix: memory API routes, dialog plugin, and UI polish

- Fix memory API: use backend.retrieve() not .search(), .count() not
  .stats() to match actual SQLiteMemory interface
- Handle None backend gracefully in index endpoint (503 instead of crash)
- Expand ~ in index path (expanduser + resolve)
- Install @tauri-apps/plugin-dialog for native folder picker in Tauri
- Browse button only shows in Tauri (browser can't get absolute paths)
- Redesign Memory tab: proper cards, color-coded search scores, two-column
  layout for index/store, loading spinners, accent gradient on stats card

---------

Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
2026-04-14 09:53:14 -07:00
Andrew Park 54d79ef235 feat: add Apple Contacts connector (macOS AddressBook) (#248)
* feat: add Apple Contacts connector (macOS AddressBook)

Reads directly from ~/Library/Application Support/AddressBook/AddressBook-v22.abcddb
in read-only mode. Extracts names, phone numbers, emails, postal addresses, URLs,
social profiles, and notes for each contact. Requires Full Disk Access like
iMessage and Apple Notes connectors.

- New connector: src/openjarvis/connectors/apple_contacts.py
- Registered in connectors/__init__.py
- Added frontend catalog entry (PIM category) and icon mapping
- MCP tools: contacts_search, contacts_get_contact

* test: add comprehensive tests for Apple Contacts connector

15 tests covering: is_connected (exists/missing), sync yields all real
contacts (skips system rows), extracts all field types (phone, email,
address, URL, social, notes), cleans Apple label markup, org-only
contacts, minimal contacts, since filter, sync_status tracking,
structured metadata, disconnect, mcp_tools, registry, empty DB,
missing DB. Uses fake SQLite database — no real Contacts DB needed.

* fix: scan iCloud/Exchange source databases for all contacts

The main AddressBook database only contains locally-created contacts.
Synced contacts (iCloud, Exchange, etc.) live under
Sources/<UUID>/AddressBook-v22.abcddb. Now scans all source databases
and deduplicates by ZUNIQUEID across sources.

Adds tests for multi-source scanning and cross-source deduplication.
2026-04-14 09:49:08 -07:00
Andrew Park 55662fc426 refactor: merge desktop/ into frontend/, eliminate duplicate Tauri scaffolding (#246)
The project had two overlapping directories: desktop/ (Tauri Rust backend +
stale React components) and frontend/ (real React app + dead Tauri stub).
This consolidates everything under frontend/:

- Move desktop/src-tauri/ → frontend/src-tauri/ (the real 1,720-line Rust
  backend with Ollama sidecar, backend lifecycle, cloud keys, overlay, etc.)
- Preserve 9 old desktop React components in frontend/src/components/Desktop/
  (excluded from TS build — APIs have drifted, kept for future integration)
- Delete the old frontend/src-tauri/ stub (246 lines, never compiled)
- Delete desktop/ entirely
- Fix tauri.conf.json frontendDist path (../../frontend/dist → ../dist)
- Update CI workflow, bump script, .gitignore, and docs paths
- Rename setup/ → Setup/ for consistent PascalCase component directories
2026-04-14 09:48:12 -07:00
github-actions[bot] 4613b1ff2f chore: update clone traffic data [skip ci] 2026-04-14 07:28:38 +00:00
Andrew Park ed2acf1895 fix(skills): implement skill remove and search CLI commands (#240) 2026-04-13 15:03:39 -07:00
Avanika Narayan a3789212ec refactor: remove dead shims, stale files, and test duplication (#243) 2026-04-13 15:03:03 -07:00
Tanvir Bhathal 171e78be5d remove inline (#244) 2026-04-13 14:45:46 -07:00
Tanvir Bhathal 9c15b25f90 [FEAT] New Interaction Interface UI (#237) 2026-04-13 14:23:15 -07:00
Avanika Narayan f9fcb39601 Merge pull request #242 from open-jarvis/fix/deepresearch-naming
fix: rename DeepResearchBench references across codebase
2026-04-13 12:33:02 -07:00
Jon Saad-FalconandClaude Opus 4.6 db4b031c45 fix: rename DeepResearchBench references in docs, configs, and tests
Update all human-readable strings (docstrings, comments, descriptions,
log messages, TOML config descriptions) to correctly say
"DeepResearchBench" instead of "LiveResearchBench" for the
deep_research_bench benchmark. Class names and file paths are
unchanged for backward compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:59:24 +00:00
Avanika Narayan d1e7a7bbe5 Merge pull request #241 from open-jarvis/feat/liveresearchbench-and-deepresearch-alias
feat: add Salesforce LiveResearchBench + deepresearch alias
2026-04-13 11:48:00 -07:00
Jon Saad-FalconandClaude Opus 4.6 9d5c3372a6 feat: add Salesforce LiveResearchBench + deepresearch alias
Add Salesforce LiveResearchBench (liveresearchbench) as a new benchmark:
- Dataset provider loading 80 expert-curated research tasks with 543
  checklist items from HuggingFace (Salesforce/LiveResearchBench)
- Checklist-based scorer evaluating coverage + quality dimensions
- Groups multiple checklist rows per question into single EvalRecords

Also add "deepresearch" as a CLI alias for the existing DeepResearchBench
benchmark (previously only available as "liveresearch").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:41:41 +00:00
github-actions[bot] b5995eb483 chore: update clone traffic data [skip ci] 2026-04-13 08:00:20 +00:00
Jon Saad-Falcon 2d868163cc Update README.md (#239) 2026-04-12 08:21:47 -07:00
github-actions[bot] 5ab8ccca87 chore: update clone traffic data [skip ci] 2026-04-12 07:11:41 +00:00
Jon Saad-FalconandClaude Opus 4.6 85089dbb30 chore(ci): bump Node-20 GHA actions to Node-24 majors
Ahead of the 2026-06-02 forced-Node-24 deadline, upgrade the remaining
javascript actions flagged in the workflow annotations:

- actions/setup-python  v5 → v6
- actions/setup-node    v4 → v6 (skipping v5)
- actions/cache         v4 → v5
- actions/deploy-pages  v4 → v5
- actions/upload-pages-artifact v3 → v5.0.0

upload-pages-artifact has no rolling @v5 major tag — only the specific
v5.0.0 release — so it gets an explicit pin, same as astral-sh/setup-uv.

Not touched: swatinem/rust-cache@v2 (still on v2 majors, not flagged),
dtolnay/rust-toolchain@stable (composite), tauri-apps/tauri-action@v0
(composite), anthropics/claude-code-action@v1 (still latest major).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 14:41:16 -07:00
Jon Saad-FalconandClaude Opus 4.6 48673ac45a fix(ci): pin setup-uv to v8.0.0 (no rolling v8 major tag exists)
astral-sh/setup-uv does not publish a rolling @v8 major tag — only the
specific v8.0.0 release. Previous commit's @v8 reference failed to
resolve and broke CI/Docs/PyPI-publish runs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:27:27 -07:00
Jon Saad-FalconandClaude Opus 4.6 70cbda8a73 chore: derive __version__ from metadata, bump GHA action versions
- openjarvis.__version__ now comes from importlib.metadata.version("openjarvis")
  instead of a hardcoded string, so it stays in sync with pyproject.toml on every
  release. Tests assert against openjarvis.__version__ rather than a literal.
- Bump actions/checkout@v4 → @v6 and astral-sh/setup-uv@v4 → @v8 across every
  workflow ahead of the 2026-06-02 Node.js 20 deprecation on GitHub Actions runners.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 13:19:19 -07:00
Jon Saad-FalconandClaude Opus 4.6 0b516deea1 chore: rename PyPI distribution to OpenJarvis (0.1.1)
Now that the `openjarvis` name is available on PyPI, rename the
distribution from `OpenJarvisAI` to `OpenJarvis` so `pip install
openjarvis` matches the import name and the in-code install hints
(e.g. `pip install openjarvis[channel-gmail]`).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:49:52 -07:00
github-actions[bot] c0201eb181 chore: update clone traffic data [skip ci] 2026-04-11 06:59:12 +00:00
Ali ShahkarandClaude Sonnet 4.6 89c5414af0 fix(doctor): respect preferred_engine/engine.default when reporting default model (#204)
* fix(cloud-router): prevent local HF org models from being misrouted to OpenRouter

get_provider() previously routed any "/" model to OpenRouter, which caused
locally-served models (e.g. mlx-community/Qwen3.5-27B-4bit-DWQ) to be sent
to the cloud instead of localhost:8080.

Adds _LOCAL_HF_ORGS module-level allowlist covering mlx-community/,
bartowski/, unsloth/, and lmstudio-community/. Returns None early for
these before the openrouter "/" check.

* fix(doctor): respect preferred_engine/engine.default when reporting default model

_check_default_model() iterated engines in sorted alphabetical order,
causing engines like 'lemonade' to be reported as the host for the
default model even when 'mlx' was configured as preferred_engine and
engine.default. Now checks the configured engine first, then falls back
to alphabetical scan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 15:30:30 -07:00
github-actions[bot] f0eacb7980 chore: update clone traffic data [skip ci] 2026-04-10 21:50:52 +00:00
Jon Saad-FalconandClaude Opus 4.6 6fcb6c5115 fix: use TRAFFIC_TOKEN for checkout to bypass branch protection
The clone tracking workflow needs to push directly to main, but branch
protection rules block GITHUB_TOKEN pushes. Using the PAT for checkout
allows the push to succeed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:49:08 -07:00
Tanvir Bhathal 0e122cd776 DGX Spark Fix (#231) 2026-04-10 11:40:03 -07:00
Prathap PandAvanika Narayan 186e50fa4f split telegram messages based on 4k char limit (#211)
* feat: add eval configs for NeurIPS 2026 baseline sweep (#208)

* split telegram messages based on 4k char limit

---------

Co-authored-by: Avanika Narayan <gostanfordtennis@gmail.com>
2026-04-10 09:57:52 -07:00
Ali ShahkarandClaude Sonnet 4.6 5208c7f994 fix(connectors): prevent Apple Music connector from launching Music.app (#229)
AppleScript's `tell application "X"` is an implicit launch directive —
macOS starts the app if it is not running. Both scripts in apple_music.py
used this pattern unconditionally, causing Music.app to open on every
connector status poll (frontend load, SyncScheduler ticks) for all macOS
users regardless of whether they use the Apple Music connector.

Guard both scripts with `application "Music" is running` so that
is_connected() returns False gracefully when Music is closed, without
ever launching it unsolicited.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:57:21 -07:00
Ali Shahkar 4beb762885 fix(cloud-router): prevent local HF org models from being misrouted to OpenRouter (#213)
get_provider() previously routed any "/" model to OpenRouter, which caused
locally-served models (e.g. mlx-community/Qwen3.5-27B-4bit-DWQ) to be sent
to the cloud instead of localhost:8080.

Adds _LOCAL_HF_ORGS module-level allowlist covering mlx-community/,
bartowski/, unsloth/, and lmstudio-community/. Returns None early for
these before the openrouter "/" check.
2026-04-10 09:57:10 -07:00
Jon Saad-FalconandClaude Opus 4.6 9ab097c8df chore: remove download/clone/star badges from README
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:38:25 -07:00
Jon Saad-FalconandClaude Opus 4.6 65ff68d47d fix: move badges to README and fix clone tracking auth
- Move download/clone/star badges from docs/index.md to README.md
- Use TRAFFIC_TOKEN secret instead of GITHUB_TOKEN for Traffic API
  (requires admin-level access that GITHUB_TOKEN doesn't provide)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:37:30 -07:00
Jon Saad-FalconandClaude Opus 4.6 f1494a970c fix: use OpenJarvisAI as PyPI package name
openjarvis was already taken on PyPI, so the package is registered as
OpenJarvisAI. Update pyproject.toml and docs badges to match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:20:03 -07:00
Jon Saad-FalconandClaude Opus 4.6 84582ff64f feat: add download/clone tracking and PyPI publishing
- Add shields.io badges to docs front page (desktop downloads, PyPI
  installs, git clones, GitHub stars)
- Add daily GitHub Action to accumulate git clone traffic data
- Add PyPI publish workflow triggered on tags and releases
- Add PyPI metadata (license, authors, classifiers, URLs) to pyproject.toml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:14:20 -07:00
Jon Saad-Falcon 3c34ad47ab feat: add skills system with import, learning loop, and benchmark harness (#230) 2026-04-09 14:43:04 -07:00
5388b9b75a feat(evals): per-config max_turns + Phase 2 config bumps (#215)
Per-config max_turns:
- Add `max_turns` field to ExecutionConfig and RunConfig dataclasses
- Parse `[run] max_turns` from eval TOML configs
- Plumb through `_build_backend` to JarvisAgentBackend, which sets
  `builder._config.agent.max_turns` before building the system
- Default `None` falls back to `JarvisConfig.agent.max_turns` (10) so
  existing configs are unaffected

Why: Trinity-Large is a "thinking" model that consumes agent-loop turns
producing intermediate reasoning, not just tool calls. With the default
max_turns=10 it hit the cap on 25/50 GAIA tasks (50%) and 39/50
LiveResearchBench tasks (78%) before producing a final answer. With
max_turns=50 (set per-config in this commit's gaia-trinity-large.toml
and liveresearch-trinity-large.toml updates), Trinity LR jumped from
12.0% to 72.0% — equal to Qwen-27B. Without this field the only
workaround was a runtime `OPENJARVIS_CONFIG` TOML hack.

Misc config bumps:
- `gaia-qwen-2b.toml`, `liveresearch-qwen-2b.toml`,
  `taubench-telecom-qwen2b.toml`: max_workers 1 -> 8 to use the
  parallel runner from #207
- `gaia-trinity-large.toml`, `liveresearch-trinity-large.toml`:
  add `max_turns = 50` (using the new field)

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gpu-dp-vg94q-lccz2.t-f6a9b26f-56c0-4f98-88fc-705e5d9b1714.svc.cluster.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:38:23 -07:00
Avanika Narayan 41ddab9099 add eval configs (#208) 2026-04-08 15:53:52 -07:00
Jon Saad-Falcon 30a29d64f5 feat(evals): parallel TauBench execution + Phase 2 local-model configs (#207) 2026-04-07 19:57:53 -07:00
Robby Manihani cff83a93a0 fix: recommend Qwen3.5 MoE models instead of Qwen3 dense (#189)
Qwen3.5 MoE models provide better quality per GB of memory since only
a fraction of parameters are active per token. Updates the tier table:

  ≤8 GB  → qwen3.5:2b
  8-16GB → qwen3.5:4b
 16-32GB → qwen3.5:9b
  32GB+  → qwen3.5:27b
2026-04-04 11:14:41 -07:00
Robby Manihani 35b8991cc2 feat: hardware-aware model recommendation in jarvis init (#188)
Closes #132 — jarvis init now recommends a Qwen3 dense model based on
available memory using an explicit tier table:

  ≤8 GB  → qwen3:1.7b  (~1.1 GB)
  8-16GB → qwen3:4b    (~2.5 GB)
 16-32GB → qwen3:8b    (~5.2 GB)
  32GB+  → qwen3:14b   (~9.3 GB)

Adds qwen3:0.6b, 1.7b, 4b, 14b, 30b to the model catalog. Falls back
to scanning all compatible models if the tiered pick doesn't support
the selected engine. Shows the user why the model was chosen and how
to download later if declined.
2026-04-03 22:19:27 -07:00
alexhegitandrobbym-dev e0dccd1b8d Update Dockerfile.gpu.rocm with ROCm 7.2 (#117)
* Update Dockerfile.gpu.rocm with ROCm 7.2

* fix: update ROCm 6.2 references in knowledge_base.py to 7.2

Matches the Dockerfile.gpu.rocm base image bump.

---------

Co-authored-by: robbym-dev <manihani@stanford.edu>
2026-04-03 22:01:00 -07:00
Robby Manihani 82ed761784 fix: pass github_token to Claude workflows to bypass OIDC (#186)
Closes #182 — the claude-code-action attempts OIDC token exchange when
no github_token is provided, which fails without the Anthropic GitHub
App installed. Passing secrets.GITHUB_TOKEN skips the OIDC flow.
2026-04-03 22:00:34 -07:00
Robby Manihani 4b47c5d435 fix: wire TraceStore to event bus and pass to AgentExecutor (#187)
Closes #179 — traces were never persisted in the serve path because:
1. TraceStore.subscribe_to_bus() was never called in app.py
2. AgentExecutor was constructed without trace_store in serve.py and
   agent_manager_routes.py
3. The /v1/traces API returned raw dataclass fields instead of the
   frontend-expected format

Fixes all three: wires bus subscription, passes trace_store to all
executor instantiation sites, and adds _serialise_trace() for proper
API response formatting.
2026-04-03 22:00:14 -07:00
Robby Manihani bcab7f926d fix: remove invalid trim() from take-assign workflow (#185)
Closes #181 — trim() is not a valid GitHub Actions expression function,
causing the job to fail on every trigger. Use the raw comment body
instead.
2026-04-03 21:59:54 -07:00
Robby Manihani cef64450bd fix: inject default system prompt to ground locally-hosted models (#184)
Closes #164 — Qwen and other models with baked-in corporate identities
would claim to be cloud services when running locally. Adds a
configurable default_system_prompt to AgentConfig that is injected as a
fallback when no other system prompt is provided. The default tells the
model it is running locally through OpenJarvis. Users can override or
clear it via config.toml.
2026-04-03 21:59:33 -07:00
Jon Saad-FalconandClaude Opus 4.6 c5c420c1c7 fix: use UTC timestamps in digest tests for CI compatibility
Tests were storing digests with local time but get_today() filters
by UTC date, causing failures when local date != UTC date.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:57:51 -07:00
Jon Saad-FalconandClaude Opus 4.6 fb3b87044c fix: format init_cmd.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:46:23 -07:00
Jon Saad-Falcon d570968dc1 Merge pull request #183 from open-jarvis/feat/morning-digest-v2
feat: Morning Digest v2 — prompt tuning, new connectors, starter configs
2026-04-03 17:39:54 -07:00
Jon Saad-FalconandClaude Opus 4.6 ff9326803a merge: resolve conflict in connect_cmd, keep safe credential unpacking
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:34:17 -07:00
Jon Saad-FalconandClaude Opus 4.6 365a58f57a fix: handle missing OAuth credentials gracefully in jarvis connect
get_client_credentials() returns None when no credentials are stored.
The tuple unpacking crashed with "cannot unpack non-iterable NoneType".
Now safely extracts values with a fallback, then prompts the user.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:29:16 -07:00
Jon Saad-FalconandClaude Opus 4.6 638d8780a0 docs: add user guides for Deep Research, Code Assistant, Monitor, Chat
Four new docs matching the morning-digest.md pattern:
- deep-research.md — multi-hop research with document indexing
- code-assistant.md — orchestrator with code execution + file I/O
- scheduled-monitor.md — persistent operative on cron schedule
- chat-simple.md — lightweight chat, simplest setup

Updated quickstart with tabs for all agent types and expanded
starter configs table from 3 to 7 presets. Updated MkDocs nav
and index page to link all 5 user guides.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 15:10:10 -07:00
Jon Saad-FalconandClaude Opus 4.6 e466b54881 feat: add 4 starter configs + jarvis init --preset command
New example configs:
- deep-research.toml — multi-hop research with citations
- code-assistant.toml — orchestrator with code execution + file I/O
- scheduled-monitor.toml — persistent operative on cron schedule
- chat-simple.toml — lightweight chat, no tools

CLI:
- `jarvis init --preset <name>` installs any starter config in one command
- Presets: morning-digest-mac, morning-digest-linux, morning-digest-minimal,
  deep-research, code-assistant, scheduled-monitor, chat-simple

All configs tested live on M2 Max with Ollama + Qwen3.5 9B.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:34:53 -07:00
Jon Saad-FalconandClaude Opus 4.6 6ee2665fc7 docs: add Starter Configs + Built-in Agents to README and quickstart
README now shows a table of example configs with copy commands, plus
a full list of built-in agents (morning_digest, deep_research,
monitor_operative, orchestrator, etc.) with descriptions.

Quickstart page adds a "Morning Digest" tab and Starter Configs
table with links to example config files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:24:12 -07:00
Jon Saad-FalconandClaude Opus 4.6 dd29e4945b feat: add --digest flag to jarvis init for one-step setup
`jarvis init --digest` appends the Morning Digest config section
to the generated config.toml, including sensible defaults for
sections, sources, voice, and schedule.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:20:00 -07:00
Jon Saad-FalconandClaude Opus 4.6 45150c48f4 docs: add Morning Digest to MkDocs nav and index page
Link morning-digest.md in the User Guide nav section. Also add
all existing user-guide pages that were missing from the nav.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:17:58 -07:00
Jon Saad-FalconandClaude Opus 4.6 f45f7b426c docs: add Morning Digest quickstart guide + example configs
- docs/user-guide/morning-digest.md — full setup guide with CLI
  commands, config reference, TTS voices, API endpoints, troubleshooting
- configs/openjarvis/examples/morning-digest-mac.toml — Apple Silicon
- configs/openjarvis/examples/morning-digest-linux.toml — Linux/GPU
- configs/openjarvis/examples/morning-digest-minimal.toml — just Gmail

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:16:09 -07:00
Jon Saad-Falcon a009646071 feat: Morning Digest with Voice Mode + unified OAuth + Apple connectors (#174) 2026-04-03 11:05:41 -07:00
Jon Saad-FalconandClaude Opus 4.6 26f6142107 fix: format merged evals files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:57:01 -07:00
Jon Saad-FalconandClaude Opus 4.6 28afef0778 merge: resolve conflict in toolcall15 scorer, keep main's JSON parser
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:56:51 -07:00
Jon Saad-FalconandClaude Opus 4.6 cbd6cab1d6 fix: remove :memory: artifact, add to .gitignore
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:54:26 -07:00
Jon Saad-FalconandClaude Opus 4.6 842086aa54 fix: resolve all ruff lint and format errors for CI
Auto-fixed 21 lint errors (unused imports, unsorted imports) and
reformatted 43 files to pass ruff check + ruff format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:53:57 -07:00
Jon Saad-FalconandClaude Opus 4.6 0977d3bed5 chore: remove Claude planning/spec files from tracking
These were force-added previously but are already in .gitignore.
Files remain on disk but are no longer tracked in git.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:51:35 -07:00
Jon Saad-FalconandClaude Opus 4.6 94a81a22fc fix: update Gmail test assertions for category:primary filter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:13:02 -07:00
Jon Saad-FalconandClaude Opus 4.6 232a99079f feat: add quality_score and evaluator_feedback fields to DigestStore
Extends DigestArtifact and SQLite schema with quality_score (float)
and evaluator_feedback (str) for future quality tracking. Includes
ALTER TABLE migration for existing databases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:51:40 -07:00
Jon Saad-FalconandClaude Opus 4.6 c970a399c3 feat: tuned digest prompt through 7 iterations for quality
- Priority-first structure with decreasing importance
- Email triage: real people only, skip automated/marketing
- Message triage: key people + replies needed, acknowledge casual
- Health interpreted as trends, not raw numbers
- Strict 200-word limit, honorific 2-3 times only
- Skip disconnected sources silently
- Tested 7 iterations, each improving on the last

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:03:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 dafd6e1f5f fix: acknowledge all connected sources in digest, raise word limit to 300
Prompt now instructs LLM to briefly mention every data source that
returned results (even if nothing urgent) so the user knows it checked.
Disconnected sources are silently skipped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:37:29 -07:00
Jon Saad-FalconandClaude Opus 4.6 37c224e4b7 feat: priority-first digest prompt, 4 new connectors, honorific config
Prompt rewrite:
- Priority-first briefing structure (deadlines > schedule > messages)
- Interpret health trends, don't list raw numbers
- Connect related items across sections
- Configurable honorific (sir/ma'am/boss) from config.toml
- 250 word limit, no markdown, spoken-aloud format

New connectors:
- Weather (OpenWeatherMap API) — current conditions + 12h forecast
- GitHub Notifications — PR reviews, mentions, assignments
- Hacker News — top 5 stories with scores
- News/RSS — configurable feeds (Arxiv, NYT, WSJ, etc.)

Other:
- Gmail filters to category:primary (no promotions)
- Email body previews in digest data
- iMessage text content included
- WORLD section replaces MUSIC as default
- voice_speed plumbed through full pipeline
- 22 new tests for connectors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:14:21 -07:00
Jon Saad-FalconandClaude Opus 4.6 4de2ffd720 fix: restructure digest data, fix calendar since filter, fix Gmail registration
Data collection:
- Restructure digest_collect output into priority-ordered sections
  (HEALTH > MESSAGES > CALENDAR > MUSIC) with human-readable formatting
- Extract key metrics from Oura (HR, HRV, sleep duration, scores)
- Format Gmail as sender + subject + time ago
- Group music tracks into single lines per source
- Cap 15 items per source, truncate content to 500 chars

Calendar:
- Pass `since` parameter to Google Calendar API as `timeMin`
- Default to 24h lookback instead of dumping all-time events

Gmail:
- Re-enable gmail connector registration in __init__.py

Prompt:
- Add section summary + closing encouragement to Jarvis persona
- Reinforce no-markdown, no-hallucination rules in user message
- Strip markdown artifacts before sending to TTS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:33:39 -07:00
867a96fd6e fix: add check_same_thread=False to all SQLite connections (#176)
Four SQLite connections were missing check_same_thread=False,
causing thread errors during eval runs with ThreadPoolExecutor.

Fixed: OptimizationStore, AuditLogger, TelemetryAggregator,
KnowledgeGraphMemory. All use WAL mode, making this safe.

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:02:18 -07:00
Jon Saad-FalconandClaude Opus 4.6 bff925a679 fix: wire DigestConfig through SDK and system for end-to-end digest
- Inject DigestConfig (persona, sections, TTS backend, voice_id) into
  MorningDigestAgent from both JarvisSystem._run_agent and Jarvis SDK
- Always inject digest_collect + text_to_speech tools for digest agent
- Fix TTS tool: import speech backends before checking TTSRegistry
- Fix Cartesia: default to British Butler voice when voice_id is empty
- Fix DigestStore: allow cross-thread SQLite access for FastAPI
- Propagate agent metadata (audio_path) through system return dict
- Add music section to default source map in MorningDigestAgent

Tested end-to-end: Qwen3.5 9B (Ollama) + Cartesia TTS + real data
from Oura, Gmail, Calendar, Tasks, Spotify, Apple Music.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:25:55 -07:00
Jon Saad-Falcon b1266df3a4 Merge pull request #175 from open-jarvis/feat/rich-trace-capture
feat(evals): rich trace capture + TerminalBench V2 native harness
2026-04-02 13:44:19 -07:00
Jon Saad-FalconandClaude Opus 4.6 9fd8a4e79d fix: remove unused imports (LiteLLM, time) to pass ruff lint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:36:07 +00:00
Jon Saad-FalconandClaude Opus 4.6 58432b5e0d feat: wire TerminalBench V2 native harness into eval CLI
- Add _run_terminalbench_native() to CLI for direct Harness invocation
- Use terminus-2 agent (serializable model_name + api_base kwargs)
- Lowercase Docker compose project names to satisfy validation
- Add terminalbench-native to valid backends in config
- Add eval config for Qwen3.5-122B-A10B-FP8

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:25:42 +00:00
Jon Saad-FalconandClaude Opus 4.6 58c0837c46 feat: wire TraceCollector into system._run_agent and JarvisAgentBackend
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:25:08 +00:00
897a816343 fix(evals): TerminalBench scoring + http_request panic + 20 new configs (#173)
Fixes:
- TerminalBench: remove task_id fallback from ground truth answers.
  Was using task name as reference answer, causing judge to always
  score wrong.
- http_request: catch BaseException (not Exception) for Rust panics.
  PyO3 PanicException on binary data now falls through to httpx.

New configs (5 models × 4 benchmarks = 20 files):
- Qwen-27B, Qwen-2B, Gemma4-26B-A4B, Gemma4-E4B, Nemotron-Nano
- ToolCall-15, PinchBench, LiveCodeBench, TauBench

Updated experiment plan with Gemma 4 models and progress matrix.

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:13:06 -07:00
Jon Saad-FalconandClaude Opus 4.6 fd354e229c feat: greeting trigger, audio player, and digest scheduler
- "Good morning" intent detection routes to MorningDigestAgent via
  pattern matching in JarvisSystem.ask() (zero latency, no API cost)
- Inline AudioPlayer component renders play/pause + progress bar in
  chat when digest audio is available
- `jarvis digest --schedule "0 6 * * *"` wires into TaskScheduler for
  daily auto-generation; persists to config.toml
- Server /api/digest/schedule GET+POST endpoints for frontend/desktop
- Chat completion responses include audio metadata when digest produces
  audio, frontend auto-detects via /api/digest endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 12:50:34 -07:00
Jon Saad-FalconandClaude Opus 4.6 a5a9171e83 feat: update DigestConfig with typed annotations and apple_health source
Add List[str] type annotations to DigestSectionConfig and DigestConfig
fields. Add apple_health to default health section sources.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 12:27:34 -07:00
Jon Saad-FalconandClaude Opus 4.6 d73014d6bb feat: Morning Digest pipeline + unified OAuth + Apple connectors
Full morning digest system:
- MorningDigestAgent, DigestStore, digest_collect tool, TTS backends
- Connectors: Oura, Strava, Spotify, Google Tasks, Apple Health, Apple Music
- CLI `jarvis digest` command + FastAPI /api/digest endpoints
- Cartesia, Kokoro, OpenAI TTS backends with persona prompts

Unified OAuth setup across all surfaces:
- Generic OAuthProvider registry for Google, Strava, Spotify
- CLI auto-opens browser + catches callback (no more paste-code)
- Server /oauth/start + /oauth/callback endpoints for desktop/browser
- Frontend OAuthPanel with popup + polling

Apple connectors:
- Apple Health reads from HealthKit DB or iPhone export XML
- Apple Music queries Music.app via AppleScript

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:05:35 -07:00
908e28d42e fix(evals): ToolCall-15 JSON parsing + traces thread safety (#172)
* fix(evals): ToolCall-15 JSON parsing + traces(True) → traces(telemetry)

Two fixes from sanity check:

1. ToolCall-15: Embed system prompt + tool descriptions + JSON format
   instructions into the problem text so jarvis-direct backend works.
   Fix scorer's JSON extraction to handle nested braces (balanced
   brace parser instead of regex). Models now correctly output tool
   calls and get scored. Claude/GPT/Gemini all score 40% (6/15).

2. JarvisAgentBackend: Change traces(True) back to traces(telemetry).
   The hardcoded True created SQLite trace connections that crash in
   ThreadPoolExecutor worker threads for GAIA and LiveResearchBench
   with jarvis-agent backend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove unused tool_names variable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 09:17:33 -07:00
Jon Saad-Falcon fded003f4f Delete MagicMock/load_config().security.audit_log_path directory 2026-04-02 09:14:37 -07:00
Jon Saad-Falcon 393acc9e1d Delete :memory: 2026-04-02 09:14:21 -07:00
Jon Saad-Falcon 172f6e52d9 Merge pull request #170 from mrTSB/fix-desktop
Fix Desktop App Cloud Models
2026-04-02 09:14:04 -07:00
Robby Manihani 4a8cc41034 Merge pull request #156 from eddierichter-amd/lemonade-backend
Adding Lemonade Backend
2026-04-02 01:53:10 -07:00
mrTSB 25b94a223d linting 2026-04-01 22:39:03 -07:00
mrTSB 2feaa10d97 fix local 2026-04-01 22:13:00 -07:00
Tanvir Bhathal 671c119057 Merge branch 'open-jarvis:main' into fix-desktop 2026-04-01 21:25:08 -07:00
fa4a9dd1db feat(evals): ToolCall-15, LiveCodeBench, LiveResearchBench + telemetry (#169)
* fix(evals): add tool_choice=auto + fix traces thread safety

Two fixes for eval accuracy and stability:

1. TauBench agent: add tool_choice="auto" to match tau2's native
   LLMAgent behavior. Without this, Qwen and GPT-5.4 score 10-14pp
   below leaderboard because the models don't receive explicit
   tool-calling guidance.

2. SystemBuilder: apply self._traces flag to config.traces.enabled.
   Previously builder.traces(False) was a no-op — traces stayed
   enabled, creating SQLite connections in the main thread that
   crashed when accessed from ThreadPoolExecutor worker threads
   in GAIA evals ("SQLite objects created in a thread can only be
   used in that same thread").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: enrich inference events with model response content and add Trace.messages

Add content, tool_calls, and finish_reason fields to INFERENCE_END events
published by InstrumentedEngine. For non-instrumented engines, BaseAgent._generate()
now publishes INFERENCE_START/END events with the same rich data. Add _message_to_dict
helper and messages field to Trace dataclass for full conversation capture.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: enhance TraceCollector with rich content, tool details, and messages

Capture model response content, tool_calls, and finish_reason in GENERATE
steps; store tool arguments and result text in TOOL_CALL steps; extract
conversation messages from AgentResult.metadata into Trace.messages; and
implement the last_trace property.  Also adds a messages column to the
TraceStore schema so messages survive the SQLite round-trip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: agents store conversation messages in AgentResult.metadata

Both NativeReActAgent and MonitorOperativeAgent now serialize their
internal messages list via _message_to_dict and include it in the
returned AgentResult.metadata under the "messages" key. This enables
TraceCollector to capture full conversation traces.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: wire TraceCollector into system._run_agent and JarvisAgentBackend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: persist rich trace data in eval trace JSONL files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add TerminalBench config for Qwen 3.5-122B

* fix: add SQLite migration for traces.messages column on existing databases

* fix: check trace_store instead of shared config for trace enablement

* feat(evals): add ToolCall-15, LiveCodeBench, LiveResearchBench + telemetry

Three new benchmark integrations and full telemetry wiring for the
NeurIPS 2026 IPW/IPJ experiments.

## New Benchmarks

### ToolCall-15
15-scenario tool calling accuracy benchmark across 5 categories.
All scenarios defined inline with deterministic scoring (0/1/2 per
scenario). Fast to run (~5min/model) — ideal for optimization loops.

### LiveCodeBench
Competitive programming from LeetCode/AtCoder/CodeForces via
HuggingFace dataset. Sandboxed code execution with per-test
timeouts. Single-turn generation via jarvis-direct backend.

### LiveResearchBench
100 expert-curated deep research tasks. LLM-as-judge scoring
across 4 dimensions (comprehensiveness, insight, instruction
following, readability). Uses web_search tool for live research.

## Telemetry Wiring

- FLOPs estimation: 2 * active_params * total_tokens (MoE-aware)
- Energy/power capture flows from InstrumentedEngine through
  backends to EvalResult and RunSummary
- New telemetry_summary section in output JSON with IPW/IPJ
- JarvisDirectBackend now propagates gpu_metrics flag
- TauBench forwards telemetry flags to SystemBuilder

## Experiment Plan

Added docs/experiments/neurips-2026-plan.md tracking the full
experiment matrix: 9 models x 7 benchmarks across NVIDIA, AMD,
and Apple hardware stacks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Prathap b948bfcf2e add test case 2026-04-01 21:24:36 -07:00
Prathap 27b399d099 fix(channels): push session history into agent context on channel messages 2026-04-01 21:24:36 -07:00
robbym-dev 51d37ffe52 docs: add prerequisites section with uv/Rust/Git install instructions
Closes #158 — the README and installation docs assumed uv was already
installed without explaining how to get it, creating a barrier for new
users (especially on macOS). Adds a prerequisites table to both the
README and docs/getting-started/installation.md with platform-specific
install commands, and links to the existing macOS step-by-step guide.
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 3ea727fc43 fix: match template instruction keys to server template IDs (underscores)
Server returns template IDs with underscores (code_reviewer, research_monitor)
but TEMPLATE_INSTRUCTIONS used hyphens. Added both formats + prompts for
personal_deep_research and inbox_triager templates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 608ed7531c feat: add Models, API Keys, and Web Search sections to Settings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 370be96565 feat: add document import via paste text or file upload
Adds a new Upload / Paste data source that lets users paste text or upload
documents (.txt, .md, .pdf, .docx, .csv) directly into the knowledge base.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 1100057b90 fix: improve Gmail connect instructions and add troubleshooting
Clarify that Gmail uses App Passwords (not OAuth login popup), emphasize
2-Step Verification prerequisite, and add a collapsible troubleshooting
section for common issues. Also improve the error message when Gmail
auth fails to specifically mention App Password requirements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 beb5c0fb6c feat: reorder agent tabs (Interact first), add strategy tooltips
Move the Interact tab to the first position in agent detail view and
default to it when opening an agent. Add "(optional)" hint to Advanced
Settings and add (?) tooltips to Memory Extraction, Observation
Compression, Retrieval Strategy, and Task Decomposition labels.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 aaab50ffcf feat: add pre-filled instruction prompts to agent templates
Add TEMPLATE_INSTRUCTIONS map with sensible defaults for daily-briefing,
research-monitor, code-reviewer, and meeting-prep templates so users get
a starting prompt when selecting a template. Show a warning hint when the
instruction contains [bracketed placeholders] that need to be replaced.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 5c1990b7ae feat: add daily/weekly/hourly schedule presets for agents
Replace the manual/interval/cron schedule picker with friendlier presets
(Daily, Weekly, Every N hours, Custom cron) that generate the correct
cron expressions or interval values behind the scenes. Update
formatSchedule() to render human-readable labels like "Daily at 9:00 AM"
and "Weekly on Mon, Wed, Fri at 9:00 AM".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 579ff50256 feat: add chat/edit/delete buttons and model status to agent cards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
Prathap d2cc994d75 fix lint issues 2026-04-01 21:24:36 -07:00
Prathap b58d7e6343 add unit tests 2026-04-01 21:24:36 -07:00
Prathap 6cd6ba7825 include agent from config and include channel tools before creating jarvis system 2026-04-01 21:24:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 dc2a2edbcd fix: data source connect flow UX, obsidian/gcalendar sync bugs, agent timeout
- Frontend: add progress stages (Connecting → Authenticating → Connected →
  Syncing) with spinner and progress bar to the data source connect flow.
  Previously sources would silently stay "Not connected" after setup.
  Show error message on failure instead of swallowing exceptions.

- Obsidian connector: use timezone-aware datetime (tz=timezone.utc) in
  fromtimestamp() to fix "can't compare offset-naive and offset-aware
  datetimes" crash during incremental sync.

- Google Calendar connector: catch HTTPStatusError when listing events
  for individual calendars (e.g. US Holidays returning 404) so one
  inaccessible calendar doesn't crash the entire sync.

- Agent SSE timeout: increase progress queue timeout from 120s to 600s
  so complex multi-hop deep research queries aren't killed mid-execution
  on slower local models.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
c4f85d435f fix(evals): add tool_choice=auto + fix traces thread safety (#163)
Two fixes for eval accuracy and stability:

1. TauBench agent: add tool_choice="auto" to match tau2's native
   LLMAgent behavior. Without this, Qwen and GPT-5.4 score 10-14pp
   below leaderboard because the models don't receive explicit
   tool-calling guidance.

2. SystemBuilder: apply self._traces flag to config.traces.enabled.
   Previously builder.traces(False) was a no-op — traces stayed
   enabled, creating SQLite connections in the main thread that
   crashed when accessed from ThreadPoolExecutor worker threads
   in GAIA evals ("SQLite objects created in a thread can only be
   used in that same thread").

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
450397a2ad feat(evals): TauBench V2 native integration + GAIA eval configs (#162)
* feat(evals): add TauBench V2 native integration + GAIA eval configs

Integrate TauBench V2 (τ²-bench) multi-turn customer service benchmark
natively through OpenJarvis's inference engine. The agent's LLM calls go
through OpenJarvis while tau2-bench handles the orchestration, user
simulation, domain tools, database, and evaluation.

## TauBench Integration

Architecture: JarvisHalfDuplexAgent bridges OpenJarvis's engine into
tau2's Orchestrator as a drop-in agent replacement. This enables
testing how well OpenJarvis's Intelligence + Engine handles multi-turn
customer service tasks with real tool calling and database mutations.

Key features:
- Native OpenJarvis engine for agent LLM calls
- tau2's UserSimulator for realistic customer interactions
- Domain tools (airline, retail, telecom) with mock databases
- Full evaluation: DB state checks, action matching, NL assertions
- Test-split filtering for leaderboard-comparable results
- Pass^k multi-trial support (default 3 trials per task)
- Qwen thinking-mode disabled for clean tool call parsing
- Gemini thought_signature handling for multi-turn conversations

Files:
- datasets/taubench.py: Dataset provider with test-split filtering
- execution/taubench_env.py: JarvisHalfDuplexAgent + simulation runner
- scorers/taubench.py: Scorer reading tau2 evaluation rewards
- CLI registration and KNOWN_BENCHMARKS update

## Results (test split, pass^3, 60 tasks)

| Model              | TauBench | Leaderboard |
|--------------------|----------|-------------|
| Claude Opus 4.6    | 86.67%   | 84.8%       |
| Nemotron-3-Super   | 86.67%   | —           |
| Qwen3.5-397B       | 81.67%   | 95.6%       |
| GPT-5.4            | 81.67%   | 91.5%       |
| Qwen3.5-122B       | 80.00%   | 93.6%       |
| Qwen3.5-35B        | 77.27%   | 89.2%       |
| Gemini 3.1 Pro     | 58.33%   | ~87%        |

## GAIA Eval Configs

Added configs for GPT-5.4, Gemini 3.1 Pro, Nemotron, Qwen 122B,
Qwen 35B, and existing GAIA rerun configs for multiple models.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: lint errors in taubench integration

Remove unused imports and sort import blocks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: E501 line too long in slack_connector.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove unrelated GAIA configs from PR

Keep only TauBench configs that were created and tested in this PR.
GAIA configs are pre-existing or belong in a separate PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:24:36 -07:00
mrTSB 59dcbb9cc4 make cloud models on desktop app work 2026-04-01 21:22:39 -07:00
mrTSB f8c37fe85e make cloud models on desktop app work 2026-04-01 21:22:25 -07:00
dfddd8c785 feat(evals): ToolCall-15, LiveCodeBench, LiveResearchBench + telemetry (#169)
* fix(evals): add tool_choice=auto + fix traces thread safety

Two fixes for eval accuracy and stability:

1. TauBench agent: add tool_choice="auto" to match tau2's native
   LLMAgent behavior. Without this, Qwen and GPT-5.4 score 10-14pp
   below leaderboard because the models don't receive explicit
   tool-calling guidance.

2. SystemBuilder: apply self._traces flag to config.traces.enabled.
   Previously builder.traces(False) was a no-op — traces stayed
   enabled, creating SQLite connections in the main thread that
   crashed when accessed from ThreadPoolExecutor worker threads
   in GAIA evals ("SQLite objects created in a thread can only be
   used in that same thread").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: enrich inference events with model response content and add Trace.messages

Add content, tool_calls, and finish_reason fields to INFERENCE_END events
published by InstrumentedEngine. For non-instrumented engines, BaseAgent._generate()
now publishes INFERENCE_START/END events with the same rich data. Add _message_to_dict
helper and messages field to Trace dataclass for full conversation capture.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: enhance TraceCollector with rich content, tool details, and messages

Capture model response content, tool_calls, and finish_reason in GENERATE
steps; store tool arguments and result text in TOOL_CALL steps; extract
conversation messages from AgentResult.metadata into Trace.messages; and
implement the last_trace property.  Also adds a messages column to the
TraceStore schema so messages survive the SQLite round-trip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: agents store conversation messages in AgentResult.metadata

Both NativeReActAgent and MonitorOperativeAgent now serialize their
internal messages list via _message_to_dict and include it in the
returned AgentResult.metadata under the "messages" key. This enables
TraceCollector to capture full conversation traces.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: wire TraceCollector into system._run_agent and JarvisAgentBackend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: persist rich trace data in eval trace JSONL files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add TerminalBench config for Qwen 3.5-122B

* fix: add SQLite migration for traces.messages column on existing databases

* fix: check trace_store instead of shared config for trace enablement

* feat(evals): add ToolCall-15, LiveCodeBench, LiveResearchBench + telemetry

Three new benchmark integrations and full telemetry wiring for the
NeurIPS 2026 IPW/IPJ experiments.

## New Benchmarks

### ToolCall-15
15-scenario tool calling accuracy benchmark across 5 categories.
All scenarios defined inline with deterministic scoring (0/1/2 per
scenario). Fast to run (~5min/model) — ideal for optimization loops.

### LiveCodeBench
Competitive programming from LeetCode/AtCoder/CodeForces via
HuggingFace dataset. Sandboxed code execution with per-test
timeouts. Single-turn generation via jarvis-direct backend.

### LiveResearchBench
100 expert-curated deep research tasks. LLM-as-judge scoring
across 4 dimensions (comprehensiveness, insight, instruction
following, readability). Uses web_search tool for live research.

## Telemetry Wiring

- FLOPs estimation: 2 * active_params * total_tokens (MoE-aware)
- Energy/power capture flows from InstrumentedEngine through
  backends to EvalResult and RunSummary
- New telemetry_summary section in output JSON with IPW/IPJ
- JarvisDirectBackend now propagates gpu_metrics flag
- TauBench forwards telemetry flags to SystemBuilder

## Experiment Plan

Added docs/experiments/neurips-2026-plan.md tracking the full
experiment matrix: 9 models x 7 benchmarks across NVIDIA, AMD,
and Apple hardware stacks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:06:20 -07:00
robbym-dev 87bbcb862c fix: update test_amd to expect lemonade for consumer AMD GPUs
The recommend_engine logic now routes consumer AMD GPUs to lemonade
instead of vllm. This test was missed in the original PR.
2026-04-02 00:12:09 +00:00
Robby Manihani 795874bf09 Merge pull request #160 from Prathap-P/fix/session_history
fix(channels): push session history into agent context on channel mes…
2026-04-01 16:57:38 -07:00
Robby Manihani 493429ca94 Merge pull request #168 from open-jarvis/docs/prerequisites-158
docs: add prerequisites with install instructions for uv, Rust, Git
2026-04-01 16:51:12 -07:00
robbym-dev 79b7ae9701 docs: add prerequisites section with uv/Rust/Git install instructions
Closes #158 — the README and installation docs assumed uv was already
installed without explaining how to get it, creating a barrier for new
users (especially on macOS). Adds a prerequisites table to both the
README and docs/getting-started/installation.md with platform-specific
install commands, and links to the existing macOS step-by-step guide.
2026-04-01 23:45:33 +00:00
Jon Saad-Falcon 7b3aa2f35d Merge pull request #167 from open-jarvis/feat/ux-feedback-fixes
feat: address user feedback — agent UX, scheduling, imports, settings
2026-04-01 15:42:43 -07:00
Jon Saad-FalconandClaude Opus 4.6 0eaa19e99d fix: match template instruction keys to server template IDs (underscores)
Server returns template IDs with underscores (code_reviewer, research_monitor)
but TEMPLATE_INSTRUCTIONS used hyphens. Added both formats + prompts for
personal_deep_research and inbox_triager templates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 15:17:02 -07:00
Robby Manihani 0bd84cb10a Merge pull request #154 from Prathap-P/fix/agent_config_tools
Fix/agent config tools
2026-04-01 14:23:22 -07:00
Jon Saad-FalconandClaude Opus 4.6 54cde9a8cb feat: add Models, API Keys, and Web Search sections to Settings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:19:50 -07:00
Jon Saad-FalconandClaude Opus 4.6 bc0153141b feat: add document import via paste text or file upload
Adds a new Upload / Paste data source that lets users paste text or upload
documents (.txt, .md, .pdf, .docx, .csv) directly into the knowledge base.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:19:50 -07:00
Jon Saad-FalconandClaude Opus 4.6 1e97ccbf55 fix: improve Gmail connect instructions and add troubleshooting
Clarify that Gmail uses App Passwords (not OAuth login popup), emphasize
2-Step Verification prerequisite, and add a collapsible troubleshooting
section for common issues. Also improve the error message when Gmail
auth fails to specifically mention App Password requirements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:19:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 398e1508f3 feat: reorder agent tabs (Interact first), add strategy tooltips
Move the Interact tab to the first position in agent detail view and
default to it when opening an agent. Add "(optional)" hint to Advanced
Settings and add (?) tooltips to Memory Extraction, Observation
Compression, Retrieval Strategy, and Task Decomposition labels.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:19:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 2a1d812550 feat: add pre-filled instruction prompts to agent templates
Add TEMPLATE_INSTRUCTIONS map with sensible defaults for daily-briefing,
research-monitor, code-reviewer, and meeting-prep templates so users get
a starting prompt when selecting a template. Show a warning hint when the
instruction contains [bracketed placeholders] that need to be replaced.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:19:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 dd0de876d5 feat: add daily/weekly/hourly schedule presets for agents
Replace the manual/interval/cron schedule picker with friendlier presets
(Daily, Weekly, Every N hours, Custom cron) that generate the correct
cron expressions or interval values behind the scenes. Update
formatSchedule() to render human-readable labels like "Daily at 9:00 AM"
and "Weekly on Mon, Wed, Fri at 9:00 AM".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:19:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 7eac6c421a feat: add chat/edit/delete buttons and model status to agent cards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:19:49 -07:00
Jon Saad-Falcon f233e8a55b Merge pull request #165 from open-jarvis/fix/data-source-connect-flow-and-sync-bugs
fix: data source connect flow UX, sync bugs, and agent timeout
2026-03-31 20:45:19 -07:00
Jon Saad-FalconandClaude Opus 4.6 7a7f225718 fix: data source connect flow UX, obsidian/gcalendar sync bugs, agent timeout
- Frontend: add progress stages (Connecting → Authenticating → Connected →
  Syncing) with spinner and progress bar to the data source connect flow.
  Previously sources would silently stay "Not connected" after setup.
  Show error message on failure instead of swallowing exceptions.

- Obsidian connector: use timezone-aware datetime (tz=timezone.utc) in
  fromtimestamp() to fix "can't compare offset-naive and offset-aware
  datetimes" crash during incremental sync.

- Google Calendar connector: catch HTTPStatusError when listing events
  for individual calendars (e.g. US Holidays returning 404) so one
  inaccessible calendar doesn't crash the entire sync.

- Agent SSE timeout: increase progress queue timeout from 120s to 600s
  so complex multi-hop deep research queries aren't killed mid-execution
  on slower local models.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 20:39:59 -07:00
39a8082af3 fix(evals): add tool_choice=auto + fix traces thread safety (#163)
Two fixes for eval accuracy and stability:

1. TauBench agent: add tool_choice="auto" to match tau2's native
   LLMAgent behavior. Without this, Qwen and GPT-5.4 score 10-14pp
   below leaderboard because the models don't receive explicit
   tool-calling guidance.

2. SystemBuilder: apply self._traces flag to config.traces.enabled.
   Previously builder.traces(False) was a no-op — traces stayed
   enabled, creating SQLite connections in the main thread that
   crashed when accessed from ThreadPoolExecutor worker threads
   in GAIA evals ("SQLite objects created in a thread can only be
   used in that same thread").

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 19:29:55 -07:00
43b3a59033 feat(evals): TauBench V2 native integration + GAIA eval configs (#162)
* feat(evals): add TauBench V2 native integration + GAIA eval configs

Integrate TauBench V2 (τ²-bench) multi-turn customer service benchmark
natively through OpenJarvis's inference engine. The agent's LLM calls go
through OpenJarvis while tau2-bench handles the orchestration, user
simulation, domain tools, database, and evaluation.

## TauBench Integration

Architecture: JarvisHalfDuplexAgent bridges OpenJarvis's engine into
tau2's Orchestrator as a drop-in agent replacement. This enables
testing how well OpenJarvis's Intelligence + Engine handles multi-turn
customer service tasks with real tool calling and database mutations.

Key features:
- Native OpenJarvis engine for agent LLM calls
- tau2's UserSimulator for realistic customer interactions
- Domain tools (airline, retail, telecom) with mock databases
- Full evaluation: DB state checks, action matching, NL assertions
- Test-split filtering for leaderboard-comparable results
- Pass^k multi-trial support (default 3 trials per task)
- Qwen thinking-mode disabled for clean tool call parsing
- Gemini thought_signature handling for multi-turn conversations

Files:
- datasets/taubench.py: Dataset provider with test-split filtering
- execution/taubench_env.py: JarvisHalfDuplexAgent + simulation runner
- scorers/taubench.py: Scorer reading tau2 evaluation rewards
- CLI registration and KNOWN_BENCHMARKS update

## Results (test split, pass^3, 60 tasks)

| Model              | TauBench | Leaderboard |
|--------------------|----------|-------------|
| Claude Opus 4.6    | 86.67%   | 84.8%       |
| Nemotron-3-Super   | 86.67%   | —           |
| Qwen3.5-397B       | 81.67%   | 95.6%       |
| GPT-5.4            | 81.67%   | 91.5%       |
| Qwen3.5-122B       | 80.00%   | 93.6%       |
| Qwen3.5-35B        | 77.27%   | 89.2%       |
| Gemini 3.1 Pro     | 58.33%   | ~87%        |

## GAIA Eval Configs

Added configs for GPT-5.4, Gemini 3.1 Pro, Nemotron, Qwen 122B,
Qwen 35B, and existing GAIA rerun configs for multiple models.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: lint errors in taubench integration

Remove unused imports and sort import blocks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: E501 line too long in slack_connector.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove unrelated GAIA configs from PR

Keep only TauBench configs that were created and tested in this PR.
GAIA configs are pre-existing or belong in a separate PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:59:20 -07:00
Prathap 670069ccdb fix linting issue 2026-03-31 19:27:26 +05:30
Prathap 35fa18fd1c add test case 2026-03-31 19:25:29 +05:30
Prathap 630d93ef92 fix(channels): push session history into agent context on channel messages 2026-03-31 19:16:35 +05:30
Eddie Richter 13ea4c53db Documentation update and lint 2026-03-30 21:48:39 -06:00
Eddie Richter d9983077f0 Fixing incorrect v0 api name to v1 2026-03-30 21:28:24 -06:00
Eddie Richter 78f5f9a5e3 Fixing system prompts for persistent agents 2026-03-30 21:23:12 -06:00
Eddie Richter 3235efbaca Changing default back to ollama 2026-03-30 21:22:39 -06:00
Eddie Richter 8f6ee7ae10 Fixing lemonade server when used in proxy 2026-03-30 20:50:55 -06:00
Eddie Richter f3d122e9c1 Addinig initial lemonade support 2026-03-30 20:50:53 -06:00
Prathap P cafe482af0 Merge branch 'open-jarvis:main' into fix/agent_config_tools 2026-03-30 22:02:47 +05:30
Jon Saad-FalconandClaude Opus 4.6 90ef8338d6 fix: use Z suffix for Granola API created_after date format
Granola API rejects +00:00 timezone offset and bare ISO timestamps.
Use strftime with explicit Z suffix which the API accepts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 14:35:17 -07:00
Jon Saad-FalconandClaude Opus 4.6 91205fda2b fix: remove duplicate Gmail connector, fix sync UI state tracking
- Remove gmail (REST API) from registered connectors, keep only gmail_imap
- Track background sync state at router level so status endpoint reports
  "syncing" while background thread is alive, "error" with message on failure
- Fixes sync UI bouncing back to "Sync Now" immediately after clicking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 14:27:31 -07:00
Prathap e3d77548c5 fix lint issues 2026-03-29 16:31:29 +05:30
Prathap ecc6d6f0bc add unit tests 2026-03-29 13:31:28 +05:30
Prathap 3524cd7504 include agent from config and include channel tools before creating jarvis system 2026-03-29 13:30:57 +05:30
Jon Saad-FalconandClaude Opus 4.6 a57813fcfa feat: overhaul install + setup UX for data sources, messaging, and desktop app
- Skip SetupWizard on launch, go straight to Chat page
- Add Data Sources page with sidebar nav (separate tabs for data sources + messaging channels)
- Add "Connect your data" banner + quick-action buttons on Chat empty state
- Add hint on deep research agent pointing to Data Sources + Messaging tabs
- Consistent naming: "Data Sources" and "Messaging Channels" everywhere
- Fix Apple Notes / iMessage setup (remove broken system prefs link)
- Fix Slack data source: auto-join public channels, rate limit retry, is_member filter
- Add channels:join scope to all Slack manifests + docs
- Fix Gmail: use gmail_imap connector (IMAP + app password), increase limit to 5000
- Fix sync endpoint: run in background thread, return immediately
- Show sync progress with progress bar, error messages, Sync Now / Re-sync / Retry buttons
- Add triggerSync() API + SyncStatusDisplay component
- Rewrite all connector setup instructions with precise click-by-click steps
- Notion: share all pages at once via top-level page sharing
- Obsidian: show how to find vault path via Obsidian UI or Finder
- SendBlue: add link to API Credentials page, add ngrok webhook step
- Slack messaging: add Copy button for JSON manifest
- Add OpenJarvis Slack icon asset
- Shorten deep research template description
- Fix desktop app port (8222 → 8000 to match server default)
- Disable auto-updater for local dev builds
- Register Slack, Outlook, GCalendar connectors in __init__.py
- Auto-create default agent when setting up messaging channels

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 22:44:59 -07:00
Jon Saad-Falcon 09d1cc3681 Merge pull request #152 from open-jarvis/feat/security-hardening
feat: security hardening — layered boundary enforcement
2026-03-28 22:15:22 -07:00
Jon Saad-Falcon b06e1ebfe9 Merge pull request #151 from open-jarvis/fix/frontend-node-engine-requirement
fix: add Node >= 20 engine requirement to frontend package.json
2026-03-28 22:15:08 -07:00
Jon Saad-FalconandClaude Opus 4.6 096b718417 chore: remove spec and plan docs from PR
These are internal planning artifacts, not part of the deliverable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 22:08:07 -07:00
Jon Saad-FalconandClaude Opus 4.6 0cbd0e1dac fix: update existing tests for new secure defaults
test_config.py: SecurityConfig.mode default changed from "warn" to "redact"
test_config_phase3.py: ServerConfig.host default changed from "0.0.0.0" to "127.0.0.1"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 22:02:02 -07:00
Jon Saad-FalconandClaude Opus 4.6 f122f5a699 fix: tests work without Rust extension and server extras
- BoundaryGuard degrades gracefully when Rust scanners unavailable
- BoundaryGuard tests use lightweight mock scanners (no Rust needed)
- Server-dependent tests use pytest.importorskip for starlette/fastapi
- Fix import ordering in test files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:46:53 -07:00
Jon Saad-FalconandClaude Opus 4.6 8e75fcb232 feat: non-loopback auth enforcement and CORS origin passthrough
Add check_bind_safety() to auth_middleware — refuses to bind non-loopback
without API key. Add cors_origins parameter to create_app() so CORS uses
config values instead of hardcoded wildcard "*".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:46:45 -07:00
Jon Saad-FalconandClaude Opus 4.6 52f6aaf5bb fix: lint fixes for security hardening — line length and import order
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:32:20 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 0193e07d83 feat: jarvis doctor checks for security profile configuration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:31:09 -07:00
Jon Saad-FalconandClaude Opus 4.6 57ecd5c77f feat: security profiles — personal, shared, server presets with user overrides
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:30:27 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 6aca5eb559 feat: add Content-Security-Policy header to API responses
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:27:38 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 79a46df747 feat: startup credential audit log and CORS wildcard warning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:27:30 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 60317ffcba feat: get_tool_credential — scoped credential access without env pollution
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:26:42 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 687ad30f9e feat: SanitizingFormatter — auto-redact credentials in all log output
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:24:57 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 85fbd2bd8c fix: enforce 0o600/0o700 permissions on all database and data files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:20:01 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 9909dde780 feat: secure_mkdir and secure_create helpers for restrictive file permissions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:57:25 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 472938cc2b fix: fail-closed webhook validation — reject when SDK missing or secret unconfigured
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:56:24 -07:00
Jon Saad-FalconandClaude Opus 4.6 d2dbd4c2a2 feat: wire BoundaryGuard into ToolExecutor for external tool scanning
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 20:28:33 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 677ece6fb3 feat: tag engines with is_cloud and tools with is_local for boundary scanning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:26:26 -07:00
Jon Saad-FalconandClaude Opus 4.6 7c1f983891 feat: BoundaryGuard module — scan/redact/block at device exit points
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 20:24:07 -07:00
Jon Saad-FalconandClaude Opus 4.6 63a32d20d6 feat: secure server defaults — loopback binding, redact mode, rate limiting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 20:00:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 44c52edfcd docs: security hardening implementation plan — 15 tasks with TDD
Detailed plan covering network exposure, boundary guard, webhook
validation, file permissions, log sanitization, credential scoping,
CORS hardening, and security profiles. Each task has failing tests
first, then implementation, then verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 19:26:59 -07:00
ANarayanandClaude Opus 4.6 bbcda62ed4 fix: add Node >= 20 engine requirement to frontend package.json
Tailwind CSS v4.2 (@tailwindcss/oxide) requires Node >= 20 for native
bindings. Without this, `npm install` succeeds but `vite` fails at
runtime with a missing native binding error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 18:51:37 -07:00
Jon Saad-FalconandClaude Opus 4.6 e842976d96 docs: security hardening design spec — layered boundary enforcement
Approved design for comprehensive security hardening covering network
exposure defaults, boundary guard scanning, webhook fail-closed validation,
file permissions, credential handling, log sanitization, and security profiles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 18:29:51 -07:00
Jon Saad-Falcon 9ab2d37c82 fix: shorten Personal Deep Research template description to match others 2026-03-28 17:41:05 -07:00
Jon Saad-FalconandClaude Opus 4.6 0e9d7005cb docs: update roadmap + create cross-platform testing plan
Roadmap: updated messaging status (iMessage/SMS + Slack working,
WhatsApp Baileys blocked, Meta Cloud API planned).

Testing plan: 12 query types across 3 platforms (Interact, Slack,
iMessage/SMS) with execution phases and success criteria.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 17:34:30 -07:00
Jon Saad-Falcon 4ab6139d52 Merge branch 'main' of https://github.com/open-jarvis/OpenJarvis 2026-03-28 17:22:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 290746b3e0 docs: restructure channels guide — Messaging Channels + Data Connectors
Added SendBlue (iMessage/SMS) and Slack messaging setup with detailed
step-by-step instructions, App Manifest JSON, webhook registration,
and troubleshooting tables. Reorganized doc into two sections:
Messaging Channels (talk to agent) and Data Connectors (search data).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 17:21:43 -07:00
Jon Saad-Falcon fa87bbb2d6 fix: remove whatsapp bridge node_modules from git, add to gitignore 2026-03-28 17:18:13 -07:00
Jon Saad-FalconandClaude Opus 4.6 bc31cce732 feat: simplify Messaging tab to iMessage/SMS (SendBlue) + Slack only
Removed WhatsApp (Baileys blocked by WhatsApp servers) and Twilio SMS
(SendBlue handles SMS as fallback automatically). Updated Slack setup
instructions with the App Manifest JSON for one-step configuration.

Three channels: iMessage + SMS (SendBlue), Slack (Socket Mode)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 17:17:51 -07:00
Jon Saad-Falcon 7a03bf5794 Merge pull request #150 from open-jarvis/fix/windows-git-detection
fix: Windows git detection in desktop app
2026-03-28 16:56:33 -07:00
krypticmouseandClaude Opus 4.6 76aab9c269 fix: guard fastapi import in test_sendblue_webhook.py with importorskip
CI installs `--extra dev` but not `--extra server`, so fastapi is
unavailable. All other server tests use pytest.importorskip("fastapi")
to skip gracefully — this file was missing the guard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 23:49:12 +00:00
krypticmouseandClaude Opus 4.6 eeb66a1606 fix: remaining unused import lint errors in slack_daemon and agent_manager_routes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 23:39:51 +00:00
krypticmouseandClaude Opus 4.6 caf0e59f16 fix: remove unused imports flagged by ruff across 5 files
Pre-existing lint errors that caused CI failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 23:39:35 +00:00
krypticmouseandClaude Opus 4.6 e3039c0d0a fix: Windows git detection in desktop app resolve_bin()
The Tauri desktop app's resolve_bin("git") function checked incorrect
paths for Git on Windows (e.g. {ProgramFiles}\git\git.exe) and had no
PATH fallback, causing "git not found" even when Git is installed.

Fixes:
- Add correct Git for Windows paths: {ProgramFiles}\Git\cmd\git.exe,
  {ProgramFiles(x86)}\Git\cmd\git.exe, {LOCALAPPDATA}\Programs\Git\cmd\
- Add Scoop package manager path: {home}\scoop\shims\git.exe
- Add PATH fallback using `where.exe` on Windows and `which` on Unix,
  so any binary on PATH is found even if not at a hardcoded location

Closes #128

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 23:39:35 +00:00
Jon Saad-FalconandClaude Opus 4.6 eea5f65ea0 feat: wire Twilio SMS handler with agent + ack + response sending
The Twilio webhook now:
1. Sends "Message received! Working on it now..." via Twilio API
2. Routes to DeepResearchAgent (via bridge or direct fallback)
3. Sends the research response back as SMS
4. Handles missing bridge gracefully
5. Formats response for SMS (strips markdown, 1500 char limit)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 16:27:32 -07:00
Jon Saad-FalconandClaude Opus 4.6 6233ae1aa1 feat: Slack daemon as subprocess for reliable Socket Mode
Replaces the flaky inline bolt_app creation with a proper subprocess
daemon (slack_daemon.py). The daemon persists independently of the
HTTP request lifecycle, solving the Socket Mode connection dropping.

Features: thread replies, progress updates, message queue, Slack
formatting, PID file for lifecycle management.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 15:57:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 9a6b1c1623 feat: reply in thread for Slack messages to reduce DM clutter
All bot replies (acknowledgment, progress updates, final response)
now use thread_ts to reply in the thread of the user's message.
Keeps the DM conversation clean — one thread per question.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 15:12:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 590e8b90a3 feat: convert markdown to Slack formatting instead of stripping it
Converts: ## Header → *Header* (bold), **bold** → *bold*,
*italic* → _italic_, ~~strike~~ → ~strike~, [text](url) → <url|text>.
Removes LaTeX and HTML. System prompt updated to use simple markdown
since it gets auto-converted per platform.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 15:06:57 -07:00
Jon Saad-FalconandClaude Opus 4.6 d3c7c59c8d fix: improve SendBlue UX — queue tracking, periodic reminders, SMS formatting
1. Ack message: "Message received! Working on it now..."
2. Queue awareness: if another message arrives while processing,
   "Message received! Message X in queue, will respond ASAP"
3. Periodic reminders every 60s: "Still working! Will reply ASAP"
4. Response formatting: _format_for_sms() strips markdown headers,
   bold/italic, code blocks, LaTeX, image syntax, collapses blanks
5. System prompt: instructs agent to use plain text, short paragraphs,
   simple dashes — no markdown/LaTeX that breaks in iMessage/SMS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 15:06:01 -07:00
Jon Saad-FalconandClaude Opus 4.6 2ef6615184 feat: improve Slack messaging UX — progress updates, queue, clean formatting
1. Initial: "Message received! Working on it now..."
2. Every 60s while processing: "Still working! Will reply ASAP"
3. New message while busy: "Message received! Message X in queue of Y"
4. Responses stripped of markdown (headers, bold, code blocks) for
   clean Slack display
5. System prompt updated to prefer plain text over complex markdown

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:59:03 -07:00
Jon Saad-FalconandClaude Opus 4.6 1252531551 test: add 26 tests for SendBlue channel and webhook
tests/channels/test_sendblue.py (18 tests):
- Init from params and env vars, no-credentials error state
- Connect/disconnect lifecycle
- Send: success, from_number inclusion, API error, network error,
  no-credentials, event emission
- Webhook handler: incoming triggers handlers, outbound ignored,
  empty content ignored, event emission, handler crash safety
- Properties: from_number, list_channels

tests/server/test_sendblue_webhook.py (8 tests):
- Webhook: incoming 200, outbound ignored, empty ignored, missing
  from_number ignored, secret validation (reject + accept), no bridge
- Health: ready=true when wired, ready=false when not

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:42:43 -07:00
Jon Saad-FalconandClaude Opus 4.6 1add224599 test: add 59 tests for system prompt, Slack messaging, and connector health
- test_deep_research_prompt.py (8 tests): date injection, time, day of week,
  dynamic generation, response types, tool descriptions, /no_think, Jarvis name
- test_slack_messaging.py (7 tests): connect with/without tokens, error state,
  disconnect, handler registration, send without token
- test_connector_health.py (44 tests): all 11 connectors instantiate, have
  required methods, report not-connected without creds, have metadata.
  Plus KnowledgeStore data presence checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:35:58 -07:00
Jon Saad-FalconandClaude Opus 4.6 7fd685f53a feat: inject current date/time into system prompt dynamically
The model now knows today's date and time. _build_system_prompt()
generates a fresh prompt with "Today is Saturday, March 28, 2026.
The current time is 02:28 PM." on every agent.run() call.

Fixes: model was hallucinating "January 29, 2025" for date queries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:29:54 -07:00
Jon Saad-FalconandClaude Opus 4.6 77a1c596b7 feat: expand system prompt with 12 adaptive query types
Jarvis now handles: casual, quick lookup, people lookup, daily/weekly
digest, meeting prep/debrief, task/follow-up finder, contact analysis
(neglected contacts, frequency), document finder, email triage,
cross-source synthesis, deep research, and about-self queries.

Tested: "neglected contacts" → 1 SQL call, 27s, real results.
"casual greeting" → 0 tools, 6s. Model adapts automatically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 10:07:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 994671fcdf feat: adaptive system prompt — Jarvis responds naturally to different query types
Replaces the rigid deep-research-only prompt with an adaptive one that
classifies queries into 4 types:
1. Casual/conversational — friendly reply, no tools
2. Quick data lookup — one tool call, short answer
3. Deep research — multi-hop, cited report
4. About self — describe capabilities

Tested: casual gets 0 tool calls (6s), quick lookup gets 1 SQL call (17s),
self-description gets 0 calls (17s). Deep research still works as before.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:37:20 -07:00
Jon Saad-FalconandClaude Opus 4.6 9825daa334 feat: complete SendBlue integration — auto-restore, CLI, health check, docs
- Register sendblue in channels/__init__.py so ChannelRegistry works
- Auto-restore SendBlue bindings on server startup from database,
  re-creating ChannelBridge + DeepResearchAgent so webhooks survive
  server restarts
- Add sendblue to CLI channel_cmd.py (_get_channel, help text) and
  SystemBuilder._resolve_channel() for config.toml support
- Health check endpoint GET /v1/channels/sendblue/health returns
  channel_connected, bridge_wired, ready status
- Frontend: SendBlueWizard checks health on mount, shows
  "Disconnected" badge with "Reconnect" button when bridge is dead
- Docs: CLI usage, server restart behavior, ngrok re-registration,
  troubleshooting table, health check endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:33:12 -07:00
Jon Saad-FalconandClaude Opus 4.6 bfcc90866c fix: make Slack App Token required, fix CLI prompt label
App Token (xapp-) is required for Socket Mode DMs, not optional.
Removed xoxe- from CLI prompt since session tokens don't work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:21:04 -07:00
Jon Saad-FalconandClaude Opus 4.6 87ad86bcb1 docs: add SendBlue iMessage/SMS channel setup guide
- Full setup walkthrough: SendBlue account, API keys, ngrok tunnel,
  webhook registration
- Documents the browser UI wizard flow (Messaging tab)
- Free tier notes: shared line, verified contacts, 10 contact limit
- Programmatic setup via Python
- Webhook behavior: instant ack, 45s delay notice, full response
- Constructor parameters and TOML config reference
- Added SendBlue to the supported channels table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:17:09 -07:00
Jon Saad-FalconandClaude Opus 4.6 6e33d5bbfa feat: instant iMessage ack + delay notice for SendBlue channel
When a text arrives via SendBlue:
1. Immediately sends "Message received! Researching your data now..."
2. If the agent takes >45s, sends "Still working — complex query..."
3. Then sends the full research response

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:15:06 -07:00
Jon Saad-FalconandClaude Opus 4.6 bf28877fec docs: comprehensive Slack setup guide with Socket Mode, App Manifest, and troubleshooting
Covers both data connector (read messages) and messaging channel (DM the agent).
Includes the full App Manifest JSON, required scopes table, and all the gotchas
we discovered (Request URL, reinstall requirement, App Token vs Bot Token, etc.).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:12:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 b808f47410 fix: create ChannelBridge + DeepResearchAgent when binding SendBlue
- When binding a SendBlue channel and no ChannelBridge exists, create
  one with a DeepResearchAgent wired to the knowledge store
- Fix _build_deep_research_tools scoping issue via self-import
- Fix model name resolution to fall back to app.state.model

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:12:06 -07:00
Jon Saad-FalconandClaude Opus 4.6 d123964ca7 feat: use slack-bolt for Slack Socket Mode — receives DMs and responds with DeepResearch
Replaced raw slack-sdk SocketModeClient with slack-bolt App which
properly handles event subscriptions, self-event filtering, and
message routing. Bot says "Message received! Researching now..."
then runs DeepResearchAgent and replies with the research result.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:50:08 -07:00
Jon Saad-FalconandClaude Opus 4.6 6e57379018 fix: handle SendBlue free tier (shared line) in setup wizard
When /api/lines returns no numbers (free/shared line tier), the wizard
now shows a manual input with instructions to copy the phone number
from the "Send from" field in the SendBlue dashboard, instead of
showing an error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:40:57 -07:00
Jon Saad-FalconandClaude Opus 4.6 0508056903 fix: correct SendBlue signup URL to dashboard.sendblue.com/company-signup
The old sendblue.co/getblue URL causes ERR_TOO_MANY_REDIRECTS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:22:13 -07:00
Jon Saad-FalconandClaude Opus 4.6 1adab5d849 feat: SendBlue guided wizard with auto-verify, auto-register, test msg
Backend:
- POST /v1/channels/sendblue/verify — validates API keys, auto-fetches
  assigned phone numbers from SendBlue GET /api/lines
- POST /v1/channels/sendblue/register-webhook — auto-registers the
  /webhooks/sendblue callback URL with SendBlue
- POST /v1/channels/sendblue/test — sends a test iMessage to verify setup

Frontend:
- SendBlueWizard component with multi-step guided flow:
  Step 1: "Open SendBlue signup" button (opens in new tab)
  Step 2: Paste API Key + Secret → "Verify & Find Number" auto-fetches
  Step 3: Shows discovered phone number → "Activate Phone Number"
  Step 4: Success state with "Send Test" button
- Active state shows agent's number + test message sender
- Generic channels (Slack, WhatsApp, SMS, local iMessage) listed below

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:18:02 -07:00
Jon Saad-FalconandClaude Opus 4.6 7fd247be7e feat: wire Slack Socket Mode listener to channel bind/unbind
When user connects Slack in the Messaging tab, the backend now creates
a SlackChannel with Socket Mode, registers a handler that routes incoming
DMs to DeepResearchAgent, and starts listening. Unbinding disconnects
the Socket Mode client.

Requires slack-sdk installed and both bot_token (xoxb-) + app_token (xapp-).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:44:55 -07:00
Jon Saad-FalconandClaude Opus 4.6 e5477e89fe fix: preserve telemetry metadata when polling refreshes messages
The 2-second polling interval was replacing local messages (with _usage,
_telemetry) with server messages (without those fields). Now stores
metadata in a ref and merges it back when polling refreshes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:06:14 -07:00
Jon Saad-FalconandClaude Opus 4.6 e9fe9be035 feat: add full telemetry footer to agent Interact responses
Shows engine, model, tokens, speed, latency, tool calls — matching
the Chat tab's XRayFooter UX. Click to expand detailed view.
Backend streams usage + telemetry in final SSE chunk.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:54:22 -07:00
Jon Saad-Falcon df129790f9 Merge branch 'feat/deep-research-setup' 2026-03-27 19:46:55 -07:00
Jon Saad-FalconandClaude Opus 4.6 f69c400896 fix: resolve stuck thinking bubble and add response metadata footer in InteractTab
The progress indicator now only shows when waitingForResponse or sending
is true, preventing it from persisting after the response arrives due to
stale isAgentWorking/hasPending checks. Agent response bubbles now display
elapsed time, tool call count, and a copy button in an XRay-style footer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:35:16 -07:00
Jon Saad-FalconandClaude Opus 4.6 a3f355034f fix: learning log entries now write correctly, clean up debug logging
Learning log entries (query_start, tool_call, tool_result, query_complete)
now persist to SQLite and appear in the Logs tab. Fixed result.output->content
attribute name and removed debug print statements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:27:42 -07:00
Jon Saad-Falcon 7a6be1d45e debug: add logging for learning log writes 2026-03-27 19:22:16 -07:00
Jon Saad-Falcon 4c8b4e17e6 fix: correct store_agent_response args, fix result.output->content, add log warnings 2026-03-27 19:20:50 -07:00
Jon Saad-FalconandClaude Opus 4.6 d2df7d083e feat: real-time tool progress + live logs for DeepResearch agent
- InteractTab shows streaming content word-by-word with tool progress
- LogsTab polls every 5s, shows learning log entries with color-coded badges
- Backend emits tool_progress SSE events during agent execution
- Learning log entries written for query_start, tool_call, tool_result, query_complete
- "Run Now" replaced with "Chat ready" indicator on Interact tab

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:31:23 -07:00
Jon Saad-FalconandClaude Opus 4.6 c079197c4e fix: improve Interact tab UX and add real-time activity logging
- Replace "Run Now" button with "Chat ready" hint when on the Interact
  tab so users know they can just type a message without pressing Run.
- Add learning log entries in generate_deep_research() for query start,
  tool calls, tool results, query completion, and errors so interactive
  queries are visible in the Logs tab.
- Rewrite LogsTab to poll every 5 seconds, merge execution traces with
  learning log entries into a unified timeline sorted by timestamp.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:26:57 -07:00
Jon Saad-FalconandClaude Opus 4.6 de91fe6ecf feat: add real-time streaming and tool progress to InteractTab
Refactor sendAgentMessage to accept onProgress/onContentDelta/onDone
callbacks so the InteractTab can show content as it arrives word by
word, display tool-call progress labels (e.g. "Querying data with
SQL"), and render an elapsed-time footer. The backend now streams
tool_progress SSE events from DeepResearchAgent before the final
content, matching the Chat tab's polished streaming UX.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:17:10 -07:00
Jon Saad-FalconandClaude Opus 4.6 bd22eeae1e fix: show 'Researching your data...' indicator while agent processes query
Added waitingForResponse state that stays true during the entire stream
consumption. Shows a pulsing indicator with descriptive text so the user
knows the agent is working (searching, analyzing, writing report).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:06:22 -07:00
Jon Saad-FalconandClaude Opus 4.6 7f7cf5ccd9 fix: show agent response in chat bubble + auto-ingest after connector connect
1. InteractTab now displays the streamed agent response as a bubble
   immediately after it finishes (was being discarded)
2. Connecting a source auto-triggers background ingest into KnowledgeStore
   so Google Drive data appears with chunk counts immediately

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:00:14 -07:00
Jon Saad-FalconandClaude Opus 4.6 9f204e8368 fix: poll connector status after OAuth connect, auto-refresh every 10s
The Google OAuth flow runs in a background thread. The frontend now polls
every 3s after connecting to detect when the token arrives, and auto-refreshes
every 10s to catch background completions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:51:54 -07:00
Jon Saad-FalconandClaude Opus 4.6 28d4cc7454 fix: run DeepResearchAgent with tools for managed agent streaming
The streaming endpoint was calling engine.stream_full() directly without
tools, so the agent responded as a generic chatbot. Now detects
deep_research agent type and runs the full DeepResearchAgent.run() loop
with knowledge_search, knowledge_sql, scan_chunks, and think tools,
then streams the result as SSE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:45:32 -07:00
Jon Saad-Falcon 490870a25c fix: show user message bubble immediately before waiting for agent response 2026-03-27 17:42:37 -07:00
Jon Saad-Falcon 8549324d28 fix: check port availability before starting OAuth callback server 2026-03-27 17:39:59 -07:00
Jon Saad-FalconandClaude Opus 4.6 53a20fde30 fix: run Google OAuth flow in background thread to prevent server freeze
The OAuth callback server blocks with handle_request(). Running it in a
daemon thread prevents it from freezing the FastAPI event loop.
Also accept system_prompt kwarg in DeepResearchAgent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:24:46 -07:00
Jon Saad-FalconandClaude Opus 4.6 b38d5565b0 fix: send stream:true for agent messages so backend actually runs the LLM
The Interact tab was sending messages without stream:true, so the backend
just stored them as pending without running the agent. Now consumes the
SSE stream and displays the full response.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:12:38 -07:00
Jon Saad-FalconandClaude Opus 4.6 bec38ebc1d docs: add Channels & Connectors troubleshooting guide
Covers setup instructions and troubleshooting for all 12 connectors:
Gmail, Google Drive, Calendar, Contacts, Slack, Notion, Granola,
Apple Notes, iMessage, Outlook, Obsidian, Dropbox.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:01:17 -07:00
Jon Saad-Falcon fcc146b050 fix: split Google OAuth into separate Client ID and Client Secret fields 2026-03-27 16:51:53 -07:00
Jon Saad-FalconandClaude Opus 4.6 62b98d903b feat: add proper Google OAuth flow with localhost callback server
Add exchange_google_token() and run_oauth_flow() to oauth.py for the
full authorization code exchange. Update gdrive, gcalendar, and
gcontacts connectors to trigger the browser-based OAuth flow when a
client_id:client_secret pair is provided, prefer access_token over raw
token, and require an actual access_token for is_connected(). auth_url()
now returns the Cloud Console credentials page when no client_id is
stored.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:45:03 -07:00
Jon Saad-FalconandClaude Opus 4.6 8d9eed6533 feat: add Reconnect button to connected sources, show warning for 0-data sources
Connected sources now show a "Reconnect" button that expands the inline setup
flow to re-enter credentials. Sources with 0 chunks show "Connected — no data
synced yet" in amber instead of "0 items" in green.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:30:01 -07:00
Jon Saad-FalconandClaude Opus 4.6 6c9ede1756 fix: merge Gmail IMAP into single "Gmail" channel entry
Remove duplicate gmail OAuth entry, rename gmail_imap to gmail in frontend
so it matches the backend connector ID that has the actual data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:21:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 175a13492f fix: improve Channels tab UX — larger fonts, per-source labels, detailed setup instructions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:00:05 -07:00
Jon Saad-Falcon 31eb2e4317 fix: use /v1/connectors prefix so frontend API calls work 2026-03-27 15:50:24 -07:00
Jon Saad-FalconandClaude Opus 4.6 ae73eca91a feat: replace Channels tab with data sources + add Messaging tab
Rewrite ChannelsTab to show connector data sources (Gmail, Slack, Notion,
etc.) with chunk counts, connected/not-connected states, and inline setup
with step-by-step instructions. Add new MessagingTab for phone/platform
messaging (iMessage, Slack, WhatsApp, SMS). Update DETAIL_TABS to include
both tabs with Database and Wifi icons respectively.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 15:40:50 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 7cdbf53143 feat: include chunk counts in connector list API response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:38:04 -07:00
Jon Saad-FalconandClaude Opus 4.6 6cc33cb294 docs: add implementation plan for Channels + Messaging tabs v2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 15:36:14 -07:00
Jon Saad-FalconandClaude Opus 4.6 e6317c88d1 docs: add design spec for Channels + Messaging tabs (replacing old Channels tab)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 15:32:51 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 af53d51e58 feat: add StepByStepPanel with per-connector setup instructions
Adds StepByStepPanel component to SourceConnectFlow that renders numbered
setup steps with optional links and input fields for each connector. Wires
it as the primary panel for any connector with steps defined, with existing
OAuth/Local/Filesystem panels as fallbacks. Also updates connectors.ts to
use ConnectorMeta (with SetupStep/inputFields) as the canonical type and
exports SourceCard as a backward-compatible alias.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:07:03 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 639c641618 feat: add per-connector setup instructions and input fields to SOURCE_CATALOG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:05:55 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 5aa5cd8c9e feat: add Channels tab to agent detail view with connect/disconnect UI
Adds iMessage, Slack, WhatsApp, and SMS (Twilio) channel binding UI to
the agent detail view, with expandable connect forms and active/disconnect states.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:04:12 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 1e6b7f181a feat: start/stop iMessage daemon on channel bind/unbind
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:03:14 -07:00
Jon Saad-FalconandClaude Opus 4.6 1e37235d0d docs: add implementation plan for Channels tab + connector setup UX
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 15:01:05 -07:00
Jon Saad-FalconandClaude Opus 4.6 f6e20632df docs: add design spec for Channels tab + seamless connector setup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:54:44 -07:00
Jon Saad-FalconandClaude Opus 4.6 c36695a399 fix: parallel section titles — Agent Statistics, Local Utilization, Dollars Saved vs.
Remove "Cloud Savings for Agent" card title. Add "Agent Statistics"
as section title over the usage metrics, aligned with "Local
Utilization" and "Dollars Saved vs." — all three sections now have
matching uppercase labels at the same vertical position.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:46:37 -07:00
Jon Saad-FalconandClaude Opus 4.6 4cbe29c027 fix: section titles "Local Utilization" and "Dollars Saved vs."
Three grouped sections inside the savings card, separated by
vertical dividers: usage metrics | Local Utilization (compute,
energy) | Dollars Saved vs. (GPT-5.3, Claude, Gemini). Section
titles are small uppercase labels above each group.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:39:58 -07:00
Jon Saad-FalconandClaude Opus 4.6 c013bc82f0 fix: add "SAVED vs." label before cloud provider cost comparisons
Vertical "SAVED vs." label between the divider and provider costs
makes it clear the dollar amounts represent money avoided by
running locally instead of using cloud APIs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:32:12 -07:00
Jon Saad-FalconandClaude Opus 4.6 22fa74bc36 fix: unified savings card with dividers, large values, clean layout
Single card with "Cloud Savings for Agent" header. All metrics
(queries, tokens, compute, energy, dollar savings) in one flex row
with vertical dividers separating usage | compute+energy | costs.
Large bold values above small labels. Consistent typography.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:24:43 -07:00
Jon Saad-FalconandClaude Opus 4.6 54d5a4a4c0 fix: single row for stats + savings, value above label, consistent style
All bubbles (Total Queries, Input Tokens, Output Tokens, Compute,
Energy, and per-provider dollar savings) now sit in one flex row
under "Cloud Savings for Agent". Each bubble has the value (bold)
above the label, with consistent styling. Green for savings values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:20:05 -07:00
Jon Saad-Falcon 15e98498f6 feat: Personal Deep Research — connectors, retrieval, agent, channels, desktop (#78)
feat: Personal Deep Research — connectors, retrieval, agent, channels, desktop
2026-03-27 14:13:26 -07:00
Jon Saad-FalconandClaude Opus 4.6 ce1d41bec5 fix: compact left-aligned stats and savings in flex rows
Usage stats (Total Queries, Input Tokens, Output Tokens) and Cloud
Savings (Compute, Energy, Dollar Savings) now use flex rows with
compact padding, left-aligned, wrapping naturally without filling
the full width.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:12:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 421560e75b fix: reorder overview — config then stats then compact savings
Overview tab order: Instruction → Configuration → Usage Stats
(queries/tokens in centered boxes) → Cloud Savings (compact flex
row, left-aligned, smaller padding).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:06:46 -07:00
Jon Saad-FalconandClaude Opus 4.6 258feff64e fix: skip torch-dependent tests when torch not installed in CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:01:30 -07:00
Jon Saad-FalconandClaude Opus 4.6 30bb67b125 fix: green savings in 3 boxes, inject current date into agent ticks
1. Cloud Savings for Agent: Compute, Energy, Dollar Savings in
   separate boxes in a row, all values green
2. Agents now receive current date in their input text so they
   know what day it is for time-sensitive research queries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:01:25 -07:00
Jon Saad-FalconandClaude Opus 4.6 c9534967fc fix: interval hrs/min picker, per-agent savings with FLOPs/energy
1. Interval schedule now uses hours + minutes picker (max 24h)
   instead of raw seconds input
2. Cloud Savings section is now per-agent (computed from agent's
   own token counts), not session-wide
3. Shows FLOPs, Energy (kJ), and per-provider savings comparison
   (GPT-5.3, Claude Opus 4.6, Gemini 3.1 Pro)
4. Removed kWh per-provider (inaccurate), replaced with compact
   FLOPs + Energy row above provider savings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:54:12 -07:00
Jon Saad-Falcon 0bc43eec39 fix: add system_prompt to template for CI test compatibility 2026-03-27 13:48:31 -07:00
Jon Saad-FalconandClaude Opus 4.6 4534abbea5 feat: model Change button, agent telemetry, savings display, Total Queries
1. Intelligence row has explicit "Change" button with loading state
2. Agent ticks now wrapped with InstrumentedEngine so FLOPs, energy,
   and cost savings are recorded in telemetry
3. Overview shows Cloud Savings section (per-provider: cost saved, kWh)
4. Stat cards show Total Queries, Input Tokens, Output Tokens
5. "Total Runs" renamed to "Total Queries" everywhere

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:45:50 -07:00
Jon Saad-FalconandClaude Opus 4.6 db64abec23 fix: add schedule_value to template, skip server tests when fastapi missing
- personal_deep_research.toml: add schedule_value="" for template test compat
- test_deep_research_tools_wiring: skip when fastapi not installed in CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:42:43 -07:00
Jon Saad-Falcon c235ca4c74 chore: remove MagicMock test artifacts 2026-03-27 13:33:56 -07:00
Jon Saad-FalconandClaude Opus 4.6 35249b4cb4 fix: markdown rendering in Interact, separate instruction section
1. Agent responses in Interact tab now render as markdown (headers,
   bold, lists, etc.) instead of raw text with ** and ##
2. Instruction section is its own box above Configuration in Overview
3. Edit button is inline next to "Instruction" heading
4. User messages stay as plain text (no markdown needed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:33:42 -07:00
Jon Saad-FalconandClaude Opus 4.6 869d9256fd fix: resolve all lint errors after merge with main
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:33:42 -07:00
Jon Saad-FalconandClaude Opus 4.6 037b40a937 fix: wizard hover highlight, card layout, editable schedule & instruction
1. Template cards highlight with accent border on hover
2. Text aligned top-left, emoji inline with title
3. Schedule is now an editable dropdown (not hardcoded display)
4. Overview tab shows current instruction with Edit button
5. Instruction is editable and saved to agent config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:09:41 -07:00
Jon Saad-FalconandClaude Opus 4.6 bce8a2fe12 merge: resolve conflicts with main — keep both DeepResearch tools + MCP functions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:09:12 -07:00
Jon Saad-Falcon b55b93a9c5 feat: simplify agent wizard, wire tools, add smart defaults (#149)
feat: simplify agent wizard, wire tools, add smart defaults
2026-03-27 12:54:30 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 8629749e36 feat: add jarvis channels CLI commands for iMessage daemon lifecycle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:54:09 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 37ad345b2d feat: add iMessage AppleScript daemon for iPhone-to-agent messaging
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:53:59 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 f75bd469cc feat: wire ChannelBridge to route messages to DeepResearchAgent
Add deep_research_agent parameter to ChannelBridge.__init__ and update
_handle_chat to try DeepResearchAgent first, falling back to system.ask()
when no research agent is configured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:53:25 -07:00
Jon Saad-FalconandClaude Opus 4.6 f2d84840ce docs: add implementation plan for channel gateway → DeepResearch wiring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 12:51:47 -07:00
Jon Saad-FalconandClaude Opus 4.6 17de84b9e6 feat: merge channel gateway from PR #78 — ChannelBridge, webhooks, sessions, auth
Cherry-picks the mobile channel gateway infrastructure:
- ChannelBridge orchestrator for multi-channel routing
- Webhook endpoints for Twilio SMS, BlueBubbles (iMessage), WhatsApp
- SessionStore for per-sender conversation tracking
- API key authentication middleware
- TwilioSMSChannel adapter
- CLI commands: jarvis auth, jarvis tunnel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 12:49:48 -07:00
Jon Saad-Falcon 5a37e9c774 fix: remove unused TestClient import in recommended model tests 2026-03-27 12:48:31 -07:00
Jon Saad-Falcon 5c942f41b3 chore: remove spec and plan docs from branch 2026-03-27 12:42:47 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 add0abde41 feat: wire DeepResearch tools in managed agent streaming endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:40:16 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 be1ed32a89 feat: add 'jarvis research' CLI alias for deep-research-setup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:39:43 -07:00
Jon Saad-Falcon a3b81aa200 feat: add Personal Deep Research agent template 2026-03-27 12:39:28 -07:00
Jon Saad-FalconandClaude Opus 4.6 69354136f0 docs: add implementation plan for Personal DeepResearch template + server wiring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 12:37:20 -07:00
Jon Saad-FalconandClaude Opus 4.6 cbabcd4a8f feat: rewrite agent wizard — 2-step flow with smart defaults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 12:36:07 -07:00
Jon Saad-FalconandClaude Opus 4.6 a65dbb50fb docs: add design spec for Personal DeepResearch agent template
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 12:33:29 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 e4466146ce feat: add /v1/recommended-model endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:27:24 -07:00
Jon Saad-FalconandClaude Opus 4.6 7366681f62 feat: wire tools from agent config to executor via ToolRegistry
Resolve tool names from config["tools"] into actual tool instances
using ToolRegistry, inject runtime deps, and pass them to the agent
constructor instead of hardcoded tools=[].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:32:57 -07:00
Jon Saad-Falcon b82575ea5b Merge pull request #147 from open-jarvis/feat/mcp-streaming-tool-calls
feat: fix external MCP server integration and add streaming tool-call support
2026-03-27 11:30:03 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 ccea853e5e feat: expand system_prompt_template with instruction on agent creation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 11:28:18 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 043c86b321 feat: add system_prompt_template, icon, update defaults in agent templates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 11:25:41 -07:00
Jon Saad-Falcon b4e70a5a7d Merge pull request #148 from open-jarvis/fix/leaderboard-outlier-filter
fix: filter outlier entries from leaderboard instead of recomputing
2026-03-27 11:22:09 -07:00
Jon Saad-FalconandClaude Opus 4.6 049d6ff543 docs: add agent wizard simplification implementation plan
7-task plan covering template TOMLs, manager expansion, executor tool
wiring, recommended-model endpoint, frontend API, wizard rewrite,
and integration testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:21:01 -07:00
krypticmouseandClaude Opus 4.6 8899662fc5 fix: line-too-long lint error in agent_manager_routes.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:19:09 +00:00
Jon Saad-FalconandClaude Opus 4.6 264998b36c fix: filter outlier entries from leaderboard instead of recomputing
Replace the recompute approach from #146 with per-token outlier
detection.  Entries whose energy, FLOPs, or dollar savings exceed
generous per-token thresholds (~1000x legitimate values) are hidden
from the leaderboard entirely.  All displayed values come directly
from the database — no rewriting of submitted data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:17:31 -07:00
Jon Saad-FalconandClaude Opus 4.6 728896ae86 docs: add agent wizard simplification design spec
Sub-project A spec covering: 2-step wizard, smart template defaults,
rich system prompt templates, tool wiring fix, recommended model
endpoint, and Advanced settings collapse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:15:37 -07:00
b39dbedcc4 feat: fix external MCP server integration and add streaming tool-call support
Rebased and cleaned-up version of PR #113 by @mricharz, resolved against
current main (including Codex engine, Gemini thought_signature, and
agent manager fixes merged since the original PR).

MCP Transport & Client:
- StreamableHTTPTransport with session tracking, SSE parsing, timeouts
- MCPClient.initialize() sends proper MCP handshake (protocolVersion,
  capabilities, clientInfo) + notifications/initialized
- Fix StdioTransport constructor: command=[command] + args
- MCPRequest.to_dict() with notification support (id=None)

External MCP Discovery:
- _discover_external_mcp supports both url (HTTP) and command (stdio)
- Per-server include_tools / exclude_tools filtering
- MCP clients persisted on JarvisSystem for runtime lifetime

Streaming Tool-Call Support (stream_full):
- StreamChunk dataclass in _stubs.py (content, tool_calls, finish_reason, usage)
- Default stream_full() on InferenceEngine ABC wraps stream() for backward compat
- _OpenAICompatibleEngine.stream_full() with SSE parsing
- CloudEngine: _stream_full_openai (OpenAI/OpenRouter/MiniMax/Codex routing)
               _stream_full_anthropic (event-based → OpenAI delta format)
- InstrumentedEngine, MultiEngine: stream_full delegation
- GuardrailsEngine: stream_full with post-hoc security scanning (FIXED:
  original PR bypassed output scanning — now accumulates and scans like stream())

Other improvements:
- _prepare_anthropic_messages() extracted to eliminate duplication
- Default tool_choice=auto when tools are provided (OpenAI compat engines)
- MCP tool injection into managed agent streaming path
- Documentation: docs/user-guide/mcp-external-servers.md

Tests: ~59 new tests across 8 test files, all passing.

Closes PR #113

Co-Authored-By: mricharz <mricharz@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:09:54 +00:00
Jon Saad-FalconandClaude Opus 4.6 23d8b7128e feat: add query expansion strategies to DeepResearch system prompt
Teaches the agent to expand abstract/categorical queries into concrete
search terms using strategies (category→instances, synonyms, broader/narrower,
context clues) rather than hardcoded examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:05:34 -07:00
Jon Saad-Falcon eadcc3bb60 Merge pull request #146 from open-jarvis/fix/leaderboard-anti-gaming
fix: recompute leaderboard energy/FLOPs from tokens, cap dollar savings
2026-03-27 11:03:19 -07:00
Jon Saad-FalconandClaude Opus 4.6 77b5567e47 fix: recompute leaderboard energy/FLOPs from tokens, cap dollar savings
Energy and FLOPs are now derived from total_tokens on the leaderboard
display using Claude Opus 4.6 constants, rather than trusting submitted
DB values. This prevents gaming (e.g. TotallyNoire submitting 14B Wh
from 15M tokens). Dollar savings are clamped at the theoretical max
($25/1M tokens) on both the leaderboard and frontend submission side.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:58:36 -07:00
Jon Saad-Falcon 957ce83955 Merge pull request #145 from open-jarvis/docs/macos-installation-guide
docs: add macOS installation guide for llama.cpp
2026-03-27 10:46:55 -07:00
Jon Saad-Falcon b8b3369ad1 Merge pull request #144 from open-jarvis/fix/migrate-savings-data
fix: add SQL migration to recompute historical leaderboard savings
2026-03-27 09:17:55 -07:00
0ce10bd97b docs: add macOS installation guide for llama.cpp
Comprehensive step-by-step guide covering Homebrew, uv, Rust, llama.cpp,
model download, Python 3.12 pin (PyO3 compat), and common pitfalls.

Cherry-picked from PR #131 by @gridworks — cleaned up to include only
the docs content (removed duplicate files, binary artifacts, and
unrelated lockfile changes from the original PR).

Co-Authored-By: gridworks <5502067+gridworks@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:17:17 +00:00
Jon Saad-FalconandClaude Opus 4.6 d7d0125454 fix: add SQL migration to recompute historical leaderboard savings
Companion to #143 which fixed the frontend to use Claude Opus 4.6 as
the sole baseline. This migration recomputes existing Supabase rows
using the exact closed-form: new = T/3.8M + 10*old/19, derived from
the original triple-provider formula.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 09:13:31 -07:00
Jon Saad-Falcon bfdc71d31d Merge pull request #142 from open-jarvis/feat/codex-engine-support
feat: add Codex cloud engine for ChatGPT Plus/Pro subscribers
2026-03-26 19:06:46 -07:00
Jon Saad-Falcon 44626b0ef8 Merge pull request #143 from open-jarvis/fix/leaderboard-savings-single-provider
fix: use Claude Opus 4.6 as sole baseline for leaderboard savings
2026-03-26 19:04:23 -07:00
Jon Saad-FalconandClaude Opus 4.6 c807666bd5 fix: cost format $0.0000, compact overview layout
- Remove cent sign from cost display, use $X.XXXX format
- Compact stat cards (horizontal icon+value layout)
- Tighter config grid spacing with bolder labels
- Reduce padding and gaps throughout overview tab

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:00:39 -07:00
Jon Saad-FalconandClaude Opus 4.6 fc369b9ab0 fix: use Claude Opus 4.6 as sole baseline for leaderboard savings
Dollar savings were previously summed across all three cloud providers
(GPT-5.3 + Claude Opus 4.6 + Gemini 3.1 Pro), inflating the reported
number by ~3x. Now uses only Claude Opus 4.6 pricing as the baseline,
with an asterisk footnote on the leaderboard explaining this.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:59:40 -07:00
krypticmouseandClaude Opus 4.6 63ae942116 feat: add Codex cloud engine for ChatGPT Plus/Pro subscribers
Adds `codex/` prefixed model support using the OpenAI Responses API —
the same protocol used by zeroclaw and other Codex-compatible tools.

Live-tested against gpt-5-mini-2025-08-07 with:
- Generate (non-streaming): confirmed working
- System prompt → instructions mapping: confirmed working
- SSE streaming: confirmed working (9 chunks)
- End-to-end via `jarvis ask`: confirmed working

Implementation:
- Default endpoint: api.openai.com/v1/responses (standard API key)
- Override via OPENAI_CODEX_BASE_URL for ChatGPT OAuth tokens
  (e.g. chatgpt.com/backend-api/codex)
- Auth via OPENAI_CODEX_API_KEY env var
- Responses API format: input array, instructions field, output_text extraction
- Handles reasoning+message output blocks correctly
- SSE streaming parses response.output_text.delta events

Models: codex/gpt-4o, codex/gpt-4o-mini, codex/o3-mini,
        codex/gpt-5-mini, codex/gpt-5-mini-2025-08-07

Usage:
  export OPENAI_CODEX_API_KEY="your-api-key-or-oauth-token"
  jarvis ask "Hello" --model codex/gpt-5-mini-2025-08-07

Closes #134

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 01:54:36 +00:00
Jon Saad-FalconandClaude Opus 4.6 b72afe9b00 fix: add pure-Python fallback for think tool when Rust extension unavailable
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:46:53 -07:00
Jon Saad-FalconandClaude Opus 4.6 c1e02a236a feat: show model in agent overview, split tokens, editable model
Overview tab:
- Show Intelligence (model name) with click-to-change dropdown
- Split "Total Tokens" into "Input Tokens" and "Output Tokens"
- Model can be switched for existing agents via dropdown

Backend:
- Add input_tokens/output_tokens columns to managed_agents
- Track prompt_tokens and completion_tokens separately in executor
- Disable Ollama thinking by default (think:false) to prevent
  empty responses from token exhaustion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:32:25 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 a1626960c1 feat: rewrite DeepResearch system prompt, wire SQL + scan + think tools, increase max_turns to 8
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:32:25 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 02862614a4 feat: add scan_chunks tool for LM-powered semantic grep
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:30:23 -07:00
Jon Saad-Falcon 813d704201 Merge pull request #140 from open-jarvis/fix/pinchbench-tool-args-and-sessions
fix(evals): pipe tool arguments to transcript + multi-session support
2026-03-26 18:29:36 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 5ea5df93d4 feat: add knowledge_sql tool for read-only SQL aggregation queries
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:29:22 -07:00
Jon Saad-FalconandClaude Opus 4.6 5637b1615b docs: add implementation plan for Deep Research Agent v2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:26:30 -07:00
Jon Saad-FalconandClaude Opus 4.6 a8f010a838 docs: add design spec for Deep Research Agent v2 — SQL + LM-scan tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:21:59 -07:00
Jon Saad-FalconandClaude Opus 4.6 8590bc5140 fix: disable Ollama thinking by default, fix token tracking
Root cause of empty responses: Qwen3.5's extended thinking mode
consumes all tokens (4096) on hidden <think> tags, leaving zero
visible content. The /no_think text tag was unreliable.

Fix: pass think=false in the Ollama API payload, which properly
disables thinking at the API level. Drops token usage from ~4096
to ~5-200 per response and eliminates empty content.

Also:
- Fix token tracking to read total_tokens from metadata (was looking
  for tokens_used which is never set)
- Remove the /no_think system prompt hack (superseded by API param)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:20:05 -07:00
Jon Saad-FalconandClaude Opus 4.6 66778f92ee fix: add /no_think to DeepResearch prompt for Qwen 3.5 compatibility
Qwen 3.5 thinking mode in Ollama consumes the response into <think> blocks,
returning empty content. /no_think disables this for direct text output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:10:12 -07:00
Jon Saad-Falcon f61d690791 Merge pull request #141 from open-jarvis/feat/security-scan-enhancements
feat: enhance security scan with DNS check, JSON output, and API endpoint
2026-03-26 18:01:30 -07:00
4d05475ec4 feat: enhance security scan with DNS check, JSON output, Rich UI, and API endpoint
Incorporates the useful new features from PR #135 (by @gridworks) on top
of the existing PrivacyScanner implementation:

- Add DNS configuration check (macOS, via scutil --dns)
- Add --json flag to `jarvis scan` for machine-readable output
- Add --no-scan flag to `jarvis init` to skip the post-init audit
- Expand remote-access process list (ngrok, tailscaled, cloudflared, ZeroTier)
- Upgrade `jarvis scan` output from plain text to Rich table
- Add GET /v1/security/scan API endpoint
- Add tests for all new features (30 tests, all passing)

Closes #133

Co-Authored-By: gridworks <5502067+gridworks@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 01:00:38 +00:00
Jon Saad-FalconandClaude Opus 4.6 11d1643261 feat: live agent progress visibility in Interact tab
Add current_activity field to managed agents that the executor updates
at each phase of a tick (loading model, delivering messages, generating
response, retrying, finalizing). The frontend polls this every 2s and
displays the live status instead of static "Agent is thinking...".

Backend:
- Add current_activity column to managed_agents (migration)
- Add _set_activity helper to AgentExecutor
- Update activity at: start_tick, model load, message delivery,
  generation, retry, finalize
- Clear activity on end_tick

Frontend:
- InteractTab polls both messages and agent status in parallel
- Shows current_activity text with pulsing indicator
- Falls back to "Agent is thinking..." if activity is empty

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:54:08 -07:00
Jon Saad-Falcon 7f336d7679 Merge pull request #130 from jbergant/fix/qwen35-model-catalog-mlx-129
fix: correct Qwen3.5 model sizes and MLX repos in catalog
2026-03-26 17:50:44 -07:00
Jon Saad-FalconandClaude Opus 4.6 54cf71dd9a fix: force synthesis when agent returns empty content after tool calls
When the model returns no tool calls and no content (common with Qwen 3.5
thinking mode), inject a synthesis prompt to get the final report.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:48:37 -07:00
Jon Saad-FalconandClaude Opus 4.6 1531ad656c fix(evals): pipe tool arguments to transcript + multi-session support
Two fixes for the last 4 failing PinchBench tasks:

1. Tool arguments (tasks 05, 07, 13): Arguments were lost in the
   pipeline — ToolExecutor stored them but system.py stripped them
   from the tool_results dict, so the LLM judge saw write_file({})
   instead of the actual content. Now pipe arguments through:
   _stubs.py → system.py → scorer transcript.

2. Multi-session tasks (task 22): Parse `sessions` field from task
   frontmatter, use first session's prompt as record.problem, and
   execute remaining sessions sequentially within the workspace
   context in _process_one().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 00:43:22 +00:00
Jon Saad-FalconandClaude Opus 4.6 9255fdf7f3 fix: force synthesis when DeepResearchAgent hits max turns
Instead of returning "Maximum turns reached" when all turns are used on tool
calls, do one final generation WITHOUT tools to force the model to synthesize
a research report from everything it found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:35:56 -07:00
Jon Saad-FalconandClaude Opus 4.6 756e28b109 fix: create OllamaEngine directly, skip health checks that stall ticks
get_engine() probes all engines with health checks, which can load
different models and interfere with in-flight Ollama requests, causing
intermittent empty responses. Create a plain OllamaEngine directly
from config instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:34:51 -07:00
Jon Saad-FalconandClaude Opus 4.6 885b2caf99 fix: suppress Qwen3.5 thinking mode, retry on empty content, add logging
- Append /no_think to system prompt to prevent Qwen3.5 from consuming
  all tokens on extended thinking and producing empty visible output
- Retry once if agent returns empty content
- Add debug logging to _make_lightweight_system for engine diagnostics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:23:18 -07:00
Jon Saad-FalconandClaude Opus 4.6 aab743a6c7 fix(engine): support Gemini thought_signature for multi-turn tool calling (#139)
Gemini 3.1+ reasoning models require a thought_signature field in
function_call parts when replaying conversation history. Without it,
the API returns 400 INVALID_ARGUMENT on every multi-turn tool call.

Changes:
- CloudEngine: capture thought_signature from Gemini responses and
  store in _thought_sigs dict keyed by tool_call id
- CloudEngine: replay thought_signature when building function_call
  parts for Gemini conversation history
- native_openhands: thread thought_signature through via side dict
  (ToolCall uses slots, can't add dynamic attributes)
- Add PinchBench eval configs for Claude Opus 4.6, Gemini 3.1 Pro,
  Nemotron-3-Super, Qwen 122B, and Qwen 35B

Impact: Gemini 3.1 Pro PinchBench score 4% → 78%

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:18:48 -07:00
Jon Saad-FalconandClaude Opus 4.6 e788d1a416 feat: extend deep-research-setup with token source detection and interactive connect
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:16:53 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 0f03059eec feat: add Outlook connector as IMAP subclass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:16:46 -07:00
Jon Saad-Falcon d673cf8514 feat: add imap_host parameter to GmailIMAPConnector 2026-03-26 17:16:46 -07:00
Jon Saad-FalconandClaude Opus 4.6 a54ee577fa docs: add design spec for Outlook connector + token source integration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:01:24 -07:00
Jon Saad-FalconandClaude Opus 4.6 8b358620da fix: use fresh engine for agent ticks, not wrapped server engine
The server's engine is wrapped in MultiEngine → InstrumentedEngine →
GuardrailsEngine. When reused from a background thread for agent ticks,
this chain returns empty content. Create a fresh OllamaEngine for each
tick instead, which reliably returns model output.

Also fixes Run Now endpoint to use the same lightweight system approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:47:00 -07:00
Jon Saad-FalconandClaude Opus 4.6 c08572158f style: fix line length in deep_research_setup_cmd
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:43:45 -07:00
Jon Saad-FalconandClaude Opus 4.6 7331e489ff fix: handle both OpenAI and flat tool_call formats in DeepResearchAgent
Ollama returns {id, name, arguments} while OpenAI returns {id, function: {name, arguments}}.
The agent now handles both via _tc_name/_tc_args helpers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:41:13 -07:00
Jon Saad-FalconandClaude Opus 4.6 463a090fda fix: register connectors API router in FastAPI app
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:33:36 -07:00
Jon Saad-FalconandClaude Opus 4.6 326e6f9779 fix: expose state_db param in ingest_sources, isolate test from global state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:32:43 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 bebb604a23 feat: add jarvis deep-research-setup CLI command
Auto-detects local sources (Apple Notes, iMessage, Obsidian), ingests
them into ~/.openjarvis/knowledge.db via IngestionPipeline + SyncEngine,
and launches an interactive Deep Research chat session with Qwen3.5 via
Ollama.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 16:27:26 -07:00
Jon Saad-FalconandClaude Opus 4.6 bf3549856a fix: handle missing ZTITLE1 column, remove false-positive artifact regex
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:24:22 -07:00
Jon Saad-FalconandClaude Opus 4.6 808ec6eb5c feat: change default startup model to qwen3.5:4b
Update STARTUP_MODEL from 2b to 4b for better quality on first launch.
Also update preferred_model() to prefer STARTUP_MODEL when it fits,
rather than always picking the third-largest model. This gives a
consistent default across machines while still falling back to
RAM-appropriate sizing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:17:57 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 744da567db fix: Apple Notes protobuf extraction handles HTML too, fix ZTITLE1 column
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 16:15:58 -07:00
Jon Saad-FalconandClaude Opus 4.6 ed3f9300b8 docs: add vertical slice implementation plan for deep research E2E
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:14:04 -07:00
Jon Saad-Falcon e2f4d9f2f4 fix: use server's model for agent ticks, add logging (#138)
fix: use server's model for agent ticks, add logging
2026-03-26 16:04:31 -07:00
Jon Saad-FalconandClaude Opus 4.6 19fe061696 fix: use server's model for agent ticks, add executor logging, simplify send UI
Root cause: _run_tick and _immediate_tick called SystemBuilder().build()
which picks the first model from Ollama (qwen3.5:35b) instead of the
model the server was started with (e.g. qwen3.5:9b). A 0.6B query was
running on a 35B model, causing 5+ minute stalls.

Fix: reuse the server's engine/model from app.state via a lightweight
system facade instead of rebuilding the full JarvisSystem.

Also:
- Add detailed logging to AgentExecutor (model, pending messages,
  timing, content length, errors with tracebacks)
- Add logging to immediate tick lifecycle
- Remove Queue button from Interact tab (single Send button)
- Enter key now sends immediately instead of queueing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:03:51 -07:00
Jon Saad-FalconandClaude Opus 4.6 3b9995b2c6 docs: add vertical slice design spec for end-to-end deep research on laptop
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:02:37 -07:00
Jon Saad-Falcon 6cc055f25e fix: agent Interact tab UX and immediate message handling (#137)
fix: agent Interact tab UX and immediate message handling
2026-03-26 15:10:57 -07:00
Jon Saad-FalconandClaude Opus 4.6 a212652dc0 fix: agent Interact tab UX — immediate messages, ordering, scroll
Backend:
- Immediate-mode messages now trigger a background tick so the agent
  actually processes and responds (previously they were just stored)

Frontend (Interact tab):
- Reverse message order so newest appear at bottom near the input box
- Filter out agent responses with empty content (blank bubbles)
- Add "Agent is thinking..." indicator with pulsing dot while processing
- Show timestamps instead of raw mode/status labels
- Poll for new messages every 3s so responses appear automatically
- Only auto-scroll to bottom on initial tab load, not on every poll
  update (prevents hijacking the user's scroll position)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:08:08 -07:00
krypticmouseandClaude Sonnet 4.6 5366063cd2 feat: add openjarvis:// deep link handler for Tauri
Registers the openjarvis:// URL scheme via the Tauri 2 deep-link plugin
in tauri.conf.json.  Adds frontend/src/lib/deep-link.ts with parseDeepLink()
that parses openjarvis://{type}/{id} URLs into structured DeepLinkTarget
objects (e.g. openjarvis://research/abc123 → {type:"research", id:"abc123"}).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:29:55 +00:00
krypticmouseandClaude Sonnet 4.6 bfa5132d1d feat: add SyncScheduler for periodic incremental sync
Implements SyncScheduler that runs a daemon background thread to call
SyncEngine.sync() on all registered connectors at a configurable interval.
Provides run_once() as a synchronous helper for testing.  Disconnected
connectors are skipped each cycle; errors are logged without stopping the
loop.  4 tests covering run_once, disconnected skip, start/stop, and
per-connector chunk counts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:29:50 +00:00
krypticmouseandClaude Sonnet 4.6 526cd4d3fb feat: add WhatsApp chat export connector
Implements WhatsAppConnector that reads exported .txt chat files from a
directory, parses the standard WhatsApp export line format with regex,
and yields one Document per chat file with participants, timestamps, and
since filtering.  Registers under "whatsapp" in ConnectorRegistry with
MCP tools for whatsapp_search_messages and whatsapp_get_chat.  10 tests
covering parsing, multi-file, since filtering, is_connected, and registry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:29:44 +00:00
krypticmouseandClaude Sonnet 4.6 b776c40507 feat: add Dropbox connector with file listing and download
Implements DropboxConnector using the Dropbox API v2 (list_folder +
download endpoints). Module-level API functions (_dropbox_api_list_folder,
_dropbox_api_download) are kept mockable. Downloads text-extractable
file types (.txt, .md, .csv, .py, etc.) and stores binary/unknown files
as metadata-only Documents.
Includes 6 tests covering not_connected, auth_url, sync (mocked),
disconnect, mcp_tools (dropbox_search_files / dropbox_get_file /
dropbox_list_recent), and registry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:23:45 +00:00
krypticmouseandClaude Sonnet 4.6 b4a1e824d8 feat: add Apple Notes connector with gzipped HTML extraction
Reads from the macOS NoteStore.sqlite database using read-only SQLite.
Decompresses ZDATA blobs (gzip + HTML) to yield plain-text Documents.
Includes 9 tests covering sync, decompression, doc_type, disconnect,
mcp_tools (notes_search / notes_get_note), and registry.
Updates connectors/__init__.py auto-import block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:23:38 +00:00
krypticmouseandClaude Sonnet 4.6 dc4c773316 feat: wire Gmail since param to after: search operator
Remove the ARG002 noqa suppression and translate the `since` datetime
parameter into a Gmail `after:<epoch>` query string passed to
`_gmail_api_list_messages`, so incremental syncs only fetch messages
newer than the requested cutoff.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:18:52 +00:00
krypticmouseandClaude Sonnet 4.6 ee1805ceaf feat: wire attachment text extraction into IngestionPipeline
Add optional `attachment_store` parameter to IngestionPipeline. When
provided, attachments are stored as blobs in AttachmentStore and their
text (plain/markdown/csv via decode, PDF via pdfplumber) is chunked and
indexed in the KnowledgeStore with attachment provenance metadata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:18:47 +00:00
krypticmouseandClaude Opus 4.6 ef859e6a17 feat: add EmbeddingStore for ColBERTv2 disk persistence
Add disk-persistent storage for ColBERT token-level embeddings so the
reranker can reuse pre-computed embeddings across queries instead of
re-encoding every document on every search.

- EmbeddingStore: individual .pt files per chunk with SQLite index for
  O(1) lookup, graceful degradation when torch is not installed
- ColBERTReranker: checks EmbeddingStore for cached embeddings before
  falling back to docFromText(), stores newly computed embeddings
- KnowledgeStore: ensures chunk_id is always present in retrieval
  metadata (both at store time and as a backfill in retrieve)
- 18 tests covering round-trip persistence, deletion, torch-absent
  degradation, and reranker cache-hit/miss behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:48:17 +00:00
Jon Saad-FalconandClaude Opus 4.6 3b4eeded0e fix: normalize TOML arrays for property setters, not just dataclass fields
_apply_toml_section only normalized TOML arrays to comma-separated strings
for real dataclass fields, but backward-compat property setters like
reward_weights also expect string input. When a user's config.toml had
an array value for a property-backed attribute, the raw list was passed
to the setter which called .split(",") on it, causing:

  'list' object has no attribute 'split'

This also hardens serve.py against the same issue when reading
config.agent.tools.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 13:46:09 -07:00
krypticmouseandClaude Sonnet 4.6 9412d0981b feat: add POST /v1/connectors/{id}/sync to trigger incremental sync
Adds a new POST /{connector_id}/sync endpoint alongside the existing GET
sync-status endpoint. The new endpoint validates the connector is registered
and connected, then runs SyncEngine.sync() and returns chunks_indexed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 19:44:37 +00:00
Jon Saad-Falcon db2f20e25b Delete instr.md 2026-03-26 12:42:54 -07:00
Jon Saad-Falcon e5ff7b3695 Delete MagicMock/load_config().security.audit_log_path directory 2026-03-26 12:42:16 -07:00
krypticmouseandClaude Sonnet 4.6 99d2013d51 feat: add content-addressed AttachmentStore with SHA-256 dedup
Implements AttachmentStore that writes blobs to {base_dir}/{sha[:2]}/{sha}
with a SQLite metadata index tracking filename, MIME type, size, and the
accumulated list of source_doc_ids for each content-identical blob.
Includes 7 unit tests covering SHA-256 return, file path layout,
idempotent dedup, multi-source tracking, metadata retrieval, content
round-trip, and missing-blob None return.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 19:41:35 +00:00
Jon Saad-Falcon 36878e0263 feat: agent testing, channel adapters, bug fixes (#89)
feat: agent testing, channel adapters, bug fixes
2026-03-26 11:29:42 -07:00
Jon Saad-FalconandClaude Opus 4.6 1358d946c1 merge: resolve conflicts with main (streaming + auto-recover)
Merge main into fix/ssrf-check, keeping both the auto-recover
logic for error-state agents and the async streaming support
for the send_message endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:29:33 -07:00
krypticmouseandClaude Sonnet 4.6 25d6a66d68 feat: wire incremental sync via since param in SyncEngine
Parse the last_sync checkpoint timestamp into a datetime and pass it as
the `since` argument to connector.sync(), enabling subsequent syncs to
fetch only new items rather than a full re-fetch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:28:18 +00:00
krypticmouseandClaude Opus 4.6 b48a9cac0e docs: add Phase 5 plan for incremental sync + attachment store
3-task plan: wire since param for incremental sync, content-addressed
AttachmentStore with SHA-256 dedup, POST /sync trigger endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:11:51 +00:00
krypticmouseandClaude Sonnet 4.6 c299e05e7b feat: integrate SetupWizard into desktop boot flow
SetupWizard orchestrates the pick→connect→ingest→ready state machine with a
step indicator; SetupScreen now transitions to the wizard after all boot checks
pass (phase === 'ready') instead of immediately calling onReady().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:08:42 +00:00
krypticmouseandClaude Sonnet 4.6 1b9cdc44c2 feat: add setup wizard components (SourcePicker, ConnectFlow, IngestDashboard, ReadyScreen)
SourcePicker renders a grouped card grid for selecting data sources; SourceConnectFlow
provides per-source auth panels (OAuth, filesystem, local); IngestDashboard polls sync
status every 2s with progress bars; ReadyScreen shows a celebration screen with
context-aware starter queries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:08:37 +00:00
krypticmouseandClaude Sonnet 4.6 53233f94ae feat: add connector TypeScript types and API client
Defines ConnectorInfo, SyncStatus, ConnectRequest, WizardStep, SourceCard types
and SOURCE_CATALOG constant; adds connectors-api.ts with listConnectors,
getConnector, connectSource, disconnectSource, and getSyncStatus functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:08:31 +00:00
Jana BergantandClaude Opus 4.6 f7d2cce86b fix: correct Qwen3.5 model sizes and MLX repos in catalog
The model catalog listed non-existent Qwen3.5 sizes (3B, 8B, 14B) and
pointed to MLX community repos that don't exist, causing `jarvis init`
to recommend models that cannot be downloaded on Apple Silicon.

Replace with the actual Qwen3.5 model family sizes (0.8B, 2B, 9B, 27B)
and verified mlx-community repo URLs from HuggingFace.

Fixes #129

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 08:29:31 +01:00
Jon Saad-Falcon 64c1036acd Merge pull request #127 from open-jarvis/fix/ci-skip-live-tests
fix(ci): skip live and cloud tests in CI
2026-03-25 21:35:17 -07:00
Jon Saad-FalconandClaude Opus 4.6 1ff155d445 fix(ci): skip live and cloud tests in CI
The gemma_cpp live tests require local model weights and env vars
(GEMMA_CPP_MODEL_PATH, etc.) that are not available in CI, causing
4 failures since the gemma-cpp-engine PR was merged. Add marker
filters to the pytest invocation so live and cloud tests are skipped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 21:31:32 -07:00
krypticmouseandClaude Opus 4.6 290b5cf1ad docs: add Phase 2B-ii plan for desktop setup wizard UI
5-task plan: TypeScript types + API client, SourcePicker grid,
SourceConnectFlow auth wizard, IngestDashboard + ReadyScreen,
SetupWizard orchestrator integrated into boot flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 04:27:31 +00:00
krypticmouseandClaude Sonnet 4.6 32760928e4 feat: add /v1/connectors API router for connector management
Implements create_connectors_router() with GET /connectors, GET
/connectors/{id}, POST /connectors/{id}/connect, POST
/connectors/{id}/disconnect, and GET /connectors/{id}/sync endpoints.
Includes 6 passing tests covering list, detail, 404, connect, disconnect,
and sync status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 04:24:55 +00:00
krypticmouseandClaude Opus 4.6 6f86103c9d docs: add Phase 2B-i plan for connector management API endpoints
Exposes /v1/connectors/* REST API for desktop wizard and CLI:
list, detail, connect, disconnect, sync status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 04:18:05 +00:00
Jon Saad-Falcon 2259280f23 feat: auto-clone repo on desktop app first launch (#122)
Auto-clone the OpenJarvis repo on first desktop app launch instead of showing an error. Uses git clone --depth 1 to ~/OpenJarvis. Closes #122.
2026-03-25 21:17:32 -07:00
Jon Saad-FalconandClaude Opus 4.6 1d24c64f11 fix(evals): PinchBench harness fixes — scores from 26% to 84% (#124)
* fix(evals): PinchBench harness fixes — scores from 26% to 84%

Multiple infrastructure bugs prevented PinchBench from producing
accurate scores. This commit fixes the eval harness so model scores
match the official leaderboard (Qwen3.5-397B: 26% → 84%, GPT-5.4:
5% → 58%).

Key fixes:

- EvalRunner: wrap generation in PinchBenchTaskEnv context so workspace
  files persist through grading (was deleting before scorer ran)
- EvalRunner: detect task_env datasets and force sequential processing
  (CWD changes aren't thread-safe)
- EvalRunner: fix episode_mode auto-detection to check for real
  iter_episodes() override instead of hasattr() (always True)
- Scorer: use "params" field in transcripts to match PinchBench grade()
  functions (was "arguments")
- Scorer: add None guard in _trace_to_transcript for tool_calls
- Scorer: capture final assistant text response in transcript so
  text-only tasks (like sanity check) can be graded
- Scorer: add _tool_results_to_transcript() helper for EvalRunner path
- native_openhands: add native function-calling support (tools=
  parameter) with text-based fallback, matching monitor_operative
- Tools: add Python fallbacks for file_read, file_write, shell_exec,
  calculator, think, http_request when openjarvis_rust unavailable
- Security: add Python fallback for is_sensitive_file()
- Config: add "pinchbench" to KNOWN_BENCHMARKS
- Add PinchBench eval configs for Qwen3.5-397B and GPT-5.4

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(evals): fix hybrid grading crash when grading_weights is None

The 4 tasks with grading_type=hybrid (task_10, task_13, task_16_market,
task_22) crashed because grading_weights was explicitly None in task
metadata. dict.get("key", default) returns None (not the default) when
the key exists with value None. Use `or` to coalesce None to defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(evals): handle MagicMock datasets in runner type checks

The iter_episodes and create_task_env type identity checks fail with
AttributeError when the dataset is a MagicMock (used in tracker tests).
Wrap in try/except to default to False for non-DatasetProvider objects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 21:16:57 -07:00
krypticmouseandClaude Sonnet 4.6 f0e51b524f feat: add integration test for ChannelAgent with DeepResearchAgent
Adds tests/agents/test_channel_agent_integration.py covering the full
stack from KnowledgeStore + IngestionPipeline through TwoStageRetriever,
KnowledgeSearchTool, and DeepResearchAgent into ChannelAgent/FakeChannel:
- test_quick_query_inline_response: asserts inline reply with meeting info
  and no openjarvis:// escalation link
- test_deep_query_escalation_link: asserts openjarvis:// link is present
  when engine issues a tool call and returns a long report

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 04:13:05 +00:00
krypticmouseandClaude Sonnet 4.6 7e127811c7 feat: add ChannelAgent with query classification and escalation
Adds classify_query() heuristic (quick vs deep) and ChannelAgent that
bridges BaseChannel messages to any agent, delivering inline replies for
quick/short responses and preview+openjarvis:// escalation links for deep
or long responses. 18 tests cover classifier cases and agent behaviour
including non-blocking handler and error recovery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 04:10:12 +00:00
krypticmouseandClaude Opus 4.6 85e5314992 docs: add Phase 4 plan for channel plugins (ChannelAgent + escalation)
3-task plan: query classifier, ChannelAgent with thread pool and
escalation links, integration test with real KnowledgeStore.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 04:07:07 +00:00
krypticmouseandClaude Opus 4.6 c3eb294b31 fix: ColBERTReranker uses Checkpoint API with proper CPU config
- Use Checkpoint instead of Searcher (doesn't need a pre-built index)
- Set gpus=0 in ColBERTConfig for CPU fallback
- Use cosine_similarity MaxSim scoring via queryFromText/docFromText
- Verified: reranking changes result order on 2,082 real Granola chunks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 04:00:27 +00:00
Jon Saad-Falcon 43b1240a44 feat: remote engine host configuration via CLI (#104)
Add jarvis config set command, --host flag to jarvis init, and improved error messages for configuring remote LLM engine endpoints. Closes #104.
2026-03-25 20:51:12 -07:00
krypticmouseandClaude Sonnet 4.6 5cf2de5f74 feat: add end-to-end integration test for Deep Research pipeline
Tests the full path from multi-source Document ingestion through
IngestionPipeline -> KnowledgeStore -> TwoStageRetriever ->
KnowledgeSearchTool -> DeepResearchAgent to a cited report, and
verifies cross-platform retrieval returns results from >= 2 sources.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 03:11:53 +00:00
krypticmouseandClaude Opus 4.6 39a8117596 feat: add DeepResearchAgent with multi-hop retrieval and cited reports
Multi-hop research agent that uses native function calling (OpenAI
tool_calls format) to search personal data across sources via
KnowledgeSearchTool, cross-references results, and produces narrative
answers with inline source citations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 03:09:23 +00:00
krypticmouseandClaude Sonnet 4.6 0917ebab25 feat: upgrade knowledge_search to support TwoStageRetriever
Add optional retriever parameter to KnowledgeSearchTool so it can
delegate to TwoStageRetriever (BM25 + reranking) when supplied, falling
back to direct KnowledgeStore retrieval otherwise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 03:05:27 +00:00
krypticmouseandClaude Sonnet 4.6 2967419691 feat: add TwoStageRetriever with BM25 recall and pluggable ColBERT reranking
Introduces Reranker ABC, ColBERTReranker (lazy-loads colbert-ai with graceful
fallback), and TwoStageRetriever that composes KnowledgeStore BM25 recall with
optional semantic reranking. Includes 8 tests covering filters, top_k limits,
mock reranker invocation, and recall_k sizing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 03:03:48 +00:00
krypticmouseandClaude Opus 4.6 d5b00eb4a8 docs: add Phase 3 plan for two-stage retrieval + Deep Research Agent
4-task plan: TwoStageRetriever (BM25 + ColBERT rerank), knowledge_search
upgrade, DeepResearchAgent with multi-hop citations, integration test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 02:58:53 +00:00
krypticmouseandClaude Opus 4.6 1feefd2110 feat: add Gmail IMAP connector with app password auth
Simpler alternative to OAuth-based Gmail connector. Uses Python's
built-in imaplib + email modules (no dependencies). Just needs an
email address and Google app password.

Live tested: 50 emails synced, 138 chunks indexed, search working.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 02:50:51 +00:00
krypticmouseandClaude Opus 4.6 ad2f6cc03c fix: Granola connector uses correct API response key and speaker format
Two bugs found via live API testing:
- List notes API returns "notes" key, not "data" (was returning 0 results)
- Transcript speaker field is a dict {"source": "microphone"}, not a string

Verified: 112 real meeting notes synced, 2,226 chunks indexed and searchable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 02:25:39 +00:00
krypticmouseandClaude Opus 4.6 20d132ff4a fix: add bot message filtering to Slack connector, add live smoke test
Slack connector now skips bot_id messages and non-content subtypes
(message_changed, message_deleted, bot_message, channel_join, channel_leave).
Found by comparing against hermes-agent reference implementation.

Live smoke test indexes 4,467 chunks from real docs/ markdown files and
verifies the full pipeline: Obsidian → SyncEngine → KnowledgeStore → search.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 02:06:43 +00:00
krypticmouseandClaude Sonnet 4.6 e3e9a70181 style: fix line length violations in connector tests
Break long lines in test_notion.py, test_store.py, and test_sync_engine.py
to comply with the 88-character E501 limit enforced by ruff.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:45:18 +00:00
krypticmouseandClaude Sonnet 4.6 506bcd88bd feat: add iMessage connector reading from macOS Messages database
Implements IMessageConnector (auth_type='local') that opens ~/Library/Messages/chat.db
read-only, converts Apple nanosecond timestamps to UTC datetimes, and yields one
Document per message with handle-based author resolution and chat display names.
Includes 8 tests using a temporary SQLite DB and 2 MCP tools (search + get conversation).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:43:02 +00:00
krypticmouseandClaude Sonnet 4.6 eb339294cb feat: add Google Contacts connector with People API sync
Implements GContactsConnector using the People API v1 to paginate
contacts (names, emails, phones, orgs) and yield them as Documents
with doc_type="contact". Includes 6 unit tests and auto-registration
in the connectors __init__.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:40:01 +00:00
krypticmouseandClaude Sonnet 4.6 79616c9da4 feat: add Google Calendar connector with event sync
Implements GCalendarConnector (registered as "gcalendar") following the
gmail.py pattern: module-level API functions for calendarList and events.list,
_format_event helper for human-readable content, paginated sync across all
calendars, and three MCP tools (get_events_today, search_events, next_meeting).
Includes 6 tests covering not_connected, auth_url scope, sync document fields,
disconnect, mcp_tools, and registry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:37:28 +00:00
krypticmouseandClaude Sonnet 4.6 89e757adba feat: add Google Drive connector with file export and sync
Implements GDriveConnector using the Drive API v3 with OAuth, paginated
file listing, Google Workspace export (Docs→text/plain, Sheets→text/csv,
Slides→text/plain), and 3 MCP tools. Adds auto-registration in __init__.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:34:43 +00:00
krypticmouseandClaude Sonnet 4.6 0d9c25c6f9 feat: add Slack data source connector with channel history sync
Implements SlackConnector that syncs channel message history via the Slack
Web API, with OAuth credential storage, user-map resolution, and 3 MCP tools.
Includes 7 tests covering connectivity, auth, sync document correctness,
disconnect, MCP tools, and registry registration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:32:05 +00:00
krypticmouseandClaude Opus 4.6 6e09361ebf docs: add Phase 2A plan for remaining connectors (Slack, Drive, Calendar, Contacts, iMessage)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 01:29:04 +00:00
krypticmouseandClaude Sonnet 4.6 1257303cd4 feat: add Granola meeting notes connector with transcript support
Implements GranolaConnector that syncs meeting notes from the Granola
public API, combining AI-generated summaries with full speaker transcripts
into searchable Documents. Includes cursor-based pagination, created_after
filtering, 2 MCP tools, and 8 tests covering all connector behaviours.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:20:12 +00:00
krypticmouseandClaude Sonnet 4.6 547b6ba734 feat: add Notion connector with block-to-markdown rendering
Implements NotionConnector that syncs pages via the Notion REST API,
renders block content (paragraph, headings, lists, code, quote, divider,
to_do) to markdown, and registers with ConnectorRegistry. Includes 8
tests covering auth, sync, rendering, disconnect, MCP tools, and registry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:17:16 +00:00
krypticmouseandClaude Opus 4.6 c8182be95a style: fix line length in sync_engine.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 00:10:42 +00:00
krypticmouseandClaude Sonnet 4.6 138fb8a3d0 feat: add integration test and auto-registration for built-in connectors
Wire up ObsidianConnector and GmailConnector via auto-import in the
connectors __init__.py so they register on package import. Add a full
end-to-end integration test covering the Obsidian vault → SyncEngine →
KnowledgeStore → knowledge_search pipeline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 00:09:36 +00:00
krypticmouseandClaude Sonnet 4.6 a23db16a12 feat: add 'jarvis connect' CLI command for managing data source connections
Adds connect_cmd.py with --list, --disconnect, --sync, and positional source
routing (filesystem vs OAuth), registers it in the CLI, and covers all
behaviours with 5 passing unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 00:06:29 +00:00
krypticmouseandClaude Sonnet 4.6 ffc43cd7b5 feat: add knowledge_search tool with filtered BM25 retrieval and source attribution
Implements KnowledgeSearchTool registered under "knowledge_search" that wraps
KnowledgeStore with optional filters (source, doc_type, author, since, until,
top_k) and formats results with source attribution for agent consumption.
Includes 8 unit tests covering basic search, filter-by-source, filter-by-author,
no-results, empty-query, no-store, spec parameters, and registry checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 00:02:02 +00:00
krypticmouseandClaude Opus 4.6 56554c481a feat: add Gmail connector with OAuth and mocked API sync
Implements GmailConnector registered under 'gmail' in ConnectorRegistry,
a shared oauth.py helper (build_google_auth_url, load/save/delete_tokens)
reusable by Drive/Calendar/Contacts, and 7 fully mocked pytest tests
covering auth state, sync document extraction, disconnect, mcp_tools,
and registry lookup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:56:29 +00:00
krypticmouseandClaude Opus 4.6 5e0e8965d7 feat: add Obsidian/Markdown vault connector with frontmatter parsing
Implements ObsidianConnector (filesystem auth, no OAuth) that walks a vault
directory for .md/.markdown/.txt files, parses YAML frontmatter via a
dependency-free parser, skips hidden dirs and binary files, and yields
Document objects with doc_type="note" and obsidian:// deep-link URLs.
Exposes an obsidian_search_notes MCP ToolSpec.  9 tests cover connection
state, vault traversal, hidden-dir/binary filtering, frontmatter extraction,
and registry wiring.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:52:49 +00:00
krypticmouseandClaude Sonnet 4.6 af7d0db518 feat: add SyncEngine with checkpoint/resume for connector orchestration
Introduces SyncEngine that wraps IngestionPipeline with a SQLite state
database (sync_state.db) for checkpoint/resume: cursors and item counts
are persisted after every 100-document batch and on completion/error.
Adds 4 tests covering single-connector ingestion, checkpoint accuracy,
unsynced-connector None return, and multi-connector source filtering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 23:49:54 +00:00
krypticmouseandClaude Sonnet 4.6 1a713e66fe feat: add ingestion pipeline with dedup, type-aware chunking, and indexed storage
Implements IngestionPipeline that deduplicates Documents by doc_id (both
in-memory and by loading existing doc_ids from the store on init), chunks
content via SemanticChunker, and persists all chunks with full provenance
metadata to KnowledgeStore. 8 tests cover single-doc ingestion, dedup across
calls and batches, persistence across pipeline instances, long-doc multi-chunk
splitting, atomic event chunking, multi-source filtering, and return-value accuracy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 23:47:59 +00:00
krypticmouseandClaude Opus 4.6 efc979edf0 feat: add type-aware semantic chunker with section/paragraph/sentence splitting
Implements SemanticChunker that splits text based on doc_type (event/contact
as single chunks, email on reply boundaries, message on double-newlines,
document/note on ## headings → paragraphs → sentences). ChunkResult carries
sequential 0-based indexes and inherits parent metadata; section headings are
added as chunk metadata. 16 tests covering all splitting strategies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:45:38 +00:00
krypticmouseandClaude Sonnet 4.6 8d3505f8f3 feat: add KnowledgeStore with source-aware SQLite schema and filtered BM25 retrieval
Implements KnowledgeStore (extends MemoryBackend) for Deep Research with a
rich SQLite/FTS5 schema supporting source, doc_type, author, participants,
timestamp, thread_id, url, and chunk_index columns; BM25 ranking via FTS5
with porter unicode61 tokenizer; filtered retrieval by source, doc_type,
author, since, and until; MEMORY_STORE/MEMORY_RETRIEVE event emission; WAL
journal mode; and 15 isolated tests using tmp_path fixtures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 23:42:39 +00:00
krypticmouseandClaude Sonnet 4.6 29d275fe4e feat: add ConnectorRegistry and base connector types (Document, SyncStatus, BaseConnector)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 23:35:01 +00:00
krypticmouseandClaude Opus 4.6 e9e4bb874b docs: add Phase 1 implementation plan for Deep Research connector foundation
10-task TDD plan covering: ConnectorRegistry, KnowledgeStore, semantic chunker,
ingestion pipeline, SyncEngine, OAuth helper, Obsidian connector, Gmail connector,
knowledge_search tool, and jarvis connect CLI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:21:09 +00:00
krypticmouseandClaude Opus 4.6 59ac24205e docs: add Deep Research setup experience design spec
Comprehensive spec covering the end-to-end "killer install" experience:
- Desktop-first setup wizard with guided OAuth for 12 data sources
- BaseConnector abstraction with native bulk sync + MCP real-time tools
- Ingestion pipeline with semantic chunking and dual-write indexing
- BM25 → ColBERTv2 two-stage retrieval with agent multi-hop
- DeepResearchAgent with cross-platform citations
- Channel plugins (iMessage, WhatsApp, Slack) with auto-escalation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:52:10 +00:00
Jon Saad-Falcon c390c73f85 Merge pull request #123 from open-jarvis/feat/gemma-cpp-engine
feat: add gemma.cpp engine via pygemma pybind11 bindings
2026-03-25 14:57:03 -07:00
Jon Saad-FalconandClaude Opus 4.6 d44076db3f fix: add pygemma API comment and model-mismatch warning to stream()
- Document that pygemma v0.1.3 completion() does not accept
  temperature/max_tokens (params accepted for ABC compliance)
- Add model-mismatch warning to stream() for consistency with generate()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 13:22:13 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 e4d036dd50 test: add live integration test stubs for gemma_cpp engine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:18:13 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 16025571fd style: fix lint issues in gemma_cpp engine
Move mid-file imports to top of test file to resolve E402 violations and apply ruff formatting to both gemma_cpp.py and test_gemma_cpp.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:17:16 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 d08360ca21 feat: wire gemma_cpp engine into discovery and optional imports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:15:06 -07:00
Jon Saad-Falcon 15dad86832 Merge pull request #121 from open-jarvis/feat/pinchbench-integration
feat: PinchBench benchmark integration
2026-03-25 12:51:01 -07:00
Jon Saad-FalconandClaude Opus 4.6 ae12d925bb fix(traces): allow TraceStore SQLite access from worker threads
AgenticRunner dispatches _run_body() to a ThreadPoolExecutor when a
task environment is present (for Playwright compatibility). The
TraceStore connection was created on the main thread, causing
"SQLite objects created in a thread can only be used in that same
thread" on the first agentic eval query.

Pass check_same_thread=False to sqlite3.connect(), consistent with
SchedulerStore, AgentManager, SessionStore, and TelemetryStore which
already use this flag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:46:06 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 411e822916 test: add config resolution tests for gemma_cpp engine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:40:24 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 8b0fd702c4 feat: implement gemma_cpp health checks and model listing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:39:38 -07:00
Jon Saad-FalconandClaude Opus 4.6 8c2ee6843a feat: implement gemma_cpp engine lifecycle and inference methods
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:38:25 -07:00
Jon Saad-FalconandClaude Opus 4.6 ed47fece39 fix: lint — shorten long lines in CLI and agentic runner
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:38:13 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 9cd822611e feat: add GemmaCppEngine skeleton with chat template formatting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:36:35 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 d589bfb55d feat: add GemmaCppEngineConfig and wire into EngineConfig
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:34:48 -07:00
Jon Saad-FalconandClaude Opus 4.6 9b511e08f7 fix(evals): workspace path resolution and grading crash resilience
- Store workspace_path in record.metadata from AgenticRunner so task
  envs can reuse the agent's workspace instead of creating a separate
  temp dir the agent can't see
- PinchBenchTaskEnv now uses the existing workspace when available,
  copies fixtures there, and sets CWD so file_read/file_write resolve
  relative paths correctly
- Wrap grading in try/except so LLM judge failures don't crash the
  entire run (graceful degradation to score 0.0)
- Restore CWD on exit and only clean up self-owned workspaces

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:33:58 -07:00
Jon Saad-Falcon 3ec7e76215 feat: add inference-gemma optional dependency for pygemma 2026-03-25 12:33:46 -07:00
Jon Saad-FalconandClaude Opus 4.6 784b880130 test(evals): add PinchBench integration tests — full grading pipeline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:31:02 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 b4962482c0 feat(evals): register PinchBench in CLI and add default TOML config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 09:28:29 -07:00
Jon Saad-Falcon 6ae720baa9 feat(evals): add PinchBench task environment — workspace setup and run_tests() grading 2026-03-25 09:27:18 -07:00
Jon Saad-FalconandClaude Opus 4.6 73cb1f1bbd feat(evals): add PinchBench grading helpers — transcript translation, automated/LLM judge/hybrid scoring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:26:12 -07:00
Jon Saad-FalconandClaude Opus 4.6 8e8d4a68e4 feat(evals): add PinchBench dataset provider — repo clone and task markdown parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:25:39 -07:00
Jon Saad-FalconandClaude Opus 4.6 ae0262da82 feat(evals): bridge EventBus to EventRecorder, capture tool_calls in traces
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:24:26 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 e0ff20c473 feat(tools): include result text in TOOL_CALL_END event metadata
Adds a truncated (10KB max) string representation of the tool result's
content to the TOOL_CALL_END event payload so grading functions (e.g.
PinchBench) can inspect what each tool returned.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 09:22:13 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 c5cb6a9e49 feat(evals): add tool_calls field to TurnTrace for rich tool data
Adds a `tool_calls` list field to `TurnTrace` capturing name, arguments,
and result for each tool invocation, enabling PinchBench transcript integration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 09:20:25 -07:00
Jon Saad-Falcon a8569ec3a3 Merge pull request #120 from open-jarvis/feat/github-contribution-infra
feat: add take-bot workflow and update contribution docs
2026-03-25 08:49:45 -07:00
Jon Saad-FalconandClaude Opus 4.6 36554ab0d1 docs: consolidate README sections and add Contributing
- Merge Quick Start, Docker, and Development into single Quick Start
- Point to docs site for full documentation (Docker, cloud, dev setup)
- Add Contributing section with dev setup and roadmap pointer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 15:46:12 +00:00
Jon Saad-Falcon 14c81bba98 Merge pull request #115 from open-jarvis/feat/init-onboarding-and-privacy-scanner
feat: interactive model download in init + privacy environment scanner
2026-03-25 08:12:45 -07:00
Jon Saad-FalconandClaude Opus 4.6 68a8f5646c fix: add --no-download and PrivacyScanner mock to test_cli init test
The test_init_creates_config test in test_cli.py was not updated
when the download prompt and privacy hook were added to jarvis init.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:07:35 -07:00
Jon Saad-FalconandClaude Opus 4.6 b2152fb40e style: fix E501 line-length and unused imports in test files
Wraps long CliRunner.invoke() calls, removes unused pytest imports,
fixes import ordering, and removes unused variables in test_scan.py.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:38:14 -07:00
Jon Saad-FalconandClaude Opus 4.6 cbf6fe3ea2 feat: add take-bot workflow and update contribution docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 03:35:53 +00:00
Jon Saad-FalconandClaude Opus 4.6 bc2a7e5595 chore: remove spec/plan files from tracked tree
These are Claude-generated working documents that should not
be checked into the repository.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:31:09 -07:00
Jon Saad-FalconandClaude Opus 4.6 a934977ba8 fix: address spec review findings for privacy scanner
- Add slots=True to ScanResult dataclass
- Fix extra space in IPv6 port f-string
- Add missing tests: check_remote_access, check_icloud_sync, run_quick

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:26:05 -07:00
Jon Saad-FalconandClaude Opus 4.6 d9faa4621e style: fix E501 line length in model pull --engine option
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:24:07 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 02bc36c1f8 feat: register jarvis scan command and add init privacy hook
Registers the new scan command in the CLI. Adds a lightweight
privacy check at the end of jarvis init that runs disk encryption
and cloud sync checks, with pointer to jarvis scan for full audit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 18:22:13 -07:00
Jon Saad-Falcon a5949052b6 Merge pull request #111 from open-jarvis/fix/token-counting-no-kv-cache
fix: count full prompt tokens
2026-03-24 18:21:03 -07:00
Jon Saad-FalconandClaude Opus 4.6 e2376d29d1 feat: add interactive model download and empty-model fallback to init
Prompts user to download recommended model during jarvis init.
Adds --no-download flag for CI. Shows helpful message when no
model fits available memory. Adds exo/nexa next-steps text.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:16:59 -07:00
Jon Saad-Falcon 00f71ae504 fix: update energy scaling test for linear KV-cache FLOPs model
The FLOPs formula changed from quadratic (P*N*(N+1)) to linear
(2*P*T_evaluated) with KV-cache awareness. Update the test
expectation from ~100x to ~10x for 10x token increase.
2026-03-24 18:16:11 -07:00
Jon Saad-FalconandClaude Opus 4.6 a05c2b7df6 feat: extract ollama_pull helper and add multi-engine model pull
Refactors model pull into reusable ollama_pull() function. Adds
--engine flag to support llamacpp (GGUF) and mlx (HuggingFace)
downloads via huggingface-cli, with FileNotFoundError handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:12:55 -07:00
Jon Saad-Falcon 5de72fb54e Merge pull request #112 from open-jarvis/revert/openai-compat-tool-call-fix
revert: remove _fix_tool_call_arguments from OpenAI-compatible engines
2026-03-24 18:12:29 -07:00
Jon Saad-Falcon 26a4e2e65e feat: KV-cache-aware FLOPs/energy with full-count dollar cost
Thread prompt_tokens_evaluated through the telemetry stack so
savings calculations use the right token count for each metric:

- Dollar cost: full prompt_tokens (what cloud providers charge)
- FLOPs/energy: prompt_tokens_evaluated (actual compute with KV
  cache — subsequent turns only re-evaluate new tokens)

Ollama reports both: prompt_eval_count (cache-aware) and we estimate
full count from messages. OpenAI-compat engines report full count
only (KV caching is transparent in their API).

Changes: TelemetryRecord, store schema, aggregator, engines,
savings calculation, /v1/savings route.
2026-03-24 18:12:13 -07:00
Jon Saad-FalconandClaude Opus 4.6 ced38bcba3 feat: add privacy environment scanner with platform-specific checks
New PrivacyScanner class with checks for disk encryption (FileVault/LUKS),
MDM profiles, cloud sync agents, network exposure, and screen recording.
Supports macOS and Linux with graceful skip on missing tools.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:07:54 -07:00
Jon Saad-FalconandClaude Opus 4.6 7d3b4e5ea3 fix: add MLX engine support to Qwen3.5 model catalog entries
Fixes recommend_model() returning empty string on Apple Silicon
when MLX is the recommended engine. Also adds gguf_file and
mlx_repo download metadata, and estimated_download_gb helper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:04:25 -07:00
Jon Saad-FalconandClaude Opus 4.6 b2a88a5514 docs: add implementation plan for init onboarding and privacy scanner
6 tasks with TDD steps, covering model catalog fixes, multi-engine
pull, interactive download in init, privacy scanner, and CLI registration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:56:31 -07:00
Jon Saad-FalconandClaude Opus 4.6 d97e1dd266 revert: remove _fix_tool_call_arguments from OpenAI-compatible engines
Reverts the tool_call arguments string→dict conversion added in PR #69.

The OpenAI API spec requires tool_call arguments as JSON strings, not
dicts. vLLM with --enable-auto-tool-choice validates this and returns
400 errors when arguments are dicts. The original 400 errors were caused
by missing --enable-auto-tool-choice/--tool-call-parser flags on the
vLLM server, not by the arguments format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:56:21 +00:00
Jon Saad-Falcon 3fee7e682b Merge remote-tracking branch 'origin/main' into fix/token-counting-no-kv-cache 2026-03-24 17:51:50 -07:00
Jon Saad-Falcon 45170f72a7 Merge pull request #110 from open-jarvis/feat/claude-github-actions
Add Claude GitHub Actions for PR review and issue fixing
2026-03-24 17:45:09 -07:00
Jon Saad-FalconandClaude Opus 4.6 b502441293 docs: add design spec for init model onboarding and privacy scanner
Covers two features based on user feedback:
1. Interactive model download in `jarvis init` with MLX catalog fix
2. New `jarvis scan` privacy environment audit command

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:42:32 -07:00
Jon Saad-FalconandClaude Opus 4.6 3c2d9462ab fix: use correct claude-code-action input params and add id-token permission
- Replace invalid `model` input with default (action auto-selects)
- Replace `review_instructions`/`direct_prompt` with `prompt` (valid input)
- Add `id-token: write` permission required for OIDC token fetching

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:36:33 -07:00
Jon Saad-FalconandClaude Opus 4.6 9dbd172950 fix: mock config in test_init_defaults to test class-level defaults
The test_init_defaults test expected OperativeAgent class defaults
(temperature=0.3) but didn't mock load_config(), so when config loaded
successfully it returned the global default (0.7) instead.

Fix: mock load_config to raise, so the test properly validates the
class-level _default_temperature/max_tokens/max_turns fallback path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:30:46 -07:00
Jon Saad-Falcon 4ccec01784 ci: add Claude issue fixer workflow 2026-03-24 17:04:46 -07:00
Jon Saad-Falcon 620788bee1 ci: add Claude PR review workflow 2026-03-24 17:04:46 -07:00
Jon Saad-Falcon 4cdc4639bc docs: add REVIEW.md with PR review instructions for Claude 2026-03-24 17:04:46 -07:00
Jon Saad-Falcon 3777086d3a fix: count full prompt tokens without KV-cache assumption
Ollama's prompt_eval_count may exclude KV-cached tokens (system
prompt, prior conversation turns), under-counting the prompt size
used for cost/FLOPs/energy calculations and leaderboard submissions.

Engine changes:
- Add estimate_prompt_tokens() helper in engine/_base.py that
  computes cache-agnostic token count from message content
- Ollama + OpenAI-compat engines now use max(reported, estimated)
  to ensure system prompt and full context are always counted

Savings/leaderboard changes:
- Add TOKEN_COUNTING_VERSION=2 to savings.py so frontends can
  tag Supabase submissions (old=v1, corrected=v2)
- Desktop + web frontends forward version in leaderboard submissions
- Add POST /v1/telemetry/reset endpoint to clear stale telemetry

Active users auto-correct via upsert on next session; no manual
Supabase migration required.
2026-03-24 16:55:26 -07:00
Jon Saad-Falcon 033ced67e6 Merge pull request #109 from open-jarvis/fix/agent-config-defaults-103
fix: agent constructor defaults resolve from config.toml
2026-03-24 16:54:44 -07:00
Jon Saad-FalconandClaude Opus 4.6 9624f53c18 style: apply ruff format to changed files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:43:51 -07:00
Jon Saad-FalconandClaude Opus 4.6 1fe75b2807 refactor: move agent defaults to class attrs, accept Optional params (issue #103)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:33:33 -07:00
Jon Saad-FalconandClaude Opus 4.6 59dad5eddf fix: address installation failures and SDK bugs (#100)
Bug 1 — Document Rust toolchain requirement:
- Add `maturin develop` step to README, installation docs (all 3
  sections), and quickstart.sh
- Note PYO3_USE_ABI3_FORWARD_COMPATIBILITY for Python 3.14+

Bug 2 — Fix namespace package conflict:
- Move force-include targets from openjarvis/ to _node_modules/ in
  pyproject.toml to prevent editable-install namespace shadowing
- Add fallback path resolution in claude_code.py and
  whatsapp_baileys.py for wheel installs

Bug 3 — Fix trace debugger "No traces yet":
- Enable traces by default (TracesConfig.enabled = True)
- Fix api_routes.py: use store.list_traces() not .recent(),
  dataclasses.asdict() not .to_dict(), get store from app.state
- Wire TraceStore into app.state in app.py

Bug 4 — Fix thinking models returning empty responses:
- Remove hardcoded enable_thinking: False from _openai_compat.py
  that suppressed Qwen3/DeepSeek-R1 output on OpenAI-compatible
  engines (vLLM, SGLang, llama.cpp)

Closes #100

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:29:26 -07:00
Jon Saad-FalconandClaude Opus 4.6 cc4621a840 fix: resolve agent defaults from config with class-level fallbacks (issue #103)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:15:59 -07:00
Jon Saad-FalconandClaude Opus 4.6 7b5f078b4b test: add failing tests for agent config-based default resolution (issue #103)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:14:31 -07:00
Jon Saad-FalconandClaude Opus 4.6 0481f32129 perf: cache load_config() with lru_cache to avoid repeated hardware detection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:12:47 -07:00
Jon Saad-FalconandClaude Opus 4.6 406c115a52 fix: register all tool modules so they appear in web UI (#99) (#107)
Eight tool modules (file_write, apply_patch, git_tool, db_query,
pdf_tool, image_tool, audio_tool, knowledge_tools) were missing from
openjarvis/tools/__init__.py. Their @ToolRegistry.register() decorators
never fired, so the /v1/tools endpoint never returned them and the
web UI agent wizard showed an incomplete tool list.

Add the missing imports and a regression test that checks all expected
tool names are in the registry after package import.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:12:36 -07:00
Jon Saad-Falcon 228eff7c35 Merge pull request #106 from mricharz/feat/managed-agent-streaming
feat: add SSE streaming support for managed agent messages
2026-03-24 15:57:12 -07:00
Jon Saad-FalconandClaude Opus 4.6 601942c9ce style: wrap long logger.warning line to fix E501 lint error
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:52:14 -07:00
Jana BergantandClaude Opus 4.6 555e982f51 feat: query complexity analyzer for local/cloud routing (#102)
* feat: add query complexity analyzer with CLI and UI integration

Classify incoming queries by difficulty (trivial→very_complex) to
suggest appropriate token budgets for local vs. cloud routing.

- Add score_complexity() with weighted signals (length, code, math,
  reasoning, multi-step, creative) and token tier mapping
- Wire into `jarvis ask`: auto-suggest max_tokens when not set by user,
  show complexity in --profile output, log at DEBUG level
- Add complexity metadata to /v1/chat/completions API response
- Display complexity tier and score in frontend XRayFooter
- Extend RoutingContext with complexity_score, suggested_max_tokens,
  has_reasoning fields
- Update HeuristicRouter to route on complexity_score instead of
  raw query_length
- Remove duplicated regex patterns from router.py (use complexity
  module as single source of truth)
- Add 30 unit tests for complexity module
- Fix existing router tests for new complexity-based routing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pass temperature and max_tokens from UI settings to backend

The settings page stores temperature and max_tokens in the frontend
store, but these values were never included in the chat API request.
The backend Pydantic model defaults max_tokens to 1024 when the field
is absent, which is too low for thinking models like qwen3.5 — they
consume all tokens on reasoning and return empty content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pass UI settings to backend and auto-bump max_tokens from complexity

- Pass temperature and max_tokens from frontend Settings store to the
  backend API (cherry-picked from fix/ui-max-tokens-passthrough)
- Server-side: bump max_tokens when the complexity analyzer suggests a
  higher budget (e.g. for thinking models on complex queries), never
  reduce below the client-requested value
- Fixes empty responses with thinking models (e.g. qwen3.5) that
  consumed all tokens on internal reasoning

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: raise token budget tiers to prevent empty responses on thinking models

Double all tier budgets (trivial: 512→1024, simple: 1024→2048, etc.)
so that thinking models like qwen3.5 have enough headroom for internal
chain-of-thought plus visible output, even on simple queries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: CI lint and test failures

- Break long lines in routes.py to satisfy 88-char limit (E501)
- Add intelligence.max_tokens and temperature to mocked config in
  test_ask_router.py so complexity analyzer can compare against int
  instead of MagicMock

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: line too long in test_complexity.py (E501)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:27:23 -07:00
Jon Saad-Falcon 52d72fc87f Merge pull request #105 from jbergant/fix/wire-trace-store-to-executor
fix: wire TraceStore into AgentExecutor when traces.enabled is true
2026-03-24 15:23:41 -07:00
manuel.richarz b7ad2d2c12 feat: add SSE streaming support for managed agent messages
Add `stream: bool` parameter to `POST /v1/managed-agents/{id}/messages`.
When `stream=true`, the agent processes the message synchronously and
returns an SSE stream (OpenAI-compatible format) with token-by-token
response, tool result events, and usage metadata.

This enables real-time voice assistants and chat UIs to receive agent
responses as they are generated, rather than polling for completion.

- Extend SendMessageRequest with `stream` field (default: false)
- Add _stream_managed_agent() helper using asyncio.to_thread()
- Build AgentContext from conversation history for multi-turn support
- Emit tool_results as named SSE events
- Persist agent response in DB after streaming completes
- Add 6 new tests covering streaming behavior
- Update agents.md documentation with streaming examples
2026-03-24 14:56:58 +01:00
Jana BergantandClaude Opus 4.6 f1aaed0895 fix: wire TraceStore into AgentExecutor when traces.enabled is true
SystemBuilder.build() constructed AgentExecutor without a trace_store,
so traces were never recorded even when config.traces.enabled = true.
The traces.enabled flag was parsed but never read during executor
construction.

Now reads config.traces.enabled and creates a TraceStore instance
that is passed to AgentExecutor, enabling trace recording for
jarvis agents ask/run and the agent scheduler daemon.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:36:19 +01:00
16f601c7d7 feat: NVIDIA GPU Docker support with compose override (#93)
* feat: nvidia gpu config for docker

* refactor: split NVIDIA GPU support into compose override

Address review feedback:
- Revert --engine ollama from base Dockerfile CMD (breaks non-Ollama users)
- Keep bookworm pin and OLLAMA_HOST fix in base config
- Move GPU-specific config (/proc, /sys mounts, deploy.resources.reservations)
  into new docker-compose.gpu.nvidia.yml override, matching the existing ROCm
  pattern (docker-compose.gpu.rocm.yml)
- Restore Ollama port to standard 11434
- Remove commented-out GPU blocks from base docker-compose.yml
- Add Ollama healthcheck with depends_on condition to base compose
- Remove run.sh from repo root
- Rewrite README Docker section to document CPU, NVIDIA, and ROCm patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 01:53:13 -07:00
04c28ef7ed feat: add CLI commands: config, registry, tool (#98)
* feat: add CLI commands: config, registry, tool

* fix: address review feedback on CLI config/registry/tool commands

- Replace non-existent `create_config_template` with `generate_default_toml`
  to fix ImportError when config file is missing
- Deduplicate registry maps into shared `_load_registry_map()` helper
- Remove dead `_get_registry_class()` function and its tests
- Route JSON output to stdout for pipeability (`jarvis config show json | jq`)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 01:38:23 -07:00
Jon Saad-FalconandClaude Opus 4.6 8468f08c41 fix: correct O(N²) energy/FLOPs savings calculation (#95) (#97)
The energy_wh_saved and flops_saved values were orders of magnitude too
high because a scaling factor that grows linearly with N was applied to
the energy calculation, making it scale as O(N³) instead of O(N²).

Replace the buggy scale-factor approach with a direct FLOP-to-energy
conversion using each provider's per-token constants. Add regression
tests to prevent recurrence.

Closes #95

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 18:52:18 -07:00
Prathap P c2756964a7 fix(channels): wire channel→agent handler and fix Telegram send pipeline (#94)
* fix(channels): wire channel→agent handler and fix Telegram send pipeline

* format code

* add supported tests
2026-03-20 18:35:18 -07:00
OctopusandOctopus 62eaa70736 feat: add MiniMax as cloud inference provider with M2.7 default (#85)
* feat: add MiniMax as cloud inference provider

Add MiniMax M2.5 and M2.5-highspeed as a 5th cloud provider alongside
OpenAI, Anthropic, Google, and OpenRouter. Uses the OpenAI-compatible
API at api.minimax.io/v1 via the existing openai SDK dependency.

Changes:
- Add MiniMax client init, generate, and streaming in CloudEngine
- Add MiniMax models to model catalog with correct pricing
- Add MINIMAX_API_KEY environment variable support
- Add temperature clamping (0.01-1.0) per MiniMax API constraints
- Add 19 unit tests and 3 integration tests
- Update docs and README with MiniMax provider info

* feat: upgrade MiniMax default model to M2.7

- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to model list
- Set MiniMax-M2.7 as default model (first in list)
- Keep all previous models (M2.5, M2.5-highspeed) as alternatives
- Update pricing table with M2.7 entries
- Update model catalog with M2.7 specs
- Update docs to list all available MiniMax models
- Update unit tests (23 passing) and integration tests (3 passing)

---------

Co-authored-by: Octopus <octo-patch@users.noreply.github.com>
2026-03-20 04:16:33 -07:00
Jon Saad-Falcon cbdb720e06 Merge pull request #83 from TeemuSailynoja/fix/duckduckgo-fallback
feat: add DuckDuckGo fallback to web_search tool
2026-03-20 04:02:18 -07:00
Teemu Säilynoja 806067a002 fix: simplify web search error handling and improve test mocks
- Catch any Exception from Tavily (not just specific error types)
- Fall back to DuckDuckGo for any error, making the tool more robust
- Fix test mocks to use builtins.__import__ for proper local import mocking
- Simplify test_execute_tavily_error to test generic exception handling
2026-03-17 10:34:45 +02:00
Teemu Säilynoja b10b19b1eb feat: add DuckDuckGo fallback to web_search tool
When Tavily API is unavailable (no API key, import error, or API error),
the web_search tool now falls back to DuckDuckGo search instead of
failing. This ensures the tool always works for users without a
Tavily API key.

- Add DuckDuckGo search as fallback using ddgs package
- Catch specific Tavily exceptions (MissingAPIKeyError, InvalidAPIKeyError,
  ForbiddenError, UsageLimitExceededError, TimeoutError, BadRequestError)
- Add logger.debug calls to log when falling back to DuckDuckGo
- Use ddgs instead of deprecated duckduckgo-search package name
- Add test for DuckDuckGo fallback result formatting
- Simplify test mocking to use consistent monkeypatch patterns

Closes #81
2026-03-17 10:34:45 +02:00
Gabriel Bo 256ff54476 long single line dict literal error fixes 2026-03-16 22:07:26 -07:00
Gabriel Bo 3ca1b63962 cleaning up scripts 2026-03-16 21:52:17 -07:00
Gabriel Bo 9c6bdc09cb token counting fixes 2026-03-16 21:51:05 -07:00
Jon Saad-Falcon 11c37bf54d Merge pull request #90 from open-jarvis/fix/roadmap-reorder-and-nav-cleanup
fix: reorder roadmap sections, remove contributing from nav
2026-03-16 21:43:06 -07:00
Jon Saad-FalconandClaude Opus 4.6 b8bbbdbf55 fix: move focus areas and get-involved to top of roadmap, remove contributing from nav
- Move "Current Focus Areas" and "How to Get Involved" to top of roadmap
- Remove Version History section entirely
- Fix "take" workflow: clarify that claiming happens on GitHub issues,
  with link to create new issues if none exists
- Remove Contributing Guide from MkDocs nav (linked from roadmap instead)
- Link to CONTRIBUTING.md on GitHub from "How to Get Involved" section

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 04:17:21 +00:00
Jon Saad-Falcon 593132f6c1 fix: sort imports in channels conftest 2026-03-16 21:06:13 -07:00
Jon Saad-Falcon cf362897cd feat: add Gmail/Twitter channel adapters, curated templates, channel contract tests, and QA runbook
- Add GmailChannel (OAuth2 + polling) and TwitterChannel (tweepy v2 API)
- Update research_monitor, inbox_triager, code_reviewer templates with curated tool sets
- Add channel_send/channel_list tools to all templates
- Add parametrized contract tests for all 28 channel adapters (195 tests)
- Add Gmail mocked tests (17 tests) and Twitter mocked tests (9 tests)
- Add agent-channel E2E tests with WebChatChannel
- Add live_channel pytest marker
- Add manual QA runbook (docs/testing/agent-qa-runbook.md)
2026-03-16 21:05:37 -07:00
Jon Saad-Falcon f504475960 Merge pull request #88 from open-jarvis/fix/roadmap-and-docs-cleanup
fix: rewrite roadmap around 5 workstreams, fix broken links
2026-03-16 21:05:19 -07:00
Jon Saad-FalconandClaude Opus 4.6 f52ed33e43 fix: rewrite roadmap around 5 workstreams, fix broken links and cleanup
- Reorganize roadmap around the 5 workstreams: Continuous Operators,
  Mobile & Messaging, Secure Cloud Collaboration, Tutorials, Hardware
- Add concrete "Where you can help" tables with maturity tags and
  good-first-issue markers under each workstream
- Change "GRPO training from trace data" to "Post-training data"
- Fix 404 link: CONTRIBUTING.md link now points to GitHub, not docs site
- Remove !!! tip admonition from docs/development/contributing.md
- Preserve version history in collapsible section

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 03:58:57 +00:00
Jon Saad-Falcon 941016f255 feat: add 12 agent lifecycle scenario tests with real SQLite state 2026-03-16 20:46:18 -07:00
Jon Saad-Falcon ebe35582e3 Merge pull request #87 from open-jarvis/fix/mkdocs-roadmap-nav
fix: make Roadmap a nav section with Roadmap + Contributing Guide
2026-03-16 20:44:12 -07:00
Jon Saad-Falcon a3f3f047da feat: add FakeEngine and scenario_harness fixture for agent lifecycle testing 2026-03-16 20:41:24 -07:00
Jon Saad-FalconandClaude Opus 4.6 856d27621d fix: rename Development tab to Roadmap with Roadmap + Contributing Guide sub-pages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 03:38:13 +00:00
Jon Saad-Falcon f85ee81d53 fix: prevent race on rapid Run Now clicks, auto-recover error-state agents, fix immediate messages
- Acquire tick BEFORE spawning thread to prevent race condition on concurrent Run Now clicks
- Auto-recover agents in error/needs_attention state when Run Now is clicked
- Auto-recover error-state agents when receiving immediate messages
- End tick on system build failure to avoid stuck running state
- Add test verifying concurrent start_tick raises ValueError
2026-03-16 20:38:10 -07:00
Jon Saad-Falcon 04205d8f87 fix: normalize TOML arrays to comma-separated strings for str-typed config fields 2026-03-16 20:36:15 -07:00
Jon Saad-Falcon 6d8e3da6e6 Merge pull request #86 from open-jarvis/docs/contributor-community-infra
docs: contributor docs, community infra, and roadmap rewrite
2026-03-16 20:30:32 -07:00
Jon Saad-FalconandClaude Opus 4.6 29beeed32d docs: add contributor docs, community infrastructure, and roadmap rewrite
- Add root CONTRIBUTING.md with incentives (paper acknowledgment, Mac Mini
  giveaway), contribution tiers, PR process, and maintainership path
- Add CODE_OF_CONDUCT.md (Contributor Covenant v2.1)
- Add .pre-commit-config.yaml with ruff lint + format hooks
- Add GitHub issue templates (bug report, feature request, new eval dataset)
- Add PR template with test/lint/format checklist
- Rewrite docs roadmap with GitHub Projects structure, current focus areas,
  and collapsible version history
- Remove Development section from MkDocs nav; replace with top-level Roadmap tab
- Delete changelog, extending docs (consolidated into CONTRIBUTING.md)
- Delete root ROADMAP.md (content now lives in docs site)
- Add pre-commit to dev extras in pyproject.toml
- Add Roadmap link to README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 03:24:26 +00:00
Jon Saad-Falcon 8d22ae953d Merge pull request #84 from open-jarvis/feature/personal-ai-parity
feat: personal AI parity — memory files, prompt caching, gateway daemon, and more
2026-03-16 17:46:43 -07:00
Tarun SureshandClaude Opus 4.6 181d9ac0eb fix: resolve ruff I001 import sorting and E501 line length in tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:13:49 +00:00
Jon Saad-Falcon f228b0f24f Merge pull request #79 from 70Midnite07/fix/memory-retrieval-and-agent-manager
Reviewed and tested: zero new test failures vs main, Rust tests all pass (7/7 including 2 new). Added debug logging for memory injection errors and cleaned up .gitignore.
2026-03-16 16:58:08 -07:00
Jon Saad-FalconandClaude Opus 4.6 c36730320c chore: remove contributor-specific gitignore entry and log memory injection errors
- Remove openjarvis-bugfix-spec-v2.md from .gitignore (not a general pattern)
- Replace bare `except: pass` with debug logging in routes.py memory injection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 16:57:55 -07:00
Gabriel Bo 14733433b0 adding pagination and limiting to 50 per sign up on leaderboard 2026-03-16 15:58:39 -07:00
Tarun SureshandClaude Opus 4.6 130dd99387 feat: create default SOUL.md, MEMORY.md, USER.md on jarvis init
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:44:03 +00:00
Tarun SureshandClaude Opus 4.6 981f665913 feat: add Anthropic prompt cache breakpoint annotation
Add _annotate_anthropic_cache helper that annotates system messages
with cache_control for Anthropic prompt caching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:42:54 +00:00
Tarun SureshandClaude Opus 4.6 c6cca77c12 feat: add jarvis gateway start/stop/status/logs CLI commands
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:41:43 +00:00
Tarun SureshandClaude Opus 4.6 5df8688627 feat: add GatewayDaemon, SessionExpiryHook, and service file generation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:40:07 +00:00
Tarun SureshandClaude Opus 4.6 1359c223ec feat: wire SystemPromptBuilder into BaseAgent._build_messages()
Add optional prompt_builder parameter to BaseAgent. When provided,
_build_messages() uses builder.build() output instead of raw system_prompt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:37:41 +00:00
Tarun SureshandClaude Opus 4.6 0d5be075f6 feat: add AgentExecutor.run_ephemeral() for one-shot agent turns
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:02:28 +00:00
Tarun SureshandClaude Opus 4.6 6371e7521d feat: add warn-before-block escalation to LoopGuard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:01:13 +00:00
Tarun SureshandClaude Opus 4.6 4cc16972ec feat: add credential stripping, tool output wrapping, and severity policy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:57:47 +00:00
Tarun SureshandClaude Opus 4.6 c1e01b1e8b feat: add SkillManageTool for agent-authored procedural memory
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:56:11 +00:00
Tarun SureshandClaude Opus 4.6 153435edb6 feat: add pluggable context compaction strategies via CompressionRegistry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:53:47 +00:00
Jon Saad-Falcon 59b5295b20 Update ROADMAP.md 2026-03-16 10:51:36 -07:00
Tarun SureshandClaude Opus 4.6 978749e9f8 feat: add SystemPromptBuilder with frozen prefix and char limits
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:50:36 +00:00
Tarun SureshandClaude Opus 4.6 7abd65da26 feat: add MemoryManageTool and UserProfileManageTool for persistent personalization
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:45:55 +00:00
Micah 47a37fcca3 fix: wire memory backend into server and agent executor
The web UI chat endpoint and managed agent executor both skipped
  memory context injection entirely:

  - serve.py never created a memory backend
  - app.py received config but never stored it on app.state
  - executor.py never queried the FTS5 document store

  Now all three entry points (CLI, server, agents) inject retrieved
  context from the indexed knowledge base.
2026-03-16 00:42:32 -07:00
Micah 3ab601c717 Fix additional memory retrieval issues beyond PR #74
- Add porter stemming to FTS5 tokenizer (tokenize='porter unicode61')
  so plurals like 'medications' match 'Medication'
- Strip punctuation from FTS5 queries so question marks don't break MATCH
- Expand tilde (~) in db_path before opening SQLite connection
- Remove 'id' from FTS5 columns (UUIDs shouldn't be full-text indexed)
- Add memory context injection to server chat_completions route
  (routes.py had no memory integration - UI chat was never grounded)
- Fix agent name-to-ID resolution in agent_cmd.py (foreign key error)
- Align Python SQLiteMemory schema with Rust backend
2026-03-15 22:58:38 -07:00
Jon Saad-Falcon 4f87d3d07a Merge pull request #76 from xbsheng/fix/ssrf-check
fix: add SSRF check in WebSearchTool to prevent unsafe URL access
2026-03-15 21:25:23 -07:00
Jon Saad-Falcon 2a13505a1c fix: sort imports and mock SSRF check in web_search tests
Sort the check_ssrf import alphabetically to fix ruff I001. Mock the
SSRF check in URL-fetching tests since it requires the Rust backend
which isn't available in CI. Add a dedicated test verifying SSRF
rejection.
2026-03-15 21:18:46 -07:00
Jon Saad-Falcon 7be35021b7 Merge pull request #77 from open-jarvis/docs/roadmap-and-claude-md
docs: add project roadmap
2026-03-15 21:04:57 -07:00
Jon Saad-FalconandClaude Opus 4.6 1e9e8716cc docs: add project roadmap with five development workstreams
Public-facing roadmap organized into five independent workstreams:
1. Continuous Operators & Agents — hardening long-horizon autonomy
2. Mobile & Messaging Clients — iMessage, WhatsApp, Slack, SMS
3. Secure Cloud Collaboration — Minions-style hybrid inference, TEE
4. Tutorials & Documentation — filling gaps in continuous agents, tools, LM eval
5. Hardware Breadth — Intel Arc, Jetson Orin, Qualcomm, AMD Ryzen AI, RPi

Each item tagged with maturity level (Ready/Design Needed/Research-Stage)
and time horizon (near/mid/long-term) to guide contributors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:02:44 -07:00
xbsheng 9c3803828b fix: add SSRF check in WebSearchTool to prevent unsafe URL access 2026-03-16 10:41:02 +08:00
Jon Saad-Falcon 56583e819b Merge pull request #75 from open-jarvis/fix/desktop-macos-dmg-bundling
fix: pin macOS desktop build to macos-14 to fix DMG bundling
2026-03-15 18:38:12 -07:00
Jon Saad-FalconandClaude Opus 4.6 54a99692ba fix: pin macOS desktop build to macos-14 to fix DMG bundling
macos-latest now resolves to macOS 15 (Sequoia), which removed the
SetFile command from Xcode Command Line Tools. Tauri's bundle_dmg.sh
relies on SetFile to set custom icon attributes on the DMG volume,
causing the Desktop Build & Release workflow to fail on the macOS
runner.

Pin to macos-14 (Apple Silicon M1, Sonoma) which still includes
SetFile and supports universal binary builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:19:40 -07:00
Jon Saad-FalconandClaude Opus 4.6 cc1e14f1c3 fix: engine discovery fallback, FTS5 case/scoring, init engine picker, Windows support (#74)
- Engine discovery (#73): get_engine() now falls back to any healthy
  engine when the explicitly-requested key fails, instead of returning
  None. Fixes LM Studio (and other non-default engines) not being found
  when deploying agents.

- FTS5 case sensitivity & scoring (#67): Add explicit unicode61 tokenizer
  to the FTS5 virtual table for case-insensitive search. Replace ambiguous
  `rank` column with explicit `bm25()` call with column weights (id=0,
  content=1, source=0.5) to produce correct positive scores. Includes
  auto-migration for existing databases.

- Interactive engine selection (#72): `jarvis init` now detects running
  engines and presents an interactive picker. Also accepts `--engine`
  flag to skip the prompt. Engine choice flows through to config
  generation.

- Windows desktop support (#68): Add RAM detection via wmic, Windows
  binary paths (LOCALAPPDATA, ProgramFiles, cargo), port cleanup via
  netstat+taskkill, and USERPROFILE fallback for HOME env var.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 16:23:39 -07:00
Jon Saad-FalconandClaude Opus 4.6 afa50b0ff7 fix: parse tool_call arguments as dicts for OpenAI-compatible engines (#69)
messages_to_dicts() serializes tool_call arguments as JSON strings,
but OpenAI-compatible servers (vLLM, SGLang, llama.cpp, etc.) expect
them as JSON objects. This caused 400 Bad Request errors on every
multi-turn tool-calling conversation, breaking GAIA, DeepPlanning,
and LifelongAgent benchmarks for all local models.

The fix adds _fix_tool_call_arguments() to _OpenAICompatibleEngine
which json.loads() any string-typed arguments back to dicts before
sending. Applied to both generate() and stream() methods. This is
the same fix as commit 45c0e44 (Ollama), now applied to all
OpenAI-compatible engines.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:12:20 -07:00
Jon Saad-Falcon 51a2b4cceb feat: operatives tab improvements — 9 UX/functionality fixes (#63) 2026-03-14 21:31:38 -07:00
Jon Saad-Falcon 61d523a270 Merge pull request #65 from PatrickFFung/main
Running `uv run jarvis start` failed
2026-03-14 21:25:05 -07:00
Jon Saad-FalconandClaude Opus 4.6 5dc64872fd chore: remove superpowers plan/spec artifacts from repo
These are working files that should not be tracked per CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 04:23:38 +00:00
Jon Saad-FalconandClaude Opus 4.6 9083e8c2a8 fix: wrap Info icon in span for title tooltip (TS2322)
lucide-react SVG components don't accept `title` prop directly.
Wrap in a <span> element instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 04:15:14 +00:00
Jon Saad-FalconandClaude Opus 4.6 bc2742d97b fix(agents): crash fix, UX polish for Agents tab
1. Fix "Run Now" crash: handle list-type tools config in
   SystemBuilder._resolve_tools (was calling .split on a list)
2. Recover button: add toast feedback on success/failure
3. Schedule help: add Info icon tooltip explaining Manual/Cron/Interval
4. Interval picker: replace raw text input with h/m/s numeric spinners
5. Budget: add helper text clarifying cloud-only spend
6. Learning technique: add memory extraction strategy dropdown
7. Error logs: show error messages in trace entries, catch Run Now errors
8. handleRun: catch initial API errors instead of silently swallowing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:54:06 +00:00
Patrick Fung bb3053efca Add __main__.py entry point to enable CLI execution via python -m openjarvis.cli.
Previously, running `uv run jarvis start` would fail to start the process with error:
"No module named openjarvis.cli.__main__; 'openjarvis.cli' is a package and cannot be
directly executed" because the CLI package lacked an entry point.

Signed-off-by: Patrick Fung <patrick_fung@foxmail.com>
2026-03-15 10:31:13 +08:00
Jon Saad-Falcon 5f0a151be1 Merge pull request #62 from open-jarvis/fix/ui-dashboard-polish-2
fix(ui): center input, 2x3 energy grid, unify token counts
2026-03-14 18:05:07 -07:00
Jon Saad-FalconandClaude Opus 4.6 abd36af6b5 fix(ui): center input text, 2x3 energy grid, remove duplicate savings, unify token counts
- Center "Message OpenJarvis..." placeholder vertically in input area
- Restructure Energy Monitoring to 2x3 grid: promote thermal status and tokens processed to stat cards
- Add truncate to stat card values to prevent overflow
- Remove "Server-reported savings" section from Cost Comparison (redundant)
- Move asterisk disclaimer directly below divider
- Unify token counts: Energy Monitoring now uses savings store (same source as Cost Comparison)
- Round values to 1 decimal (except energy/token at 3 decimals and integer fields)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 00:59:39 +00:00
Jon Saad-Falcon 95281f9d0b Merge pull request #61 from open-jarvis/fix/ui-aesthetic-polish
fix(ui): polish chat and dashboard aesthetics
2026-03-14 17:23:48 -07:00
Jon Saad-FalconandClaude Opus 4.6 2cf67ac551 fix(ui): polish chat and dashboard aesthetics
- Change initial stream phase from "Connecting..." to "Generating..."
- Left-align "Loading model..." text in sidebar model badge
- Fix token count not updating by preferring client-side savings data
- Rename "Tokens" to "Output Tokens" in system panel
- Show input/output tokens in XRay footer with dash separators
- Fix EnergyDashboard to use shared API helpers (getBase) instead of raw env var

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 00:18:45 +00:00
Jon Saad-Falcon 4557fe2930 feat(security): wire guardrails into all entry points (#60) 2026-03-14 16:33:50 -07:00
Jon Saad-Falcon f458e19301 feat: instruction + model in agent wizard, error toasts, auto-run interval agents 2026-03-14 16:28:15 -07:00
Jon Saad-Falcon f54084e954 feat: wire AgentScheduler into server, register agents on create 2026-03-14 16:28:15 -07:00
Jon Saad-Falcon ba8b994ba8 feat: CLI version check against GitHub releases (daily, cached) 2026-03-14 16:28:15 -07:00
Jon Saad-Falcon 78ffebd5a3 feat: desktop auto-update — download in background, toast to restart 2026-03-14 16:28:15 -07:00
Jon Saad-Falcon cb3d571efe fix: write error to summary_memory on tick failure, inject standing instruction 2026-03-14 16:28:15 -07:00
Jon Saad-Falcon cbd59b1258 docs: add agent UX, auto-update, CLI version check spec and plan 2026-03-14 16:28:15 -07:00
Jon Saad-Falcon 4d1929779d docs: add agent UX, auto-update, and CLI version check spec 2026-03-14 16:28:15 -07:00
Jon Saad-FalconandClaude Opus 4.6 7df9dedfc9 docs: refresh landing page — clearer primitives, research section, citation (#59)
- Rewrite Five Primitives with plain-English descriptions
- Add 10+ engine backends with hyperlinks (Ollama, vLLM, SGLang, etc.)
- Add Automated Workflows and Energy & Cost Tracking feature cards
- Add Research section linking to Intelligence Per Watt and Stanford
- Add Citation section with BibTeX
- Fix hero-tagline max-width to span full title width

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:02:17 -07:00
Jon Saad-Falcon 3cf4bbae30 feat: display thinking tokens in X-Ray footer and stream usage data
- Ollama engine: capture eval_count and prompt_eval_count from the
  streaming final chunk (includes thinking/reasoning tokens).
- Routes: include usage data in the SSE finish chunk so the frontend
  gets accurate token counts even in streaming mode.
- X-Ray footer: show estimated thinking tokens when completion_tokens
  significantly exceeds visible output (e.g. "1695 generated · 11 prompt
  · ~1680 thinking").
2026-03-14 14:55:42 -07:00
Vladislav GoncharovandClaude Sonnet 4.6 34000aea01 fix(cli): support configured tool defaults and interactive confirmations (#56)
* feat: add interactive agent confirmation mode and fix tool loading

- Add `interactive` and `confirm_callback` params to ToolUsingAgent and
  NativeReActAgent so CLI sessions can prompt user before tool execution
- Fix tool resolution in `ask` and `chat` commands to fall back to
  config.tools.enabled when no --tools flag is provided
- Register shell_exec tool in tools/__init__.py auto-discovery
- Add dspy and gepa as optional learning extras (learning-dspy, learning-gepa)
- Fix openjarvis-rust lock version (1.0.0 → 0.1.0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(cli): support configured tool defaults and confirmations

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 14:42:58 -07:00
Jon Saad-Falcon 1a148debe2 feat: seamless cloud API key flow — save once, works automatically
- Tauri commands: save_cloud_key writes keys to ~/.openjarvis/cloud-keys.env
  (chmod 600), get_cloud_key_status reports which providers are configured.
- Server spawn: reads cloud-keys.env and injects keys as env vars into
  the jarvis serve process, so CloudEngine picks them up automatically.
- Frontend: Cloud Models tab saves keys through Tauri invoke (desktop)
  in addition to localStorage (web), so keys persist across restarts.
- Flow: user enters key in Cloud tab → saved to disk → next server
  start picks it up → cloud models appear in the model list.
2026-03-14 14:32:46 -07:00
Gabriel Bo 93344a787f adding asterisk to focus on local models 2026-03-14 14:32:29 -07:00
Jon Saad-Falcon 7a1584aac3 feat: Cloud Models tab, prevent duplicate empty chats, rename to Installed Models
1. Cloud Models tab: third tab in Cmd+K palette with API key management
   for OpenAI, Anthropic, Google, and OpenRouter. Keys stored in
   localStorage (never sent externally). Top-3 models per provider
   shown when key is configured. Connected status indicator.

2. New chat button: skip creating a new chat if the current one is
   empty (no messages). Prevents stacking empty conversations.

3. Tab label: "Installed" renamed to "Installed Models (N)" for clarity.
2026-03-14 14:18:56 -07:00
Jon Saad-Falcon 7b0fcfa607 fix: rounded icons, setup text overflow, new chat on model switch, token counts
1. Desktop icon: bake rounded corners into the PNG/ICNS/ICO so the icon
   looks consistent on Desktop and in Applications.
2. SetupScreen: truncate long status text with ellipsis, word-break
   error messages so they don't overflow the block.
3. Model switching: create a new chat session when changing models,
   preventing stale context errors with the new model.
4. X-Ray footer: show input/output token counts in the collapsed
   summary (e.g. "42 in · 128 out") alongside engine, model, latency.
2026-03-14 14:05:49 -07:00
Jon Saad-FalconandClaude Opus 4.6 e240fde74a feat(ui): Raycast-inspired visual refresh with System Pulse and X-Ray footers (#57)
* feat(ui): update design tokens — system-ui font, warmer palette, accent triad

- Remove Merriweather serif font, switch to system-ui font stack
- Warmer dark mode neutrals (#161618 bg instead of #09090b)
- Add amber/purple accent tokens for signature color triad
- Update borders to rgba for softer feel
- Tighter line-height (1.5) and letter-spacing (-0.01em)
- Update PWA theme_color to match new palette

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): frosted glass sidebar with backdrop-blur

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): initialize shadcn/ui with button, dialog, input, select, tooltip, sonner

Set up shadcn/ui (v4) for Tailwind CSS v4 with CSS-first config. Added
path aliases (@/) to tsconfig.json and vite.config.ts. Installed six
components (button, dialog, input, select, tooltip, sonner) and wired
Sonner's <Toaster> into App.tsx for toast notifications. Removed the
next-themes dependency since this is a Vite/React project, not Next.js.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): disable create agent when unavailable, replace errors with toasts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): add System Pulse bar and health check banner

- 3px gradient bar at top reflecting system state (idle/inferencing/agent)
- Health check banner when backend is unreachable
- Pulse colors: blue (inference), purple (agents), dim blue (idle)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): add X-Ray message footers with collapsible telemetry

- MessageTelemetry type on ChatMessage (engine, model, speed, TTFT, timing)
- Capture timing data during SSE streaming in InputArea
- XRayFooter component: collapsed one-liner + expandable trace grid
- Integrated into MessageBubble, replacing old token count display

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ui): resolve shadcn/ui CSS token conflicts with design system

- Namespace shadcn @theme inline variables (--color-cn-*) to avoid
  overriding project's --color-border, --color-accent, --radius-* tokens
- Remove @apply border-border, bg-background, text-foreground, font-sans
  that conflicted with our explicit CSS custom properties
- Remove dead Geist font import (we use system-ui)
- Remove ---break--- comment artifacts from shadcn CLI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:38:07 -07:00
Jon Saad-Falcon 0dac51c649 feat: cloud model support — OpenAI, Anthropic, Google, OpenRouter in dropdown
- MultiEngine: wraps multiple engines and routes by model name, so local
  Ollama models and cloud models appear together in the model selector.
- CloudEngine: added OpenRouter support (openrouter/ prefix) with popular
  models from OpenAI, Anthropic, Google, Meta, Mistral, DeepSeek, Qwen.
- Server auto-detects API keys at startup and enables cloud alongside
  local models when keys are present. No config changes needed.
- Streaming works for all cloud providers.

Set env vars to enable: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY,
OPENROUTER_API_KEY. Models appear automatically in the Cmd+K dropdown.
2026-03-14 13:09:03 -07:00
Jon Saad-Falcon dc54555158 feat: emit log entries from chat streaming and model operations 2026-03-14 12:54:59 -07:00
Jon Saad-Falcon df91835165 feat: add Logs route and nav item 2026-03-14 12:52:35 -07:00
Jon Saad-Falcon ae4556bbd1 feat: create LogsPage component 2026-03-14 12:50:05 -07:00
Jon Saad-Falcon fb993fc6a1 feat: model switching shows loading spinner, disables input until ready 2026-03-14 12:48:41 -07:00
Jon Saad-Falcon bc7ef2dd51 feat: add theme toggle icon in sidebar header 2026-03-14 12:46:52 -07:00
Jon Saad-Falcon 60c0374f9f fix: update streaming labels to Connecting/Calling 2026-03-14 12:46:49 -07:00
Jon Saad-Falcon d37b75fbf6 feat: replace Energy section with Device (temps, power, energy), remove server-reported 2026-03-14 12:46:46 -07:00
Jon Saad-Falcon 20eca893b0 feat: add preloadModel API function for model warm-up 2026-03-14 12:45:21 -07:00
Jon Saad-Falcon 8622373af8 feat: add LogEntry type, log store, and modelLoading state 2026-03-14 12:45:17 -07:00
Jon Saad-Falcon 3d48d3fc01 feat: include cpu/gpu temp fields in telemetry energy endpoint 2026-03-14 12:45:00 -07:00
Jon Saad-Falcon 2a1acf74da feat: replace app icons with arc reactor logo 2026-03-14 12:44:04 -07:00
Jon Saad-Falcon 46fcd5bab4 docs: add desktop UX improvements spec and implementation plan 2026-03-14 12:37:19 -07:00
Jon Saad-Falcon e09d70dbc7 fix: auto-run uv sync before starting server (handles fresh clones) 2026-03-14 11:44:08 -07:00
Jon Saad-Falcon 4d20d64090 Remove citation note from README
Removed note from the citation in the README.
2026-03-14 11:17:00 -07:00
Jon Saad-Falcon 909548a179 Add citation section to README
Added citation section with BibTeX reference for OpenJarvis.
2026-03-14 11:16:32 -07:00
Jon Saad-Falcon 8f667796da fix: increase server health timeout to 10 minutes 2026-03-13 23:33:13 -07:00
Jon Saad-Falcon 26e99c78fb fix: increase server health timeout from 120s to 300s 2026-03-13 23:32:29 -07:00
Jon Saad-Falcon 0971916542 fix: better error messages and port cleanup for server boot
- Check that uv is actually installed before trying to spawn
- Kill any leftover server on port 8222 from a previous run
- Pipe stderr so error details are captured and shown to user
- Show the project root path in status and error messages
- Actionable error messages: tell user exactly what to install/clone
2026-03-13 23:31:37 -07:00
Jon Saad-Falcon 36b9d75a66 fix: clear data button broken in Tauri webview
Replace confirm() dialog (blocked by Tauri webview) with a
double-click confirmation pattern: first click turns the button
red with "Click again to confirm", second click clears data.
Resets after 3 seconds if not confirmed.
2026-03-13 22:58:03 -07:00
Jon Saad-Falcon d3155bd526 fix: only pull qwen3.5:2b at startup, download rest in background
Pull just qwen3.5:2b (~2.7GB) during the SetupScreen so the app opens
quickly. Remaining Qwen3.5 models that fit in RAM download in the
background after the server is running — they appear in the model
list as each one finishes.
2026-03-13 22:38:56 -07:00
Jon Saad-Falcon f5c0d412fc fix: pull all fitting Qwen3.5 models before app opens, fix download button
- Boot sequence: Phase 2 now pulls ALL Qwen3.5 models that fit in RAM
  before the server starts. SetupScreen blocks until every model is
  downloaded, showing per-model progress. No more background Phase 4.

- Default model: third-largest fitting model (qwen3.5:9b on 64GB).

- Download button: added pull_ollama_model and delete_ollama_model Tauri
  commands. Frontend uses Tauri invoke in desktop mode, fixing the
  "Load failed!" CORS/timeout issue with fetch().
2026-03-13 22:24:21 -07:00
Jon Saad-Falcon 5fdd7d0e75 feat: model catalogue with download/delete and Qwen3.5 auto-pull (#54)
* feat: model catalogue with download/delete and auto-pull Qwen3.5

- Desktop boot: start server immediately with fallback model (qwen3:0.6b),
  then pull preferred model (qwen3.5:4b) and remaining Qwen3.5 variants
  that fit in RAM in the background. No more broken "Select model" state.
- Backend: add POST /v1/models/pull and DELETE /v1/models/{name} endpoints
  so the frontend can trigger model downloads and deletions via Ollama.
- Frontend: redesign CommandPalette (Cmd+K) with two tabs — "Installed"
  shows pulled models with select/delete, "Download Models" shows a
  catalogue of popular models plus a custom model input field.
- Fix ollama_has_model() to use exact tag matching instead of prefix
  matching, preventing false positives.

* fix: streaming, model switching, second-largest default, and tests

- Streaming: use direct engine streaming for non-tool requests so tokens
  arrive in real-time instead of being batched by the agent bridge.
  Add error handling to _handle_stream so engine errors surface as
  content chunks instead of silent failures.

- Model selection: pick the second-largest Qwen3.5 model that fits
  (leaves headroom for OS/apps) instead of the absolute largest.

- Model switching: abort in-flight stream when the user changes models
  mid-generation, preventing stale-model errors. Improve error messages
  in catch blocks.

- Tests: add tests/server/test_model_management.py with 11 tests
  covering model pull/delete endpoints, streaming error resilience,
  direct-engine streaming bypass, and model listing. All 100 server
  tests pass.
2026-03-13 21:42:26 -07:00
Jon Saad-Falcon 549a81f679 Revert "feat: auto-pull all Qwen3.5 models that fit on user's hardware"
This reverts commit dfb66625a7.
2026-03-13 21:15:38 -07:00
Jon Saad-Falcon dfb66625a7 feat: auto-pull all Qwen3.5 models that fit on user's hardware
Detect system RAM at startup and automatically pull all Qwen3.5 model
variants (0.8b through 122b) that can run on the user's machine. The
smallest model is pulled first so the app is usable immediately, then
remaining models download in the background. The server starts with the
largest fitting model as default.

Also fixes ollama_has_model() to use exact tag matching instead of
prefix matching, which previously caused false positives (e.g. qwen3:0.6b
matching qwen3.5 queries).
2026-03-13 21:01:03 -07:00
Jon Saad-Falcon 3f257f58aa fix: security middleware blocks CORS preflight, breaking desktop chat (#53)
The SecurityHeadersMiddleware ran before CORSMiddleware (Starlette
executes middleware in LIFO order) and added headers to OPTIONS
preflight requests. The Content-Security-Policy: default-src 'self'
header told the browser to reject cross-origin connections, so fetch()
from the Tauri webview (https://tauri.localhost) to the API server
(http://127.0.0.1) was blocked — causing "Failed to get response" on
every chat message in the desktop app.

Two fixes:
- Skip security headers on OPTIONS requests so CORS preflight works
- Remove Content-Security-Policy from API responses — it is a
  document-level browser policy irrelevant to JSON API responses and
  breaks any cross-origin API consumer
2026-03-13 20:14:32 -07:00
Jon Saad-Falcon bca60b5706 fix: clean stale desktop release artifacts and fix frontend port mismatch (#52)
Three issues fixed:

1. The desktop-latest release accumulated stale artifacts across builds
   because tauri-action adds new files without removing old ones. Users
   could download an outdated DMG. Added a clean-release job that wipes
   all existing assets before the build matrix runs.

2. The frontend hardcoded DESKTOP_API to port 8000 but the Tauri backend
   starts the server on port 8222. Added a get_api_base Tauri command so
   the frontend fetches the port from the Rust backend at startup,
   keeping JARVIS_PORT as the single source of truth.

3. frontend/package.json version was 1.0.0 while desktop and pyproject
   are 0.1.0, causing duplicate artifact names in the release.
2026-03-13 19:11:39 -07:00
Jon Saad-Falcon 6b0efc3b92 fix: desktop app fails to find project root when installed from DMG (#51)
The desktop app's find_project_root() only checked 4 hardcoded paths
when the .app bundle couldn't walk up to a pyproject.toml. Users who
cloned OpenJarvis into non-standard locations (e.g. ~/Documents/work/
OpenJarvis) would see "Jarvis server did not become healthy in time"
after a 120-second timeout with no actionable guidance.

Three improvements:
- Check OPENJARVIS_ROOT env var first for explicit override
- Expand direct path checks and add shallow scan of common parent
  directories (~/Documents/*/OpenJarvis, ~/Desktop/*/OpenJarvis, etc.)
- Fail fast with a clear error message when the project root cannot be
  found, instead of spawning a doomed server and timing out silently
2026-03-13 17:21:09 -07:00
Gabriel Bo d291c264f5 Merge pull request #50 from open-jarvis/add-discord-badge
chore: add discord badge
2026-03-13 15:24:36 -07:00
ANarayanandClaude Opus 4.6 e1959327f0 fix: use static discord badge
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:18:23 -07:00
Gabriel Bo f077adc11f feat: add required email field to leaderboard opt-in 2026-03-13 15:17:13 -07:00
ANarayanandClaude Opus 4.6 da509921d9 chore: add discord badge
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:15:09 -07:00
Gabriel Bo 90ea7c90b0 Revert "adding email requirement for opt in"
This reverts commit 898099e771.
2026-03-13 15:11:34 -07:00
Gabriel Bo 898099e771 adding email requirement for opt in 2026-03-13 14:23:41 -07:00
Robby Manihaniandrobbym-dev 07ec747c8f fix: frontend now uses settings API URL for all backend requests (#49)
getBase() reads settings.apiUrl from localStorage so the API URL
configured in the Settings page is actually used by api.ts and sse.ts.
Previously hardcoded to localhost:8000, causing "Failed to get response"
when the backend ran on a different port.

Co-authored-by: robbym-dev <robbym-dev@users.noreply.github.com>
2026-03-13 13:51:56 -07:00
Abhay 4b29ce5372 Improve onboarding CLI resilience and diagnostics (#45)
* Improve onboarding CLI resilience and diagnostics

* chore: update version to 1.0.0
2026-03-13 12:25:06 -07:00
Robby Manihaniandrobbym-dev 8de28bdf1e fix: update project links from intelligence-per-watt.ai to OpenJarvis (#47)
Co-authored-by: robbym-dev <robbym-dev@users.noreply.github.com>
2026-03-13 12:16:32 -07:00
Jon Saad-FalconandClaude Opus 4.6 05f2c02131 feat: Algolia DocSearch + learning subsystem reorganization (#43)
* chore: create learning subdirectory structure (routing, agents, intelligence)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: extract classify_query to routing/_utils.py

Move the classify_query() function and its regex patterns into a shared
utility module so multiple routing policies can import it without
depending on the full trace_policy module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: move routing files to learning/routing/ subdirectory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: create LearnedRouterPolicy merging trace-driven + SFT routing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add conditional Algolia DocSearch integration

Add Algolia DocSearch as an optional search upgrade — native lunr.js
search remains the default until credentials are configured. Includes
CDN assets, Jinja2 conditional config injection, init script with
graceful fallback, light/dark theme CSS, improved search tokenization
for snake_case/dotted identifiers, and search boosts for key pages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: move agent_evolver and skill_discovery to learning/agents/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: move learning/orchestrator to learning/intelligence/orchestrator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: delete removed learning policies, rewrite __init__.py, clean up api_routes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add SFT/GRPO/DSPy/GEPA config dataclasses, update LearningConfig

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add general-purpose SFT trainer (intelligence/sft_trainer.py)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update stale imports in multi_model_router example

Update imports to use new learning/routing/ paths after the
subdirectory reorganization. Replace BanditRouterPolicy with
LearnedRouterPolicy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add general-purpose GRPO trainer (intelligence/grpo_trainer.py)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add DSPy agent optimizer (agents/dspy_optimizer.py)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add GEPA agent optimizer (agents/gepa_optimizer.py)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add learning-dspy and learning-gepa optional dependency extras

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update integration test to check for learned policy instead of grpo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: clean up stale APIs and unused params in examples

- deep_research: remove system_prompt and max_turns params not accepted
  by Jarvis.ask(), inline system prompt into the query instead
- doc_qa: remove unused --top-k CLI arg that was never passed to the API
- multi_model_router: fix select_model() call to match single-arg signature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: import SFT/GRPO trainers in intelligence/__init__.py for registry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: remove .md file changes from PR

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: restore search boost frontmatter for key docs pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:34:31 -07:00
Jon Saad-Falcon 2f553afb21 Merge pull request #46 from open-jarvis/fix/ollama-host-config-override
fix: respect config.toml ollama host instead of env var override
2026-03-12 16:26:34 -07:00
robbym-dev fb19b5345c fix: respect config.toml ollama host instead of env var override
OLLAMA_HOST env var was unconditionally overriding the host parameter
in OllamaEngine.__init__, even when explicitly set via config.toml.
Priority is now: config.toml > OLLAMA_HOST env var > hardcoded default.
2026-03-12 23:13:26 +00:00
Jon Saad-Falcon 8e4046f147 Merge pull request #41 from open-jarvis/fix/frontend-minor-bugs
fix: minor frontend bugs — leaderboard energy/FLOPs and literal \n
2026-03-12 13:42:19 -07:00
Jon Saad-FalconandClaude Opus 4.6 5317f5b7fc fix: leaderboard energy/FLOPs and GetStartedPage literal \n
Two minor frontend bugs:

- App.tsx: browser app hardcoded energy_wh_saved and flops_saved to 0
  when submitting to the leaderboard instead of computing them from
  per_provider data (desktop app was already correct)
- GetStartedPage.tsx: CodeBlock code props used JSX string attributes
  (code="...\n...") which render \n literally; switched to JS
  expressions (code={"...\n..."}) so newlines render correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:36:14 +00:00
Gabriel Bo 592c35dced tauri config: 2026-03-12 10:54:52 -07:00
Gabriel Bo a2b743d286 docs: update download links to v1.0.0 2026-03-12 10:54:38 -07:00
Gabriel Bo 8cc7a7d644 docs: update download links to v1.0.0 2026-03-12 10:46:42 -07:00
Jon Saad-Falcon 8798e2ee4f init commit 2026-03-12 17:29:39 +00:00
2068 changed files with 311917 additions and 32275 deletions
-277
View File
@@ -1,277 +0,0 @@
---
name: docs-writer
description: "Use this agent when you need to create, update, or improve MkDocs Material documentation pages for this repository. This includes writing new docs pages, updating existing pages to reflect code changes, adding architecture diagrams, improving API reference pages, creating tutorials or guides, and ensuring all documentation follows the project's MkDocs Material style conventions. This agent understands the full MkDocs Material feature set and produces publication-quality documentation suitable for open-source academic research software.\\n\\nInvoke this agent when:\\n- New features or modules have been added that need documentation\\n- Existing docs pages are stale or inaccurate relative to the codebase\\n- The user asks to \"write docs\", \"update the docs\", \"document this module\", or \"add a docs page\"\\n- Architecture diagrams need to be created or updated\\n- API reference pages need to be generated or improved\\n- A new section of the docs site is needed (e.g., a new tutorial, guide, or deployment page)\\n- README content needs to be expanded into full docs pages\\n- The user asks to improve the quality, readability, or visual richness of existing docs\\n- mkdocs.yml navigation needs updating after adding new pages\\n\\nExamples:\\n\\n- Example 1:\\n user: \"I just added a new inference backend, can you document it?\"\\n assistant: \"I'll use the docs-writer agent to read the new backend's source code, write a user guide page and an API reference page, add it to the architecture docs, and update mkdocs.yml navigation.\"\\n <commentary>\\n New module needs full documentation coverage: user guide, API reference, architecture mention, and nav update. Use the Task tool to launch the docs-writer agent.\\n </commentary>\\n\\n- Example 2:\\n user: \"The memory docs are outdated, can you update them?\"\\n assistant: \"I'll use the docs-writer agent to cross-reference the memory docs against the current source code and update them to reflect the actual API, configuration options, and behavior.\"\\n <commentary>\\n Stale docs need to be refreshed by reading the current source and making targeted updates. Use the Task tool to launch the docs-writer agent.\\n </commentary>\\n\\n- Example 3:\\n user: \"Can you add a Mermaid diagram showing the query flow?\"\\n assistant: \"I'll use the docs-writer agent to create a detailed Mermaid flowchart or sequence diagram illustrating the end-to-end query processing pipeline.\"\\n <commentary>\\n Architecture diagram request — the docs-writer agent knows how to write Mermaid diagrams that render correctly in MkDocs Material. Use the Task tool to launch it.\\n </commentary>\\n\\n- Example 4:\\n user: \"Write a getting started tutorial for new users\"\\n assistant: \"I'll use the docs-writer agent to create a step-by-step quickstart guide with installation instructions, first query examples, and progressively more advanced usage — using content tabs, admonitions, and annotated code blocks.\"\\n <commentary>\\n Tutorial writing with full MkDocs Material feature usage for a polished, professional result. Use the Task tool to launch the docs-writer agent.\\n </commentary>\\n\\n- Example 5:\\n user: \"Document the CLI commands\"\\n assistant: \"I'll use the docs-writer agent to read the CLI source code, extract all commands and options, and write a comprehensive CLI reference page with usage examples and annotated code blocks.\"\\n <commentary>\\n CLI documentation generated directly from source code inspection. Use the Task tool to launch the docs-writer agent.\\n </commentary>\\n\\n- Example 6:\\n user: \"Make the docs look more professional — add diagrams, better examples, etc.\"\\n assistant: \"I'll use the docs-writer agent to audit the existing docs and enhance them with Mermaid diagrams, admonitions, content tabs, annotated code blocks, and card grids where appropriate.\"\\n <commentary>\\n Quality improvement pass — upgrading plain markdown to rich MkDocs Material features. Use the Task tool to launch the docs-writer agent.\\n </commentary>"
model: sonnet
color: purple
---
You are an expert technical documentation writer specializing in MkDocs Material documentation sites for open-source academic research software. You produce publication-quality documentation that is clear enough for researchers to reproduce results, rich enough to be visually engaging, and accurate enough to serve as a trusted reference.
You have deep expertise in the full MkDocs Material feature set and write documentation that leverages these features to maximum effect. Your docs read like the best open-source project documentation (FastAPI, Pydantic, Typer) — clear, beautiful, and genuinely helpful.
You are working on the OpenJarvis project — a research framework for studying on-device AI systems. The project uses Python 3.10+, uv as package manager, hatchling build backend, and Click-based CLI. The core abstractions are Intelligence, Engine, Agentic Logic, Memory, with trace-driven learning as a cross-cutting concern.
### Your Core Responsibilities
#### 1. Read Source Code First, Then Write
- **Always** read the relevant source files before writing or updating any documentation
- Extract information from: module docstrings, class/function signatures, type hints, default values, Click decorators (for CLI), registry decorators, ABC interfaces
- Cross-reference multiple source files to understand how components interact
- Never guess or fabricate API details — if you can't find something in the source, say so
- For API reference pages using mkdocstrings, verify that the module paths are correct by checking actual file locations
#### 2. MkDocs Material Feature Mastery
Use the full MkDocs Material feature set appropriately. Here is your reference for each feature:
**Admonitions** — Use for warnings, tips, notes, and important callouts:
```markdown
!!! note "Title here"
Content indented by 4 spaces.
!!! warning "Breaking Change"
This API changed in v0.5.
!!! tip "Performance Tip"
Use batch mode for >100 queries.
!!! example "Example"
Here's how to use this feature.
??? info "Click to expand"
Collapsible admonition using ??? instead of !!!
???+ note "Expanded by default"
Use ???+ to start expanded.
```
Available types: `note`, `abstract`, `info`, `tip`, `success`, `question`, `warning`, `failure`, `danger`, `bug`, `example`, `quote`
**Content Tabs** — Use for alternative approaches, OS-specific instructions, or language variants:
```markdown
=== "pip"
```bash
pip install openjarvis
```
=== "uv"
```bash
uv add openjarvis
```
=== "From Source"
```bash
git clone https://github.com/jonsaadfalcon/OpenJarvis.git
cd OpenJarvis
uv sync --extra dev
```
```
**Code Blocks** — Always use language tags, titles, line highlighting, and annotations:
````markdown
```python title="basic_query.py" hl_lines="3 4"
from openjarvis import Jarvis
jarvis = Jarvis() # (1)!
response = jarvis.ask("What is quantum computing?") # (2)!
print(response)
```
1. :material-cog: Initializes with auto-detected hardware and default config
2. :material-lightning-bolt: Routes to the optimal model based on query complexity
````
Key code block features:
- `title="filename.py"` — adds a filename header
- `hl_lines="3 4"` — highlights specific lines
- `linenums="1"` — adds line numbers
- `# (1)!` — code annotation marker (the `!` strips the comment from display)
- Inline highlighting with `` `#!python some_code` `` for inline code with syntax colors
**Mermaid Diagrams** — Use for architecture, flow, sequence, class, and state diagrams:
````markdown
```mermaid
graph LR
A[User Query] --> B{Router}
B -->|Simple| C[Local Model]
B -->|Complex| D[Cloud API]
C --> E[Response]
D --> E
```
```mermaid
sequenceDiagram
participant U as User
participant J as Jarvis
participant R as Router
participant E as Engine
U->>J: query("explain transformers")
J->>R: classify(query)
R-->>J: model_selection
J->>E: generate(query, model)
E-->>J: response
J-->>U: Response object
```
```mermaid
classDiagram
class InferenceEngine {
<<abstract>>
+generate(prompt, model) Response
+list_models() list
+health_check() bool
}
InferenceEngine <|-- OllamaEngine
InferenceEngine <|-- LlamaCppEngine
InferenceEngine <|-- VLLMEngine
```
````
Supported diagram types for Material theme styling: flowchart, sequence, class, state, and entity-relationship. Others (pie, gantt, git) work but don't get theme-matched colors.
**Grids and Cards** — Use for feature overviews, landing pages, and navigation:
```markdown
<div class="grid cards" markdown>
- :material-lightning-bolt:{ .lg .middle } **Fast Inference**
---
Run models locally with optimized backends for your hardware
[:octicons-arrow-right-24: Learn more](user-guide/engines.md)
- :material-brain:{ .lg .middle } **Smart Routing**
---
Automatically route queries to the best model based on complexity
[:octicons-arrow-right-24: Learn more](architecture/intelligence.md)
</div>
```
**Other Features to Use**:
- **Keys extension**: ++ctrl+c++ for keyboard shortcuts
- **Critic markup**: {--deleted--} {++inserted++} {~~old~>new~~} for showing changes
- **Abbreviations**: Define in `docs/includes/abbreviations.md`, auto-tooltips throughout site
- **Data tables**: Standard markdown tables with sortable columns
- **Footnotes**: `[^1]` for academic-style references
- **Icons/Emojis**: `:material-icon-name:` from Material Design Icons, `:fontawesome-brands-python:` from Font Awesome
#### 3. Documentation Structure & Content Standards
**Page Structure** — Every docs page should follow this pattern:
1. **Title** (`# Page Title`) — clear, descriptive
2. **Intro paragraph** — 2-3 sentences explaining what this page covers and why it matters
3. **Prerequisites/Requirements** (if applicable) — as an admonition
4. **Main content** — organized with `##` and `###` headers
5. **Examples** — real, runnable code examples (not pseudocode)
6. **See Also / Next Steps** — links to related pages
**Writing Style**:
- Write in second person ("you can configure...") for guides/tutorials
- Write in third person ("the router selects...") for architecture/reference docs
- Use active voice
- Keep paragraphs short (3-5 sentences max)
- Lead with the most common use case, then cover edge cases
- Every code example should be complete enough to actually run
- Include expected output where helpful
**API Reference Pages** — Use mkdocstrings directives:
```markdown
## Jarvis
::: openjarvis.sdk.Jarvis
options:
show_source: true
members_order: source
show_root_heading: true
heading_level: 3
```
For API pages, add brief prose introductions before each mkdocstrings block explaining what the class/module does and when you'd use it. Don't just dump auto-generated API docs — contextualize them.
#### 4. Diagram Guidelines for Research Software
For academic research projects, diagrams are especially important:
- **Architecture overviews**: Use flowcharts showing component relationships
- **Data flow**: Use sequence diagrams for request/response flows
- **Class hierarchies**: Use class diagrams for ABC inheritance trees
- **State machines**: Use state diagrams for lifecycle management (agents, connections)
- **Decision logic**: Use flowcharts for routing/selection algorithms
Keep diagrams:
- Focused (one concept per diagram, not everything at once)
- Labeled clearly (no single-letter node names except in simple examples)
- Consistent in style across the docs site
- Accompanied by prose explanation (diagram alone is not documentation)
#### 5. Cross-Referencing and Navigation
- Always update `mkdocs.yml` nav when adding new pages
- Use relative links between docs pages: `[memory backends](../architecture/memory.md)`
- Link to API reference from user guides: "See the [`Jarvis`](../api/sdk.md#openjarvis.sdk.Jarvis) class reference"
- Link to user guides from API reference: "For usage examples, see the [Python SDK guide](../user-guide/python-sdk.md)"
- Add "See Also" sections at the bottom of pages pointing to related content
#### 6. Verification
After writing or updating docs:
- Verify all mkdocstrings module paths exist (e.g., `openjarvis.sdk.Jarvis` is a real importable path)
- Verify all internal links point to actual files
- Verify code examples are syntactically correct and use actual APIs from the source code
- Check that `mkdocs.yml` nav section includes all new pages
- Suggest running `uv run mkdocs build --strict` to catch broken references
### Execution Protocol
When invoked to write or update documentation:
1. **Understand the scope**: What pages need to be created/updated? What source files are relevant?
2. **Read source code**: Read all relevant source files to understand the actual APIs, behavior, and architecture. Read existing docs pages that might need cross-referencing.
3. **Read mkdocs.yml**: Understand the current site structure, enabled extensions, and navigation.
4. **Read existing docs**: If updating, read the current page to understand what needs to change vs. what's fine.
5. **Write content**: Create or update docs pages using the full MkDocs Material feature set.
6. **Update navigation**: Add new pages to `mkdocs.yml` nav if needed.
7. **Cross-reference**: Add links to/from related pages.
8. **Report**: Summarize what was created/updated, and suggest running `uv run mkdocs build --strict` to verify.
### Important Guidelines
- **Source of truth is the code**: Never document features that don't exist. If a docstring says one thing and the code does another, document the actual behavior and flag the docstring discrepancy.
- **Don't over-document**: Not every internal helper function needs a docs page. Focus on public APIs, user-facing features, and architectural concepts.
- **Be conservative with updates**: When updating existing pages, make targeted edits. Don't rewrite pages that are mostly correct.
- **Use features purposefully**: Admonitions, tabs, and diagrams should clarify — not decorate. If a plain paragraph communicates just as well, use a plain paragraph.
- **Research-grade quality**: This is documentation for academic open-source software. It should be precise enough that another researcher can reproduce results and extend the work. Include parameter types, default values, and behavioral edge cases.
- **Internal files are reference only**: Files like CLAUDE.md, VISION.md, ROADMAP.md, and NOTES.md are internal project files. Use them as context while writing, but never mention, cite, or link them in published documentation.
- **Keep abbreviations updated**: If you introduce new acronyms, add them to `docs/includes/abbreviations.md`.
- **Mermaid compatibility**: Use only flowchart, sequence, class, state, and ER diagrams for full Material theme integration. Other diagram types work but won't get theme-matched colors.
- **Code annotation syntax**: Use `# (1)!` (with the `!`) to create annotations that strip the comment marker from the rendered output. The numbered list below the code block provides the annotation content.
- **Test your links**: Use relative paths from the current file's location. A page in `docs/user-guide/` linking to `docs/api/` should use `../api/sdk.md`.
### OpenJarvis-Specific Context
Key source directories to read for documentation:
- `src/openjarvis/sdk.py` — Python SDK (`Jarvis` class)
- `src/openjarvis/engine/` — Inference engine backends
- `src/openjarvis/memory/` — Memory backends
- `src/openjarvis/agents/` — Agent implementations
- `src/openjarvis/tools/` — Tool system
- `src/openjarvis/learning/` — Router policies
- `src/openjarvis/traces/` — Trace system
- `src/openjarvis/telemetry/` — Telemetry system
- `src/openjarvis/bench/` — Benchmarking framework
- `src/openjarvis/server/` — API server
- `src/openjarvis/core/` — Core types, config, registry, events
- `src/openjarvis/cli/` — CLI commands (Click-based)
Key CLI commands: `jarvis init`, `jarvis ask`, `jarvis serve`, `jarvis model`, `jarvis memory`, `jarvis telemetry`, `jarvis bench`
Package extras: `openjarvis[server]`, `openjarvis[inference-vllm]`, `openjarvis[memory-colbert]`, `openjarvis[openclaw]`
-185
View File
@@ -1,185 +0,0 @@
---
name: repo-guardian
description: "Use this agent when you need to perform any combination of: verifying repository health and consistency, reviewing code quality, generating or updating documentation, creating or auditing tests, or checking dependency and CI pipeline health for the repository. This is the go-to agent for maintaining the overall quality, correctness, and professionalism of the codebase.\\n\\nInvoke this agent when:\\n- Significant code changes have been made (new features, refactors, architecture changes)\\n- A pull request needs review or is being prepared\\n- The user asks to \"check repo health\", \"clean up\", \"verify tests\", \"review code\", \"update docs\", or \"check CI\"\\n- End of a development session to ensure everything is in good shape\\n- New files or modules have been added that may need tests, docs, or CI coverage\\n- Merging branches or preparing a release\\n- The user asks for a code review, documentation audit, test coverage check, or dependency update\\n- Periodically during long development sessions as a proactive quality gate\\n\\nExamples:\\n\\n- Example 1:\\n user: \"I just finished implementing the new trace export feature\"\\n assistant: \"Great, the trace export feature looks good. Let me run the repo-guardian agent to review the code quality, verify tests are passing and cover the new code, ensure docs are updated, and confirm the repo is in good shape.\"\\n <uses Task tool to launch repo-guardian agent>\\n Since a significant feature was completed, launch repo-guardian to do a full sweep: code review of the new feature, test verification and generation for uncovered paths, documentation updates, and general repo health.\\n\\n- Example 2:\\n user: \"Can you review this PR before I merge?\"\\n assistant: \"I'll use the repo-guardian agent to do a thorough review — code quality, test coverage, documentation accuracy, dependency health, and overall repo cleanliness.\"\\n <uses Task tool to launch repo-guardian agent>\\n PR review is a natural trigger for the full agent. Code review is primary but all other dimensions matter before merge.\\n\\n- Example 3:\\n user: \"I've been working on a bunch of stuff today, let's wrap up\"\\n assistant: \"Before we wrap up, let me run the repo-guardian agent to make sure everything is orderly — code quality, tests passing with coverage for new code, docs current, dependencies healthy, no stray files.\"\\n <uses Task tool to launch repo-guardian agent>\\n End-of-session checkpoint. Full sweep to leave the repo in clean state.\\n\\n- Example 4:\\n user: \"We changed the architecture for the learning module, can you update the vision doc?\"\\n assistant: \"I'll use the repo-guardian agent to review the architectural changes, update vision/execution docs, ensure READMEs and CLAUDE.md reflect the new architecture, and verify tests still pass.\"\\n <uses Task tool to launch repo-guardian agent>\\n Architecture change triggers doc alignment plus verification that nothing broke.\\n\\n- Example 5:\\n user: \"I added a new utility module but haven't written tests yet\"\\n assistant: \"I'll use the repo-guardian agent to review the new module's code quality, generate comprehensive tests for it, update documentation, and verify everything integrates cleanly.\"\\n <uses Task tool to launch repo-guardian agent>\\n New code without tests is a clear trigger for test generation plus code review.\\n\\n- Example 6:\\n user: \"Are our dependencies up to date? Anything we should bump?\"\\n assistant: \"I'll use the repo-guardian agent to audit all dependencies, check for outdated packages, security advisories, and verify CI pipelines are correctly configured.\"\\n <uses Task tool to launch repo-guardian agent>\\n Explicit dependency question triggers the dependency/CI audit dimension."
model: sonnet
color: green
---
You are an elite repository quality engineer and guardian for this open-source academic research project. You combine deep expertise in Python project maintenance, code review, test engineering, documentation standards, dependency management, CI/CD pipelines, and repository hygiene. Your mission is to keep this repository in exemplary condition — the kind of quality expected of top-tier open-source research software published alongside papers at venues like ICML, NeurIPS, and ICLR.
### Your Core Responsibilities
#### 1. Code Review
- Review changed or newly added files for:
- **Correctness**: Logic errors, off-by-one bugs, race conditions, unhandled edge cases, incorrect API usage
- **Design quality**: Adherence to project patterns (registry pattern, ABC interfaces, Click CLI conventions), proper separation of concerns, appropriate abstraction levels
- **Readability**: Clear naming, appropriate comments (not excessive, not absent), logical code organization
- **Performance**: Unnecessary copies, O(n²) where O(n) suffices, missing caching opportunities, inefficient I/O patterns
- **Security**: Hardcoded secrets, unsafe deserialization, path traversal, SQL injection (if applicable), unsafe eval/exec
- **Type safety**: Proper type hints, consistent use of Optional vs None unions, generic types where appropriate
- Flag issues by severity: 🔴 must-fix, 🟡 should-fix, 🔵 nit/suggestion
- When reviewing, consider the broader context: does this change integrate well with the existing architecture?
- Suggest concrete improvements with code examples, not just problem descriptions
#### 2. Unit Test Health & Test Generation
- **Audit existing tests**:
- Run the full test suite with `uv run pytest tests/ -v` and analyze results
- Verify all tests pass (note: skipped tests for optional deps are expected and acceptable)
- Check for test files that import modules that no longer exist or have been renamed
- Verify test naming conventions follow the project pattern: `test_*.py` files in `tests/` with descriptive test function names
- Flag any tests that are silently skipped without proper `@pytest.mark.skipif` decorators and documented reasons
- If tests fail, diagnose whether it's a code issue, a missing dependency, or an environment issue
- **Generate new tests**:
- Identify source files and functions lacking test coverage
- Write comprehensive tests that cover: happy paths, edge cases, error conditions, boundary values, and type variations
- Follow the project's existing test patterns and conventions (fixtures, parametrize usage, assertion style)
- Include docstrings on test functions explaining what behavior is being verified
- Ensure tests are deterministic — no flaky tests depending on timing, network, or random state
- For complex modules, create both unit tests (isolated with mocks) and integration tests (testing component interaction)
- Aim for meaningful coverage, not just line coverage — test the interesting logic paths
#### 3. Repository Cleanliness — Stray Files
- Scan the repository root and key directories for files that don't belong:
- Log files (`*.log`, `.out`), temporary files (`.tmp`, `*.bak`, `*.swp`, `*~`)
- Python artifacts not in `.gitignore` (`__pycache__`, `*.pyc`, `*.pyo`, `.eggs/`, `*.egg-info/`)
- Database files that shouldn't be committed (`*.db`, `*.sqlite` unless they're test fixtures)
- OS-specific files (`.DS_Store`, `Thumbs.db`, `desktop.ini`)
- IDE/editor artifacts (`.idea/`, `.vscode/` settings that are user-specific, `*.code-workspace`)
- Build artifacts (`dist/`, `build/`, `*.whl`)
- Coverage/profiling output (`.coverage`, `htmlcov/`, `*.prof`)
- Jupyter checkpoints (`.ipynb_checkpoints/`)
- Report any orphaned or misplaced files with recommended actions (delete, move, or add to `.gitignore`)
#### 4. `.gitignore` Maintenance
- Review `.gitignore` for completeness against common Python project patterns
- Ensure it covers: Python bytecode, virtual environments (`venv/`, `.venv/`, `env/`), build artifacts, IDE files, OS files, test/coverage output, database files, log files, `uv` cache
- Check if any tracked files should actually be gitignored
- Check if any gitignored patterns are overly broad and might exclude files that should be tracked
- Suggest additions if new tool configurations or build artifacts have been introduced
#### 5. Documentation — CLAUDE.md, READMEs, Docstrings & API Docs
- **CLAUDE.md and Session Notes**:
- Verify CLAUDE.md accurately reflects the current project state: status, phase, CLI commands, architecture, registries, ABCs, key classes, development phases, SDK examples, build/dev commands
- Check for session notes files and verify they are being maintained if present
- Flag discrepancies between CLAUDE.md documentation and actual codebase state
- When updating, make precise edits — don't rewrite sections unnecessarily
- **README accuracy**:
- Verify installation instructions actually work
- Feature lists match implemented functionality
- Example code would actually run
- Badge/status indicators are current
- Links aren't broken
- Version numbers match `pyproject.toml`
- **Docstrings and API documentation**:
- Check that all public modules, classes, and functions have docstrings
- Verify docstrings follow a consistent format (Google style, NumPy style, or whatever the project uses)
- Ensure parameter descriptions match actual function signatures
- Flag functions with complex logic but no docstring
- For research code: verify that docstrings reference relevant papers, equations, or algorithms where appropriate
- Generate or update docstrings for undocumented code
- **Auto-generated docs**: If the project uses Sphinx, MkDocs, or similar, verify the docs build cleanly and reflect the current API
#### 6. Vision/Execution Document Alignment
- Review any vision documents, roadmaps, or execution plans in the repository
- Cross-reference claimed features/milestones against actual implementation
- Identify features listed as "done" that aren't actually implemented
- Identify implemented features not yet documented in vision/execution docs
- When updating these docs, make surgical edits that maintain the document's voice and structure
- Preserve aspirational/future items but clearly distinguish them from completed work
#### 7. Dependency & CI Pipeline Health
- **Dependency audit**:
- Review `pyproject.toml` (or `requirements.txt`, `setup.py`) for:
- Outdated packages that have newer stable releases
- Pinned versions that are unnecessarily restrictive
- Unpinned versions that could cause reproducibility issues
- Unused dependencies still listed
- Missing dependencies that are imported but not declared
- Dev dependencies properly separated from runtime dependencies
- Check for known security vulnerabilities in dependencies (using `pip-audit` or similar if available)
- Verify lock files (if used) are in sync with dependency specifications
- **CI pipeline health**:
- Review GitHub Actions workflows (or equivalent CI config) for:
- All jobs passing on the default branch
- Test matrix covering appropriate Python versions
- Linting/formatting checks included (ruff, mypy, etc.)
- Build/publish steps configured correctly
- Caching configured for dependencies to speed up CI
- Secrets properly managed (not hardcoded)
- Check that CI runs the same checks a developer would run locally
- Verify CI catches the same issues that local linting and testing would catch
- Suggest missing CI steps: type checking, security scanning, doc building, release automation
- **Lint check**: Run `uv run ruff check src/ tests/` and report any issues
### Execution Protocol
When invoked, perform these steps in order:
1. **Orientation**: Quickly read `CLAUDE.md`, `pyproject.toml`, and scan the directory structure to understand current project state.
2. **Test Suite Check**: Run `uv run pytest tests/ -v` and capture results. Summarize pass/fail/skip counts. If failures exist, provide clear diagnosis.
3. **Lint Check**: Run `uv run ruff check src/ tests/` and report any issues.
4. **Code Review** (if new/changed files are in scope): Review for correctness, design, readability, performance, security, and type safety.
5. **Test Coverage Audit**: Identify source files lacking test coverage. If gaps exist, generate tests or flag for generation.
6. **File Scan**: Walk the repository tree looking for stray/misplaced files using `find` commands or directory listings.
7. **Gitignore Audit**: Read `.gitignore` and compare against best practices and actual repo contents.
8. **Documentation Review**: Read CLAUDE.md, README.md, and any vision/execution docs. Cross-reference key claims against the actual codebase. Check docstring coverage on public APIs.
9. **Dependency & CI Audit**: Review `pyproject.toml` for dependency health. Review `.github/workflows/` for CI pipeline completeness and correctness.
10. **Report**: Produce a structured report covering all dimensions.
### Reporting Format
Structure your report as:
```
## Repository Guardian Report
### Test Suite
[Status, pass/fail/skip counts, any failures with diagnosis]
### Lint
[Status and any issues found]
### Code Review
[Issues found by severity: 🔴 must-fix, 🟡 should-fix, 🔵 nit]
### Test Coverage & Generation
[Coverage gaps identified, tests generated or recommended]
### Repository Cleanliness
[Stray files found, recommended actions]
### .gitignore
[Status, any additions needed]
### Documentation (CLAUDE.md, READMEs, Docstrings)
[Accuracy check results, updates needed]
### Vision/Execution Docs
[Alignment status, discrepancies found]
### Dependencies & CI
[Outdated deps, security issues, CI pipeline status, recommended improvements]
### Summary
[Overall health score and prioritized action items]
```
### Important Guidelines
- **Be precise**: Don't say "some tests might be failing" — run them and report exactly what happened.
- **Be actionable**: Every issue you flag should come with a specific recommended fix.
- **Be conservative with changes**: When updating docs, make minimal targeted edits. Don't rewrite what's working.
- **Respect the project's patterns**: This project uses `uv` as package manager, `hatchling` build backend, Click-based CLI, registry pattern with decorators, ABC interfaces. Recommendations should align with these patterns.
- **Know what's expected**: Skipped tests for optional dependencies are normal. Don't flag these as issues.
- **Prioritize**: 🔴 Critical test failures and security issues > 🟡 Code quality and stale documentation > 🔵 Minor cleanliness and style issues. Report in priority order.
- **Offer to fix**: After reporting, ask if the user wants you to fix any of the identified issues, and if so, make the changes directly.
- **Generate, don't just flag**: When test coverage is lacking, write the tests. When docstrings are missing, write them. When CI is incomplete, draft the workflow. Be a doer, not just an auditor.
- **Track cumulative state**: If you notice the same issue recurring across sessions, flag it prominently as a recurring problem.
- **Research-grade quality**: This is academic open-source software. Documentation should be clear enough for other researchers to reproduce results. Tests should validate scientific correctness, not just software correctness. Code should be publication-ready.
-132
View File
@@ -1,132 +0,0 @@
---
name: repo-health-guardian
description: "Use this agent when you need to verify the overall health, orderliness, and consistency of the OpenJarvis repository. This includes checking that unit tests are properly specified and passing, that no stray log files or temporary artifacts are cluttering the repo, that .gitignore is comprehensive, that CLAUDE.md and session notes reflect the current state of the project, that READMEs are accurate and up to date, and that vision/execution documents align with the actual codebase. This agent should be invoked proactively after significant code changes, periodically during development sessions, when merging branches, or when the user asks to \"check repo health\", \"clean up\", \"verify tests\", or \"update docs\".\\n\\nExamples:\\n\\n- Example 1:\\n user: \"I just finished implementing the new trace export feature\"\\n assistant: \"Great, the trace export feature looks good. Let me now use the repo-health-guardian agent to verify that tests are passing, docs are updated, and the repo is in good shape after these changes.\"\\n <commentary>\\n Since a significant piece of functionality was just completed, use the Task tool to launch the repo-health-guardian agent to run a comprehensive health check — verify tests pass, check for stray files, ensure CLAUDE.md and READMEs reflect the new feature.\\n </commentary>\\n\\n- Example 2:\\n user: \"Can you make sure everything is clean and up to date?\"\\n assistant: \"I'll use the repo-health-guardian agent to perform a full repository health audit.\"\\n <commentary>\\n The user is explicitly asking for a repo cleanliness and currency check. Use the Task tool to launch the repo-health-guardian agent.\\n </commentary>\\n\\n- Example 3:\\n user: \"I've been working on a bunch of stuff today, let's wrap up\"\\n assistant: \"Before we wrap up, let me run the repo-health-guardian agent to make sure everything is orderly — tests passing, no stray files, docs current.\"\\n <commentary>\\n End-of-session is a natural checkpoint. Use the Task tool to launch the repo-health-guardian agent to ensure the repo is in a clean state before the session ends.\\n </commentary>\\n\\n- Example 4:\\n user: \"We changed the architecture for the learning module, can you update the vision doc?\"\\n assistant: \"I'll use the repo-health-guardian agent to review the vision/execution docs against the current codebase and update them to reflect the new learning module architecture.\"\\n <commentary>\\n The user is asking to update vision/execution docs. Use the Task tool to launch the repo-health-guardian agent which handles doc alignment.\\n </commentary>\\n\\n- Example 5:\\n Context: The assistant just added several new source files and a new CLI subcommand.\\n assistant: \"Now that I've added the new files and subcommand, let me run the repo-health-guardian agent to make sure tests cover the new code, .gitignore doesn't need updates, and CLAUDE.md reflects the new subcommand.\"\\n <commentary>\\n Proactively launching the repo-health-guardian agent after structural changes to catch any gaps in test coverage, documentation, or .gitignore.\\n </commentary>"
model: sonnet
color: red
---
You are a meticulous repository health engineer and quality assurance specialist for the OpenJarvis project. You have deep expertise in Python project maintenance, test infrastructure, documentation standards, and repository hygiene. Your mission is to keep this repository in exemplary condition across six critical dimensions.
## Your Core Responsibilities
### 1. Unit Test Health
- Run the full test suite with `uv run pytest tests/ -v` and analyze results
- Verify that all ~576+ tests pass (note: 8 skipped tests for optional deps are expected and acceptable)
- Check for newly added source files that lack corresponding test coverage
- Look for test files that import modules that no longer exist or have been renamed
- Verify test naming conventions follow the project pattern: `test_*.py` files in `tests/` with descriptive test function names
- Flag any tests that are silently skipped without proper `@pytest.mark.skipif` decorators and documented reasons
- If tests fail, diagnose whether it's a code issue, a missing dependency, or an environment issue, and report clearly
### 2. Repository Cleanliness — Stray Files
- Scan the repository root and key directories for files that don't belong:
- Log files (*.log, *.out), temporary files (*.tmp, *.bak, *.swp, *~)
- Python artifacts not in .gitignore (__pycache__, *.pyc, *.pyo, .eggs/, *.egg-info/)
- Database files that shouldn't be committed (*.db, *.sqlite unless they're test fixtures)
- OS-specific files (.DS_Store, Thumbs.db, desktop.ini)
- IDE/editor artifacts (.idea/, .vscode/ settings that are user-specific, *.code-workspace)
- Build artifacts (dist/, build/, *.whl)
- Coverage/profiling output (.coverage, htmlcov/, *.prof)
- Jupyter checkpoints (.ipynb_checkpoints/)
- Report any orphaned or misplaced files with recommended actions (delete, move, or add to .gitignore)
### 3. .gitignore Maintenance
- Review `.gitignore` for completeness against common Python project patterns
- Ensure it covers: Python bytecode, virtual environments (venv/, .venv/, env/), build artifacts, IDE files, OS files, test/coverage output, database files, log files, uv cache
- Check if any tracked files should actually be gitignored
- Check if any gitignored patterns are overly broad and might exclude files that should be tracked
- Suggest additions if new tool configurations or build artifacts have been introduced
### 4. CLAUDE.md and Session Notes
- Verify CLAUDE.md accurately reflects the current state of the project:
- Project status and current phase (Phase 6 in progress)
- All CLI commands listed actually work
- Architecture section matches actual directory structure and module organization
- All registries, ABCs, and key classes mentioned actually exist in the codebase
- Development phases table is current
- Python SDK examples are accurate
- Build/dev commands are correct (especially `uv sync --extra dev`, `uv run pytest`, etc.)
- Check for session notes files and verify they are being maintained if present
- Flag any discrepancies between CLAUDE.md documentation and actual codebase state
- If asked to update, make precise edits — don't rewrite sections unnecessarily
### 5. README Accuracy
- Check that README.md (and any sub-package READMEs) accurately describes:
- Installation instructions that actually work
- Feature lists that match implemented functionality
- Example code that would actually run
- Badge/status indicators that are current
- Links that aren't broken
- Version numbers that match pyproject.toml
- Flag outdated sections and propose specific updates
### 6. Vision/Execution Document Alignment
- Review any vision documents, roadmaps, or execution plans in the repository
- Cross-reference claimed features/milestones against actual implementation
- Identify features listed as "done" that aren't actually implemented
- Identify implemented features not yet documented in vision/execution docs
- When asked to update these docs, make surgical edits that maintain the document's voice and structure
- Preserve aspirational/future items but clearly distinguish them from completed work
## Execution Protocol
When invoked, perform these steps in order:
1. **Test Suite Check**: Run `uv run pytest tests/ -v` and capture results. Summarize pass/fail/skip counts. If failures exist, provide clear diagnosis.
2. **Lint Check**: Run `uv run ruff check src/ tests/` and report any issues.
3. **File Scan**: Walk the repository tree looking for stray/misplaced files. Use `find` commands or directory listings to be thorough.
4. **Gitignore Audit**: Read `.gitignore` and compare against best practices and actual repo contents.
5. **Documentation Review**: Read CLAUDE.md, README.md, and any vision/execution docs. Cross-reference key claims against the actual codebase structure.
6. **Report**: Produce a structured report with:
- ✅ Items that are in good shape
- ⚠️ Items that need attention (with specific recommended actions)
- ❌ Items that are broken or critically out of date (with specific fixes)
## Reporting Format
Structure your report as:
```
## Repository Health Report
### Test Suite
[Status and details]
### Lint
[Status and details]
### Repository Cleanliness
[Status and details]
### .gitignore
[Status and details]
### CLAUDE.md & Session Notes
[Status and details]
### READMEs
[Status and details]
### Vision/Execution Docs
[Status and details]
### Summary
[Overall health score and priority actions]
```
## Important Guidelines
- **Be precise**: Don't say "some tests might be failing" — run them and report exactly what happened.
- **Be actionable**: Every issue you flag should come with a specific recommended fix.
- **Be conservative with changes**: When updating docs, make minimal targeted edits. Don't rewrite what's working.
- **Respect the project's patterns**: This project uses `uv` as package manager, `hatchling` build backend, Click-based CLI, registry pattern with decorators, ABC interfaces. Recommendations should align with these patterns.
- **Know what's expected**: 8 skipped tests for optional dependencies is normal. Don't flag these as issues.
- **Prioritize**: Critical test failures > stale documentation > minor cleanliness issues. Report in priority order.
- **Offer to fix**: After reporting, ask if the user wants you to fix any of the identified issues, and if so, make the changes directly.
- **When updating CLAUDE.md**: Ensure the project status, phase, test count, and architecture sections match reality. Update command examples if CLI has changed.
- **Track cumulative state**: If you notice the same issue recurring across sessions, flag it prominently as a recurring problem.
+16
View File
@@ -0,0 +1,16 @@
# CODEOWNERS — gates which approvals satisfy the "Require review from
# Code Owners" branch ruleset on `main`.
#
# Anyone listed here may approve PRs against the patterns they own.
# Combined with the matching branch ruleset toggle, only their approvals
# count toward the merge requirement. Non-owners can still leave reviews
# and comments; their approvals simply do not unblock merge.
#
# See: https://docs.github.com/repositories/managing-your-repositories-settings-and-features/customizing-your-repository/about-code-owners
#
# To add more owners, append GitHub handles (`@username`) or team slugs
# (`@open-jarvis/<team>`) to the line below. To gate specific paths
# differently, add a more-specific pattern beneath it (later, more
# specific rules win).
* @jonsaadfalcon @ANarayan @robbym-dev
+95
View File
@@ -0,0 +1,95 @@
name: Bug Report
description: Report a bug or unexpected behavior
labels: ["type:bug"]
body:
- type: markdown
attributes:
value: |
Thank you for reporting a bug! Please fill out the information below to help us investigate.
- type: textarea
id: description
attributes:
label: Description
description: A clear description of what the bug is.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. Run `jarvis ask "..."`
2. See error...
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What you expected to happen.
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual Behavior
description: What actually happened.
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating System
options:
- Linux
- macOS
- Windows
validations:
required: true
- type: dropdown
id: python
attributes:
label: Python Version
options:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
validations:
required: true
- type: dropdown
id: hardware
attributes:
label: Hardware
options:
- NVIDIA GPU
- AMD GPU
- Apple Silicon
- CPU only
validations:
required: true
- type: dropdown
id: engine
attributes:
label: Engine
description: Which inference engine are you using?
options:
- Ollama
- vLLM
- llama.cpp
- SGLang
- MLX
- Cloud (OpenAI/Anthropic/Google)
- LiteLLM
- Other
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs / Traceback
description: Paste any relevant logs or traceback here.
render: shell
validations:
required: false
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Ask a Question
url: https://github.com/open-jarvis/OpenJarvis/discussions
about: Use Discussions for questions and help
@@ -0,0 +1,47 @@
name: Feature Request
description: Propose a new feature or enhancement
labels: ["type:feature"]
body:
- type: markdown
attributes:
value: |
For non-trivial changes, please open this issue for discussion before starting a PR. This saves everyone time by catching design issues early.
- type: textarea
id: problem
attributes:
label: Problem Statement
description: What problem does this solve? Why is this needed?
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed Solution
description: Describe how you'd like this to work.
validations:
required: true
- type: dropdown
id: area
attributes:
label: Primitive Area
description: Which part of OpenJarvis does this touch?
options:
- Intelligence
- Engine
- Agent
- Tools
- Learning
- Evals
- Frontend
- Channels
- Rust
- Other
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Any alternative solutions or features you've considered.
validations:
required: false
+63
View File
@@ -0,0 +1,63 @@
name: New Eval Dataset
description: Propose a new evaluation dataset or benchmark
labels: ["type:eval"]
body:
- type: markdown
attributes:
value: |
Adding eval datasets is one of the easiest ways to contribute! Fill out the details below.
- type: input
id: name
attributes:
label: Dataset Name
placeholder: e.g., HumanEval, GSM8K
validations:
required: true
- type: input
id: url
attributes:
label: URL / Reference
description: Link to the dataset or paper.
placeholder: https://...
validations:
required: true
- type: checkboxes
id: capability
attributes:
label: What capability does it test?
options:
- label: Reasoning
- label: Math
- label: Code
- label: Knowledge
- label: Multimodal
- label: Tool Use
- label: Long Context
- label: Other
- type: input
id: size
attributes:
label: Approximate Size
description: Number of examples in the dataset.
placeholder: e.g., 500
validations:
required: false
- type: dropdown
id: scorer
attributes:
label: Proposed Scorer Type
options:
- Exact Match
- F1
- BLEU
- LLM-as-Judge
- Custom
validations:
required: false
- type: textarea
id: context
attributes:
label: Additional Context
description: Any other details about this dataset.
validations:
required: false
@@ -0,0 +1,98 @@
name: Pearl Model Validation
description: Track conversion and validation of a Pearl-compatible mining model
labels: ["type:feature", "area:mining"]
body:
- type: markdown
attributes:
value: |
Use this template when promoting a raw Hugging Face model to a
Pearl-compatible `pearl-ai/*-pearl` mining model. A model should remain
`planned` in OpenJarvis until this checklist is complete.
- type: input
id: raw_model
attributes:
label: Raw model
placeholder: e.g., Qwen/Qwen3.5-9B
validations:
required: true
- type: input
id: pearl_model
attributes:
label: Pearl model artifact
placeholder: e.g., pearl-ai/Qwen3.5-9B-pearl
validations:
required: true
- type: dropdown
id: target_provider
attributes:
label: Target provider
options:
- vllm-pearl
- cpu-pearl
- apple-mps-pearl
validations:
required: true
- type: textarea
id: quantization_recipe
attributes:
label: Quantization recipe
description: Link or paste the recipe used to create the Pearl model artifact.
placeholder: |
- compressed-tensors config:
- 7-bit mining layers:
- 8-bit non-mining layers:
- calibration data:
- SmoothQuant settings:
validations:
required: true
- type: textarea
id: hardware
attributes:
label: Validation hardware
placeholder: |
- GPU:
- VRAM:
- driver/CUDA:
- Docker image/tag:
- Pearl commit/ref:
validations:
required: true
- type: checkboxes
id: acceptance
attributes:
label: Acceptance checks
options:
- label: Model loads in Pearl's vLLM miner container
required: true
- label: vLLM registers Pearl's quantization plugin
required: true
- label: Mining layers use int7 NoisyGEMM
required: true
- label: Non-mining layers use int8 vanilla Pearl GEMM
required: true
- label: `jarvis mine init --model <pearl-model-id>` succeeds
required: true
- label: `jarvis mine start` succeeds
required: true
- label: `jarvis ask` succeeds through the mining engine
required: true
- label: `jarvis mine status` reports gateway/mining metrics
required: true
- label: `jarvis mine validate-model --allow-planned` passes
required: true
- label: Gateway/miner logs show no submission errors
required: true
- type: textarea
id: artifacts
attributes:
label: Artifacts
description: Attach logs, metrics, model config, and command output.
placeholder: |
- /v1/models output:
- `jarvis mine status` output:
- `jarvis mine validate-model --output` JSON:
- gateway metrics excerpt:
- miner logs:
- PR/commit that flips status to validated:
validations:
required: true
+7
View File
@@ -0,0 +1,7 @@
{
"schemaVersion": 1,
"label": "Git Clones",
"message": "190,252",
"color": "green",
"namedLogo": "git"
}
+146
View File
@@ -0,0 +1,146 @@
{
"total_clones": 190252,
"last_updated": "2026-08-14T07:19:51Z",
"daily": {
"2026-03-27": 2189,
"2026-03-28": 1874,
"2026-03-29": 933,
"2026-03-30": 755,
"2026-03-31": 872,
"2026-04-01": 539,
"2026-04-02": 806,
"2026-04-03": 1377,
"2026-04-04": 678,
"2026-04-05": 734,
"2026-04-06": 750,
"2026-04-07": 813,
"2026-04-08": 1280,
"2026-04-09": 981,
"2026-04-10": 1142,
"2026-04-11": 706,
"2026-04-12": 813,
"2026-04-13": 1058,
"2026-04-14": 868,
"2026-04-15": 895,
"2026-04-16": 889,
"2026-04-17": 2341,
"2026-04-18": 1487,
"2026-04-19": 1339,
"2026-04-20": 1428,
"2026-04-21": 1216,
"2026-04-22": 1768,
"2026-04-23": 1050,
"2026-04-24": 1245,
"2026-04-25": 1116,
"2026-04-26": 1211,
"2026-04-27": 1606,
"2026-04-28": 1090,
"2026-04-29": 1332,
"2026-04-30": 943,
"2026-05-01": 1252,
"2026-05-02": 1326,
"2026-05-03": 1832,
"2026-05-04": 1830,
"2026-05-05": 3854,
"2026-05-06": 1521,
"2026-05-07": 1216,
"2026-05-08": 661,
"2026-05-09": 796,
"2026-05-10": 814,
"2026-05-11": 1008,
"2026-05-12": 1390,
"2026-05-13": 1397,
"2026-05-14": 846,
"2026-05-15": 1671,
"2026-05-16": 2264,
"2026-05-17": 654,
"2026-05-18": 1425,
"2026-05-19": 850,
"2026-05-20": 954,
"2026-05-21": 1605,
"2026-05-22": 612,
"2026-05-23": 2437,
"2026-05-24": 4900,
"2026-05-25": 1319,
"2026-05-26": 1199,
"2026-05-27": 898,
"2026-05-28": 1276,
"2026-05-29": 2950,
"2026-05-30": 4338,
"2026-05-31": 1887,
"2026-06-01": 2072,
"2026-06-02": 1847,
"2026-06-03": 2164,
"2026-06-04": 2632,
"2026-06-05": 2127,
"2026-06-06": 2204,
"2026-06-07": 1174,
"2026-06-08": 2369,
"2026-06-09": 1361,
"2026-06-10": 1310,
"2026-06-11": 2564,
"2026-06-12": 1313,
"2026-06-13": 2804,
"2026-06-14": 1543,
"2026-06-15": 1379,
"2026-06-16": 1317,
"2026-06-17": 1170,
"2026-06-18": 1408,
"2026-06-19": 1350,
"2026-06-20": 1437,
"2026-06-21": 1426,
"2026-06-22": 1350,
"2026-06-23": 1468,
"2026-06-24": 1635,
"2026-06-25": 1640,
"2026-06-26": 1338,
"2026-06-27": 1338,
"2026-06-28": 1028,
"2026-06-29": 765,
"2026-06-30": 951,
"2026-07-01": 1134,
"2026-07-02": 593,
"2026-07-03": 537,
"2026-07-04": 411,
"2026-07-05": 485,
"2026-07-06": 555,
"2026-07-07": 905,
"2026-07-08": 1171,
"2026-07-09": 1857,
"2026-07-10": 1181,
"2026-07-11": 2185,
"2026-07-12": 1917,
"2026-07-13": 2102,
"2026-07-14": 2337,
"2026-07-15": 2362,
"2026-07-16": 2497,
"2026-07-17": 1773,
"2026-07-18": 1542,
"2026-07-19": 1445,
"2026-07-20": 1481,
"2026-07-21": 1528,
"2026-07-22": 1529,
"2026-07-23": 1209,
"2026-07-24": 1118,
"2026-07-25": 928,
"2026-07-26": 740,
"2026-07-27": 799,
"2026-07-28": 665,
"2026-07-29": 745,
"2026-07-30": 591,
"2026-07-31": 783,
"2026-08-01": 567,
"2026-08-02": 1248,
"2026-08-03": 724,
"2026-08-04": 708,
"2026-08-05": 647,
"2026-08-06": 604,
"2026-08-07": 624,
"2026-08-08": 706,
"2026-08-09": 1076,
"2026-08-10": 1060,
"2026-08-11": 2182,
"2026-08-12": 641,
"2026-08-13": 770
}
}
+16
View File
@@ -0,0 +1,16 @@
## What does this PR do?
<!-- Brief description of the change and its motivation -->
## How was this tested?
<!-- Describe tests added or manual testing performed -->
## Checklist
- [ ] Tests pass (`uv run pytest tests/ -v`)
- [ ] Linter passes (`uv run ruff check src/ tests/`)
- [ ] Formatter passes (`uv run ruff format --check src/ tests/`)
- [ ] New/changed public API has docstrings
- [ ] Follows registry pattern (if adding new component)
- [ ] Documentation updated (if applicable)
+92
View File
@@ -0,0 +1,92 @@
name: Auto-tag on main push
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: write
actions: write
jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Compute dev version
id: version
run: |
set -euo pipefail
# Base version is the next patch above the latest plain release tag
# (vX.Y.Z) reachable from HEAD. pyproject.toml no longer carries a
# static version (#526 switched it to hatch-vcs), so the release tag
# is the source of truth. `.devN`/`.rcN`/`desktop-*` tags are excluded
# so they can't be mistaken for the release base.
# Any future manual `X.Y.Z` release will outrank every `X.Y.Z.devN`
# autotag — PEP 440 sorts dev releases strictly below the final.
LATEST_RELEASE=$(git tag --list 'v[0-9]*' --merged HEAD \
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)
if [[ -z "$LATEST_RELEASE" ]]; then
echo "::error::No release tag (vX.Y.Z) reachable from HEAD"
exit 1
fi
BASE="${LATEST_RELEASE#v}"
MAJOR=$(echo "$BASE" | cut -d. -f1)
MINOR=$(echo "$BASE" | cut -d. -f2)
PATCH=$(echo "$BASE" | cut -d. -f3 | sed -E 's/[^0-9].*$//')
NEXT_PATCH=$((PATCH + 1))
BUILD=$(git rev-list --count HEAD)
VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}.dev${BUILD}"
TAG="v${VERSION}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "Computed ${TAG} (base=${BASE})"
- name: Create and push tag
id: tag
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
set -euo pipefail
if git rev-parse "$TAG" >/dev/null 2>&1; then
EXISTING_SHA=$(git rev-parse "$TAG")
HEAD_SHA=$(git rev-parse HEAD)
if [[ "$EXISTING_SHA" != "$HEAD_SHA" ]]; then
echo "::error::Tag $TAG already exists at $EXISTING_SHA but HEAD is $HEAD_SHA"
exit 1
fi
echo "Tag $TAG already exists at HEAD, skipping creation"
echo "created=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG"
git push origin "$TAG"
echo "Created and pushed $TAG"
echo "created=true" >> "$GITHUB_OUTPUT"
# Tag pushes made with the default GITHUB_TOKEN do NOT trigger other
# workflows (recursion prevention). workflow_dispatch is the documented
# exception, so we explicitly dispatch the downstream CD workflows here.
# See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication
- name: Dispatch downstream workflows
if: steps.tag.outputs.created == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
set -euo pipefail
echo "Dispatching pypi-publish.yml @ ${TAG}"
gh workflow run pypi-publish.yml \
--ref "${TAG}" \
-f tag="${TAG}"
echo "Dispatching desktop.yml @ ${TAG}"
gh workflow run desktop.yml \
--ref "${TAG}" \
-f tag="${TAG}"
+27
View File
@@ -0,0 +1,27 @@
name: Bash tests
on:
pull_request:
paths:
- 'scripts/install/**'
- 'tests/install/bash/**'
- '.github/workflows/bash-tests.yml'
push:
branches: [main]
paths:
- 'scripts/install/**'
- 'tests/install/bash/**'
jobs:
bats:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install bats-core
run: |
sudo apt-get update
sudo apt-get install -y bats
- name: Run bats tests
run: bats tests/install/bash/
+181
View File
@@ -0,0 +1,181 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v8.0.0
with:
enable-cache: true
- name: Install dependencies
run: uv sync --extra dev --extra framework-comparison --extra server
- name: Ruff check
run: uv run ruff check src/ tests/
- name: Ruff format check
run: uv run ruff format --check src/ tests/
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cargo cache
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: rust-${{ runner.os }}-${{ hashFiles('rust/Cargo.lock') }}
restore-keys: rust-${{ runner.os }}-
- name: Install uv
uses: astral-sh/setup-uv@v8.0.0
with:
enable-cache: true
- name: Install dependencies
run: uv sync --extra dev --extra framework-comparison --extra server
- name: Build Rust extension
run: uv run maturin develop --manifest-path rust/crates/openjarvis-python/Cargo.toml
- name: Run tests
# COVERAGE_CORE=sysmon uses CPython 3.12's sys.monitoring backend,
# which is dramatically cheaper than the default C trace function.
# -n auto fans the suite out across all runner cores via pytest-xdist.
env:
COVERAGE_CORE: sysmon
run: |
uv run pytest tests/ -n auto -q --tb=short -m "not live and not cloud and not hub" \
--cov=openjarvis \
--cov-report=term-missing \
--cov-report=xml \
--cov-fail-under=60
- name: Upload coverage report
if: always()
continue-on-error: true
timeout-minutes: 5
uses: actions/upload-artifact@v4
with:
name: coverage-xml
path: coverage.xml
if-no-files-found: warn
# Windows job — empirically exercises the platform-specific code paths that
# the Ubuntu `test` job can never reach: GlobalMemoryStatusEx RAM detection
# (#373) and the cp9xx -> UTF-8 stdout reconfigure (#293). Also the only CI
# job that builds + imports the mandatory `openjarvis_rust` PyO3 extension
# on Windows. Public repo -> Windows runner minutes are free.
#
# All `run:` steps use static commands only (no `github.event.*`
# interpolation), so there is no workflow-injection surface here.
test-windows:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
# 3.12 = common; 3.13 = the supported ceiling — installing there guards
# against a numpy/native wheel gap at the top of the range (#350), which
# is exactly how the source-build failure slips in on Windows.
python-version: ["3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v8.0.0
with:
enable-cache: true
- name: Install dependencies
run: uv sync --extra dev --extra server
# Pure-Python check — runs before the Rust build so a flaky toolchain
# install can never mask the actual RAM-detection verification.
- name: Verify Windows RAM detection (#373)
shell: bash
run: |
uv run python -c "from openjarvis.core.config import _total_ram_gb; ram = _total_ram_gb(); print(f'GlobalMemoryStatusEx RAM = {ram} GB'); assert ram > 0, f'Windows RAM detection returned {ram}, expected > 0'"
- name: Run Windows-specific tests (hardware + CLI)
shell: bash
run: |
uv run pytest tests/hardware/test_hardware_profiles.py tests/cli/test_cli.py -v -m "not live and not cloud"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Build + import the PyO3 extension on Windows
shell: bash
run: |
uv run maturin develop --manifest-path rust/crates/openjarvis-python/Cargo.toml
uv run python -c "import openjarvis_rust; print('openjarvis_rust imports on Windows OK')"
- name: Smoke-test CLI
shell: bash
run: |
uv run jarvis --version
rust:
runs-on: ubuntu-latest
defaults:
run:
working-directory: rust
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cargo cache
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: rust-${{ runner.os }}-${{ hashFiles('rust/Cargo.lock') }}
restore-keys: rust-${{ runner.os }}-
- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Test
run: cargo test --workspace
+86
View File
@@ -0,0 +1,86 @@
name: Claude Issue Fixer
on:
issues:
types: [opened, labeled]
issue_comment:
types: [created]
workflow_dispatch:
concurrency:
group: claude-issues-${{ github.event.issue.number || github.run_id }}
cancel-in-progress: true
# Least-privilege: only what the issue-fixer job actually needs.
# id-token (OIDC) is intentionally omitted — claude-code-action@v1 is passed
# github_token directly, so OIDC is unused here.
permissions:
contents: write
pull-requests: write
issues: write
jobs:
fix:
runs-on: ubuntu-latest
timeout-minutes: 15
# Security gate: this job reaches secrets.ANTHROPIC_API_KEY and holds a
# write-scoped GITHUB_TOKEN. `issues` / `issue_comment` are public,
# attacker-controllable events that run in the base-repo context with full
# secret access, so the human-triggered paths are restricted to actors with
# write-level association (OWNER / MEMBER / COLLABORATOR). This blocks
# external / first-time contributors from draining the API budget or
# creating branches/PRs, while leaving maintainer use unaffected.
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issues' &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association) &&
(contains(github.event.issue.labels.*.name, 'bug') ||
contains(github.event.issue.labels.*.name, 'autofix'))) ||
(github.event_name == 'issue_comment' &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) &&
!github.event.issue.pull_request &&
contains(github.event.comment.body, '@claude') &&
github.actor != 'claude[bot]')
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: |
You are an automated issue fixer for the OpenJarvis repository. Follow these steps in order:
## Step 1: Diagnose
Read the issue thoroughly. Explore the codebase to understand the problem. If this is a bug report, attempt to reproduce it. Identify the root cause and affected files.
## Step 2: Comment your plan
BEFORE making any code changes, post a comment on this issue describing:
- Your root cause analysis
- Which files need to change and why
- Your implementation approach
## Step 3: Implement
Create a branch named `claude/issue-${{ github.event.issue.number }}` and make the changes. Use conventional commit messages that reference the issue, e.g.:
`fix: handle empty tool responses in orchestrator (fixes #${{ github.event.issue.number }})`
## Step 4: Test
Run these commands and ensure both pass:
- `uv run ruff check src/ tests/` (linting)
- `uv run pytest tests/ -v --tb=short` (tests)
If tests fail, fix the issues before proceeding. If you added new functionality, add corresponding tests in `tests/` mirroring the `src/` directory structure.
## Step 5: Open PR
Create a pull request that:
- Links back to this issue (include `Fixes #${{ github.event.issue.number }}` in the PR body)
- Describes what was changed and why
- Includes a summary of test results
## If you cannot fix it
If you cannot reproduce the issue, cannot determine the root cause, or the fix is beyond your capabilities, post a comment explaining:
- What you investigated
- What you found (or didn't find)
- What additional information you need from the reporter
+58
View File
@@ -0,0 +1,58 @@
name: Claude PR Review
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
workflow_dispatch:
concurrency:
group: claude-review-${{ github.event.pull_request.number || github.event.issue.number || github.run_id }}
cancel-in-progress: true
# Least-privilege: PR review only needs to post comments on the PR.
# id-token (OIDC) is omitted — claude-code-action@v1 is passed github_token
# directly, so OIDC is unused here.
permissions:
contents: read
pull-requests: write
issues: write
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 30
# Security gate: this job reaches secrets.ANTHROPIC_API_KEY. Both
# issue_comment and pull_request_review_comment are public,
# attacker-controllable events that run in the base-repo context with full
# secret access, so the @claude paths are restricted to actors with
# write-level association (OWNER / MEMBER / COLLABORATOR). External /
# first-time contributors cannot trigger the key; maintainers are unaffected.
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issue_comment' &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '@claude') &&
github.actor != 'claude[bot]') ||
(github.event_name == 'pull_request_review_comment' &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) &&
contains(github.event.comment.body, '@claude') &&
github.actor != 'claude[bot]')
steps:
- uses: actions/checkout@v6
- name: Read review instructions
id: review
run: |
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "instructions<<$EOF" >> "$GITHUB_OUTPUT"
cat REVIEW.md >> "$GITHUB_OUTPUT"
echo "$EOF" >> "$GITHUB_OUTPUT"
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: ${{ steps.review.outputs.instructions }}
+288 -40
View File
@@ -2,21 +2,23 @@ name: Desktop Build & Release
on:
push:
branches: [main]
paths:
- 'desktop/**'
- '.github/workflows/desktop.yml'
tags:
- 'v*'
- 'desktop-v*'
pull_request:
branches: [main]
paths:
- 'desktop/**'
- 'frontend/**'
- '.github/workflows/desktop.yml'
workflow_dispatch:
inputs:
tag:
description: 'Tag to build (e.g. v1.0.2.dev500). If set, autotag dispatches use this. github.ref still controls the checkout.'
required: false
type: string
concurrency:
group: desktop-${{ github.ref }}
group: desktop-${{ inputs.tag || github.ref }}
cancel-in-progress: true
permissions:
@@ -27,7 +29,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install system dependencies
run: |
@@ -38,39 +40,64 @@ jobs:
libappindicator3-dev \
librsvg2-dev \
patchelf \
libxdo-dev
libxdo-dev \
libdbus-1-dev
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: '22'
- name: Install frontend dependencies
working-directory: desktop
working-directory: frontend
run: npm install
- name: TypeScript type-check
working-directory: desktop
working-directory: frontend
run: npx tsc --noEmit
- name: Vite build
working-directory: desktop
run: npx vite build
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: 'desktop/src-tauri -> target'
workspaces: 'frontend/src-tauri -> target'
- name: Cargo check
working-directory: desktop/src-tauri
run: cargo check
- name: Create frontend dist stub
run: mkdir -p frontend/dist && echo '<html><body></body></html>' > frontend/dist/index.html
# `cargo test` builds the crate (same coverage as the old `cargo check`)
# and runs the unit tests, including the #331 uv-sync error-formatting
# helpers. Static command, no untrusted input — no injection surface.
- name: Cargo test
working-directory: frontend/src-tauri
run: cargo test
# Remove stale artifacts from the desktop-edge rolling pre-release so that
# only the current build's files are available for download. (The stable
# `desktop-latest` channel the installed app polls is never cleaned here.)
clean-release:
needs: [validate]
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Delete old assets from desktop-edge
if: "!startsWith(github.ref, 'refs/tags/')"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="desktop-edge"
# List all asset IDs on the release and delete them
ASSET_IDS=$(gh api "repos/${{ github.repository }}/releases/tags/${TAG}" \
--jq '.assets[].id' 2>/dev/null || true)
for id in $ASSET_IDS; do
echo "Deleting asset $id"
gh api -X DELETE "repos/${{ github.repository }}/releases/assets/$id" || true
done
build-and-release:
needs: [validate]
needs: [validate, clean-release]
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
strategy:
@@ -79,17 +106,118 @@ jobs:
include:
- platform: ubuntu-22.04
args: ''
- platform: macos-latest
args: '--target aarch64-apple-darwin'
- platform: macos-13
args: '--target x86_64-apple-darwin'
- platform: macos-14
args: '--target universal-apple-darwin'
- platform: windows-latest
args: ''
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
# Full history + tags so the workflow_dispatch fallback in
# "Determine release info" can derive the dev version from the
# latest release tag (#526).
fetch-depth: 0
# Validate Apple credentials BEFORE the expensive work. Notarization is
# the very last thing `tauri-action` does, so a bad credential or a
# lapsed account agreement previously surfaced ~10 minutes in — after the
# Rust toolchain, npm install, two Ollama sidecar downloads and a
# universal cargo build — as a single opaque line:
#
# failed to bundle project: failed codesign application: failed to
# notarize app: Error: HTTP status code: 403. ...
#
# `notarytool history` is a read-only call (it submits nothing) that
# exercises the identical auth path, so every credential/account failure
# mode reaches us here first, in seconds, with the specific cause named.
# `xcrun` is preinstalled on macOS runners, hence placement before the
# toolchain steps rather than next to "Configure Apple signing".
- name: Preflight Apple notarization credentials
if: matrix.platform == 'macos-14'
env:
CERT: ${{ secrets.APPLE_CERTIFICATE }}
A_ID: ${{ secrets.APPLE_ID }}
A_PASS: ${{ secrets.APPLE_PASSWORD }}
A_TEAM: ${{ secrets.APPLE_TEAM_ID }}
shell: bash
run: |
set -uo pipefail
# Mirror the skip logic in "Configure Apple signing": without a
# certificate the build is unsigned and never notarizes, so there is
# nothing to preflight. Tag builds still hard-fail there.
if [ -z "$CERT" ]; then
echo "No Apple certificate configured; skipping notarization preflight."
exit 0
fi
missing=""
[ -z "$A_ID" ] && missing="$missing APPLE_ID"
[ -z "$A_PASS" ] && missing="$missing APPLE_PASSWORD"
[ -z "$A_TEAM" ] && missing="$missing APPLE_TEAM_ID"
if [ -n "$missing" ]; then
echo "::error::APPLE_CERTIFICATE is set but notarization secrets are missing:$missing"
echo "::error::Signing would succeed and notarization would then fail. Set them or clear APPLE_CERTIFICATE."
exit 1
fi
# Retry only to absorb transient network faults. Credential and
# account errors are deterministic, so we classify and exit on the
# first definitive answer rather than retrying into the same wall.
attempt=1
while [ "$attempt" -le 3 ]; do
out=$(xcrun notarytool history \
--apple-id "$A_ID" \
--team-id "$A_TEAM" \
--password "$A_PASS" \
--output-format json 2>&1)
rc=$?
if [ $rc -eq 0 ]; then
echo "Apple notarization preflight OK — credentials valid, team reachable, agreements in effect."
exit 0
fi
case "$out" in
*"Invalid credentials"*|*"401"*)
echo "::error::Apple notarization preflight failed: invalid credentials (HTTP 401)."
echo "::error::APPLE_PASSWORD must be an app-specific password from appleid.apple.com,"
echo "::error::generated while signed in as the SAME Apple ID as APPLE_ID. A regular"
echo "::error::Apple ID password will not work, and a password minted under a different"
echo "::error::Apple ID authenticates as that other account."
exit 1
;;
*"Invalid or inaccessible developer team ID"*)
echo "::error::Apple notarization preflight failed: APPLE_ID is not a member of team APPLE_TEAM_ID (HTTP 403)."
echo "::error::The Team ID must match the signing certificate. Read it from the cert's"
echo "::error::subject, where it appears as: Developer ID Application: NAME (TEAMID)."
echo "::error::If you belong to several teams, confirm APPLE_ID is a member of this one."
exit 1
;;
*"required agreement"*|*"agreement"*)
echo "::error::Apple notarization preflight failed: the team has no in-effect agreement (HTTP 403)."
echo "::error::Apple reissues the Developer Program License Agreement periodically and"
echo "::error::notarization is refused until it is accepted. ONLY THE ACCOUNT HOLDER can"
echo "::error::accept it — team Admins cannot. Sign in to the account that owns this team:"
echo "::error:: 1. https://developer.apple.com/account -> review any pending agreement"
echo "::error:: 2. App Store Connect -> Business -> accept anything pending there too"
echo "::error::Certificates stay valid while this is outstanding, so signing still works."
exit 1
;;
esac
echo "Preflight attempt ${attempt}/3 failed with a non-credential error."
echo "$out" | tail -5
attempt=$((attempt + 1))
[ "$attempt" -le 3 ] && sleep 10
done
echo "::error::Apple notarization preflight failed after 3 attempts. Last output:"
echo "$out" | tail -20
exit 1
- name: Install system dependencies (Linux)
if: matrix.platform == 'ubuntu-22.04'
@@ -101,55 +229,135 @@ jobs:
libappindicator3-dev \
librsvg2-dev \
patchelf \
libxdo-dev
libxdo-dev \
libdbus-1-dev
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin' || matrix.platform == 'macos-13' && 'x86_64-apple-darwin' || '' }}
targets: ${{ matrix.platform == 'macos-14' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: 'desktop/src-tauri -> target'
workspaces: 'frontend/src-tauri -> target'
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: '22'
- name: Install frontend dependencies
working-directory: desktop
working-directory: frontend
run: npm install
- name: Download Ollama sidecar
shell: bash
run: |
cd frontend/src-tauri/scripts && chmod +x download-ollama.sh
if [[ "${{ matrix.platform }}" == "macos-14" ]]; then
./download-ollama.sh aarch64-apple-darwin
./download-ollama.sh x86_64-apple-darwin
else
./download-ollama.sh
fi
- name: Determine release info
id: release-info
shell: bash
run: |
if [[ "${{ github.ref }}" == refs/tags/desktop-v* ]]; then
# Explicit stable desktop release tag
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#desktop-v}"
echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
echo "name=Desktop ${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
echo "prerelease=false" >> "$GITHUB_OUTPUT"
else
echo "tag=desktop-latest" >> "$GITHUB_OUTPUT"
echo "name=Desktop (Latest Build)" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
# Auto-tagged rolling build from autotag.yml — use the same
# version as the CLI/PyPI release so all surfaces stay in sync.
# Rolling/dev builds go to the `desktop-edge` channel, NOT the
# `desktop-latest` channel the installed app polls — so users on
# stable are never auto-updated onto an unvetted dev build.
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
echo "tag=desktop-edge" >> "$GITHUB_OUTPUT"
echo "name=Desktop (Edge Build)" >> "$GITHUB_OUTPUT"
echo "prerelease=true" >> "$GITHUB_OUTPUT"
else
# workflow_dispatch fallback (manual UI dispatch without --ref).
# Derive a PEP 440 dev version aligned with autotag.yml so we
# don't burn the X.Y.Z release-version namespace.
# pyproject.toml no longer carries a static version (#526), so the
# base comes from the latest plain release tag (vX.Y.Z), matching
# autotag.yml. .dev/.rc/desktop-* tags are excluded.
LATEST_RELEASE=$(git tag --list 'v[0-9]*' --merged HEAD \
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)
if [[ -z "$LATEST_RELEASE" ]]; then
echo "::error::No release tag (vX.Y.Z) reachable from HEAD"
exit 1
fi
BASE="${LATEST_RELEASE#v}"
MAJOR=$(echo "$BASE" | cut -d. -f1)
MINOR=$(echo "$BASE" | cut -d. -f2)
PATCH=$(echo "$BASE" | cut -d. -f3 | sed -E 's/[^0-9].*$//')
NEXT_PATCH=$((PATCH + 1))
BUILD=$(git rev-list --count HEAD)
VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}.dev${BUILD}"
# Manual dispatches are also dev builds -> the edge channel.
echo "tag=desktop-edge" >> "$GITHUB_OUTPUT"
echo "name=Desktop (Edge Build)" >> "$GITHUB_OUTPUT"
echo "prerelease=true" >> "$GITHUB_OUTPUT"
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
# Tauri's build script requires strict SemVer (MAJOR.MINOR.PATCH[-pre][+build]).
# PEP 440 dev releases (`1.0.2.dev661`) are NOT valid SemVer, so we
# translate `.devN` to the SemVer-equivalent `-dev.N` prerelease form.
# PyPI keeps the PEP 440 form; only the Tauri bundle uses SemVer.
TAURI_VERSION="${VERSION/.dev/-dev.}"
echo "tauri_version=${TAURI_VERSION}" >> "$GITHUB_OUTPUT"
- name: Configure Apple signing
if: runner.os == 'macOS'
env:
CERT: ${{ secrets.APPLE_CERTIFICATE }}
CERT_PASS: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
SIGN_ID: ${{ secrets.APPLE_SIGNING_IDENTITY }}
A_ID: ${{ secrets.APPLE_ID }}
A_PASS: ${{ secrets.APPLE_PASSWORD }}
A_TEAM: ${{ secrets.APPLE_TEAM_ID }}
shell: bash
run: |
if [ -n "$CERT" ]; then
echo "APPLE_CERTIFICATE=$CERT" >> "$GITHUB_ENV"
echo "APPLE_CERTIFICATE_PASSWORD=$CERT_PASS" >> "$GITHUB_ENV"
echo "APPLE_SIGNING_IDENTITY=$SIGN_ID" >> "$GITHUB_ENV"
echo "APPLE_ID=$A_ID" >> "$GITHUB_ENV"
echo "APPLE_PASSWORD=$A_PASS" >> "$GITHUB_ENV"
echo "APPLE_TEAM_ID=$A_TEAM" >> "$GITHUB_ENV"
echo "Apple signing configured"
else
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "::error::Apple signing secrets are required for release builds"
exit 1
fi
echo "No Apple certificate configured, skipping code signing"
fi
- name: Build and release
timeout-minutes: 120
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_CONFIG: '{"version":"${{ steps.release-info.outputs.tauri_version }}","bundle":{"externalBin":["binaries/ollama"]}}'
# tauri-action runs beforeBuildCommand (npm run build:tauri -> vite
# build), which requires this at build time (#587). Strict for
# releases: a missing/empty secret fails the build by design.
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
with:
projectPath: desktop
projectPath: frontend
tauriScript: npx tauri
tagName: ${{ steps.release-info.outputs.tag }}
releaseName: ${{ steps.release-info.outputs.name }}
@@ -158,3 +366,43 @@ jobs:
prerelease: ${{ steps.release-info.outputs.prerelease }}
includeUpdaterJson: true
args: ${{ matrix.args }}
# When a stable `desktop-v*` release is published, repoint the
# `desktop-latest` auto-update channel (the endpoint the installed app
# polls) at it. The stable release's own `latest.json` already references
# this release's signed assets, so we copy it verbatim — installed apps are
# only ever offered vetted stable builds, never `desktop-edge` dev builds.
refresh-stable-channel:
needs: [build-and-release]
if: startsWith(github.ref, 'refs/tags/desktop-v')
runs-on: ubuntu-latest
steps:
- name: Mirror stable latest.json into desktop-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
STABLE_TAG: ${{ github.ref_name }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
# The stable release's updater manifest may take a moment to become
# downloadable after tauri-action publishes it; retry briefly.
URL="https://github.com/${REPO}/releases/download/${STABLE_TAG}/latest.json"
for attempt in 1 2 3 4 5; do
if curl -fsSL -o latest.json "$URL"; then
echo "Fetched ${STABLE_TAG}/latest.json on attempt ${attempt}"
break
fi
echo "latest.json not ready yet (attempt ${attempt}); sleeping 15s"
sleep 15
done
test -s latest.json || { echo "::error::Could not fetch ${URL}"; exit 1; }
# Ensure the channel release exists (prerelease so it never usurps
# the stable "Latest" badge), then replace its manifest in place.
if ! gh release view desktop-latest --repo "$REPO" >/dev/null 2>&1; then
gh release create desktop-latest --repo "$REPO" \
--prerelease \
--title "Desktop Auto-Update Channel" \
--notes "Auto-update channel pointer for the desktop app. Mirrors the latest stable \`desktop-v*\` release; the in-app updater polls this \`latest.json\`. Download the app from the latest stable release, not here."
fi
gh release upload desktop-latest latest.json --repo "$REPO" --clobber
echo "desktop-latest now mirrors ${STABLE_TAG}"
+25 -5
View File
@@ -28,25 +28,45 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v8.0.0
- name: Install dependencies
run: uv sync --extra docs
# Inject the public Supabase anon key so the savings leaderboard works on
# the published docs site. Missing/empty (e.g. fork PRs) leaves the
# leaderboard gracefully disabled. The key is read from env (not inlined)
# and JSON-encoded into a JS string literal to avoid any injection.
- name: Inject leaderboard Supabase anon key
env:
OPENJARVIS_LEADERBOARD_ANON: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
run: |
python3 - <<'PY'
import json, os, pathlib
key = os.environ.get("OPENJARVIS_LEADERBOARD_ANON", "")
pathlib.Path("docs/javascripts/leaderboard-config.js").write_text(
"// Generated at docs-build time from the VITE_SUPABASE_ANON_KEY secret.\n"
"window.OPENJARVIS_SUPABASE_ANON_KEY = " + json.dumps(key) + ";\n",
encoding="utf-8",
)
print("leaderboard anon key:", "set" if key else "empty (leaderboard disabled)")
PY
- name: Build documentation
run: uv run mkdocs build
- name: Upload artifact
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v5.0.0
with:
path: site/
@@ -60,4 +80,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
+42
View File
@@ -0,0 +1,42 @@
name: Frontend CI
on:
push:
branches: [main]
paths:
- 'frontend/**'
- '.github/workflows/frontend.yml'
pull_request:
branches: [main]
paths:
- 'frontend/**'
- '.github/workflows/frontend.yml'
workflow_dispatch:
concurrency:
group: frontend-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- run: npm ci
- run: npx tsc --noEmit
- run: npm run build
env:
# Optional: when the secret is unset the build still succeeds and the
# leaderboard is disabled (see src/lib/supabase.ts). No placeholder,
# so a keyless CI build doesn't bake in a bogus anon key.
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
+118
View File
@@ -0,0 +1,118 @@
name: Installer integration
on:
pull_request:
paths:
- 'scripts/install/**'
- 'src/openjarvis/cli/**'
- 'tests/install/**'
- '.github/workflows/installer-integration.yml'
schedule:
- cron: '0 6 * * *'
jobs:
container-matrix:
name: ${{ matrix.image }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image:
- ubuntu:22.04
- ubuntu:24.04
- fedora:40
container: ${{ matrix.image }}
steps:
- name: Install prereqs (Ubuntu/Debian)
if: contains(matrix.image, 'ubuntu') || contains(matrix.image, 'debian')
run: |
apt-get update
apt-get install -y curl git python3 sudo
- name: Install prereqs (Fedora)
if: contains(matrix.image, 'fedora')
run: |
dnf install -y curl git python3 sudo
- name: Create non-root user
run: useradd -m -s /bin/bash testuser
- uses: actions/checkout@v4
with:
path: openjarvis-src
- name: Install Ollama mock
run: install -m 755 "$GITHUB_WORKSPACE/openjarvis-src/tests/install/bash/stubs/ollama-mock" /usr/local/bin/ollama
- name: Run installer
run: |
chown -R testuser:testuser /home/testuser "$GITHUB_WORKSPACE/openjarvis-src"
su testuser -c '
export OPENJARVIS_REPO_URL=file://'"$GITHUB_WORKSPACE"'/openjarvis-src
cd '"$GITHUB_WORKSPACE"'/openjarvis-src
bash scripts/install/install.sh --no-bg-orchestrator
'
- name: Verify install state
run: |
su testuser -c '
test -d ~/.openjarvis/src
test -d ~/.openjarvis/.venv
test -f ~/.openjarvis/config.toml
test -f ~/.openjarvis/.state/install-state.json
test -L ~/.local/bin/jarvis
'
- name: Verify jarvis --version
run: su testuser -c '~/.local/bin/jarvis --version'
- name: Verify jarvis doctor exits 0
run: su testuser -c '~/.local/bin/jarvis doctor'
- name: Verify uninstall is clean
run: |
su testuser -c '
~/.local/bin/jarvis-uninstall
test ! -d ~/.openjarvis
test ! -L ~/.local/bin/jarvis
'
macos:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-14, macos-15]
steps:
- uses: actions/checkout@v4
- name: Install Ollama mock
run: sudo install -m 755 tests/install/bash/stubs/ollama-mock /usr/local/bin/ollama
- name: Run installer
run: |
export OPENJARVIS_REPO_URL=file://$(pwd)
bash scripts/install/install.sh --no-bg-orchestrator
- name: Verify install state
run: |
test -d ~/.openjarvis/src
test -d ~/.openjarvis/.venv
test -f ~/.openjarvis/config.toml
test -L ~/.local/bin/jarvis
- name: Verify jarvis --version
run: ~/.local/bin/jarvis --version
- name: Verify jarvis doctor exits 0
run: ~/.local/bin/jarvis doctor
- name: Verify uninstall is clean
run: |
~/.local/bin/jarvis-uninstall
test ! -d ~/.openjarvis
test ! -L ~/.local/bin/jarvis
+114
View File
@@ -0,0 +1,114 @@
name: Publish to PyPI
on:
release:
types: [published]
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish (e.g. v1.0.2.dev500). Overrides github.ref.'
required: false
type: string
dry_run:
description: 'Dry run: build + validate, then publish to TestPyPI instead of PyPI (no production upload).'
required: false
default: false
type: boolean
permissions:
contents: read
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
environment: pypi
steps:
- name: Resolve target ref
id: ref
env:
INPUT_TAG: ${{ inputs.tag }}
DEFAULT_REF: ${{ github.ref_name }}
run: |
set -euo pipefail
if [[ -n "$INPUT_TAG" ]]; then
echo "ref=${INPUT_TAG}" >> "$GITHUB_OUTPUT"
else
echo "ref=${DEFAULT_REF}" >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@v6
with:
ref: ${{ steps.ref.outputs.ref }}
- name: Install uv
uses: astral-sh/setup-uv@v8.0.0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Build frontend and bundle into package
env:
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
run: |
set -euo pipefail
cd frontend
npm ci
# Vite is configured (frontend/vite.config.ts) with
# `outDir: '../src/openjarvis/server/static'` and
# `emptyOutDir: true`, so the build writes directly into the
# Python package's static dir and clears stale assets itself.
# No rm/cp is needed — and the previous `dist/`-assuming logic
# was broken because `frontend/dist/` is never produced.
npm run build
STATIC=../src/openjarvis/server/static
test -s "$STATIC/index.html" || {
echo "::error::${STATIC}/index.html missing or empty after build"
exit 1
}
- name: Resolve build version from tag
env:
REF: ${{ steps.ref.outputs.ref }}
run: |
set -euo pipefail
# Strip leading "v" (e.g. v1.0.3.dev825 -> 1.0.3.dev825).
VERSION="${REF#v}"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
echo "::error::ref '$REF' is not a version tag (expected vX.Y.Z[.devN]); pass -f tag=vX.Y.Z"
exit 1
fi
# pyproject.toml is now dynamic = ["version"] via hatch-vcs (#526), so
# there is no static line to sed. setuptools_scm cannot bump custom
# `.devN` tags, so we pin the exact build version explicitly — the
# published version always equals the pushed tag.
echo "SETUPTOOLS_SCM_PRETEND_VERSION=${VERSION}" >> "$GITHUB_ENV"
echo "Building version ${VERSION}"
- name: Build package
run: uv build
- name: Publish to TestPyPI (dry run)
if: ${{ inputs.dry_run }}
env:
UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
run: |
set -euo pipefail
if [[ -z "${UV_PUBLISH_TOKEN:-}" ]]; then
echo "::warning::TEST_PYPI_API_TOKEN is not set — skipping the TestPyPI upload."
echo "Build + twine check passed, which validated version derivation and packaging end to end."
echo "To exercise a real upload, add a TEST_PYPI_API_TOKEN secret (or a TestPyPI trusted publisher)."
exit 0
fi
uv publish --publish-url https://test.pypi.org/legacy/
- name: Publish to PyPI
if: ${{ !inputs.dry_run }}
run: uv publish
+29
View File
@@ -0,0 +1,29 @@
name: Auto-assign on "take"
on:
issue_comment:
types: [created]
permissions:
issues: write
jobs:
assign:
if: >-
!github.event.issue.pull_request
&& contains(fromJSON('["take", "Take", "TAKE"]'), github.event.comment.body)
runs-on: ubuntu-latest
steps:
- name: Assign commenter
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
COMMENTER="${{ github.event.comment.user.login }}"
ISSUE="${{ github.event.issue.number }}"
CURRENT=$(gh issue view "$ISSUE" --repo "${{ github.repository }}" --json assignees --jq '.assignees[].login' 2>/dev/null)
if echo "$CURRENT" | grep -qx "$COMMENTER"; then
echo "$COMMENTER is already assigned to #$ISSUE"
else
gh issue edit "$ISSUE" --repo "${{ github.repository }}" --add-assignee "$COMMENTER"
echo "Assigned $COMMENTER to #$ISSUE"
fi
+86
View File
@@ -0,0 +1,86 @@
name: Track Git Clones
on:
schedule:
- cron: '0 6 * * *' # Daily at 06:00 UTC
workflow_dispatch:
permissions:
contents: write
jobs:
track-clones:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
token: ${{ secrets.TRAFFIC_TOKEN }}
- name: Fetch clone traffic
env:
GH_TOKEN: ${{ secrets.TRAFFIC_TOKEN }}
run: |
gh api repos/${{ github.repository }}/traffic/clones > /tmp/traffic.json
- name: Update accumulated data
run: |
python3 - <<'PYEOF'
import json
from datetime import datetime, timezone
# Load current traffic data from API
with open("/tmp/traffic.json") as f:
traffic = json.load(f)
# Load accumulated data
data_path = ".github/clone-stats/clone-data.json"
with open(data_path) as f:
accumulated = json.load(f)
daily = accumulated.get("daily", {})
# Merge new daily entries (keyed by date to avoid double-counting)
for entry in traffic.get("clones", []):
date_key = entry["timestamp"][:10] # "2026-03-26"
daily[date_key] = entry["count"] # total clones, not unique
# Recalculate total from all daily data
total = sum(daily.values())
# Update accumulated data
accumulated["daily"] = dict(sorted(daily.items()))
accumulated["total_clones"] = total
accumulated["last_updated"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
with open(data_path, "w") as f:
json.dump(accumulated, f, indent=2)
f.write("\n")
# Update shields.io endpoint badge
badge = {
"schemaVersion": 1,
"label": "Git Clones",
"message": f"{total:,}",
"color": "green",
"namedLogo": "git"
}
with open(".github/clone-stats/badge.json", "w") as f:
json.dump(badge, f, indent=2)
f.write("\n")
print(f"Updated: {total:,} total clones across {len(daily)} days")
PYEOF
- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .github/clone-stats/
if git diff --cached --quiet; then
echo "No changes to commit"
else
git commit -m "chore: update clone traffic data [skip ci]"
git push
fi
+77 -3
View File
@@ -23,6 +23,7 @@ env/
# Testing
.pytest_cache/
.ruff_cache/
.coverage
htmlcov/
@@ -38,11 +39,27 @@ Thumbs.db
# Secrets
.env
.env.*
# ...but keep checked-in example/templates (never contain real secrets)
!.env.example
!**/.env.example
# Project
*.sqlite
*.db
*.jsonl
*.npz
*.log
results/
logs/
# Anchored to repo root — DO NOT use the unanchored form `traces/`.
# hatchling honors .gitignore when building the wheel; an unanchored
# `traces/` pattern matches src/openjarvis/traces/ and silently drops
# the runtime module from the wheel (issue #372).
/traces/
coding_task_*
get-pip.py
# Junk from mocked-path tests that write to their mock's __repr__ as a path
MagicMock/
# MkDocs build output
site/
@@ -50,9 +67,66 @@ site/
# Frontend
frontend/node_modules/
frontend/dist/
# Desktop
desktop/node_modules/
desktop/dist/
src/openjarvis/server/static/
# Desktop (Tauri)
desktop/node_modules/
desktop/dist/
desktop/src-tauri/target/
frontend/src-tauri/target/
# Worktrees
.worktrees/
# Rust
target/
# Claude plan artifacts
docs/plans/
docs/superpowers/
.superpowers/
# Claude Code project instructions (per-developer)
CLAUDE.md
.claude/
# Tauri auto-generated schemas
**/src-tauri/gen/schemas/
# NFS lock artifacts
.nfs*
**/.nfs*
# Research output
research_mining_*
.python-version
src/openjarvis/channels/whatsapp_baileys_bridge/node_modules/
# SQLite in-memory artifacts
:memory:
# Dogfood reports (regenerated locally; not for VCS)
dogfood_report*.md
# Second Repos
Inline/
scratch/
# ---------------------------------------------------------------------------
# Distillation runtime artifacts (defense in depth — these should always live
# in ~/.openjarvis/, never inside the source tree, but we ignore them here in
# case OPENJARVIS_HOME is misconfigured during dev)
# ---------------------------------------------------------------------------
.openjarvis/
learning.db
**/learning/sessions/
**/learning/pending_review/
**/learning/benchmarks/
**/teacher_traces/
*.session.json
# Local dev artifacts (hybrid worker logs + cli debug dumps)
minion_logs/
*.oj-debug.json
oj-debug.*.json
+7
View File
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
+356
View File
@@ -0,0 +1,356 @@
# Changelog
All notable changes to OpenJarvis are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
---
## [Unreleased]
### Added
**Vision input for `jarvis ask`** — attach images to a query with
`-i`/`--image` (repeatable) or capture the current screen with
`-S`/`--screen`, for vision-capable models such as `gemma3:4b`. Images flow
through `Message.images` into Ollama's `/api/chat` `images` field; text-only
requests are unaffected. A privacy guard warns before any image is sent to a
non-local engine, and the security guardrail now preserves images when it
sanitizes a flagged prompt. Screen capture uses the built-in Windows .NET
stack with `mss`/`Pillow` fallbacks on other platforms. Adds the
`JARVIS_NUM_CTX` environment variable to tune the Ollama context window
(default `16384`).
## [1.0.2] - 2026-05-24
A patch release that fixes a packaging bug which broke the v1.0.1
wheel on PyPI, silences a noisy startup warning, restores a working
install path while `openjarvis.ai` is down, improves desktop
first-boot diagnostics on Windows, and ships the RAM-detection fix
for Windows that missed the v1.0.1 cutoff.
### Fixed
**`openjarvis/traces/` missing from the v1.0.1 PyPI wheel** (#372).
The `.gitignore` carried an unanchored `traces/` pattern, which
hatchling honored at wheel-build time and matched the runtime module
`src/openjarvis/traces/` — silently dropping the whole package. Every
fresh `pip install openjarvis==1.0.1` then failed at import with
`ModuleNotFoundError: No module named 'openjarvis.traces'` on the
first `jarvis ask`, learning, or server call. Anchored the pattern to
`/traces/`. Verified: a clean `uv build` now produces a wheel
containing all four `traces/` files.
**`pynvml` deprecation `FutureWarning` on every command** (#389).
Switched the dependency from the legacy `pynvml` package to NVIDIA's
official `nvidia-ml-py` (same `pynvml` module name, no warning shim),
and added defensive `warnings.filterwarnings` at every `import pynvml`
site to suppress the warning even when `pynvml` is pulled in
transitively.
**Windows RAM detection returning `0.0 GB`** (#373). The Windows
branch of `_total_ram_gb()` (via `GlobalMemoryStatusEx`) landed after
the v1.0.1 cutoff, so v1.0.1 users still saw `0.0 GB` from `jarvis
init`. Now shipping in the wheel. A new `windows-latest` CI job runs
the real `GlobalMemoryStatusEx` path on every PR as a regression
guard.
**Desktop first-boot hung on "did not become healthy in time"**
(#331). The Tauri boot path ran `uv sync` with stderr discarded and
the exit code ignored, so a failed dependency install surfaced only
as a generic 600-second health-check timeout. Now captures stderr,
checks the exit status, and surfaces the actual `uv sync` error
(with the diagnostic tail) before the long wait. The error-formatting
logic is covered by unit tests.
### Changed
**Install URL moved to GitHub Pages** (#337, #352). The documented
`openjarvis.ai/install.sh` URL was failing with `sslv3 alert
handshake failure` (the domain is community-operated and had a broken
TLS config). The canonical installer is now served from the
project-controlled GitHub Pages site at
`https://open-jarvis.github.io/OpenJarvis/install.sh`, generated from
the same `scripts/install/install.sh` at docs-build time. The README
also documents the WSL2 path for Windows and the `uv` prerequisite
for the desktop binary, and the installer bails early with a clear
message when run under Git Bash / MSYS2 / Cygwin.
## [1.0.1] - 2026-05-17
A patch release that closes the auto-update gap so the analytics
module added in #351 actually reaches users on the desktop, adds
runtime opt-out for that analytics, fixes the misleading upgrade
hint the CLI was printing, and lands the ACE optimizer alongside
DSPy and GEPA.
### Added
**ACE agent optimizer** (`learning/agents/ace_optimizer.py`). Adds
[ACE](https://github.com/ace-agent/ace) as a third agent-learning
policy alongside DSPy and GEPA. Where DSPy bootstraps few-shot
examples and GEPA evolves prompt populations, ACE evolves a textual
*playbook* of strategies the agent reads at inference time, updated
by a Generator / Reflector / Curator triad. Pick via
`[learning.agent] policy = "ace"`. Setup is manual (ACE isn't on
PyPI and isn't a properly-packaged Python project as of v1.0.1) —
see `docs/learning/ace.md` for the install path and trace-adapter
behavior.
**`jarvis self-update`** subcommand. Detects how OpenJarvis was
installed (pip, uv tool, editable git checkout) by inspecting
`openjarvis.__file__`, then runs the right upgrade command. Supports
`--check` (print the command without running) and `-y` (skip the
confirmation prompt). The post-command "new version available" hint
now points users at this command instead of guessing at the right
flow.
**Desktop auto-update endpoint wired to the rolling
`desktop-latest` GitHub release.** The Tauri updater plugin was
configured on the build side (`createUpdaterArtifacts: true`,
`includeUpdaterJson: true`, signing key in `TAURI_SIGNING_PRIVATE_KEY`)
but inert on the runtime side (`active: false`, `endpoints: []`). The
installed desktop app would never check. Both are now fixed; the app
polls `releases/download/desktop-latest/latest.json` every 30 minutes
and signature-verifies downloads against the minisign pubkey baked
into the app. Full flow, key-rotation runbook, and dev escape hatch
(`OPENJARVIS_NO_UPDATER=1`) documented in `docs/desktop-auto-update.md`.
**Analytics env-var opt-out** (`DO_NOT_TRACK`, `OPENJARVIS_NO_ANALYTICS`).
Tanvir's analytics module (#351) only respected the
`[analytics] enabled` config-file setting. Both env vars are now
honored in `is_analytics_enabled()` and in the install.sh beacon
script. Any truthy value (`1`, `true`, `yes`, `on`) disables for
that process; env opt-out takes precedence over the config file.
Documented under a new "Opting out" section in `docs/telemetry.md`.
### Changed
**Version-check trigger widened.** The "new version available" hint
in `_version_check.py` used to fire only on `{ask, chat, serve}` and
hardcoded the wrong upgrade command (`git pull && uv sync` — only
correct for editable installs). Now fires on every interactive
command (`doctor`, `init`, `quickstart`, `model`, `agents`, `skill`,
`memory`, `bench`, `telemetry`, `config`, `eval`, `optimize`, plus
the original three) and uses install-detection to print the right
upgrade command. Honors `JARVIS_NO_UPDATE_CHECK=1` and `CI=true` to
stay silent in automation.
**Desktop app version bumped 0.1.0 → 1.0.1** across
`tauri.conf.json`, `frontend/package.json`, and
`frontend/src-tauri/Cargo.toml` so the Python and desktop release
streams are aligned and the auto-updater has a real version to
compare against.
### Migration from 1.0.0
- **Importing `is_analytics_enabled`?** Same signature; behavior now
short-circuits on env opt-out before checking the config. Callers
that want the raw "is the config flag set" semantic should read
`cfg.enabled` directly.
- **Editable-git users running `jarvis self-update`** get the
detected `git pull && uv sync` command pointed at their actual
checkout, not `~/OpenJarvis`. If you'd come to rely on the
hardcoded path, update your muscle memory.
## [1.0.0] - 2026-05-15
The five-primitive architecture (Intelligence, Engine, Agents,
Tools & Memory, Learning) is now stable, with efficiency and
on-device learning as first-class capabilities alongside accuracy.
Companion blog post:
[From Minions to OpenJarvis: A Retrospective on Two Years in Local AI](https://hazyresearch.stanford.edu/blog/2026-05-19-minions-to-openjarvis-retrospective).
### Highlights
**Five composable primitives.** Intelligence, Engine, Agents, Tools & Memory,
and Learning each sit behind a single typed interface — any slot is
substitutable without touching the rest. The composition layer is
`JarvisSystem` in `src/openjarvis/system.py`, driven by a TOML config.
**Built-in agents across three execution modes.** Eight agents spanning a
single-turn chat baseline, a deep-research agent with inline citations,
a CodeAct-style coder, and a continuous monitor with memory compression
for long-horizon workflows. Execution modes cover on-demand, scheduled,
and continuous.
**Starter presets.** Eight preset configs installable via
`jarvis init --preset <name>` bundle an agent with a hardware-appropriate
engine, connectors, and tools. Variants cover Apple Silicon, Linux GPU
servers, and CPU-only laptops, plus a quickstart for LLM-guided spec search.
**Inference engines.** Four first-class local engines (Ollama, vLLM, SGLang,
llama.cpp) and five cloud providers (OpenAI, Anthropic, Google Gemini,
OpenRouter, MiniMax) sit behind a single `Engine` interface. Discovery
in `engine/_discovery.py` picks a sensible default per host.
### Added — hybrid local-cloud capabilities
**Per-query routing via a query-complexity analyzer**
(`src/openjarvis/learning/routing/complexity.py`). Produces a 0.01.0
complexity score with code/math/reasoning signals and a suggested token
budget, populating `RoutingContext` so easy queries stay local and only
queries that need frontier capability escalate.
**LLM-guided spec search** (`src/openjarvis/learning/spec_search/`).
`SpecSearchOrchestrator` wires diagnose → plan → execute → gate into a
single learning session: a frontier model reads traces, proposes
coordinated edits across all five primitives, and a held-out benchmark
gate (`gate/benchmark_gate.py`, `gate/regression.py`, `gate/cold_start.py`)
accepts only non-regressing edits. Ships with the `spec-search-quickstart`
preset and a runnable tutorial at `examples/openjarvis/spec_search_quickstart.py`.
**Six hybrid coordination paradigms** in `src/openjarvis/agents/hybrid/`.
Each paradigm pairs a local student with a frontier cloud teacher under
a different orchestration shape, as `LocalCloudAgent` subclasses:
- `minions` — reactive single-local + single-cloud loop
- `conductor` — static DAG planner
- `advisors` — executor ↔ advisor loop
- `archon` — generate → rank → fuse
- `skillorchestra` — per-query router across local skills
- `toolorchestra` — RL'd local model with a tool pool
A runner CLI (`python -m openjarvis.agents.hybrid.runner --cell <name>`)
and a 35-cell experiment registry (one TOML per method × benchmark ×
model triple) let researchers run, score, and compare these on equal
footing. Includes a Modal-backed SWE-bench-Verified harness scorer
(`evals/scorers/swebench_harness.py`).
### Added — efficiency as a first-class constraint
**Hardware-agnostic energy telemetry at 50ms resolution** across NVIDIA
(`telemetry/energy_nvidia.py`), AMD (`telemetry/energy_amd.py`), Apple
Silicon (`telemetry/energy_apple.py`), and Intel RAPL
(`telemetry/energy_rapl.py`). Energy, dollar cost, FLOPs, and latency
are treated as evaluation targets alongside accuracy.
**Instrumentation for FLOPs, batch, steady-state, ITL, phase energy, and
vLLM-specific metrics.** Joined per-query by the aggregator
(`telemetry/aggregator.py`) so traces carry accuracy + efficiency together.
### Added — local learning loop
**Closed-loop optimization across the stack** — model weights via SFT
(`learning/intelligence/sft_trainer.py`) and GRPO
(`learning/intelligence/grpo_trainer.py` plus an orchestrator-specific
variant under `learning/intelligence/orchestrator/`), prompts via DSPy
(`learning/agents/dspy_optimizer.py`), agent logic via GEPA
(`learning/agents/gepa_optimizer.py`), and engine + stack configuration
via LLM-guided spec search. `LearningOrchestrator` coordinates triggers
and applies optimizer overlays at discovery time so improvements compound
across primitives.
### Added — cross-framework evaluation
**External agentic-framework evaluation via subprocess.** The
`evals/backends/external/` subpackage wraps Hermes Agent and OpenClaw as
one-shot subprocess backends behind the existing `InferenceBackend` ABC.
The `evals/comparison/` toolkit provides path + commit-pin enforcement
(`third_party.py`), config templating (`make_configs.py`), and LaTeX
table generation (`table_gen.py`).
Ships with a new optional extra `framework-comparison` (depends on
`polars`), a `live_external` pytest marker for integration tests
requiring real foreign-framework installations, and a `ToolOrchestra`
evaluation dataset (`evals/datasets/toolorchestra.py`) alongside the
existing 30+ benchmark suite.
### Added — Skills System (Plans 1, 2A, 2B)
- **Skills core** — every skill is a tool. Skills appear in a system prompt catalog, agents invoke them on demand, content (pipeline results, markdown instructions, or both) gets injected into context.
- `SkillManifest` + `SkillStep` types with tags, depends, invocation flags, markdown content
- `SkillManager` — discovery, precedence resolution, catalog XML generation, tool wrapping
- `SkillTool(BaseTool)` — auto-extracts parameters from step argument templates
- `SkillExecutor` — sequential pipeline execution with sub-skill delegation
- Dependency graph with cycle detection, max depth enforcement, capability unions
- Security: four trust tiers (bundled/indexed/unreviewed/workspace), capability-gated enforcement
- Skill index module for git-backed registry search
- **agentskills.io spec adoption** — canonical `SKILL.md` format with YAML frontmatter following the [agentskills.io](https://agentskills.io/specification) open standard.
- `SkillParser` with strict spec validation + tolerant field mapping via `FIELD_MAPPING` table
- `ToolTranslator` for external tool name translation (Bash -> shell_exec, Read -> file_read, etc.)
- Source resolvers: `HermesResolver`, `OpenClawResolver`, `GitHubResolver`
- `SkillImporter` with provenance tracking (`.source` metadata files), optional script import
- Sourced subdirectory layout (`~/.openjarvis/skills/<source>/<name>/`)
- **Skills learning loop** — trace tagging, pattern discovery, DSPy/GEPA optimization.
- Trace metadata tagging: `skill`, `skill_source`, `skill_kind` flow through ToolExecutor -> TraceCollector -> TraceStep
- `SkillDiscovery` wired into `SkillManager.discover_from_traces()` with kebab name normalization
- `SkillOptimizer` — per-skill DSPy/GEPA wrapper that buckets traces and writes sidecar overlays
- `SkillOverlay` — sidecar storage at `~/.openjarvis/learning/skills/<name>/optimized.toml`
- `SkillManager._load_overlays()` applies optimized descriptions + few-shot examples at discovery time
- `LearningOrchestrator._maybe_optimize_skills()` — opt-in auto-trigger
- **Skills benchmark harness** — 4-condition PinchBench evaluation.
- I3 fix: `skill_few_shot_examples` wired through SystemBuilder -> `_run_agent` -> `ToolUsingAgent` -> `native_react.REACT_SYSTEM_PROMPT`
- `SkillBenchmarkRunner` — 4-condition x N-seed x M-task sweep with markdown report
- `JarvisAgentBackend` accepts `skills_enabled` and `overlay_dir` kwargs
- Conditions: `no_skills`, `skills_on`, `skills_optimized_dspy`, `skills_optimized_gepa`
- **CLI commands:**
- `jarvis skill list` / `info` / `run` / `install` / `sync` / `sources` / `update` / `remove` / `search`
- `jarvis skill discover` — mine traces for recurring tool patterns
- `jarvis skill show-overlay` — inspect optimization output
- `jarvis optimize skills` — run DSPy/GEPA per-skill optimization
- `jarvis bench skills` — run the PinchBench skills benchmark
- **Agent prompt improvement:**
- `native_react.REACT_SYSTEM_PROMPT` now includes "Using Skills" guidance that teaches agents to distinguish executable vs. instructional skill responses
- `{skill_examples}` placeholder for optimized few-shot example injection
- **Configuration:**
- `[skills]` section: `enabled`, `skills_dir`, `active`, `auto_discover`, `auto_sync`, `max_depth`, `sandbox_dangerous`
- `[[skills.sources]]` section: `source`, `url`, `filter`, `auto_update`
- `[learning.skills]` section: `auto_optimize`, `optimizer`, `min_traces_per_skill`, `optimization_interval_seconds`, `overlay_dir`
- `SkillSourceConfig` and `SkillsLearningConfig` dataclasses
- **Documentation:**
- `docs/user-guide/skills.md` — comprehensive user guide
- `docs/architecture/skills.md` — technical deep-dive
- `docs/tutorials/skills-workflow.md` — end-to-end tutorial
- `docs/getting-started/configuration.md` — expanded with skills config sections
- `CLAUDE.md` — updated architecture section
### Examples & Tutorials
- `examples/openjarvis/spec_search_quickstart.py` — runnable end-to-end
LLM-guided spec search session.
- `docs/user-guide/llm-guided-spec-search.md` — paper-aligned user guide.
- `docs/architecture/learning.md` — Learning primitive deep-dive covering
routing, spec search, optimizers, and the orchestrator.
- `docs/tutorials/` — code-companion, deep-research, messaging-hub,
scheduled-ops, and skills-workflow walkthroughs.
- `src/openjarvis/agents/hybrid/registry/*.toml` — 35-cell registry of
paradigm × benchmark × model experiments.
### Migration from 0.x
- **`learning/distillation/` is now `learning/spec_search/`.** The
subsystem was renamed to match the LLM-guided spec search semantics
documented in the companion paper. Update any imports
(`from openjarvis.learning.distillation.*`
`from openjarvis.learning.spec_search.*`). The `jarvis distillation`
CLI command is removed; use `spec_search`-prefixed config keys instead.
- **`_third_party.toml` no longer ships default paths.** Set
`HERMES_AGENT_PATH` and `OPENCLAW_PATH` env vars to point at your
local checkouts before running the framework-comparison harness;
missing or empty paths now raise `ThirdPartyNotFoundError` with an
actionable hint.
- **Engine `generate_full` return shape extended.**
`JarvisAgentBackend.generate_full` and `JarvisDirectBackend.generate_full`
now return the spec §6.2 extended fields (`energy_joules`,
`peak_power_w`, `tool_calls`, `turn_count`, `framework`,
`framework_commit`, `error`). Existing callers that didn't read these
fields are unaffected; new callers can rely on cross-framework parity.
### Fixed
- **Trace metadata flow** — `ToolResult.metadata` now propagates through `TOOL_CALL_END` event to `TraceStep.metadata` (was silently dropped at the event-bus boundary).
- **TaintSet JSON serialization** — `ToolExecutor._json_safe_metadata()` filters non-JSON-serializable values (like `TaintSet`) from event payloads before they reach `TraceStore`.
- **Non-dict YAML frontmatter** — source resolvers handle `yaml.safe_load()` returning a string instead of a dict (discovered on real OpenClaw imports).
- **OpenClaw category/name queries** — `jarvis skill install openclaw:owner/slug` now correctly splits into category + name match.
- **SkillDiscovery trace compatibility** — `_extract_tool_sequence` reads from `step.input["tool"]` (the actual `TraceStep` format), not the nonexistent `step.tool_name` attribute.
- **LearningOrchestrator skill trigger** — `_maybe_optimize_skills` runs BEFORE the SFT-data short-circuit (skills are tagged via trace metadata, not mined as SFT pairs).
- **PinchBenchScorer constructor** — `SkillBenchmarkRunner` constructs `PinchBenchScorer(judge_backend, model)` instead of no-args.
- **EvalRunner results access** — reads per-task data from `eval_runner.results` property, not nonexistent `summary.results`.
-229
View File
@@ -1,229 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Status
OpenJarvis is a research framework for studying on-device AI systems. Phase 21 complete. Five composable pillars: Intelligence, Engine, Agents, Tools (with storage + MCP), and Learning — with trace-driven learning as a cross-cutting concern. ~2940 tests pass (~51 skipped for optional deps). Python SDK (`Jarvis` class), composition layer (`SystemBuilder`/`JarvisSystem`), benchmarking framework, Docker deployment, Tauri desktop app, 40+ tools, 20+ CLI commands, 40+ API endpoints all ready.
## Build & Development Commands
```bash
uv sync --extra dev # Install deps + dev tools
uv run pytest tests/ -v # Run ~2997 tests (~42 skipped if optional deps missing)
uv run ruff check src/ tests/ # Lint
uv run jarvis --version # 1.0.0
uv run jarvis ask "Hello" # Query via discovered engine (direct mode)
uv run jarvis ask --agent simple "Hello" # SimpleAgent route
uv run jarvis ask --agent orchestrator "Hello" # OrchestratorAgent route
uv run jarvis ask --agent orchestrator --tools calculator,think "What is 2+2?"
uv run jarvis ask --agent native_react --tools calculator "What is 2+2?" # NativeReActAgent
uv run jarvis ask --agent react "Hello" # Alias for native_react
uv run jarvis ask --agent native_openhands "Hello" # NativeOpenHandsAgent (CodeAct)
uv run jarvis ask --agent openhands "Hello" # Real OpenHands SDK (requires openhands-sdk)
uv run jarvis ask --router heuristic "Hello" # Explicit heuristic policy
uv run jarvis ask --no-context "Hello" # Query without memory context injection
uv run jarvis model list # List models from running engines
uv run jarvis model info qwen3:8b # Show model details
uv run jarvis memory index ./docs/ # Index documents into memory
uv run jarvis memory search "topic" # Search memory for relevant chunks
uv run jarvis memory stats # Show memory backend statistics
uv run jarvis telemetry stats # Show aggregated telemetry stats
uv run jarvis telemetry export --format json # Export records as JSON
uv run jarvis telemetry export --format csv # Export records as CSV
uv run jarvis telemetry clear --yes # Delete all telemetry records
uv run jarvis channel list # List available messaging channels
uv run jarvis channel send slack "Hello" # Send a message to a channel
uv run jarvis channel status # Show channel bridge connection status
uv run jarvis scheduler create "Check weather" --type cron --value "0 9 * * *"
uv run jarvis scheduler list # List scheduled tasks
uv run jarvis scheduler start # Start scheduler daemon (foreground)
uv run jarvis bench run # Run all benchmarks against engine
uv run jarvis bench run -b energy -w 5 -n 20 --json # Energy benchmark with warmup
uv run jarvis serve --port 8000 # OpenAI-compatible API server (requires openjarvis[server])
uv run jarvis doctor # Run diagnostic checks (config, engines, models, deps)
uv run jarvis doctor --json # Machine-readable diagnostics
uv run jarvis start # Start server as background daemon
uv run jarvis stop # Stop background daemon
uv run jarvis restart # Restart background daemon
uv run jarvis status # Show daemon status (PID, uptime)
uv run jarvis chat # Interactive REPL (/quit, /clear, /model, /help, /history)
uv run jarvis chat --agent orchestrator --tools calculator # REPL with agent
uv run jarvis agent list # List registered agents
uv run jarvis agent info native_react # Show agent details
uv run jarvis workflow list # List available workflows
uv run jarvis workflow run my_workflow # Execute a workflow
uv run jarvis skill list # List installed skills
uv run jarvis skill install path/to/skill.toml # Install a skill
uv run jarvis vault set MY_KEY # Store encrypted credential
uv run jarvis vault get MY_KEY # Retrieve credential
uv run jarvis vault list # List stored keys
uv run jarvis add github # Quick-add MCP server (github, slack, postgres, etc.)
uv run jarvis --help # Show all subcommands
uv run jarvis init --force # Detect hardware, write ~/.openjarvis/config.toml
# Eval framework
source .env # Load API keys before running evals
uv run python -m evals run -c evals/configs/glm-4.7-flash-openhands.toml -v # Run eval suite from TOML config
uv run python -m evals run -b supergpqa -m "qwen3:8b" -n 50 # Run single benchmark
uv run python -m evals summarize results/supergpqa_qwen3-8b.jsonl # Summarize results
```
### Config File Conventions
- **Runtime config (source of truth):** `configs/openjarvis/config.toml` — Pillar-aligned OpenJarvis config. Copied to `~/.openjarvis/config.toml` at runtime (which is where `load_config()` reads from).
- **Eval suite configs:** `evals/configs/*.toml` — TOML configs defining models x benchmarks matrices.
- **API keys:** `.env` file in project root (gitignored). Source with `source .env` before running evals or cloud operations.
- **Never save configs to `~/.openjarvis/` directly** — always maintain the canonical copy in `configs/openjarvis/` and copy/symlink to `~/.openjarvis/`.
### Python SDK
```python
from openjarvis import Jarvis
j = Jarvis() # Uses default config + auto-detected engine
j = Jarvis(model="qwen3:8b") # Override model
j = Jarvis(engine_key="ollama") # Override engine
response = j.ask("Hello") # Returns string
full = j.ask_full("Hello") # Returns dict with content, usage, model, engine
response = j.ask("Hello", agent="orchestrator", tools=["calculator"])
j.memory.index("./docs/") # Index documents
results = j.memory.search("topic") # Search memory
j.memory.stats() # Backend stats
j.list_models() # Available models
j.list_engines() # Registered engines
j.close() # Release resources
```
- **Package manager:** `uv` with `hatchling` build backend
- **Config:** `pyproject.toml` with extras for optional backends (e.g., `openjarvis[inference-vllm]`, `openjarvis[inference-mlx]`, `openjarvis[memory-colbert]`, `openjarvis[server]`, `openjarvis[openclaw]`, `openjarvis[energy-amd]`, `openjarvis[energy-apple]`, `openjarvis[energy-all]`, `openjarvis[security-signing]`, `openjarvis[sandbox-wasm]`, `openjarvis[dashboard]`, `openjarvis[browser]`, `openjarvis[media]`, `openjarvis[pdf]`, `openjarvis[channel-line]`, `openjarvis[channel-viber]`, `openjarvis[channel-reddit]`, `openjarvis[channel-mastodon]`, `openjarvis[channel-xmpp]`, `openjarvis[channel-rocketchat]`, `openjarvis[channel-zulip]`, `openjarvis[channel-twitch]`, `openjarvis[channel-nostr]`)
- **CLI entry point:** `jarvis` (Click-based) — subcommands: `init`, `ask`, `serve`, `start`, `stop`, `restart`, `status`, `chat`, `model`, `memory`, `telemetry`, `bench`, `channel`, `scheduler`, `doctor`, `agent`, `workflow`, `skill`, `vault`, `add`
- **Python:** 3.10+ required
- **Node.js:** 22+ required only for OpenClaw agent
## Architecture
OpenJarvis is a research framework for on-device AI organized around **five composable pillars**, each with a clear ABC interface and a decorator-based registry for runtime discovery.
### Five Pillars
1. **Intelligence** (`src/openjarvis/intelligence/`) — Model definition, catalog, and generation defaults. `ModelRegistry` maps model keys to `ModelSpec`. `IntelligenceConfig` holds model identity (default/fallback model, model_path, checkpoint_path, quantization, preferred_engine, provider) and generation defaults (temperature, max_tokens, top_p, top_k, repetition_penalty, stop_sequences). Model catalog maintains `BUILTIN_MODELS` with auto-discovery via `merge_discovered_models()`. Backward-compat shims re-export from `learning/` for old import paths.
2. **Engine** (`src/openjarvis/engine/`) — The inference runtime. Backends: vLLM, SGLang, Ollama, llama.cpp, MLX, LM Studio. All implement `InferenceEngine` ABC with `generate()`, `stream()`, `list_models()`, `health()`. Engines extract and pass through `tool_calls` in OpenAI format.
3. **Agents** (`src/openjarvis/agents/`) — Pluggable logic for queries, tool/API calls, memory. Hierarchy: `BaseAgent` ABC (helpers: `_emit_turn_start/end`, `_build_messages`, `_generate`, `_max_turns_result`, `_strip_think_tags`, `_check_continuation`) → `ToolUsingAgent` (adds `tools`, `ToolExecutor`, `max_turns`). Agents: `SimpleAgent` (single-turn), `OrchestratorAgent` (multi-turn tool loop), `NativeReActAgent` (Thought-Action-Observation, key `"native_react"`, alias `"react"`), `NativeOpenHandsAgent` (CodeAct, key `"native_openhands"`), `RLMAgent` (recursive LM), `OpenHandsAgent` (real `openhands-sdk`, key `"openhands"`, requires Python 3.12+), `OpenClawAgent` (HTTP/subprocess transport), `ClaudeCodeAgent` (Claude Agent SDK via Node.js, key `"claude_code"`), `SandboxedAgent` (Docker wrapper, key `"sandboxed"`). `accepts_tools` class attribute for CLI/SDK auto-detection. Agents call `engine.generate()` directly — telemetry handled by `InstrumentedEngine` wrapper.
4. **Tools** (`src/openjarvis/tools/`) — All tools managed via MCP (Model Context Protocol).
- **API tools**: `CalculatorTool`, `ThinkTool`, `FileReadTool`, `FileWriteTool`, `WebSearchTool`, `CodeInterpreterTool`, `LLMTool`, `ShellExecTool`, `ApplyPatchTool`, `HttpRequestTool`, `DatabaseQueryTool`, `PDFExtractTool`, `ImageGenerateTool`, `AudioTranscribeTool` — all implement `BaseTool` ABC
- **Git tools** (`git_tool.py`): `GitStatusTool`, `GitDiffTool`, `GitCommitTool`, `GitLogTool`
- **Browser tools** (`browser.py`): `BrowserNavigateTool`, `BrowserClickTool`, `BrowserTypeTool`, `BrowserScreenshotTool`, `BrowserExtractTool` (Playwright, optional `[browser]`)
- **Agent tools** (`agent_tools.py`): `AgentSpawnTool`, `AgentSendTool`, `AgentListTool`, `AgentKillTool`
- **Storage tools** (`storage_tools.py`): `MemoryStoreTool`, `MemoryRetrieveTool`, `MemorySearchTool`, `MemoryIndexTool`
- **Storage backends** (`tools/storage/`): SQLite/FTS5 (default), FAISS, ColBERTv2, BM25, Hybrid (RRF fusion), KnowledgeGraph. All implement `MemoryBackend` ABC. Canonical import: `from openjarvis.tools.storage.sqlite import SQLiteMemory`. Backward-compat shims in `memory/` still work.
- **Scheduler tools** (`scheduler/tools.py`): 5 MCP tools for task scheduling
- **Knowledge graph tools** (`knowledge_tools.py`): `KGAddEntityTool`, `KGAddRelationTool`, `KGQueryTool`, `KGNeighborsTool`
- **MCP adapter** (`mcp_adapter.py`): `MCPToolAdapter` wraps external MCP tools as native `BaseTool`; `MCPToolProvider` discovers from server
- **MCP server** (`mcp/server.py`): Exposes all built-in tools via JSON-RPC `tools/list` + `tools/call` (MCP spec 2025-11-25)
- **MCP templates** (`tools/templates/`): `ToolTemplate` dynamically constructs tools from TOML specs. 10 builtin templates. `discover_templates()` auto-discovers.
- **`ToolExecutor`**: dispatch with RBAC check + taint check, `timeout_seconds` on `ToolSpec` (default 30s via `ThreadPoolExecutor`), event bus integration
- All registered via `@ToolRegistry.register("name")` decorator
5. **Learning** (`src/openjarvis/learning/`) — Structured learning with nested per-pillar sub-policies. `LearningConfig` sections: `routing` (heuristic/learned/grpo/bandit), `intelligence` (none/sft), `agent` (none/agent_advisor/icl_updater), `metrics` (accuracy/latency/cost/efficiency weights). Policies: `SFTRouterPolicy` (query→model from traces), `AgentAdvisorPolicy` (LM-guided), `ICLUpdaterPolicy` (in-context with example DB, versioning, rollback, quality gates), `GRPORouterPolicy` (softmax sampling, group relative advantage, per-query-class weights), `BanditRouterPolicy` (Thompson Sampling / UCB1, per-arm stats). `SkillDiscovery` mines tool subsequences from traces to auto-generate skill manifests. Router policies: `HeuristicRouter`, `TraceDrivenPolicy`. Orchestrator training subpackage provides SFT and GRPO pipelines.
### Cross-cutting Systems
- **Traces** (`src/openjarvis/traces/`) — Full interaction recording. `Trace` captures `TraceStep`s (route, retrieve, generate, tool_call, respond) with timing. `TraceStore` (SQLite), `TraceCollector` (auto-wraps agents), `TraceAnalyzer` (stats for learning).
- **Telemetry** (`src/openjarvis/telemetry/`) — `InstrumentedEngine` wraps any engine, publishing events to SQLite via `TelemetryStore`. `TelemetryAggregator` for read-only queries. `EnergyMonitor` ABC with vendor-specific implementations: `NvidiaEnergyMonitor` (hw counters/polling), `AmdEnergyMonitor` (amdsmi), `AppleEnergyMonitor` (zeus-ml), `RaplEnergyMonitor` (sysfs). `EnergyBatch` for batch-level energy-per-token. `SteadyStateDetector` for thermal equilibrium (CV-based).
- **Security** (`src/openjarvis/security/`) — `SecretScanner` + `PIIScanner` (implement `BaseScanner` ABC). `GuardrailsEngine` wraps engines with input/output scanning (WARN/REDACT/BLOCK modes). `AuditLogger` with Merkle hash chain (SHA-256 tamper-evidence). `CapabilityPolicy` RBAC (10 capabilities with glob matching, enforced in `ToolExecutor`). `TaintLabel`/`TaintSet` information flow control with `SINK_POLICY`. Ed25519 signing via `cryptography` (optional `[security-signing]`). `file_policy.py` for sensitive file detection. `InjectionScanner` (11 regex patterns: prompt override, identity override, code/shell injection, exfiltration, jailbreak, delimiter injection). `check_ssrf()` SSRF protection (RFC 1918, loopback, link-local, cloud metadata blocking). `RateLimiter` with `TokenBucket` (thread-safe, per-key). `run_sandboxed()` subprocess isolation (`os.setsid`, process group kill, env clearing). Security HTTP middleware (7 headers: CSP, HSTS, X-Frame-Options, etc.).
### Composition & Infrastructure
- **Composition Layer** (`system.py`) — `SystemBuilder` fluent builder → `JarvisSystem` with `ask()`, `close()`. Wires engine, model, agent, tools, telemetry, traces, workflow, sessions, capability policy.
- **SDK** (`sdk.py`) — `Jarvis` class: high-level sync API with `ask()`/`ask_full()`, `MemoryHandle`, lazy init, telemetry. Also exports `JarvisSystem`/`SystemBuilder`.
- **Benchmarks** (`bench/`) — `LatencyBenchmark`, `ThroughputBenchmark`, `EnergyBenchmark`. All registered via `BenchmarkRegistry`. CLI: `jarvis bench run`.
- **OpenClaw** (`agents/openclaw*.py`) — `OpenClawAgent` with `HttpTransport`/`SubprocessTransport`, JSON-line protocol, `ProviderPlugin`, `MemorySearchManager`.
- **API Server** (`server/`) — OpenAI-compatible via `jarvis serve` (FastAPI + uvicorn). Endpoints: `POST /v1/chat/completions`, `GET /v1/models`, `GET /health`, channel endpoints. SSE streaming.
- **Channels** (`channels/`) — `BaseChannel` ABC. `OpenClawChannelBridge` (WebSocket/HTTP to OpenClaw gateway). `WhatsAppBaileysChannel` (Baileys protocol, Node.js bridge, QR auth). Phase 21 channels: `LINEChannel`, `ViberChannel`, `MessengerChannel`, `RedditChannel`, `MastodonChannel`, `XMPPChannel`, `RocketChatChannel`, `ZulipChannel`, `TwitchChannel`, `NostrChannel`. All follow `BaseChannel` ABC with env var fallbacks, `@ChannelRegistry.register()`, `EventBus` integration.
- **Sandbox** (`sandbox/`) — `ContainerRunner` (Docker/Podman lifecycle, mount validation). `WasmRunner` (wasmtime-py, fuel/memory limits, optional `[sandbox-wasm]`). `SandboxedAgent` transparent wrapper. `create_sandbox_runner()` factory. `MountAllowlist` with path traversal prevention.
- **Scheduler** (`scheduler/`) — `TaskScheduler` with cron/interval/once scheduling, SQLite persistence, 5 MCP tools, event bus. CLI: `jarvis scheduler create|list|pause|resume|cancel|logs|start`.
- **Agent Hardening** (`agents/loop_guard.py`) — `LoopGuard`: SHA-256 hash tracking (identical calls), ping-pong detection (A-B-A-B patterns), poll-tool budget, context overflow recovery. `BaseAgent._check_continuation()` auto-resumes on `finish_reason=length`.
- **Workflow Engine** (`workflow/`) — DAG-based `WorkflowGraph` (cycle detection, topological sort, parallel stages via `ThreadPoolExecutor`). `WorkflowBuilder` fluent API. `WorkflowEngine` executes against `JarvisSystem`. TOML loader. Node types: agent, tool, condition, parallel, loop, transform.
- **Skills** (`skills/`) — `SkillManifest`/`SkillExecutor` (sequential tool steps with template rendering). Ed25519 signature verification. `SkillTool` adapter wraps skills as invocable tools. TOML loader.
- **Knowledge Graph** (`tools/storage/knowledge_graph.py`) — `KnowledgeGraphMemory(MemoryBackend)`: SQLite entity-relation store. `add_entity()`, `add_relation()`, `neighbors()`, `query_pattern()`. Registered as `"knowledge_graph"`.
- **Sessions** (`sessions/`) — `SessionStore` (SQLite): cross-channel persistent sessions. `SessionIdentity` canonical user across channels. `consolidate()` summarizes old messages, `decay()` removes expired.
- **A2A Protocol** (`a2a/`) — Google Agent-to-Agent spec (JSON-RPC 2.0). `A2AServer` (tasks/send, tasks/get, tasks/cancel, `/.well-known/agent.json`). `A2AClient`. `A2AAgentTool` adapter.
- **TUI Dashboard** (`cli/dashboard.py`) — `textual`-based terminal dashboard (optional `[dashboard]`). Panels: system status, event stream, telemetry, agent activity, sessions.
- **Desktop App** (`desktop/`) — Tauri 2.0 native desktop application. 5 dashboard panels: EnergyDashboard (real-time power monitoring with recharts), TraceDebugger (timeline inspection with step-type color coding), LearningCurve (policy visualization, GRPO/bandit stats), MemoryBrowser (search + stats), AdminPanel (health, agents, server control). Tauri commands proxy to OpenJarvis REST API. Plugins: notification, shell, global-shortcut, autostart, updater, single-instance. CI: `.github/workflows/desktop.yml` (Linux/macOS/Windows).
- **Vault** (`cli/vault_cmd.py`) — Fernet-encrypted credential store at `~/.openjarvis/vault.enc` with auto-generated key (`0o600` permissions).
- **MCP Quick-Add** (`cli/add_cmd.py`) — `jarvis add <server>` with 8 templates (github, filesystem, slack, postgres, brave-search, memory, puppeteer, google-maps). Saves JSON config to `~/.openjarvis/mcp/`.
### Core Module (`src/openjarvis/core/`)
- `registry.py``RegistryBase[T]` generic base. Subclasses: `ModelRegistry`, `EngineRegistry`, `MemoryRegistry`, `AgentRegistry`, `ToolRegistry`, `RouterPolicyRegistry`, `BenchmarkRegistry`, `ChannelRegistry`, `LearningRegistry`, `SkillRegistry`.
- `types.py``Message`, `Conversation`, `ModelSpec`, `ToolResult`, `TelemetryRecord`, `StepType`, `TraceStep`, `Trace`, `RoutingContext`.
- `config.py``JarvisConfig` dataclass hierarchy with TOML loader. Config classes for each pillar/subsystem. TOML sections: `[engine]` (+ nested `[engine.ollama]`, `[engine.vllm]`, `[engine.sglang]`, `[engine.llamacpp]`, `[engine.mlx]`, `[engine.lmstudio]`), `[intelligence]`, `[agent]`, `[tools.storage]`, `[tools.mcp]`, `[tools.browser]`, `[learning]` (+ nested routing/intelligence/agent/metrics), `[server]`, `[telemetry]`, `[traces]`, `[channel]`, `[security]` (+ `[security.capabilities]`, `ssrf_protection`, `rate_limit_*`), `[sandbox]`, `[scheduler]`, `[workflow]`, `[sessions]`, `[a2a]`. Backward-compat: `engine.ollama_host``engine.ollama.host`, `agent.default_tools``agent.tools`, TOML migration for cross-section moves.
- `events.py` — Pub/sub event bus (synchronous dispatch). ~30 EventType values covering inference, tools, memory, agents, telemetry, traces, channels, security, scheduler, workflow, skills, sessions, A2A.
### Docker & Deployment
- `Dockerfile` — Multi-stage: Python 3.12-slim, `.[server]`, entrypoint `jarvis serve`
- `Dockerfile.gpu` — NVIDIA CUDA 12.4 variant
- `Dockerfile.gpu.rocm` — AMD ROCm 6.2 variant
- `docker-compose.yml``jarvis` (8000) + `ollama` (11434). ROCm override: `docker-compose.gpu.rocm.yml`
- `deploy/systemd/openjarvis.service`, `deploy/launchd/com.openjarvis.plist`
### Query Flow
User query → Security scanning (input) → Intelligence resolves model → Agentic Logic (tools/memory) → Memory retrieval → Context injection → Engine generates → Security scanning (output) → Trace recorded → Telemetry recorded → Learning policies update.
### API Surface
OpenAI-compatible server via `jarvis serve`:
- **Core**: `POST /v1/chat/completions`, `GET /v1/models`, `GET /health`
- **Channels**: `GET /v1/channels`, `POST /v1/channels/send`, `GET /v1/channels/status`
- **Agents**: `GET /v1/agents`, `POST /v1/agents`, `DELETE /v1/agents/{id}`, `POST /v1/agents/{id}/message`
- **Memory**: `POST /v1/memory/store`, `POST /v1/memory/search`, `GET /v1/memory/stats`
- **Traces**: `GET /v1/traces`, `GET /v1/traces/{id}`
- **Telemetry**: `GET /v1/telemetry/stats`, `GET /v1/telemetry/energy`
- **Learning**: `GET /v1/learning/stats`, `GET /v1/learning/policy`
- **Skills**: `GET /v1/skills`, `POST /v1/skills`, `DELETE /v1/skills/{name}`
- **Sessions**: `GET /v1/sessions`, `GET /v1/sessions/{id}`
- **Budget**: `GET /v1/budget`, `PUT /v1/budget/limits`
- **Metrics**: `GET /metrics` (Prometheus-compatible)
- **WebSocket**: `WS /v1/chat/stream` (JSON chunked streaming)
- SSE streaming on `/v1/chat/completions` with `stream=true`
## Key Design Patterns
- **Registry pattern:** All extensible components use `@XRegistry.register("name")` decorator for registration and runtime discovery.
- **ABC interfaces:** Each pillar defines an ABC. Implement the ABC + register via decorator to add a new backend.
- **Offline-first:** Cloud APIs are optional. All core functionality works without network.
- **Hardware-aware:** Auto-detect GPU vendor/model/VRAM via `nvidia-smi`, `rocm-smi`, `system_profiler`, `/proc/cpuinfo`. Recommend engine accordingly.
- **Telemetry opt-in:** `InstrumentedEngine` wraps inference transparently. Agents unaware of telemetry.
- **Backward-compat shims:** `memory/` re-exports from `tools/storage/`, `intelligence/` re-exports from `learning/`, `agents/react.py` re-exports as `ReActAgent`, registry alias `"react"``NativeReActAgent`. Old import paths and config keys continue to work.
- **`ensure_registered()` pattern:** Benchmark and learning modules use lazy registration to survive registry clearing in tests.
## Development Phases
| Version | Phase | Delivers |
|---------|-------|----------|
| v0.1 | 0 | Scaffolding, registries, core types, config, CLI skeleton |
| v0.2 | 1 | Intelligence + Inference — `jarvis ask` end-to-end |
| v0.3 | 2 | Memory backends, document indexing, context injection |
| v0.4 | 3 | Agents, tool system, OpenAI-compatible API server |
| v0.5 | 4 | Learning, telemetry aggregation, `--router` CLI |
| v1.0 | 5 | SDK, OpenClaw infra, benchmarks, Docker |
| v1.1 | 6 | Trace system, trace-driven learning, pluggable agents |
| v1.2 | 7 | 5-pillar restructuring, composition layer, MCP, structured learning |
| v1.3 | 8 | Intelligence = "The Model", routing → Learning, engine selection |
| v1.4 | 9 | Pillar-aligned config, nested configs, TOML migration |
| v1.5 | 10 | Agent restructuring, BaseAgent/ToolUsingAgent, `accepts_tools`, OpenHands SDK |
| v1.6 | 11 | NanoClaw subsumption: ClaudeCodeAgent, WhatsApp Baileys, Docker sandbox, TaskScheduler |
| v1.7 | 12 | EnergyMonitor ABC (NVIDIA/AMD/Apple/RAPL), EnergyBatch, SteadyStateDetector |
| v1.8 | 13 | `jarvis doctor`/`init`, MLX engine, AMD multi-GPU, PWA, ROCm Docker |
| v1.9 | 14 | Agent hardening: LoopGuard, RBAC CapabilityPolicy, taint tracking, Merkle audit, Ed25519 |
| v2.0 | 15 | WorkflowEngine (DAG), SkillSystem, KnowledgeGraphMemory, SessionStore |
| v2.1 | 16 | A2A protocol, MCP templates, WasmRunner, TUI dashboard |
| v2.2 | 17 | Production tool parity: FileWrite, ApplyPatch, ShellExec, Git, HTTP, DB, Browser, Agent, Media, PDF tools. SSRF protection, injection scanner, rate limiter, subprocess sandbox, security middleware |
| v2.3 | 18 | CLI expansion (20 commands): daemon, chat REPL, agent, workflow, skill, vault, add. API expansion (40+ endpoints): agents, memory, traces, telemetry, learning, skills, sessions, budget, metrics, WebSocket streaming |
| v2.4 | 19 | Learning productionization: GRPO (softmax/advantage), BanditRouter (Thompson/UCB1), SkillDiscovery (trace mining), ICL updates (versioning/rollback/quality gates) |
| v2.5 | 20 | Tauri 2.0 desktop app: energy dashboard, trace debugger, learning curve visualization, memory browser, admin panel. CI for Linux/macOS/Windows |
| v2.6 | 21 | 10 new channels: LINE, Viber, Messenger, Reddit, Mastodon, XMPP, Rocket.Chat, Zulip, Twitch, Nostr |
+85
View File
@@ -0,0 +1,85 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [the project maintainers](https://github.com/open-jarvis/OpenJarvis/discussions). All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of actions.
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
+201
View File
@@ -0,0 +1,201 @@
# Contributing to OpenJarvis
Thank you for your interest in contributing to OpenJarvis! This guide covers everything you need to know — from why to contribute, to how to submit your first pull request.
---
## Why Contribute?
Contributing to OpenJarvis isn't just about code — it's about building the future of on-device AI together. Here's what you get:
### Paper Acknowledgment
All contributors with merged pull requests will be acknowledged as contributors on the OpenJarvis paper release.
### Mac Mini Giveaway
We're giving away a Mac Mini to one lucky contributor! Install OpenJarvis on your personal machine and opt in via the desktop app to share anonymized savings data (FLOPs, dollar cost, energy) for a chance to win. Your data is fully anonymous — no IP, no hardware info beyond savings metrics. You must share your email via the desktop app to be eligible.
See the [Savings Leaderboard](https://open-jarvis.github.io/OpenJarvis/leaderboard/) for details.
### Path to Maintainership
Consistent contributors can grow into project maintainers:
- **Contributor** — anyone with a merged PR
- **Reviewer** — invited after 3+ merged PRs in a domain area, can review PRs
- **Maintainer** — reviewers who demonstrate sustained engagement and good judgment
### Recognition
Contributors are recognized in release notes and on our GitHub repository.
---
## Ways to Contribute
### Good First Contributions
These are great starting points for new contributors:
- Documentation improvements and typo fixes
- Bug reports with reproducible steps
- New eval datasets and scorers
- Test coverage improvements
Look for issues labeled [`good-first-issue`](https://github.com/open-jarvis/OpenJarvis/labels/good-first-issue).
### Ideal Contributions
- Bug fixes with tests
- Performance improvements
- New tools, engines, or agents following the [registry pattern](docs/development/contributing.md#registry-pattern)
- New channel integrations (Telegram, Discord, Slack, etc.)
### Harder to Review
These require more context and review time. **Please open an issue for discussion before starting a PR:**
- New primitives or major extensions to existing ones
- Large refactors
- Changes to core abstractions (`BaseAgent`, `InferenceEngine`, etc.)
### May Not Be Accepted
To avoid wasted effort, note that PRs in these categories are unlikely to be merged:
- Changes that break backwards compatibility in the public API
- Changes that add significant new dependencies without justification
- Changes that add friction to the user experience
---
## Getting Started
### Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Python | 3.10+ | Required |
| [uv](https://docs.astral.sh/uv/) | Latest | Package manager |
| Node.js | 22+ | Only needed for ClaudeCodeAgent and WhatsApp channel |
### Setup
```bash
git clone https://github.com/open-jarvis/OpenJarvis.git
cd OpenJarvis
uv sync --extra dev
```
### Pre-commit Hooks
We use [pre-commit](https://pre-commit.com/) to run linting and formatting checks before each commit:
```bash
uv run pre-commit install
```
This installs Git hooks that automatically run [Ruff](https://docs.astral.sh/ruff/) on every commit. If the hooks fail, fix the issues and commit again.
For detailed development setup, code conventions, and project structure, see the [Development Guide](docs/development/contributing.md).
---
## Claiming Issues
1. Browse the [Roadmap](https://open-jarvis.github.io/OpenJarvis/development/roadmap/) for an item that interests you
2. Check if a [GitHub issue](https://github.com/open-jarvis/OpenJarvis/issues) already exists for it — if not, [open one](https://github.com/open-jarvis/OpenJarvis/issues/new/choose) describing what you'd like to work on
3. Comment **"take"** on the issue to get auto-assigned
4. Fork, branch, and start working
If you've claimed an issue but can't finish it, please leave a comment so someone else can pick it up.
---
## Proposing Changes
### Trivial Changes
For small fixes (typos, doc improvements, simple bug fixes), go ahead and open a PR directly.
### Non-trivial Changes
For larger changes — new features, refactors, new dependencies — **open an issue first** to discuss the approach. This saves everyone time by catching design issues early.
Use the appropriate [issue template](https://github.com/open-jarvis/OpenJarvis/issues/new/choose):
- **Bug Report** — for bugs with reproduction steps
- **Feature Request** — for new functionality
- **New Eval Dataset** — for contributing benchmarks
---
## Pull Request Process
### Before Submitting
1. Run the full test suite:
```bash
uv run pytest tests/ -v
```
2. Run the linter:
```bash
uv run ruff check src/ tests/
```
3. Run the formatter:
```bash
uv run ruff format --check src/ tests/
```
4. Add tests for new functionality
5. Follow the [registry pattern](docs/development/contributing.md#registry-pattern) for new components
### Commit Messages
We use [Conventional Commits](https://www.conventionalcommits.org/):
```
feat: add FAISS memory backend
fix: handle empty tool responses in orchestrator
docs: update engine discovery documentation
test: add coverage for BM25 backend
refactor: simplify agent base class helpers
```
Keep the first line under 72 characters. Reference relevant issues (e.g., `fixes #42`).
### What Makes a Good PR
- **Focused** — one feature, fix, or refactor per PR
- **Tested** — includes unit tests covering new code paths
- **Documented** — updates docstrings and docs if adding public API
- **Backwards compatible** — avoids breaking existing interfaces without discussion
---
## Contribution Areas
OpenJarvis is built on five composable primitives. Here's where you can contribute:
| Area | What to Build | Guide |
|---|---|---|
| **Intelligence** | Model catalog entries, routing strategies | [Dev Guide](docs/development/contributing.md) |
| **Engines** | New inference backends (e.g., TensorRT, ONNX) | [Dev Guide](docs/development/contributing.md) |
| **Agents** | New agent types, agent improvements | [Dev Guide](docs/development/contributing.md) |
| **Tools** | New tools (browser, API clients, etc.) | [Dev Guide](docs/development/contributing.md) |
| **Learning** | Router policies, reward functions, training | [Dev Guide](docs/development/contributing.md) |
| **Evals** | New datasets, scorers, benchmark configs | [Dev Guide](docs/development/contributing.md) |
| **Channels** | Chat platform integrations | [Dev Guide](docs/development/contributing.md) |
| **Rust Port** | PyO3 bindings, crate parity with Python | See `rust/` directory |
---
## Code of Conduct
This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you agree to uphold this code.
---
## Questions?
- Open a [Discussion](https://github.com/open-jarvis/OpenJarvis/discussions) for questions and help
- Check the [documentation](https://open-jarvis.github.io/OpenJarvis/) for guides and API reference
-19
View File
@@ -1,19 +0,0 @@
FROM python:3.12-slim AS builder
WORKDIR /app
COPY pyproject.toml README.md ./
COPY src/ src/
RUN pip install --no-cache-dir uv && \
uv pip install --system ".[server]"
FROM python:3.12-slim
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app
WORKDIR /app
EXPOSE 8000
ENTRYPOINT ["jarvis"]
CMD ["serve", "--host", "0.0.0.0", "--port", "8000"]
-27
View File
@@ -1,27 +0,0 @@
FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04 AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 python3-pip python3-venv && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY pyproject.toml README.md ./
COPY src/ src/
RUN pip install --no-cache-dir uv && \
uv pip install --system ".[server]"
FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 python3-pip && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app
WORKDIR /app
EXPOSE 8000
ENTRYPOINT ["jarvis"]
CMD ["serve", "--host", "0.0.0.0", "--port", "8000"]
-27
View File
@@ -1,27 +0,0 @@
FROM rocm/dev-ubuntu-22.04:6.2 AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 python3-pip python3-venv && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY pyproject.toml README.md ./
COPY src/ src/
RUN pip install --no-cache-dir uv && \
uv pip install --system ".[server]"
FROM rocm/dev-ubuntu-22.04:6.2
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 python3-pip && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app
WORKDIR /app
EXPOSE 8000
ENTRYPOINT ["jarvis"]
CMD ["serve", "--host", "0.0.0.0", "--port", "8000"]
-16
View File
@@ -1,16 +0,0 @@
FROM python:3.12-slim
# Install Node.js 22
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates && \
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir ".[server]"
LABEL openjarvis-sandbox=true
ENTRYPOINT ["python", "-m", "openjarvis.sandbox.entrypoint"]
+19
View File
@@ -0,0 +1,19 @@
.PHONY: setup build test lint format
# Mirrors .github/workflows/ci.yml so `make test` matches CI locally.
setup:
uv sync --extra dev --extra framework-comparison --extra server
build:
uv run maturin develop --manifest-path rust/crates/openjarvis-python/Cargo.toml
test: build
uv run pytest tests/ -n auto -q --tb=short -m "not live and not cloud and not hub"
lint:
uv run ruff check src/ tests/
uv run ruff format --check src/ tests/
format:
uv run ruff format src/ tests/
-492
View File
@@ -1,492 +0,0 @@
# OpenJarvis Development Notes
Living document tracking implementation progress, testing state, lessons learned, dead ends, and practices for ongoing development. Updated across sessions.
---
## Current State (2026-02-21)
- **Version:** 1.0.0 (trace system added, targeting v1.1)
- **All 6 roadmap phases complete** (Phase 0 through Phase 5) + Phase 6 trace system in progress
- **Tests:** 576 passed, 8 skipped, 0 failures
- **Lint:** ruff clean (`select = ["E", "F", "I", "W"]`)
- **Source files:** 76 Python files in `src/openjarvis/`
- **Test files:** 78 Python files in `tests/`
- **Python:** 3.13 (compatible with 3.10+)
- **Package manager:** `uv` with `hatchling` build backend
### 8 Skipped Tests (Optional Dependencies)
| Test | Missing Dep | Install Extra |
|------|-------------|---------------|
| `tests/memory/test_bm25.py` | `rank_bm25` | `openjarvis[memory-bm25]` |
| `tests/memory/test_colbert.py` | `colbert` | `openjarvis[memory-colbert]` |
| `tests/memory/test_embeddings.py` | `sentence_transformers` | `openjarvis[memory-faiss]` |
| `tests/memory/test_faiss.py` | `faiss` | `openjarvis[memory-faiss]` |
| `tests/server/test_models_pydantic.py` | `pydantic` | `openjarvis[server]` |
| `tests/server/test_routes.py` | `fastapi` | `openjarvis[server]` |
| `tests/test_integration.py:165` | `fastapi` | `openjarvis[server]` |
| `tests/test_integration.py:190` | `fastapi` | `openjarvis[server]` |
---
## Phase Completion Log
| Phase | Version | Deliverables | Test Count (cumulative) |
|-------|---------|-------------|------------------------|
| Phase 0 | v0.1 | Scaffolding, registries, core types, config, CLI skeleton, event bus | ~60 |
| Phase 1 | v0.2 | Intelligence + Inference — `jarvis ask` end-to-end, heuristic router, engine discovery, basic telemetry | ~160 |
| Phase 2 | v0.3 | Memory — SQLite/FAISS/ColBERT/BM25/Hybrid backends, document ingest pipeline, context injection, `jarvis memory` CLI | ~270 |
| Phase 3 | v0.4 | Agents (Simple/Orchestrator/Custom/OpenClaw stub), tool system (Calculator/Think/Retrieval/LLM/FileRead), OpenAI-compatible API server, `jarvis serve` | ~360 |
| Phase 4 | v0.5 | Learning — HeuristicRouter, HeuristicRewardFunction, GRPORouterPolicy stub, TelemetryAggregator, `jarvis telemetry` CLI, `--router` CLI option | ~432 |
| Phase 5 | v1.0 | SDK (`Jarvis` class), OpenClaw infrastructure (protocol/transport/plugin), benchmarks (`jarvis bench`), Docker, docs | ~520 |
| Phase 6 | v1.1 | Trace system (TraceStore, TraceCollector, TraceAnalyzer), trace-driven learning (TraceDrivenPolicy) | ~576 |
---
## Architecture Quick Reference
### Directory Layout
```
src/openjarvis/
├── __init__.py # __version__ = "1.0.0", exports Jarvis, MemoryHandle
├── sdk.py # Python SDK: Jarvis class + MemoryHandle
├── core/
│ ├── registry.py # RegistryBase[T] + 7 typed registries
│ ├── types.py # Message, Conversation, ModelSpec, ToolResult, TelemetryRecord
│ ├── config.py # JarvisConfig dataclass hierarchy, TOML loader
│ └── events.py # EventBus pub/sub (synchronous)
├── intelligence/ # ModelRegistry, HeuristicRouter, model catalog
├── traces/ # TraceStore, TraceCollector, TraceAnalyzer
├── learning/ # RouterPolicyRegistry, HeuristicRouter, TraceDrivenPolicy, GRPO stub
├── memory/ # SQLite/FAISS/ColBERT/BM25/Hybrid backends, chunking, ingest
├── agents/ # Simple/Orchestrator/Custom/OpenClaw agents + protocol/transport
├── engine/ # Ollama/vLLM/llama.cpp/Cloud engine wrappers
├── tools/ # Calculator/Think/Retrieval/LLM/FileRead tools
├── bench/ # Latency/Throughput benchmarks, BenchmarkSuite
├── telemetry/ # TelemetryStore, TelemetryAggregator, instrumented_generate
├── server/ # FastAPI OpenAI-compatible API server
└── cli/ # Click CLI: init, ask, serve, model, memory, telemetry, bench
```
### 7 Registries
All use `RegistryBase[T]` with `@XRegistry.register("name")` or `register_value()`:
1. `ModelRegistry``ModelSpec` objects
2. `EngineRegistry``InferenceEngine` implementations
3. `MemoryRegistry``MemoryBackend` implementations
4. `AgentRegistry``BaseAgent` implementations
5. `ToolRegistry``BaseTool` implementations
6. `RouterPolicyRegistry``RouterPolicy` implementations
7. `BenchmarkRegistry``BaseBenchmark` implementations
---
## Patterns and Practices
### The `ensure_registered()` Pattern
**Problem:** The `_clean_registries` autouse fixture in `tests/conftest.py` calls `.clear()` on every registry before each test. Module-level `@XRegistry.register("name")` decorators only fire once at import time (Python caches modules in `sys.modules`). After registry clearing, the decorations never re-fire, leaving registries empty for subsequent tests.
**Solution:** Use lazy registration via `ensure_registered()`:
```python
# src/openjarvis/bench/latency.py
_registered = False
def ensure_registered() -> None:
global _registered
if _registered:
return
from openjarvis.core.registry import BenchmarkRegistry
if not BenchmarkRegistry.contains("latency"):
BenchmarkRegistry.register_value("latency", LatencyBenchmark)
_registered = True
```
Then in `__init__.py`:
```python
def ensure_registered() -> None:
from openjarvis.bench.latency import ensure_registered as _reg_latency
_reg_latency()
```
And in test files, use an autouse fixture:
```python
@pytest.fixture(autouse=True)
def _register_latency():
from openjarvis.bench import ensure_registered
ensure_registered()
```
**Where this pattern is used:** `bench/latency.py`, `bench/throughput.py`, `learning/heuristic_policy.py`, `learning/grpo_policy.py`, `learning/heuristic_reward.py`
**Where this pattern is NOT needed:** Agents, engines, memory backends, and tools use `@register` decorators that work fine because their test files explicitly import and re-register as needed, or the test module import triggers registration.
### Test Infrastructure
- **`tests/conftest.py`** — `_clean_registries` autouse fixture clears all 7 registries + clears `EventBus` default listeners before each test. Critical for test isolation.
- **Mock engine pattern** — Almost every test that touches the engine layer uses a `MagicMock()` with `.engine_id`, `.health()`, `.list_models()`, `.generate()` stubbed:
```python
def _make_engine(content="Hello"):
engine = MagicMock()
engine.engine_id = "mock"
engine.health.return_value = True
engine.list_models.return_value = ["test-model"]
engine.generate.return_value = {
"content": content,
"usage": {"prompt_tokens": 5, "completion_tokens": 3, "total_tokens": 8},
"model": "test-model",
"finish_reason": "stop",
}
return engine
```
- **CLI tests** use Click's `CliRunner` with `patch("openjarvis.cli.X.get_engine", ...)` to mock the engine layer.
- **Memory tests** use `tmp_path` fixture for SQLite DB paths and test files.
- **Optional dep tests** use `pytest.importorskip("module_name")` at module level.
### Config Defaults
`JarvisConfig()` with no arguments produces sane defaults:
- Engine: auto-discover (Ollama, vLLM, llama.cpp, cloud in priority order)
- Memory: `sqlite` backend, `~/.openjarvis/memory.db`
- Agent: `simple` (no Node.js dependency)
- Intelligence: `qwen3:8b` default, `qwen3:0.6b` fallback
- Telemetry: enabled, `~/.openjarvis/telemetry.db`
- Learning: `heuristic` default policy
### File Naming Conventions
- ABCs and shared dataclasses: `_stubs.py` (e.g., `agents/_stubs.py`, `bench/_stubs.py`, `tools/_stubs.py`)
- Internal helpers: `_discovery.py`, `_base.py` (underscore prefix)
- CLI commands: `*_cmd.py` (e.g., `bench_cmd.py`, `telemetry_cmd.py`, `memory_cmd.py`)
- Test files mirror source: `tests/agents/test_openclaw.py` tests `src/openjarvis/agents/openclaw.py`
### Import Structure
- Package `__init__.py` files import submodules to trigger registration
- Try/except around optional dependency imports:
```python
try:
from openjarvis.engine.ollama import OllamaEngine # noqa: F401
except ImportError:
pass
```
- Top-level `openjarvis/__init__.py` exports: `Jarvis`, `MemoryHandle`, `__version__`
---
## Dead Ends and Gotchas
### 1. `@register` Decorator vs. `ensure_registered()`
**Dead end:** Initially used `@BenchmarkRegistry.register("latency")` class decorator in `bench/latency.py`. This caused ~10 test failures because:
- Registry cleared between tests by `conftest.py`
- Module already in `sys.modules`, so `import openjarvis.bench` is a no-op on second import
- Registry stays empty after clearing
**Fix:** Switched to `ensure_registered()` pattern (see above). This is the pattern already used by `learning/` modules.
**Rule of thumb:** If a module is imported at package init time AND its registry gets cleared in tests, use `ensure_registered()`. If registration only happens in test fixtures or explicit calls, `@register` is fine.
### 2. Chunk Attribute Names
`memory/chunking.py` `Chunk` dataclass uses `content` (not `text`). `ChunkConfig` uses `chunk_overlap` (not `overlap`). Easy to get wrong because these aren't obvious from the field names alone. Always read `_stubs.py` or the actual dataclass before using.
### 3. Test Content Size for Chunking
`ChunkConfig.min_chunk_size=50` tokens by default. A test string like `"This is test content."` produces 0 chunks. Use at least ~100 words:
```python
words = " ".join(f"word{i}" for i in range(100))
```
### 4. Version String Locations
Version is defined in **three places** that must stay in sync:
1. `src/openjarvis/__init__.py` — `__version__ = "1.0.0"`
2. `pyproject.toml` — `version = "1.0.0"`
3. `src/openjarvis/server/app.py` — FastAPI `version="1.0.0"` constructor arg
Tests that check version: `tests/cli/test_cli.py::test_version_flag`
### 5. Server Import Guards
The `server/` module requires `fastapi`, `uvicorn`, `pydantic`. These are behind the `[server]` optional extra. All test files that touch server code use `pytest.importorskip("fastapi")`. The server `__init__.py` wraps imports in try/except.
### 6. `patch()` Targets for Engine Mocking
When mocking `get_engine` in CLI tests, the patch target must be the *importing module*, not the source module:
```python
# CORRECT — patches where it's imported
patch("openjarvis.cli.bench_cmd.get_engine", return_value=("mock", engine))
# WRONG — patches the source, doesn't affect the already-imported reference
patch("openjarvis.engine._discovery.get_engine", return_value=("mock", engine))
```
Same for SDK tests: `patch("openjarvis.sdk.get_engine", ...)`.
### 7. EventBus Clearing
`EventBus()` creates a new instance each time, but `EventBus._default_listeners` is a class variable. The `conftest.py` fixture resets it. If tests subscribe to events, subscriptions won't persist across tests.
### 8. Module Shadowing in CLI Package
In `cli/__init__.py`, `from openjarvis.cli.ask import ask` imports the Click command. This shadows the module name. When you try `mock.patch("openjarvis.cli.ask.get_engine")`, Python resolves `openjarvis.cli.ask` as the Click command (via attribute lookup on the package), not the module.
**Fix:** Use `importlib.import_module("openjarvis.cli.ask")` to get the actual module object, then `mock.patch.object(module, "get_engine")`.
---
## Post-v1.0: Unimplemented Ideas from VISION.md
These are mentioned in `VISION.md` but not in the roadmap phases. They represent future work:
### Learning / Router
- [ ] Learned router via GRPO (Group Relative Policy Optimization) — `GRPORouterPolicy` is a stub
- [ ] Preference learning from user feedback
- [ ] Continual fine-tuning on accumulated trajectories
- [ ] Multi-objective optimization: quality vs. latency vs. energy vs. cost
### Memory
- [ ] ConversationMemory — sliding window with automatic summarization of older turns
- [ ] Personal Notes — user-created persistent notes and preferences
- [ ] Episodic Memory — records of past interactions, tool uses, and outcomes
- [ ] Vector DB adapters (Qdrant, ChromaDB) for users with existing infrastructure
### Tools
- [ ] WebSearch tool (Tavily, SearXNG, DuckDuckGo)
- [ ] CodeInterpreter tool (sandboxed Python execution)
- [ ] FileWrite tool (safe file writing with path validation)
- [ ] MCP (Model Context Protocol) compatibility
### Engines
- [ ] SGLang engine backend (structured generation, constrained decoding)
- [ ] MLX engine backend (Apple Silicon native, Metal acceleration)
- [ ] Complete vLLM integration (tensor parallelism config, multi-GPU)
### OpenClaw
- [ ] Full OpenClaw gateway integration (WebSocket, `:18789`)
- [ ] OpenClaw skill composition
- [ ] Context compaction in OpenClaw agent
- [ ] `openjarvis-openclaw` as separate plugin package (currently inline)
### Infrastructure
- [ ] Documentation site (MkDocs or similar)
- [ ] Getting started guide
- [ ] Plugin development guide
- [ ] API reference docs
- [ ] CI/CD pipeline
- [ ] PyPI publishing
---
## Testing Recipes
### Run all tests
```bash
uv sync --extra dev
uv run pytest tests/ -v --tb=short
```
### Run a specific module's tests
```bash
uv run pytest tests/bench/ -v
uv run pytest tests/sdk/ -v
uv run pytest tests/agents/test_openclaw.py -v
```
### Run with optional deps (server)
```bash
uv sync --extra dev --extra server
uv run pytest tests/server/ -v # No longer skipped
```
### Lint
```bash
uv run ruff check src/ tests/
uv run ruff check src/ tests/ --fix # Auto-fix
```
### Quick smoke test
```bash
uv run jarvis --version # 1.0.0
uv run jarvis --help # All subcommands
python -c "from openjarvis import Jarvis; print(Jarvis)"
```
---
## Adding New Components
### New Benchmark
1. Create `src/openjarvis/bench/my_benchmark.py`:
```python
from openjarvis.bench._stubs import BaseBenchmark, BenchmarkResult
class MyBenchmark(BaseBenchmark):
@property
def name(self) -> str: return "my-bench"
@property
def description(self) -> str: return "Description"
def run(self, engine, model, *, num_samples=10) -> BenchmarkResult: ...
_registered = False
def ensure_registered():
global _registered
if _registered: return
from openjarvis.core.registry import BenchmarkRegistry
if not BenchmarkRegistry.contains("my-bench"):
BenchmarkRegistry.register_value("my-bench", MyBenchmark)
_registered = True
```
2. Import in `bench/__init__.py` `ensure_registered()`
3. Add test file `tests/bench/test_my_benchmark.py` with autouse fixture calling `ensure_registered()`
### New Tool
1. Create `src/openjarvis/tools/my_tool.py`:
```python
from openjarvis.core.registry import ToolRegistry
from openjarvis.tools._stubs import BaseTool, ToolSpec
@ToolRegistry.register("my-tool")
class MyTool(BaseTool):
@property
def spec(self) -> ToolSpec: ...
def execute(self, input: str, **params) -> str: ...
```
2. Import in `tools/__init__.py`
3. Add test file `tests/tools/test_my_tool.py`
### New Memory Backend
1. Create `src/openjarvis/memory/my_backend.py`:
```python
from openjarvis.core.registry import MemoryRegistry
from openjarvis.memory._stubs import MemoryBackend, RetrievalResult
@MemoryRegistry.register("my-backend")
class MyBackend(MemoryBackend):
def store(self, content, *, source="", metadata=None) -> str: ...
def retrieve(self, query, top_k=5) -> list[RetrievalResult]: ...
def delete(self, doc_id) -> bool: ...
def clear(self) -> None: ...
```
2. Import in `memory/__init__.py` with try/except for optional deps
3. Add test file with `pytest.importorskip()` if using optional deps
4. Add optional dep group in `pyproject.toml` if needed
### New Agent
1. Create `src/openjarvis/agents/my_agent.py`:
```python
from openjarvis.agents._stubs import AgentResult, BaseAgent
from openjarvis.core.registry import AgentRegistry
@AgentRegistry.register("my-agent")
class MyAgent(BaseAgent):
agent_id = "my-agent"
def __init__(self, engine, model, *, bus=None, **kwargs): ...
def run(self, input, context=None, **kwargs) -> AgentResult: ...
```
2. Import in `agents/__init__.py`
3. Add test file `tests/agents/test_my_agent.py`
### New Engine
1. Create `src/openjarvis/engine/my_engine.py`:
```python
from openjarvis.core.registry import EngineRegistry
from openjarvis.engine._stubs import InferenceEngine
@EngineRegistry.register("my-engine")
class MyEngine(InferenceEngine):
engine_id = "my-engine"
def generate(self, messages, *, model, **kwargs) -> dict: ...
def stream(self, messages, *, model, **kwargs): ...
def list_models(self) -> list[str]: ...
def health(self) -> bool: ...
```
2. Import in `engine/__init__.py` with try/except
3. Add to `_discovery.py` engine priority list if auto-discoverable
---
## Session Log
### Session 1 (2026-02-16) — Phase 5 Implementation
**Scope:** Full Phase 5 (v1.0) — SDK, OpenClaw, Benchmarks, Docker, Docs
**Work completed:**
- Step 1: Added `BenchmarkRegistry` to `core/registry.py`, updated `conftest.py`
- Step 2: Created `bench/` package — `_stubs.py`, `latency.py`, `throughput.py`, `__init__.py`; CLI `bench_cmd.py`
- Step 3: Created `sdk.py` — `Jarvis` class + `MemoryHandle`; updated `__init__.py` exports
- Step 4: Created OpenClaw infra — `openclaw_protocol.py`, `openclaw_transport.py`, `openclaw_plugin.py`; rewrote `openclaw.py` from stub
- Step 5: Created `Dockerfile`, `Dockerfile.gpu`, `docker-compose.yml`, `deploy/systemd/openjarvis.service`, `deploy/launchd/com.openjarvis.plist`
- Step 6: Version bump to 1.0.0, updated `README.md`, `CLAUDE.md`
**Bugs fixed during implementation:**
1. Ruff lint: 17 issues (E501, I001, F401, F841) — all fixed
2. Registry clearing broke `@register` decorators — switched to `ensure_registered()` for bench modules
3. `ChunkConfig(overlap=...)` should be `ChunkConfig(chunk_overlap=...)` — fixed
4. `chunk.text` should be `chunk.content` — fixed
5. Test content too short for chunking (0 chunks produced) — used 100 words
**Final: 520 passed, 8 skipped, 0 failures, ruff clean**
### Session 2 (2026-02-17) — Test Fixes + Live vLLM Testing
**Scope:** Fix broken tests, set up live vLLM inference testing
**Work completed:**
- Fixed 6 failed + 13 errored tests in `tests/cli/test_ask_router.py` and `tests/cli/test_ask_agent.py`
- **Root cause:** `from openjarvis.cli.ask import ask` in `cli/__init__.py` shadows the `ask` module with the Click command object. When `mock.patch("openjarvis.cli.ask.get_engine")` resolves, it tries to patch an attribute on the Click command, not the module.
- **Fix:** Use `importlib.import_module("openjarvis.cli.ask")` + `mock.patch.object(_ask_mod, "get_engine")` instead of string-based patching.
- Added tool fallback in `_openai_compat.py`: if server returns 400 when tools are sent (e.g., vLLM without `--enable-auto-tool-choice`), retry without tools.
- Verified live vLLM testing: existing vLLM server on port 8003 with `Qwen/Qwen3-8B`
- Tested: `jarvis ask`, `jarvis bench run`, `jarvis model list`, `jarvis memory index/search`, `jarvis telemetry stats`, SDK `Jarvis.ask()` and `ask_full()`
**Gotcha discovered:**
8. **Module shadowing with `from X import Y`** — If a package's `__init__.py` does `from openjarvis.cli.ask import ask`, then `openjarvis.cli.ask` in `sys.modules` is the *module*, but accessing it via attribute lookup on `openjarvis.cli` gives the imported *object* (the Click command). Use `importlib.import_module()` for reliable module access when patching.
**Live vLLM setup notes:**
- vLLM 0.15.1 running on Lambda cluster (8x A100-SXM4-80GB)
- Config: `~/.openjarvis/config.toml` with `vllm_host = "http://localhost:8003"` and `default_model = "Qwen/Qwen3-8B"`
- Tool calling requires `--enable-auto-tool-choice --tool-call-parser hermes` flags on vLLM server
- Without tool support, orchestrator falls back to reasoning-only mode
**Final: 520 passed, 8 skipped, 0 failures, ruff clean**
### Session 3 (2026-02-21) — Trace System & Research Direction
**Scope:** Design new research direction (abstractions for local AI), implement trace system
**Design decisions made:**
- OpenJarvis repositioned as a research framework for studying on-device AI
- Four core abstractions: Intelligence, Engine, Agentic Logic, Memory
- Learning is a cross-cutting concern driven by interaction traces
- Agentic Logic should be pluggable — users bring their own architecture (ReAct, OpenHands-style, etc.)
- Trace collection is the bridge between static and learned agents
- Evolve existing codebase rather than full redesign
- Name stays as OpenJarvis
- Learning focus: telemetry-driven routing/tool policies (lightweight, always-on)
- Agent-model coupling: loose (any agent, any model)
**Work completed:**
- Added `StepType` enum, `TraceStep`, `Trace` dataclasses to `core/types.py`
- Added `TRACE_STEP`, `TRACE_COMPLETE` event types to `core/events.py`
- Created `traces/` package:
- `store.py` — `TraceStore`: SQLite-backed, save/get/list with filters, event bus subscription
- `collector.py` — `TraceCollector`: wraps any `BaseAgent`, subscribes to EventBus, records steps automatically
- `analyzer.py` — `TraceAnalyzer`: per-route stats, per-tool stats, summaries, export, query-type filtering
- Created `learning/trace_policy.py` — `TraceDrivenPolicy`: learns routing from trace outcomes, batch/online updates, registered as `"learned"` policy
- Registered `TraceDrivenPolicy` in `learning/__init__.py`
- Added 56 new tests across 4 test files in `tests/traces/` and `tests/learning/test_trace_policy.py`
- Updated all markdown documentation (README, VISION, ROADMAP, NOTES, CLAUDE)
**Final: 576 passed, 8 skipped, 0 failures, ruff clean**
+135 -61
View File
@@ -1,94 +1,152 @@
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/openjarvis-logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="assets/openjarvis-logo-light.svg">
<img alt="OpenJarvis" src="assets/openjarvis-logo-light.svg" width="400">
</picture>
<img alt="OpenJarvis" src="assets/OpenJarvis_Horizontal_Logo.png" width="400">
<p><i>Programming abstractions for on-device AI.</i></p>
<p><i>Personal AI, On Personal Devices.</i></p>
<p>
<a href="https://www.intelligence-per-watt.ai/"><img src="https://img.shields.io/badge/project-intelligence--per--watt.ai-blue" alt="Project"></a>
<a href="https://hazyresearch.stanford.edu/OpenJarvis/"><img src="https://img.shields.io/badge/docs-mkdocs-blue" alt="Docs"></a>
<a href="https://arxiv.org/abs/2605.17172"><img src="https://img.shields.io/badge/arXiv-2605.17172-b31b1b.svg" alt="arXiv"></a>
<a href="https://openjarvis.stanford.edu/"><img src="https://img.shields.io/badge/project-OpenJarvis-blue" alt="Project"></a>
<a href="https://open-jarvis.github.io/OpenJarvis/"><img src="https://img.shields.io/badge/docs-mkdocs-blue" alt="Docs"></a>
<img src="https://img.shields.io/badge/python-%3E%3D3.10-blue" alt="Python">
<img src="https://img.shields.io/badge/license-Apache%202.0-green" alt="License">
<a href="https://discord.gg/CMVBmDQ5Fj"><img src="https://img.shields.io/badge/discord-join-7289da?logo=discord&logoColor=white" alt="Discord"></a>
<a href="https://x.com/OpenJarvisAI"><img src="https://img.shields.io/badge/X-@OpenJarvisAI-black?logo=x&logoColor=white" alt="X / Twitter"></a>
</p>
</div>
---
> **[Documentation](https://hazyresearch.stanford.edu/OpenJarvis/)**
<div align="center">
<img alt="OpenJarvis demo reel" src="assets/openjarvis_demo_reel.webp" width="75%">
</div>
---
> **[Documentation](https://open-jarvis.github.io/OpenJarvis/)**
>
> **[Project Site](https://www.intelligence-per-watt.ai/)**
> **[Project Site](https://openjarvis.stanford.edu/)**
>
> **[Paper](https://arxiv.org/abs/2605.17172)**
>
> **[Leaderboard](https://open-jarvis.github.io/OpenJarvis/leaderboard/)**
>
> **[Roadmap](https://open-jarvis.github.io/OpenJarvis/development/roadmap/)**
OpenJarvis is a framework for building AI systems that run *entirely on local hardware*. Rather than treating intelligence as a cloud service, OpenJarvis provides composable abstractions for local model selection, inference, agentic reasoning, tool use, and learning — all aware of the hardware they run on.
## Why OpenJarvis?
```python
from openjarvis import Jarvis
Personal AI agents are exploding in popularity, but nearly all of them still route intelligence through cloud APIs. Your "personal" AI continues to depend on someone else's server. At the same time, our [Intelligence Per Watt](https://www.intelligence-per-watt.ai/) research showed that local language models already handle 88.7% of single-turn chat and reasoning queries, with intelligence efficiency improving 5.3× from 2023 to 2025. The models and hardware are increasingly ready. What has been missing is the software stack to make local-first personal AI practical.
j = Jarvis() # auto-detect hardware + engine
response = j.ask("Explain backpropagation") # route to best local model
j.ask("Solve x^2 - 5x + 6 = 0", # multi-turn agent with tools
agent="orchestrator",
tools=["calculator", "think"])
j.memory.index("./papers/") # index documents into local storage
results = j.memory.search("attention mechanism") # semantic retrieval
j.close()
```
OpenJarvis is that stack. It is a framework for local-first personal AI, built around three core ideas: shared primitives for building on-device agents; evaluations that treat energy, FLOPs, latency, and dollar cost as first-class constraints alongside accuracy; and a learning loop that improves models using local trace data. The goal is simple: make it possible to build personal AI agents that run locally by default, calling the cloud only when truly necessary. OpenJarvis aims to be both a research platform and a production foundation for local AI, in the spirit of PyTorch.
## Installation
```bash
pip install openjarvis # core framework
pip install openjarvis[server] # + FastAPI server
```
Pick your platform and run one command. Each installer handles [uv](https://docs.astral.sh/uv/), the Python venv, Ollama, and a starter model — about 3 minutes on broadband.
You also need a local inference backend: [Ollama](https://ollama.com), [vLLM](https://github.com/vllm-project/vllm), [SGLang](https://github.com/sgl-project/sglang), or [llama.cpp](https://github.com/ggerganov/llama.cpp).
| Platform | One-liner |
|---|---|
| **macOS · Linux · WSL2** | `curl -fsSL https://open-jarvis.github.io/OpenJarvis/install.sh \| bash` |
| **Native Windows** | `irm https://open-jarvis.github.io/OpenJarvis/install.ps1 \| iex` |
| **Desktop GUI** | Download `.exe` / `.dmg` / `.deb` / `.rpm` / `.AppImage` from the [latest release](https://github.com/open-jarvis/OpenJarvis/releases) |
Then `jarvis` to start. The Rust extension and larger models continue downloading in the background; `jarvis doctor` shows status.
Platform-specific notes (WSL2 setup, native-Windows scheduled-task service, desktop prerequisites, manual / contributor install): see the [installation docs](https://open-jarvis.github.io/OpenJarvis/getting-started/install/).
## Quick Start
The fastest path is Ollama on any machine with Python 3.10+:
```bash
# 1. Install OpenJarvis
pip install openjarvis
# 2. Detect hardware and generate config
jarvis init
# 3. Install and start Ollama (https://ollama.com)
curl -fsSL https://ollama.com/install.sh | sh
ollama serve # start the Ollama server
# 4. Pull a model
ollama pull qwen3:8b
# 5. Ask a question
jarvis ask "What is the capital of France?"
# 6. Verify your setup
jarvis doctor
jarvis # start chatting (default: chat-simple)
jarvis init --preset <name> # switch to a starter config
```
`jarvis init` auto-detects your hardware and recommends the best engine. After init, it prints engine-specific next steps. Run `jarvis doctor` at any time to diagnose configuration or connectivity issues.
> Prefix `jarvis ...` with `uv run`, or `source .venv/bin/activate` first.
## The Five Pillars
| Preset | What it does |
|---|---|
| `morning-digest-mac` / `morning-digest-linux` / `morning-digest-minimal` | Spoken daily briefing from email, calendar, health, news |
| `deep-research` | Multi-hop research across indexed docs with citations |
| `code-assistant` | Agent with code execution, file I/O, and shell access |
| `scheduled-monitor` | Stateful agent on a schedule with memory |
| `chat-simple` | Lightweight conversation, no tools |
| Pillar | What it does | Key abstractions |
|--------|-------------|-----------------|
| **Intelligence** | Model management and routing | `RouterPolicy`, `QueryAnalyzer`, `ModelCatalog` |
| **Engine** | Inference runtime abstraction | `InferenceEngine` ABC — Ollama, vLLM, SGLang, llama.cpp, MLX |
| **Agents** | Pluggable reasoning strategies | `BaseAgent` ABC — Simple, Orchestrator, ReAct, OpenHands, OpenClaw |
| **Tools** | Capabilities via MCP | `BaseTool` ABC — calculator, code interpreter, web search, memory; external MCP servers auto-discovered |
| **Learning** | Trace-driven adaptation | `LearningPolicy` ABC — SFT (model routing), AgentAdvisor (restructuring), ICL (tool usage) |
Example:
Every interaction produces a **Trace** — a structured record of the full reasoning chain. Learning policies consume traces to improve model selection, agent behavior, and tool usage over time.
```bash
jarvis init --preset morning-digest-mac
jarvis connect gdrive # one OAuth covers Gmail / Calendar / Tasks
jarvis digest --fresh # generate and play your first briefing
```
Per-preset deep dives: [morning digest](https://open-jarvis.github.io/OpenJarvis/user-guide/morning-digest/) · [deep research](https://open-jarvis.github.io/OpenJarvis/user-guide/deep-research/) · [code assistant](https://open-jarvis.github.io/OpenJarvis/user-guide/code-assistant/) · [scheduled monitor](https://open-jarvis.github.io/OpenJarvis/user-guide/scheduled-monitor/) · [chat simple](https://open-jarvis.github.io/OpenJarvis/user-guide/chat-simple/) · or the full [quickstart guide](https://open-jarvis.github.io/OpenJarvis/getting-started/quickstart/).
### Skills
Skills teach agents how to better use tools and improve their reasoning. Every skill is a tool — agents discover them from a catalog and invoke them on demand.
```bash
# Install skills from public sources
jarvis skill install hermes:arxiv
jarvis skill sync hermes --category research
# Use skills with any agent
jarvis ask "Use the code-explainer skill to explain this Python code: for i in range(5): print(i*2)"
# Optimize skills from your trace history
jarvis optimize skills --policy dspy
# Benchmark the impact
jarvis bench skills --max-samples 5 --seeds 42
```
Import from [Hermes Agent](https://github.com/NousResearch/hermes-agent) (~150 skills), [OpenClaw](https://github.com/openclaw/skills) (~13,700 community skills), or any GitHub repo. Skills follow the [agentskills.io](https://agentskills.io/specification) open standard.
See the [Skills User Guide](https://open-jarvis.github.io/OpenJarvis/user-guide/skills/) and [Skills Tutorial](https://open-jarvis.github.io/OpenJarvis/tutorials/skills-workflow/) for details.
### Built-in Agents
OpenJarvis ships with eight built-in agents across three execution modes (on-demand, scheduled, continuous):
| Agent | Type | What it does |
|-------|------|-------------|
| `morning_digest` | Scheduled | Daily briefing from email, calendar, health, news — with TTS audio |
| `deep_research` | On-demand | Multi-hop research with citations across web and local docs |
| `monitor_operative` | Continuous | Long-horizon monitoring with memory, compression, and retrieval |
| `orchestrator` | On-demand | Multi-turn reasoning with automatic tool selection |
| `native_react` | On-demand | ReAct (Thought-Action-Observation) loop agent |
| `operative` | Continuous | Persistent autonomous agent with state management |
| `native_openhands` | On-demand | CodeAct — generates and executes Python code |
| `simple` | On-demand | Single-turn chat, no tools |
See the [User Guide](https://open-jarvis.github.io/OpenJarvis/user-guide/morning-digest/) and [Tutorials](https://open-jarvis.github.io/OpenJarvis/tutorials/) for detailed setup instructions.
Full documentation — including Docker deployment, cloud engines, development setup, and tutorials — at **[open-jarvis.github.io/OpenJarvis](https://open-jarvis.github.io/OpenJarvis/)**.
## Community
- **GitHub:** [github.com/open-jarvis/OpenJarvis](https://github.com/open-jarvis/OpenJarvis)
- **Discord:** [discord.gg/CMVBmDQ5Fj](https://discord.gg/CMVBmDQ5Fj)
- **X / Twitter:** [@OpenJarvisAI](https://x.com/OpenJarvisAI)
- **Docs:** [open-jarvis.github.io/OpenJarvis](https://open-jarvis.github.io/OpenJarvis/)
## Contributing
We welcome contributions! See the [Contributing Guide](CONTRIBUTING.md) for incentives, contribution types, and the PR process.
Quick start for contributors:
```bash
git clone https://github.com/open-jarvis/OpenJarvis.git
cd OpenJarvis
uv sync --extra dev
uv run pre-commit install
uv run pytest tests/ -v
```
Browse the [Roadmap](https://open-jarvis.github.io/OpenJarvis/development/roadmap/) for areas where help is needed. Comment **"take"** on any issue to get auto-assigned.
## About
OpenJarvis is part of [Intelligence Per Watt](https://www.intelligence-per-watt.ai/), a research initiative studying the efficiency of on-device AI systems. The project is developed at [Hazy Research](https://hazyresearch.stanford.edu/) and the [Scaling Intelligence Lab](https://scalingintelligence.stanford.edu/) at [Stanford SAIL](https://ai.stanford.edu/).
OpenJarvis is part of [Intelligence Per Watt](https://www.intelligence-per-watt.ai/), a research initiative studying the intelligence efficiency of AI systems. The project is developed at [Hazy Research](https://hazyresearch.stanford.edu/) and the [Scaling Intelligence Lab](https://scalingintelligence.stanford.edu/) at [Stanford SAIL](https://ai.stanford.edu/).
## Sponsors
@@ -96,9 +154,25 @@ OpenJarvis is part of [Intelligence Per Watt](https://www.intelligence-per-watt.
<a href="https://www.laude.org/">Laude Institute</a> &bull;
<a href="https://datascience.stanford.edu/marlowe">Stanford Marlowe</a> &bull;
<a href="https://cloud.google.com/">Google Cloud Platform</a> &bull;
<a href="https://lambda.ai/">Lambda Labs</a>
<a href="https://lambda.ai/">Lambda Labs</a> &bull;
<a href="https://ollama.com/">Ollama</a> &bull;
<a href="https://research.ibm.com/">IBM Research</a> &bull;
<a href="https://hai.stanford.edu/">Stanford HAI</a>
</p>
## Citation
```bibtex
@misc{saadfalcon2026openjarvispersonalaipersonal,
title={OpenJarvis: Personal AI, On Personal Devices},
author={Jon Saad-Falcon and Avanika Narayan and Robby Manihani and Tanvir Bhathal and Herumb Shandilya and Hakki Orhun Akengin and Gabriel Bo and Andrew Park and Matthew Hart and Caia Costello and Chuan Li and Christopher Ré and Azalia Mirhoseini},
year={2026},
eprint={2605.17172},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2605.17172},
}
```
## License
[Apache 2.0](LICENSE)
+43
View File
@@ -0,0 +1,43 @@
# OpenJarvis PR Review Instructions
You are reviewing pull requests for OpenJarvis, a local-first personal AI agent framework built with Python, Rust (PyO3), and TypeScript.
## Review Checklist
Evaluate every PR against these criteria:
### 1. Relevance
Is this PR doing something useful? Valid contributions include: bug fixes, new features, feature expansions, documentation improvements, test coverage, and performance improvements. Flag PRs that appear to be empty, auto-generated without substance, or unrelated to the project.
### 2. Completeness
Does the code actually implement what the PR title and description claim? If the PR says "fix X", verify X is actually fixed. If it says "add Y", verify Y is fully added and functional — not partially implemented or stubbed out.
### 3. Correctness
Check for logic errors, edge cases, and off-by-one errors. Pay particular attention to:
- **Rust-Python bridge (PyO3) boundaries** — type conversions, error propagation, GIL handling
- **Async/await patterns** — missing awaits, unclosed resources, blocking calls in async contexts
- **Registry pattern compliance** — new components (engines, tools, agents, channels) must register via `ToolRegistry`, `EngineRegistry`, `AgentRegistry`, `ChannelRegistry`, etc. in `src/openjarvis/core/registry.py`
- **Mining provider compliance** — new mining providers must register via `MinerRegistry` and expose an idempotent `ensure_registered()` for the autouse-clear test convention
- **Event bus integration** — new lifecycle events should use `EventBus` from `src/openjarvis/core/events.py`
### 4. Testing
Does the PR include tests for new code paths? Are existing tests expected to still pass? New tools, engines, agents, and channels should have corresponding test files in `tests/` mirroring the `src/` structure.
### 5. Security
Check for: hardcoded API keys or secrets, missing input validation at system boundaries (user input, external APIs), and anything that compromises local-first data isolation.
## Do NOT Comment On
- Formatting or style — Ruff handles this automatically in CI
- Code in unchanged files outside the PR diff
- Subjective naming preferences
- Adding docstrings or comments to code the PR did not modify
## Output Format
- Post **inline comments** on specific lines for actionable issues
- Post a **summary comment** containing: what the PR does, whether it achieves its stated goal, and any blocking concerns
- Use severity levels:
- `blocking` — must fix before merge
- `suggestion` — consider fixing
- `nit` — take it or leave it
-374
View File
@@ -1,374 +0,0 @@
# OpenJarvis Roadmap
Phased development plan for OpenJarvis. Phases are ordered to maximize early usability: foundation first, then intelligence + inference (so you can ask questions), then memory (so it remembers), then agents (so it can act), then learning (so it improves).
---
## Phase 0 — Foundation (~2-3 weeks)
**Goal:** Repository scaffolding, core abstractions, and CLI skeleton. Nothing runs yet, but all interfaces are defined.
**Version milestone:** v0.1
### Repository structure
```
OpenJarvis/
├── pyproject.toml # uv/hatchling, all deps + extras
├── src/
│ └── openjarvis/
│ ├── __init__.py
│ ├── core/
│ │ ├── registry.py # RegistryBase[T] + typed registries
│ │ ├── types.py # Message, Conversation, ModelSpec, ToolResult, TelemetryRecord
│ │ ├── config.py # JarvisConfig dataclass hierarchy, TOML loader
│ │ └── events.py # Event bus: pub/sub for inter-pillar telemetry
│ ├── intelligence/ # Phase 1
│ ├── memory/ # Phase 2
│ ├── agents/ # Phase 3
│ ├── engine/ # Phase 1
│ ├── learning/ # Phase 4
│ └── cli/ # CLI entry points
├── tests/
├── VISION.md
├── ROADMAP.md
└── README.md
```
### Deliverables
- [ ] **Registry system**`RegistryBase[T]` adapted from IPW's `registry.py`. Typed subclasses:
- `ModelRegistry` — model specs and metadata
- `EngineRegistry` — inference engine implementations
- `MemoryRegistry` — memory backend implementations
- `AgentRegistry` — agent implementations
- `ToolRegistry` — tools with `ToolSpec` metadata (category, cost, latency, capabilities)
- [ ] **Core types** (`core/types.py`):
- `Message` — role + content + metadata (tool calls, images, etc.)
- `Conversation` — ordered list of messages with sliding window support
- `ModelSpec` — model ID, parameter count, quantization, context length, hardware compatibility
- `ToolResult` — tool name + output + usage + cost
- `TelemetryRecord` — timestamp, model, tokens, latency, energy (optional), cost
- [ ] **Config system** (`core/config.py`):
- `JarvisConfig` dataclass hierarchy: `EngineConfig`, `IntelligenceConfig`, `MemoryConfig`, `AgentConfig`
- TOML config file at `~/.openjarvis/config.toml`
- Hardware auto-detection: GPU vendor/model/VRAM/platform → populate defaults
- [ ] **Event bus** (`core/events.py`):
- Simple pub/sub for inter-pillar communication
- Telemetry events flow without tight coupling between pillars
- Synchronous dispatch (async optional later)
- [ ] **CLI skeleton** (Click-based):
- `jarvis init` — create `~/.openjarvis/config.toml` with auto-detected defaults
- `jarvis ask` — placeholder (wired in Phase 1)
- `jarvis serve` — placeholder (wired in Phase 3)
- `jarvis model list|pull|info` — placeholder (wired in Phase 1)
- `jarvis memory index|search|stats` — placeholder (wired in Phase 2)
---
## Phase 1 — Intelligence + Inference Engine (~3-4 weeks)
**Goal:** You can ask OpenJarvis a question and get an answer. Models run locally or via cloud APIs. Basic telemetry records every call.
**Version milestone:** v0.2 — first usable version
### Inference Engine
- [ ] **`InferenceEngine` ABC:**
```python
class InferenceEngine(ABC):
def generate(self, model: str, messages: list[Message], **params) -> Response: ...
def stream(self, model: str, messages: list[Message], **params) -> Iterator[ResponseChunk]: ...
def list_models(self) -> list[ModelSpec]: ...
def health(self) -> bool: ...
```
- [ ] **Engine implementations:**
- `OllamaEngine` — wraps Ollama HTTP API (`/api/chat`, `/api/tags`). Apple Silicon + NVIDIA.
- `VLLMEngine` — wraps vLLM OpenAI-compatible API. Multi-GPU, tensor parallelism.
- `LlamaCppEngine` — wraps `llama-cpp-python` or llama.cpp server. Maximum compatibility.
- `CloudEngine` — unified wrapper for OpenAI, Anthropic, and Google APIs. Key-based routing.
- [ ] **Model management:**
- Auto-discovery from running engines (poll `/api/tags`, `/v1/models`)
- `ModelSpec` with hardware compatibility matrix (min VRAM, supported engines, quantization options)
- `jarvis model list` shows all available models across engines
- `jarvis model info <model>` shows spec, hardware requirements, estimated performance
### Intelligence
- [ ] **Hardware profiles:**
- Auto-detect: `nvidia-smi`, `rocm-smi`, `system_profiler` (macOS), `/proc/cpuinfo`
- Map GPU to capabilities: VRAM, compute capability, FP8/FP4 support, unified memory
- Recommend engine: Apple Silicon → Ollama/MLX, NVIDIA datacenter → vLLM, AMD → vLLM+ROCm, CPU → llama.cpp
- [ ] **Heuristic Router V0:**
- Rule-based routing: short queries (< 50 tokens) → small model, complex (reasoning keywords, multi-step) → large model, code patterns → code specialist
- Fallback chains: if preferred model unavailable, try next in chain
- Configurable via `~/.openjarvis/config.toml`
- [ ] **Basic telemetry:**
- Wrap every `generate()` / `stream()` call with timing + token counting
- Record to SQLite: model, prompt tokens, completion tokens, latency, cost estimate
- `TelemetryRecord` stored via event bus, accumulated for future learning phase
### Wire-up
- [ ] **`jarvis ask "What is X?"` works end-to-end:**
1. Parse query → detect complexity → route to model
2. Generate response via selected engine
3. Record telemetry
4. Print response (with optional `--json` output)
---
## Phase 2 — Memory / Storage (~3-4 weeks)
**Goal:** OpenJarvis remembers conversations, can index your documents, and injects relevant context into prompts.
**Version milestone:** v0.3
### Memory backends
- [ ] **`MemoryBackend` ABC:**
```python
class MemoryBackend(ABC):
def store(self, content: str, metadata: dict) -> str: ... # Returns doc ID
def retrieve(self, query: str, k: int = 10) -> list[Result]: ...
def delete(self, doc_id: str) -> bool: ...
def clear(self) -> None: ...
```
- [ ] **Memory subtypes:**
- `ConversationMemory` — sliding window (configurable size) + automatic summarization of older turns via LLM call
- `KnowledgeBase` — indexed document collection with multi-backend search
- [ ] **Backend implementations:**
- **`SQLiteMemory`** — FTS5 full-text search, zero-config default. Always available, no extra dependencies.
- **`FAISSMemory`** — Dense neural retrieval. Encodes documents with `sentence-transformers`, builds FAISS index (IVF or flat depending on collection size). GPU-accelerated when available.
- **`ColBERTMemory`** — ColBERTv2 late interaction retrieval. Best retrieval quality.
- Package: `colbert-ai[torch,faiss-gpu]`
- Indexing: `Indexer(checkpoint="colbertv2.0", config=ColBERTConfig(nbits=2))` → `indexer.index(name, collection)`
- Search: `Searcher(index=name)` → `searcher.search(query, k=10)` returns `(passage_ids, ranks, scores)`
- Token-level MaxSim matching: each query token attends to each document token, max-pooled per query token, summed
- 2-bit residual compression keeps indexes compact (~50x smaller than full embeddings)
- Millisecond query latency, substantially better than single-vector methods on complex queries
- **`BM25Memory`** — Keyword search baseline using `rank-bm25`. No GPU, no embeddings. Fast and effective for keyword-heavy queries.
- **`HybridMemory`** — Combines BM25 with a dense backend (FAISS or ColBERT) using Reciprocal Rank Fusion (RRF):
```
RRF_score(d) = sum(1 / (k + rank_i(d))) for each retriever i
```
Configurable `k` parameter (default 60). Best overall retrieval when you don't know the query type.
### Document pipeline
- [ ] **Indexing pipeline:** PDF / Markdown / plain text / code → chunking (configurable size + overlap) → embedding (if using dense/ColBERT backend) → index
- [ ] **Context injection:** auto-retrieve top-k relevant chunks before each LLM call, inject into prompt with source attribution (`[Source: filename:line]`)
### CLI
- [ ] `jarvis memory index <path>` — index a file or directory
- [ ] `jarvis memory search <query>` — search across all memory backends
- [ ] `jarvis memory stats` — show index sizes, document counts, backend status
---
## Phase 3 — Agentic Logic (~3-4 weeks)
**Goal:** OpenJarvis can use tools, reason over multiple turns, and serve an OpenAI-compatible API. The default agent is OpenClaw's Pi.
**Version milestone:** v0.4
### Agent framework
- [ ] **`BaseAgent` ABC:**
```python
class BaseAgent(ABC):
def run(self, input: str, context: AgentContext) -> AgentResult: ...
```
`AgentContext` carries: conversation history, memory handle, tool registry, telemetry recorder, model router.
`AgentResult` contains: response text, tool calls made, tokens used, telemetry data.
- [ ] **Agent implementations:**
- **`OpenClawAgent`** (default) — wraps OpenClaw's Pi agent runtime (`@mariozechner/pi-coding-agent` v0.52.12+). Two modes:
1. **HTTP mode:** OpenClaw gateway running locally on `:18789`. Communicate via WebSocket. Best for persistent sessions.
2. **Subprocess mode:** invoke `node` with `runEmbeddedPiAgent()` call, JSON over stdin/stdout. No gateway needed.
- Capabilities: multi-turn reasoning, tool calling, streaming, skill composition, context compaction
- Requires Node.js 22+
- **`SimpleAgent`** — single-turn: parse query → call model → return response. No tool calling, no multi-turn. Works without Node.js. Good for testing and simple Q&A.
- **`OrchestratorAgent`** — multi-turn with model selection per step. Adapted from IPW's executor pattern. Each reasoning step can route to a different model (e.g., fast model for planning, large model for synthesis).
- **`CustomAgent`** — template class for user-defined agents. Subclass `BaseAgent`, implement `run()`, register with `@AgentRegistry.register("my-agent")`.
### Tool system
- [ ] **`BaseTool` ABC:**
```python
class BaseTool(ABC):
name: str
spec: ToolSpec # category, cost_estimate, latency_estimate, capabilities
def execute(self, input: str, **params) -> ToolResult: ...
```
- [ ] **Built-in tools:**
- `Calculator` — evaluate math expressions
- `WebSearch` — search the web (Tavily, SearXNG, or DuckDuckGo)
- `CodeInterpreter` — execute Python in sandboxed environment
- `FileRead` / `FileWrite` — local file operations
- `Think` — internal reasoning scratchpad (zero-cost tool for chain-of-thought)
- `Retrieval` — wired to memory backends, returns relevant documents
- `LLMTool` — call another LLM as a tool (for model composition)
- [ ] **`ToolRegistry`** with discovery:
- `ToolSpec` metadata: category, estimated latency, estimated cost, required API keys, capabilities list
- Auto-discover available tools based on installed packages and environment
### API server
- [ ] **OpenAI-compatible API server:**
- `POST /v1/chat/completions` — standard chat completion with tool use
- `GET /v1/models` — list available models
- Streaming via Server-Sent Events (SSE)
- `jarvis serve --port 8000 --agent openclaw`
### CLI
- [ ] `jarvis serve --port 8000 --agent <agent>` — start API server
- [ ] `jarvis ask` now supports `--agent <agent>` flag
---
## Phase 4 — Learning Approach (placeholder)
**Goal:** Stub interfaces for the learned router. No ML training in this phase — just the contracts and telemetry plumbing so everything is ready when we build it.
**Version milestone:** v0.5
### Stubs
- [ ] **`RouterPolicy` ABC:**
```python
class RouterPolicy(ABC):
def select_model(self, query: str, context: RoutingContext) -> ModelSpec: ...
```
The heuristic router from Phase 1 implements this as the default.
- [ ] **`RewardFunction` ABC:**
```python
class RewardFunction(ABC):
def compute(self, trajectory: Trajectory) -> float: ...
```
Placeholder implementations: `QualityReward` (LLM-judge), `LatencyReward` (inverse latency), `EnergyReward` (inverse energy), `CostReward` (inverse cost), `CompositeReward` (weighted combination).
- [ ] **`TelemetryAggregator`:**
- Reads `TelemetryRecord` entries from SQLite (accumulated since Phase 1)
- Computes per-model statistics: average latency, token throughput, cost, quality (when graded)
- Exports training-ready datasets for the future GRPO pipeline
- [ ] **Design document:** `docs/learning-pipeline.md` describing the planned GRPO training pipeline:
- Trajectory generation from Phases 1-3 telemetry
- Reward model training
- Policy optimization with GRPO
- Online evaluation and rollout strategy
---
## Phase 5 — Integration & Polish (~3-4 weeks)
**Goal:** Production-ready packaging, OpenClaw integration, benchmarking, SDK, and documentation.
**Version milestone:** v1.0
### OpenClaw integration
- [ ] **`openjarvis-openclaw` plugin package:**
- `register()` hook implementing OpenClaw's plugin API
- `registerProvider()` — wraps OpenJarvis as an OpenClaw `ProviderPlugin` (routes through OpenJarvis intelligence + engine)
- `registerTool()` — exposes OpenJarvis tools to OpenClaw
- `MemorySearchManager` — implements OpenClaw's `search()` / `sync()` / `status()` interface, backed by OpenJarvis memory
### Deployment
- [ ] **Dockerfile** — multi-stage build with optional GPU support
- [ ] **docker-compose.yml** — OpenJarvis + Ollama/vLLM + optional gateway
- [ ] **Service files** — systemd (Linux) and launchd (macOS) for running as a system service
### Python SDK
- [ ] **Programmatic API:**
```python
from openjarvis import Jarvis
j = Jarvis() # Auto-loads config
response = await j.ask("Explain transformers") # Uses router + engine
await j.memory.index("~/papers/") # Index documents
results = await j.memory.search("attention mechanism")
```
### Benchmarking
- [ ] **`jarvis bench` CLI:**
- `BaseBenchmark` / `DatasetBenchmark` ABCs (adapted from IPW's `BenchmarkSuite`)
- Run benchmarks across models, measure accuracy + latency + energy
- Output JSONL results + summary JSON
### Documentation
- [ ] Documentation site (MkDocs or similar)
- [ ] Getting started guide
- [ ] Plugin development guide
- [ ] API reference
---
## Phase 6 — Trace System & Learning (~ongoing)
**Goal:** Full interaction-level trace recording, trace-driven learning, and pluggable agentic architectures. The foundation for studying local AI systems.
**Version milestone:** v1.1
### Trace System (complete)
- [x] **`Trace` and `TraceStep` types** — full interaction recording with step types: route, retrieve, generate, tool_call, respond
- [x] **`TraceStore`** — SQLite-backed append-only store with filtering (by agent, model, outcome, time range)
- [x] **`TraceCollector`** — wraps any `BaseAgent`, subscribes to EventBus, records steps automatically
- [x] **`TraceAnalyzer`** — read-only query layer: per-route stats, per-tool stats, summaries, query-type filtering, export
- [x] **`TraceDrivenPolicy`** — learns routing from trace outcomes, batch and online updates, registered as `"learned"` policy
- [x] **Event bus integration** — `TRACE_STEP` and `TRACE_COMPLETE` event types
### Next Steps
- [ ] Wire `TraceCollector` into SDK/CLI for automatic trace collection
- [ ] `jarvis trace` CLI subcommand (list, inspect, export traces)
- [ ] User feedback mechanisms (thumbs up/down, quality scores)
- [ ] Hierarchical memory (episodic/semantic/procedural layers)
- [ ] Pluggable agentic architectures (ReAct, tree-of-thought, custom loops)
- [ ] Prompt optimization from traces (DSPy-style compilation for local models)
- [ ] Model weight updates from traces (LoRA/QLoRA finetuning)
- [ ] GAIA benchmark evaluation with local models
---
## Version Summary
| Version | Phase | What you get |
|---------|-------|-------------|
| **v0.1** | Phase 0 | Scaffolding, registries, config, CLI skeleton |
| **v0.2** | Phase 1 | `jarvis ask` works — local & cloud inference with telemetry |
| **v0.3** | Phase 2 | Memory — index docs, conversation history, context injection |
| **v0.4** | Phase 3 | Agents + tools + OpenAI-compatible API server |
| **v0.5** | Phase 4 | Learning stubs — router policy interface, telemetry aggregation |
| **v1.0** | Phase 5 | Production — SDK, OpenClaw plugin, Docker, benchmarks, docs |
| **v1.1** | Phase 6 | Trace system, trace-driven learning, pluggable agent architectures |
-311
View File
@@ -1,311 +0,0 @@
# OpenJarvis
**Programming abstractions for on-device AI.**
OpenJarvis defines the abstractions needed to study and build AI systems that run entirely on local hardware. Instead of locking you into one model, one memory system, or one inference engine, OpenJarvis lets you compose your own stack across four core abstractions — then swap any piece without touching the rest.
Built for researchers studying local AI systems and developers who want full control over their AI stack. Every interaction generates a trace; the system learns from its own usage to improve over time.
## Why Local AI Needs New Abstractions
Cloud AI treats intelligence as a **service** — you send a request, get a response, pay per token. Local AI treats intelligence as a **resource** — it lives on your machine, it's always available, it has fixed capabilities, it can be modified, and it accumulates state over time. This inversion changes everything:
- **Fixed resource budget** — You have 8-24GB VRAM, period. Scheduling and allocation are first-class problems.
- **Persistent state is free, compute is expensive** — The opposite of cloud. You can store everything forever but can only run one model at a time.
- **You own the weights** — Fine-tuning, RL, prompt compilation are all possible on your data, your hardware, with immediate feedback loops.
- **Hardware heterogeneity** — Apple Silicon, NVIDIA consumer GPUs, AMD, CPU-only — each with different optimal strategies.
- **Every interaction is a learning signal** — Traces accumulate locally, enabling the system to learn routing, tool selection, and memory strategies from personal usage patterns.
---
## The Five Pillars
OpenJarvis is organized around five composable pillars. Each pillar defines a clear interface; implementations are discovered at runtime via a decorator-based registry system.
```
┌─────────────────────────────────────────────────────────────────────┐
│ OpenJarvis │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Intelligence │ │ Learning │ │ Memory / │ │
│ │ (Models) │ │ Approach │ │ Storage │ │
│ │ │ │ (Router) │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Agentic Logic │ │
│ │ (Orchestration, Tools, Reasoning) │ │
│ └──────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Inference Engine │ │
│ │ (vLLM, Ollama, llama.cpp, SGLang, MLX) │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
---
### 1. Intelligence (Model Layer)
**What it does:** Manages available language models — local and cloud — and routes queries to the best model for the task.
**What's pluggable:** Models, model providers, routing heuristics.
**Supported at launch:**
| Category | Models |
|----------|--------|
| Open-source (local) | Qwen3 8B, Qwen3 32B, GPT OSS 120B, Kimi-K2.5, MiniMax-M2.5 |
| Cloud APIs | Claude (Anthropic), GPT-4o / GPT-5 (OpenAI), Gemini (Google) |
**Key components:**
- **`ModelRegistry`** — decorator-based registry mapping model keys to `ModelSpec` objects (parameter count, quantization, hardware compatibility, context length)
- **Heuristic Router (V0)** — rule-based routing: short queries → small model, complex reasoning → large model, code → code specialist, fallback chains for unavailable models
- **Auto-discovery** — detects models from running inference engines (Ollama, vLLM) and available API keys
---
### 2. Learning Approach (Router Policy)
**What it does:** Determines *which* model handles a given query. Static policies use rules; learned policies update from interaction traces.
**What's pluggable:** Routing policy, reward functions, training pipeline, trace analyzers.
**Implemented:**
- **Heuristic routing** — rule-based routing based on query characteristics (length, complexity keywords, domain detection), fallback chains
- **Trace-driven routing** — learns from accumulated interaction traces which model/agent/tool combinations produce the best outcomes for different query types. Registered as `"learned"` policy.
- **Trace system** — every interaction generates a `Trace` recording the full sequence of steps (route, retrieve, generate, tool_call, respond) with timing, inputs, outputs, and outcomes. Stored in SQLite via `TraceStore`.
- **Trace analysis** — `TraceAnalyzer` computes per-route stats, per-tool stats, success rates, and query-type distributions from stored traces.
**Future:**
- Learned router via GRPO (Group Relative Policy Optimization)
- Preference learning from user feedback
- Continual fine-tuning on accumulated trajectories
- Multi-objective optimization: quality vs. latency vs. energy vs. cost
---
### 3. Memory / Storage
**What it does:** Provides persistent, searchable memory across conversations, documents, and personal notes. Memory is automatically injected into prompts with source attribution.
**What's pluggable:** Storage backends, retrieval strategies, embedding models, chunking strategies.
**Memory types:**
- **Conversation Memory** — sliding window with automatic summarization of older turns
- **Knowledge Base** — indexed documents (PDF, Markdown, code, text) with multi-backend search
- **Personal Notes** — user-created persistent notes and preferences
- **Episodic Memory** — records of past interactions, tool uses, and outcomes
**Backend implementations:**
| Backend | Type | Description |
|---------|------|-------------|
| **SQLite** (default) | Keyword + FTS | FTS5 full-text search. Zero dependencies, zero config. Always available. |
| **FAISS** | Dense retrieval | Neural semantic search via `sentence-transformers` + FAISS indexes. |
| **ColBERTv2** | Late interaction | Token-level MaxSim matching with 2-bit residual compression. Best retrieval quality. Uses `colbert-ai` package with `Indexer` for offline indexing and `Searcher` for millisecond-latency queries. |
| **BM25** | Sparse retrieval | Classic keyword search baseline. Fast, no GPU needed. |
| **Hybrid** | Fusion | BM25 + dense (or ColBERT) with Reciprocal Rank Fusion (RRF). Best of both worlds. |
| **Vector DB adapters** | Dense retrieval | Qdrant, ChromaDB connectors for users with existing vector infrastructure. |
**ColBERTv2 details:**
- Late interaction model: queries and documents are encoded independently, then matched at the token level via MaxSim
- 2-bit residual compression keeps indexes compact while preserving quality
- Offline indexing via `Indexer(checkpoint="colbertv2.0", config=ColBERTConfig(nbits=2))`
- Millisecond query latency via `Searcher(index=name).search(query, k=10)`
- Substantially better retrieval quality than single-vector dense methods on complex queries
---
### 4. Agentic Logic
**What it does:** Orchestrates multi-turn reasoning, tool calling, and task execution. The agent layer sits between the user and the model, managing context, tools, and conversation flow.
**What's pluggable:** Agent implementations, tools, tool registries, execution strategies.
**Agent implementations:**
| Agent | Description |
|-------|-------------|
| **`OpenClawAgent`** (default) | Wraps OpenClaw's Pi agent runtime. Multi-turn reasoning, tool calling, streaming responses, skill composition, context compaction. Two modes: **HTTP** (WebSocket to OpenClaw gateway on `:18789`) or **subprocess** (invoke `node` with `runEmbeddedPiAgent()`, JSON over stdin/stdout). Requires Node.js 22+. |
| **`SimpleAgent`** | Single-turn: query → model → response. No tool calling. Works without Node.js. Good for quick answers and testing. |
| **`OrchestratorAgent`** | Multi-turn with per-step model selection. Adapted from IPW's executor pattern. Routes each reasoning step to the optimal model. |
| **`CustomAgent`** | Template for user-defined agent logic. Subclass `BaseAgent`, implement `run()`, register with `AgentRegistry`. |
**Tool system:**
- `BaseTool` ABC with `ToolSpec` metadata (category, cost estimate, latency estimate, capabilities)
- `ToolRegistry` — runtime-discoverable tool catalog
- Built-in tools: Calculator, WebSearch, CodeInterpreter, FileRead/Write, Think, Retrieval (wired to memory backends), LLM-as-tool
- MCP (Model Context Protocol) compatible
**API server:**
- OpenAI-compatible `/v1/chat/completions` and `/v1/models` endpoints
- Streaming via Server-Sent Events (SSE)
- Drop-in replacement for any OpenAI-compatible client
---
### 5. Inference Engine
**What it does:** Manages the actual LLM inference runtime — loading models, generating tokens, managing GPU memory.
**What's pluggable:** Engine backends, hardware profiles, quantization strategies.
**Supported engines:**
| Engine | Best for | GPU | CPU |
|--------|----------|-----|-----|
| **vLLM** | High-throughput server, multi-GPU, production | NVIDIA, AMD | — |
| **SGLang** | Structured generation, constrained decoding | NVIDIA, AMD | — |
| **Ollama** | Easy setup, Apple Silicon, single-model | NVIDIA, Apple | Yes |
| **llama.cpp** | Maximum hardware compatibility, GGUF models | NVIDIA, AMD, Apple | Yes |
| **MLX** | Apple Silicon native, Metal acceleration | Apple | Apple |
**Hardware auto-detection:**
- Detects GPU vendor (NVIDIA/AMD/Apple), model, VRAM, compute capability
- Recommends the best engine for detected hardware
- Apple Silicon → Ollama or MLX; NVIDIA datacenter → vLLM; AMD → vLLM with ROCm; CPU-only → llama.cpp
---
## Query Flow
```
User query
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Agentic │────▶│ Memory │────▶│ Context │
│ Logic │ │ Retrieve │ │ Inject │
└────┬─────┘ └──────────┘ └────┬─────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Learning │────▶│ Model │────▶│ Inference│
│ (Router) │ │ Select │ │ Engine │
└──────────┘ └──────────┘ └────┬─────┘
┌──────────┐
│ Response │
│ + Telem. │
└──────────┘
```
1. **Agentic Logic** receives the user query, determines if tools or memory are needed
2. **Memory** retrieves relevant context (conversation history, documents, notes)
3. **Context Injection** assembles the full prompt with retrieved content and source attribution
4. **Learning/Router** selects the best model for this query based on routing policy (heuristic or trace-driven)
5. **Inference Engine** runs the selected model and streams the response
6. **Trace** records the full interaction sequence: every routing decision, memory retrieval, tool call, and generation step with timing and outcomes
7. **Learning** periodically updates routing policies from accumulated traces
---
## User Scenarios
### Developer on M4 Max MacBook Pro (128 GB unified memory)
```toml
# ~/.openjarvis/config.toml
[engine]
backend = "ollama" # Native Apple Silicon support
[intelligence]
default_model = "qwen3-32b" # Fits in 128 GB unified memory
fallback = "qwen3-8b"
[memory]
backend = "sqlite" # Zero-config, always works
retrieval = "hybrid" # BM25 + FAISS for local docs
[agent]
type = "openclaw" # Full agent capabilities
mode = "subprocess" # No separate gateway needed
```
Day-to-day: codes with `jarvis ask`, indexes project docs with `jarvis memory index`, runs a local OpenAI-compatible server with `jarvis serve` for editor integration.
### Researcher on DGX Spark (2x B200, 384 GB GPU memory)
```toml
[engine]
backend = "vllm"
tensor_parallel = 2
[intelligence]
default_model = "qwen3-235b-a22b"
router = "heuristic" # Route small queries to 8B, large to 235B
[memory]
backend = "colbert" # Best retrieval quality for papers
knowledge_base = "~/papers/"
[agent]
type = "orchestrator" # Multi-model orchestration
```
Running benchmarks with `jarvis bench`, profiling energy per query, comparing model efficiency across hardware configurations.
### Privacy-Focused Offline Setup
```toml
[engine]
backend = "llamacpp" # No server needed
network = "offline"
[intelligence]
default_model = "qwen3-8b-q4" # Quantized to fit available RAM
[memory]
backend = "sqlite" # Everything local
retrieval = "bm25" # No neural models needed
[agent]
type = "simple" # No external dependencies
```
Fully air-gapped. No cloud APIs, no network calls, no telemetry export. All data stays on the machine.
---
## Comparison
| Feature | OpenJarvis | Ollama | LangChain | OpenClaw | vLLM |
|---------|-----------|--------|-----------|----------|------|
| **Focus** | Composable AI backend | Model runner | LLM app framework | AI coding assistant | Inference server |
| **Model management** | Multi-engine, auto-detect | Single engine | Bring your own | Cloud-first | Single engine |
| **Memory** | Multi-backend retrieval | None | Vector store wrappers | Conversation only | None |
| **Agents** | Pluggable (Pi, custom) | None | Chain-based | Pi agent (built-in) | None |
| **Inference** | vLLM/SGLang/Ollama/llama.cpp/MLX | Ollama only | External | External | vLLM only |
| **Hardware-aware** | Auto-detect + recommend | Manual | No | No | Manual |
| **Telemetry** | Energy, latency, cost | None | Callbacks | Basic | Metrics |
| **Offline** | Full support | Full support | Partial | No | Full support |
| **API** | OpenAI-compatible | OpenAI-compatible | Custom | Custom | OpenAI-compatible |
| **Language** | Python | Go | Python | TypeScript | Python |
OpenJarvis is **not** a replacement for these tools — it *composes* them. Ollama and vLLM are inference engine options. OpenClaw's Pi agent is the default agentic logic. LangChain-style chains can be implemented as custom agents.
---
## Design Principles
1. **Pluggable everything** — every component is registered and discoverable at runtime. Swap models, engines, memory backends, and agents without code changes.
2. **Registry-driven**`RegistryBase[T]` pattern (adapted from IPW) provides type-safe, decorator-based registration for all extensible components: `ModelRegistry`, `EngineRegistry`, `MemoryRegistry`, `AgentRegistry`, `ToolRegistry`.
3. **Offline-first** — works without network access. Cloud APIs are optional enhancements, never requirements.
4. **Telemetry-native** — every inference call records timing, token counts, and (when hardware supports it) energy consumption. Data lands in SQLite for analysis.
5. **Hardware-aware** — auto-detects GPU vendor, model, VRAM, and platform. Recommends the best engine and model configuration for your hardware.
6. **Python-first** — core is pure Python (3.10+). Node.js required only for OpenClaw agent integration. No Java, no JVM, no heavy runtimes.
7. **OpenAI-compatible API**`jarvis serve` exposes `/v1/chat/completions` and `/v1/models`. Any client that speaks OpenAI protocol works out of the box.
8. **Standalone** — OpenJarvis is a self-contained backend. OpenClaw is one possible frontend; so is `curl`, a Python SDK call, or any OpenAI-compatible client.
Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 KiB

-20
View File
@@ -1,20 +0,0 @@
<svg width="345" height="80" viewBox="0 0 345 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Circuit Node Mark -->
<g transform="translate(0, 0)">
<circle cx="40" cy="40" r="12" fill="#e0e0e8"/>
<circle cx="40" cy="40" r="7.5" fill="#3b82f6"/>
<line x1="40" y1="28" x2="40" y2="10" stroke="#e0e0e8" stroke-width="3" stroke-linecap="round"/>
<line x1="40" y1="52" x2="40" y2="70" stroke="#e0e0e8" stroke-width="3" stroke-linecap="round"/>
<line x1="28" y1="40" x2="10" y2="40" stroke="#e0e0e8" stroke-width="3" stroke-linecap="round"/>
<line x1="52" y1="40" x2="70" y2="40" stroke="#e0e0e8" stroke-width="3" stroke-linecap="round"/>
<line x1="31.5" y1="31.5" x2="17" y2="17" stroke="#e0e0e8" stroke-width="2.5" stroke-linecap="round"/>
<line x1="48.5" y1="31.5" x2="63" y2="17" stroke="#e0e0e8" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="40" cy="7" r="5" fill="#e0e0e8"/>
<circle cx="40" cy="73" r="5" fill="#e0e0e8"/>
<circle cx="7" cy="40" r="5" fill="#e0e0e8"/>
<circle cx="73" cy="40" r="5" fill="#e0e0e8"/>
<circle cx="14.5" cy="14.5" r="4.5" fill="#6366f1"/>
<circle cx="65.5" cy="14.5" r="4.5" fill="#10b981"/>
</g>
<text x="100" y="52" font-family="'IBM Plex Mono', 'SF Mono', 'Menlo', monospace" font-weight="600" font-size="42" fill="#eeeef4" letter-spacing="-1.5">openjarvis</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-20
View File
@@ -1,20 +0,0 @@
<svg width="345" height="80" viewBox="0 0 345 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Circuit Node Mark -->
<g transform="translate(0, 0)">
<circle cx="40" cy="40" r="12" fill="#1a1a1e"/>
<circle cx="40" cy="40" r="7.5" fill="#3b82f6"/>
<line x1="40" y1="28" x2="40" y2="10" stroke="#1a1a1e" stroke-width="3" stroke-linecap="round"/>
<line x1="40" y1="52" x2="40" y2="70" stroke="#1a1a1e" stroke-width="3" stroke-linecap="round"/>
<line x1="28" y1="40" x2="10" y2="40" stroke="#1a1a1e" stroke-width="3" stroke-linecap="round"/>
<line x1="52" y1="40" x2="70" y2="40" stroke="#1a1a1e" stroke-width="3" stroke-linecap="round"/>
<line x1="31.5" y1="31.5" x2="17" y2="17" stroke="#1a1a1e" stroke-width="2.5" stroke-linecap="round"/>
<line x1="48.5" y1="31.5" x2="63" y2="17" stroke="#1a1a1e" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="40" cy="7" r="5" fill="#1a1a1e"/>
<circle cx="40" cy="73" r="5" fill="#1a1a1e"/>
<circle cx="7" cy="40" r="5" fill="#1a1a1e"/>
<circle cx="73" cy="40" r="5" fill="#1a1a1e"/>
<circle cx="14.5" cy="14.5" r="4.5" fill="#6366f1"/>
<circle cx="65.5" cy="14.5" r="4.5" fill="#10b981"/>
</g>
<text x="100" y="52" font-family="'IBM Plex Mono', 'SF Mono', 'Menlo', monospace" font-weight="600" font-size="42" fill="#1a1a1e" letter-spacing="-1.5">openjarvis</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

+20 -5
View File
@@ -43,7 +43,7 @@ enabled = true
default = "vllm"
[engine.vllm]
host = "http://localhost:8000" # Default vLLM port
host = "http://localhost:8001" # vLLM serving port
[engine.ollama]
host = "http://localhost:11434"
@@ -54,6 +54,19 @@ host = "http://localhost:30000"
[engine.llamacpp]
host = "http://localhost:8080"
[engine.exo]
host = "http://localhost:52415"
[engine.nexa]
host = "http://localhost:18181"
# device = "npu" # optional: cpu, gpu, npu
[engine.uzu]
host = "http://localhost:8080"
[engine.apple_fm]
host = "http://localhost:8079"
# ═══════════════════════════════════════════════════════════════
# PILLAR 5: Learning — Improvement Methodologies
# ═══════════════════════════════════════════════════════════════
@@ -93,9 +106,11 @@ enabled = true # Record traces for analysis
db_path = "~/.openjarvis/traces.db"
[server]
host = "0.0.0.0"
# Bind to loopback by default so the API is not exposed to the local network.
# To serve other devices on your LAN, set host = "0.0.0.0" AND set an API key
# (OPENJARVIS_API_KEY / `jarvis auth generate-key`) — startup refuses a
# non-loopback bind without a key. The "server" security profile also flips
# this to 0.0.0.0 intentionally.
host = "127.0.0.1"
port = 8000
agent = "native_openhands"
[security]
enabled = false # Disable for eval (no PII scanning overhead)
@@ -0,0 +1,24 @@
# Simple Chat — lightweight conversational AI, no tools
# Copy to ~/.openjarvis/config.toml
#
# The fastest setup: just Ollama + a model.
#
# Usage:
# jarvis ask "What is quantum computing?"
# jarvis chat # interactive chat session
# jarvis serve # start API server for browser/desktop app
[engine]
default = "ollama"
[intelligence]
default_model = "qwen3.5:4b" # Fast and lightweight
# default_model = "qwen3.5:9b" # Better quality
# default_model = "llama3.1:8b" # Alternative model
[agent]
default_agent = "simple" # Single-turn, no tools
[server]
host = "0.0.0.0"
port = 8000
@@ -0,0 +1,21 @@
# Code Assistant — agent with code execution, file I/O, and shell access
# Copy to ~/.openjarvis/config.toml
#
# Usage:
# jarvis ask "Write a Python script that parses CSV files"
# jarvis ask "Read main.py and explain the architecture"
# jarvis ask --agent orchestrator "Find and fix the bug in test_utils.py"
[engine]
default = "ollama"
[intelligence]
default_model = "qwen3.5:9b"
# default_model = "qwen3.5:35b" # Better for complex code tasks
[agent]
default_agent = "orchestrator" # Multi-turn with tool selection
max_turns = 10
[tools]
enabled = ["code_interpreter", "file_read", "file_write", "shell_exec", "web_search", "think", "calculator"]
@@ -0,0 +1,27 @@
# Deep Research Agent — multi-hop research across your indexed documents
# Copy to ~/.openjarvis/config.toml
#
# First index your documents:
# jarvis memory index ./docs/
# jarvis memory index ~/Documents/papers/
#
# Then ask complex questions:
# jarvis ask --agent deep_research "Summarize all emails about Project X"
# jarvis ask --agent deep_research "What meetings did I have with Alice last month?"
[engine]
default = "ollama"
[intelligence]
default_model = "qwen3.5:9b"
temperature = 0.3 # Low temperature for factual research
[agent]
default_agent = "deep_research"
max_turns = 8 # Multi-hop reasoning steps
[tools]
enabled = ["knowledge_search", "knowledge_sql", "scan_chunks", "think", "web_search"]
[tools.storage]
default_backend = "sqlite"
@@ -0,0 +1,39 @@
# Full system access: unrestricted shell and filesystem
# Copy to ~/.openjarvis/config.toml
#
# WARNING: shell_exec runs arbitrary commands as your user. No command
# allowlist, no denylist, no working-directory restriction. file_read and
# file_write aren't restricted to any directory either. Only enable what you
# actually want the agent to have. tools.enabled is the whole permission grant;
# there's no second allowlist to configure.
#
# On macOS, this config alone does not reach TCC-protected data (Messages,
# Mail, Photos, Safari). That requires Full Disk Access granted to the process
# hosting the backend. See docs/user-guide/system-access.md.
#
# Usage:
# jarvis ask "What's using the most disk space in my home directory?"
# jarvis chat # prompts before each shell_exec call
[engine]
default = "ollama"
[intelligence]
default_model = "qwen3.5:9b"
[agent]
default_agent = "orchestrator"
max_turns = 10
[tools]
enabled = [
"shell_exec",
"file_read",
"file_write",
"apply_patch",
"code_interpreter",
"git_status",
"git_diff",
"think",
"calculator",
]
@@ -0,0 +1,51 @@
# Morning Digest — Linux / Cloud GPU with Ollama or vLLM
# Copy to ~/.openjarvis/config.toml and customize
#
# Requirements:
# - Ollama or vLLM running locally
# - Cartesia or OpenAI API key for TTS
[engine]
default = "ollama"
# default = "vllm" # Use vLLM for GPU servers
[engine.ollama]
host = "http://localhost:11434"
# [engine.vllm]
# host = "http://localhost:8001"
[intelligence]
default_model = "qwen3.5:9b"
[agent]
default_agent = "simple"
[tools]
enabled = ["code_interpreter", "web_search", "file_read", "shell_exec", "digest_collect", "text_to_speech"]
# ─── Morning Digest ─────────────────────────────────────────
[digest]
enabled = true
schedule = "0 7 * * *"
timezone = "America/New_York" # Change to your timezone
persona = "jarvis"
honorific = "sir"
tts_backend = "openai" # OpenAI TTS works everywhere
voice_id = "onyx" # Deep male voice
voice_speed = 1.1
sections = ["health", "messages", "calendar", "world"]
[digest.health]
sources = ["oura"]
[digest.messages]
sources = ["gmail", "google_tasks", "slack"]
[digest.calendar]
sources = ["gcalendar"]
[digest.world]
sources = ["hackernews", "news_rss", "weather"]
@@ -0,0 +1,56 @@
# Morning Digest — Mac (Apple Silicon) with Ollama
# Copy to ~/.openjarvis/config.toml and customize
#
# Requirements:
# - Ollama installed (https://ollama.com)
# - ollama pull qwen3.5:9b
# - Cartesia API key (https://play.cartesia.ai) or OpenAI API key
[engine]
default = "ollama"
[intelligence]
default_model = "qwen3.5:9b" # Good balance of speed + quality on M1/M2/M3
# default_model = "qwen3.5:4b" # Faster, lower quality
# default_model = "qwen3.5:35b" # Slower, higher quality (needs 32GB+ RAM)
[agent]
default_agent = "simple"
[tools]
enabled = ["code_interpreter", "web_search", "file_read", "shell_exec", "digest_collect", "text_to_speech"]
# ─── Morning Digest ─────────────────────────────────────────
[digest]
enabled = true
schedule = "0 7 * * *" # 7 AM daily
timezone = "America/Los_Angeles" # Change to your timezone
persona = "jarvis"
honorific = "sir" # "sir", "ma'am", "boss", or any custom
tts_backend = "cartesia" # "cartesia" or "openai"
voice_id = "c8f7835e-28a3-4f0c-80d7-c1302ac62aae" # Alistair (British male)
voice_speed = 1.2 # 1.0 = normal, 1.2 = 20% faster
# Sections in order of priority (remove any you don't want):
sections = ["health", "messages", "calendar", "world"]
[digest.health]
sources = ["oura"] # Add "apple_health" if you export from iPhone
# sources = ["oura", "apple_health", "strava"]
[digest.messages]
sources = ["gmail", "google_tasks", "imessage"]
# Add any of: "slack", "notion", "github_notifications"
[digest.calendar]
sources = ["gcalendar"]
[digest.world]
sources = ["hackernews", "news_rss"]
# Add "weather" after setting up OpenWeatherMap API key
# ─── Optional: Music section ────────────────────────────────
# Uncomment and add "music" to sections list above
# [digest.music]
# sources = ["spotify", "apple_music"]
@@ -0,0 +1,31 @@
# Morning Digest — Minimal setup (just Ollama + Gmail)
# The simplest possible config to get a working digest.
# Copy to ~/.openjarvis/config.toml
#
# Requirements:
# - Ollama installed with any model
# - Google OAuth credentials (jarvis connect gdrive)
# - OpenAI API key for TTS (or skip audio with --text-only)
[engine]
default = "ollama"
[intelligence]
default_model = "qwen3.5:4b" # Small, fast, runs on any machine
[tools]
enabled = ["digest_collect", "text_to_speech"]
[digest]
enabled = true
persona = "jarvis"
honorific = "sir"
tts_backend = "openai"
voice_id = "onyx"
sections = ["messages", "calendar"]
[digest.messages]
sources = ["gmail"]
[digest.calendar]
sources = ["gcalendar"]
@@ -0,0 +1,35 @@
# Scheduled Monitor — persistent agent that runs on a schedule
# Copy to ~/.openjarvis/config.toml
#
# The operative agent maintains state across runs, making it ideal for:
# - Daily email/inbox monitoring
# - Recurring status checks
# - Long-running research projects
#
# Setup:
# 1. Index your data: jarvis memory index ~/Documents/
# 2. Start the scheduler: jarvis scheduler start
# 3. Create a task:
# jarvis scheduler create \
# --prompt "Check for new emails about Project X and update your notes" \
# --schedule "0 9 * * 1-5" \
# --agent operative \
# --tools "knowledge_search,knowledge_sql,memory_store,think"
[engine]
default = "ollama"
[intelligence]
default_model = "qwen3.5:9b"
temperature = 0.3
[agent]
default_agent = "operative"
max_turns = 20
context_from_memory = true # Inject relevant memory into context
[tools]
enabled = ["knowledge_search", "knowledge_sql", "scan_chunks", "memory_store", "memory_search", "think", "web_search"]
[tools.storage]
default_backend = "sqlite"
@@ -0,0 +1,76 @@
# LLM-Guided Spec Search — quickstart configuration
# Copy to ~/.openjarvis/config.toml and run:
# python -m openjarvis_examples.spec_search_quickstart
#
# This config has two parts:
# 1. The agent system being optimized (intelligence / engine / agent / tools).
# Same schema as the other examples in this directory; parsed by
# ``openjarvis.core.config.load_config``.
# 2. ``[learning.spec_search]`` and its sub-tables — the search hyperparameters
# consumed by ``SpecSearchOrchestrator.from_config`` and ``SpecSearchLoop``.
#
# Defaults below match the paper (Saad-Falcon et al., 2026):
# - max_regression = 0.01 (epsilon in GateOK)
# - stagnation_k = 5 (Algorithm 1 stopping rule)
# - composite_reward weights (alpha, beta, gamma, delta) = (0.5, 0.1, 0.1, 0.3)
#
# Teacher API keys come from your environment / credentials store, not this file.
# ---------------------------------------------------------------------------
# Agent system being optimized
# ---------------------------------------------------------------------------
[engine]
default = "ollama" # swap to "vllm" on H100/RTX 6000 / DGX Spark
[intelligence]
default_model = "qwen3.5:9b" # the local student
# default_model = "qwen3.5:27b-fp8" # workstation tier
[agent]
default_agent = "orchestrator" # multi-turn, tool-using
max_turns = 10
[tools]
enabled = [
"code_interpreter",
"file_read",
"web_search",
"think",
"calculator",
]
# ---------------------------------------------------------------------------
# LLM-guided spec search hyperparameters (paper §3.3, Algorithm 1)
# ---------------------------------------------------------------------------
[learning.spec_search]
enabled = true
teacher_model = "claude-opus-4-6" # frontier proposer
teacher_engine = "cloud" # CloudEngine registry key (uses LiteLLM)
autonomy_mode = "tiered" # auto | tiered | manual
# Per-session bounds (one diagnose / plan / execute / record pass)
min_traces = 20
max_cost_per_session_usd = 5.0
max_tool_calls_per_diagnosis = 30
# Multi-session loop (paper Algorithm 1 stopping)
stagnation_k = 5 # stop after this many sessions with no gate-score gain
stagnation_eps = 0.001 # delta below this counts as no improvement
max_total_cost_usd = 50.0 # cumulative teacher-cost budget across all sessions
# GateOK predicate — accept iff target cluster improves AND every other cluster
# regresses by at most max_regression (epsilon in the paper).
max_regression = 0.01 # paper default: 1%
min_improvement = 0.0
benchmark_subsample_size = 50
benchmark_version = "personal_v1"
# Composite reward (paper Eq. 1) — used only when an Intelligence edit triggers
# LoRA / GRPO training inside an accepted edit. The held-out gate is unaffected.
[learning.spec_search.composite_reward]
alpha = 0.5 # accuracy weight
beta = 0.1 # energy penalty
gamma = 0.1 # latency penalty
delta = 0.3 # cost penalty
@@ -0,0 +1,30 @@
You are Jarvis — the local AI assistant. You are loyal, efficient, dry-witted, and genuinely care about the person you serve. You have a warm British sensibility: polite but never obsequious, witty but never frivolous.
PERSONALITY:
- You anticipate needs before being asked
- You deliver bad news with constructive dry wit: "Your rebuttals appear to have slipped past their deadline, sir. I'd suggest making them your first order of business — before anyone notices."
- Your humor is understated — a raised eyebrow in voice form
- You are calm under pressure and never flustered
- You treat the briefing as a conversation with someone you respect, not a status report
ADDRESS:
- Use the user's preferred honorific (provided in the system prompt)
- Use it 2-3 times per briefing: once in greeting, once mid-briefing, once in closing
- Never every sentence — that would be a parody, not Jarvis
EMAIL TRIAGE:
- Important emails are from REAL PEOPLE (not automated senders, newsletters, or marketing)
- Prioritize emails that need a REPLY or DECISION, or contain a DEADLINE
- Skip promotional, automated, and notification emails entirely
- For important emails, mention the sender name and what they need
MESSAGE TRIAGE (iMessage, Slack, etc.):
- Highlight messages from key people and threads needing a reply
- Briefly acknowledge casual threads so the user knows you checked: "Your group chat has been lively but nothing requiring a response"
- Skip reactions, emoji-only messages, and automated notifications
CONSTRAINTS:
- ONLY report facts present in the provided data. Never invent.
- NEVER describe actions you are taking (adjusting lights, ordering food, queuing playlists, etc.)
- No markdown formatting, no emojis, no bullet points, no headers — this is spoken aloud
- If a data source is disconnected or errored, skip it silently — do not mention connection issues
@@ -0,0 +1,11 @@
You are an AI assistant generating a daily briefing. Be clear, concise, and factual.
## Voice & Tone
- Straightforward and professional
- No personality or humor — just the facts
- Use plain language
## Structure
- Open with the date and a one-line summary
- Deliver each section with bullet points
- Close with a summary of action items
+7
View File
@@ -0,0 +1,7 @@
# Copy to `.env` in this directory (deploy/docker/.env) before `docker compose up`.
# docker-compose.yml requires this — the container binds 0.0.0.0, so the
# server refuses to start without an API key.
#
# Generate a key with: jarvis auth generate-key
# Then clients must send: Authorization: Bearer <key>
OPENJARVIS_API_KEY=
+84
View File
@@ -0,0 +1,84 @@
# Base images are pinned to an immutable digest (in addition to a human-readable
# tag) so every build resolves the exact same layers — reproducible builds and
# safe rollbacks (#563).
# Stage 1: Build frontend SPA
FROM node:22.23.0-slim@sha256:d9f850096136edbc402debdd8729579a288aac64574ada0ff4db26b6ae58b0b2 AS frontend
# Public Supabase anon key for the savings leaderboard; empty by default so
# the image's leaderboard stays disabled (#589). Pass --build-arg to enable.
ARG OPENJARVIS_LEADERBOARD_PUBLIC_ANON=
WORKDIR /frontend
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm ci --ignore-scripts 2>/dev/null || npm install
COPY frontend/ .
RUN VITE_SUPABASE_ANON_KEY="${OPENJARVIS_LEADERBOARD_PUBLIC_ANON}" npm run build
# Stage 2: Build Python package
FROM python:3.12.13-slim-bookworm@sha256:76d4b7b6305788c6b4c6a19d6a22a3921bf802e9af4d5e1e5bd771208dba74bf AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential ca-certificates curl && \
rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.cargo/bin:${PATH}"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal --default-toolchain none && \
rustup toolchain install 1.88 --profile minimal && \
rustup default 1.88
WORKDIR /app
# Install dependencies from the committed lockfile (#567). `uv export --frozen`
# reads uv.lock as-is (no re-resolution) and emits a fully pinned, hash-verified
# requirements set; `--no-deps` then installs exactly that set. This is a
# separate layer from the source copy so dependency installs stay cached when
# only application code changes.
COPY pyproject.toml uv.lock README.md ./
RUN pip install --no-cache-dir uv && \
uv export --frozen --no-dev --extra server --no-emit-project > requirements.txt && \
uv pip install --system --no-deps -r requirements.txt && \
uv pip install --system --no-deps "maturin>=1.12.6,<2"
# Copy the source and the non-src force-include paths (see pyproject
# [tool.hatch.build.targets.wheel.force-include]) before building the project.
COPY src/ src/
COPY rust/ rust/
COPY scripts/install scripts/install
COPY deploy/windows deploy/windows
# Copy built frontend into the server static directory
COPY --from=frontend /src/openjarvis/server/static src/openjarvis/server/static/
# Install the project itself without re-resolving dependencies.
RUN uv pip install --system --no-deps . && \
maturin build --release \
--manifest-path rust/crates/openjarvis-python/Cargo.toml \
--interpreter python3 \
--out /tmp/openjarvis-rust-wheel && \
uv pip install --system --no-deps /tmp/openjarvis-rust-wheel/*.whl && \
python3 -c "import openjarvis_rust; print('openjarvis_rust ok')" && \
python3 -m pip uninstall -y maturin && \
rm -rf /tmp/openjarvis-rust-wheel rust
# Stage 3: Runtime
FROM python:3.12.13-slim-bookworm@sha256:76d4b7b6305788c6b4c6a19d6a22a3921bf802e9af4d5e1e5bd771208dba74bf
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app
WORKDIR /app
# Run as an unprivileged user — the server needs no root privileges, so dropping
# them limits the blast radius of a compromise (#565). The app writes only to
# $HOME (config/cache/state), which is owned by this user.
RUN groupadd --system --gid 10001 openjarvis && \
useradd --system --uid 10001 --gid openjarvis \
--create-home --home-dir /home/openjarvis openjarvis
ENV HOME=/home/openjarvis
USER openjarvis
EXPOSE 8000
ENTRYPOINT ["jarvis"]
CMD ["serve", "--host", "0.0.0.0", "--port", "8000"]
+87
View File
@@ -0,0 +1,87 @@
# Base images are pinned to an immutable digest (in addition to a human-readable
# tag) so every build resolves the exact same layers — reproducible builds and
# safe rollbacks (#563).
# Stage 1: Build frontend SPA
FROM node:22.23.0-slim@sha256:d9f850096136edbc402debdd8729579a288aac64574ada0ff4db26b6ae58b0b2 AS frontend
# Public Supabase anon key for the savings leaderboard; empty by default so
# the image's leaderboard stays disabled (#589). Pass --build-arg to enable.
ARG OPENJARVIS_LEADERBOARD_PUBLIC_ANON=
WORKDIR /frontend
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm ci --ignore-scripts 2>/dev/null || npm install
COPY frontend/ .
RUN VITE_SUPABASE_ANON_KEY="${OPENJARVIS_LEADERBOARD_PUBLIC_ANON}" npm run build
# Stage 2: Build Python package (NVIDIA CUDA 12.4)
FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04@sha256:af8bd179ed3bf69d4b63b19a763662a6141f0f62ef099283f68d0b14b4bab0e3 AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
python3 \
python3-dev \
python3-pip \
python3-venv && \
rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.cargo/bin:${PATH}"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal --default-toolchain none && \
rustup toolchain install 1.88 --profile minimal && \
rustup default 1.88
WORKDIR /app
# Install dependencies from the committed lockfile (#567). See deploy/docker/Dockerfile
# for the rationale behind the frozen export + --no-deps install.
COPY pyproject.toml uv.lock README.md ./
RUN pip install --no-cache-dir uv && \
uv export --frozen --no-dev --extra server --no-emit-project > requirements.txt && \
uv pip install --system --no-deps -r requirements.txt && \
uv pip install --system --no-deps "maturin>=1.12.6,<2"
COPY src/ src/
COPY rust/ rust/
COPY scripts/install scripts/install
COPY deploy/windows deploy/windows
COPY --from=frontend /src/openjarvis/server/static src/openjarvis/server/static/
RUN uv pip install --system --no-deps . && \
maturin build --release \
--manifest-path rust/crates/openjarvis-python/Cargo.toml \
--interpreter python3 \
--out /tmp/openjarvis-rust-wheel && \
uv pip install --system --no-deps /tmp/openjarvis-rust-wheel/*.whl && \
python3 -c "import openjarvis_rust; print('openjarvis_rust ok')" && \
python3 -m pip uninstall -y maturin && \
rm -rf /tmp/openjarvis-rust-wheel rust
# Stage 3: Runtime
FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04@sha256:af8bd179ed3bf69d4b63b19a763662a6141f0f62ef099283f68d0b14b4bab0e3
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 python3-pip && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app
WORKDIR /app
# Run as an unprivileged user (#565). NVIDIA device nodes (/dev/nvidia*) are
# world-accessible, so GPU workloads do not require root.
RUN groupadd --system --gid 10001 openjarvis && \
useradd --system --uid 10001 --gid openjarvis \
--create-home --home-dir /home/openjarvis openjarvis
ENV HOME=/home/openjarvis
USER openjarvis
EXPOSE 8000
ENTRYPOINT ["jarvis"]
CMD ["serve", "--host", "0.0.0.0", "--port", "8000"]
+91
View File
@@ -0,0 +1,91 @@
# Base images are pinned to an immutable digest (in addition to a human-readable
# tag) so every build resolves the exact same layers — reproducible builds and
# safe rollbacks (#563).
# Stage 1: Build frontend SPA
FROM node:22.23.0-slim@sha256:d9f850096136edbc402debdd8729579a288aac64574ada0ff4db26b6ae58b0b2 AS frontend
# Public Supabase anon key for the savings leaderboard; empty by default so
# the image's leaderboard stays disabled (#589). Pass --build-arg to enable.
ARG OPENJARVIS_LEADERBOARD_PUBLIC_ANON=
WORKDIR /frontend
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm ci --ignore-scripts 2>/dev/null || npm install
COPY frontend/ .
RUN VITE_SUPABASE_ANON_KEY="${OPENJARVIS_LEADERBOARD_PUBLIC_ANON}" npm run build
# Stage 2: Build Python package (AMD ROCm 7.2)
FROM rocm/dev-ubuntu-22.04:7.2@sha256:05af5f04a06b04676d4c7438997d0deadaeb7478961ad621376e199bf3aeb644 AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
python3 \
python3-dev \
python3-pip \
python3-venv && \
rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.cargo/bin:${PATH}"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal --default-toolchain none && \
rustup toolchain install 1.88 --profile minimal && \
rustup default 1.88
WORKDIR /app
# Install dependencies from the committed lockfile (#567). See deploy/docker/Dockerfile
# for the rationale behind the frozen export + --no-deps install.
COPY pyproject.toml uv.lock README.md ./
RUN pip install --no-cache-dir uv && \
uv export --frozen --no-dev --extra server --no-emit-project > requirements.txt && \
uv pip install --system --no-deps -r requirements.txt && \
uv pip install --system --no-deps "maturin>=1.12.6,<2"
COPY src/ src/
COPY rust/ rust/
COPY scripts/install scripts/install
COPY deploy/windows deploy/windows
COPY --from=frontend /src/openjarvis/server/static src/openjarvis/server/static/
RUN uv pip install --system --no-deps . && \
maturin build --release \
--manifest-path rust/crates/openjarvis-python/Cargo.toml \
--interpreter python3 \
--out /tmp/openjarvis-rust-wheel && \
uv pip install --system --no-deps /tmp/openjarvis-rust-wheel/*.whl && \
python3 -c "import openjarvis_rust; print('openjarvis_rust ok')" && \
python3 -m pip uninstall -y maturin && \
rm -rf /tmp/openjarvis-rust-wheel rust
# Stage 3: Runtime
FROM rocm/dev-ubuntu-22.04:7.2@sha256:05af5f04a06b04676d4c7438997d0deadaeb7478961ad621376e199bf3aeb644
RUN apt-get update && \
apt-get install -y --no-install-recommends python3 python3-pip && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app
WORKDIR /app
# Run as an unprivileged user (#565). ROCm GPU access is gated by the `video` and
# `render` groups (see group_add in docker-compose.gpu.rocm.yml), so the user is
# added to both; root is not required.
RUN groupadd --system --gid 10001 openjarvis && \
useradd --system --uid 10001 --gid openjarvis \
--create-home --home-dir /home/openjarvis openjarvis && \
(getent group video >/dev/null || groupadd --system video) && \
(getent group render >/dev/null || groupadd --system render) && \
usermod -aG video,render openjarvis
ENV HOME=/home/openjarvis
USER openjarvis
EXPOSE 8000
ENTRYPOINT ["jarvis"]
CMD ["serve", "--host", "0.0.0.0", "--port", "8000"]
+70
View File
@@ -0,0 +1,70 @@
# Base images are pinned to an immutable digest (in addition to a human-readable
# tag) so every build resolves the exact same layers (#563).
# Node.js is sourced from the official, digest-pinned image rather than piping a
# remote setup script into bash (`curl ... | bash -`), which performed no
# checksum or signature verification of the downloaded installer (#566). The
# image digest is the integrity check, and the copy is architecture-agnostic.
FROM node:22.23.0-slim@sha256:d9f850096136edbc402debdd8729579a288aac64574ada0ff4db26b6ae58b0b2 AS node
FROM python:3.12.13-slim-bookworm@sha256:76d4b7b6305788c6b4c6a19d6a22a3921bf802e9af4d5e1e5bd771208dba74bf AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential ca-certificates curl && \
rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.cargo/bin:${PATH}"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal --default-toolchain none && \
rustup toolchain install 1.88 --profile minimal && \
rustup default 1.88
WORKDIR /app
# Install dependencies from the committed lockfile (#567): `uv export --frozen`
# reads uv.lock as-is and emits a pinned, hash-verified set installed with
# --no-deps (no re-resolution). Copied first so this layer caches independently
# of application source.
COPY pyproject.toml uv.lock README.md ./
RUN pip install --no-cache-dir uv && \
uv export --frozen --no-dev --extra server --no-emit-project > requirements.txt && \
uv pip install --system --no-deps -r requirements.txt && \
uv pip install --system --no-deps "maturin>=1.12.6,<2"
COPY . .
# Install the project itself without re-resolving dependencies.
RUN uv pip install --system --no-deps . && \
maturin build --release \
--manifest-path rust/crates/openjarvis-python/Cargo.toml \
--interpreter python3 \
--out /tmp/openjarvis-rust-wheel && \
uv pip install --system --no-deps /tmp/openjarvis-rust-wheel/*.whl && \
python3 -c "import openjarvis_rust; print('openjarvis_rust ok')" && \
python3 -m pip uninstall -y maturin && \
rm -rf /tmp/openjarvis-rust-wheel rust/target
FROM python:3.12.13-slim-bookworm@sha256:76d4b7b6305788c6b4c6a19d6a22a3921bf802e9af4d5e1e5bd771208dba74bf
# libstdc++6 + ca-certificates are the only runtime requirements of the Node
# binary copied below (the python slim image already provides libc/libgcc).
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates libstdc++6 && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app
# Transplant the Node.js runtime from the official image. Both images are Debian
# bookworm, so the glibc/libstdc++ ABI matches.
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \
ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
WORKDIR /app
LABEL openjarvis-sandbox=true
ENTRYPOINT ["python", "-m", "openjarvis.sandbox.entrypoint"]
@@ -0,0 +1,33 @@
# NVIDIA GPU override — use with:
# docker compose -f deploy/docker/docker-compose.yml -f deploy/docker/docker-compose.gpu.nvidia.yml up
services:
jarvis:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.gpu
volumes:
- /proc:/proc:ro
- /sys:/sys:ro
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
ollama:
# Pinned to a fixed version + digest for reproducible deployments (#563);
# must match the tag in docker-compose.yml.
image: ollama/ollama:0.30.10@sha256:bfc9c6d53cc6989aa5131a6fde6b162b2802d4d337657f3253b5f69579bddeee
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
+15
View File
@@ -0,0 +1,15 @@
# ROCm GPU override — use with:
# docker compose -f deploy/docker/docker-compose.yml -f deploy/docker/docker-compose.gpu.rocm.yml up
version: "3.9"
services:
jarvis:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.gpu.rocm
devices:
- /dev/kfd
- /dev/dri
group_add:
- video
- render
+37
View File
@@ -0,0 +1,37 @@
services:
jarvis:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile
ports:
- "8000:8000"
environment:
- OPENJARVIS_ENGINE_DEFAULT=ollama
- OLLAMA_HOST=http://ollama:11434
# The container binds 0.0.0.0, so an API key is REQUIRED. Compose fails
# fast if OPENJARVIS_API_KEY is unset (set it in deploy/docker/.env —
# see .env.example, or `export` it). Generate one: `jarvis auth generate-key`.
- OPENJARVIS_API_KEY=${OPENJARVIS_API_KEY:?OPENJARVIS_API_KEY must be set (see deploy/docker/.env.example)}
depends_on:
ollama:
condition: service_healthy
restart: unless-stopped
ollama:
# Pinned to a fixed version + digest for reproducible deployments and
# predictable rollbacks (#563). Bump deliberately, not implicitly via :latest.
image: ollama/ollama:0.30.10@sha256:bfc9c6d53cc6989aa5131a6fde6b162b2802d4d337657f3253b5f69579bddeee
ports:
- "11434:11434"
volumes:
- ollama-models:/root/.ollama
healthcheck:
test: ["CMD", "ollama", "list"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
restart: unless-stopped
volumes:
ollama-models:
+13 -1
View File
@@ -4,15 +4,27 @@
<dict>
<key>Label</key>
<string>com.openjarvis</string>
<!-- Binds loopback only: the personal-device default, reachable from this
Mac but not the network, so no API key is required. To expose it on
your LAN, change the host below to 0.0.0.0 AND uncomment the
EnvironmentVariables block to set an API key (an unauthenticated
0.0.0.0 server will refuse to start). -->
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/jarvis</string>
<string>serve</string>
<string>--host</string>
<string>0.0.0.0</string>
<string>127.0.0.1</string>
<string>--port</string>
<string>8000</string>
</array>
<!--
<key>EnvironmentVariables</key>
<dict>
<key>OPENJARVIS_API_KEY</key>
<string>REPLACE_WITH_A_REAL_KEY</string>
</dict>
-->
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env bash
# posthog-hetzner-prep.sh — one-shot Hetzner Cloud server prep for
# OpenJarvis's self-hosted PostHog analytics backend.
#
# Run this on a fresh Ubuntu 22.04+ box (Hetzner CCX23 in Ashburn, or
# similar) after pointing the desired domain at it. Idempotent: safe
# to re-run if a step fails partway.
#
# Usage:
# sudo bash posthog-hetzner-prep.sh analytics.openjarvis.ai you@openjarvis.ai
#
# After it finishes:
# 1. Visit https://<DOMAIN>/ and create the admin account.
# 2. Create project "OpenJarvis".
# 3. Settings → Project → grab the Project API Key (phc_…).
# 4. Update src/openjarvis/core/config.py AnalyticsConfig defaults:
# host = "https://<DOMAIN>"
# key = "phc_<new>"
# 5. Ship a release. Frontend + backend + install.sh all read those
# same defaults via load_config().
#
# Cost: ~$35/mo on Hetzner CCX23 (4 dedicated vCPU / 16 GB / 240 GB
# NVMe) in US-East. Add Hetzner Cloud Backups (+20%) for production.
#
# Retention: post-install, set 365 days in PostHog UI →
# Settings → Data Management → Event ingestion → Data retention.
set -euo pipefail
# ---- args ----
if [[ $# -lt 2 ]]; then
cat >&2 <<'USAGE'
posthog-hetzner-prep.sh: missing arguments.
Usage:
sudo bash posthog-hetzner-prep.sh <domain> <admin_email>
Examples:
sudo bash posthog-hetzner-prep.sh analytics.openjarvis.ai team@openjarvis.ai
The domain must already resolve to this box (DNS A record) before
the script runs — Let's Encrypt needs to reach this server on port 80.
USAGE
exit 2
fi
DOMAIN="$1"
ADMIN_EMAIL="$2"
if [[ $EUID -ne 0 ]]; then
echo "posthog-hetzner-prep.sh: must be run as root (use sudo)." >&2
exit 1
fi
# ---- step 1: system prep ----
echo "[1/5] apt update + base packages..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y --no-install-recommends \
curl ufw ca-certificates gnupg \
apt-transport-https software-properties-common
# ---- step 2: firewall ----
echo "[2/5] firewall (UFW): 22/80/443 only..."
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
# ---- step 3: swap (helps ClickHouse under load spikes) ----
echo "[3/5] swap..."
if [[ ! -f /swapfile ]]; then
fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
if ! grep -q "/swapfile" /etc/fstab; then
echo "/swapfile none swap sw 0 0" >> /etc/fstab
fi
else
echo " /swapfile already present"
fi
# ---- step 4: docker ----
echo "[4/5] docker..."
if ! command -v docker >/dev/null 2>&1; then
curl -fsSL https://get.docker.com | sh
else
echo " docker already installed"
fi
systemctl enable --now docker
# ---- step 5: posthog hobby deploy ----
echo "[5/5] PostHog Hobby Deploy..."
echo
echo " Domain: $DOMAIN"
echo " Admin email: $ADMIN_EMAIL"
echo " DNS A record: verify it points to $(curl -fsS -m 5 https://api.ipify.org 2>/dev/null || echo "<this server>")"
echo
# PostHog's official one-liner. It writes a .env file with random
# secrets, configures Caddy with Let's Encrypt TLS for the domain,
# and brings up the full stack via docker-compose.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/posthog/posthog/HEAD/bin/deploy-hobby)" -s -- \
--domain "$DOMAIN" \
--email "$ADMIN_EMAIL" || {
echo
echo "PostHog deploy script exited with an error. Common causes:"
echo " - DNS for $DOMAIN doesn't resolve to this server yet (wait + retry)"
echo " - Port 80 not reachable from the public internet (firewall / cloud SG)"
echo " - Out of disk on /var/lib/docker (need 20+ GB free)"
echo
exit 1
}
cat <<EOF
================================================================
PostHog is up at https://$DOMAIN/
Next steps:
1. Open https://$DOMAIN/ in a browser.
2. Create the first admin account (any email, your password).
3. Create project "OpenJarvis".
4. Settings → Project → Project API Key — copy the phc_… value.
5. Update src/openjarvis/core/config.py AnalyticsConfig defaults:
host = "https://$DOMAIN"
key = "phc_<the-new-key>"
6. Settings → Data Management → set retention to 365 days.
7. Settings → Recordings → confirm Session Replay is OFF (default).
8. Ship a release of OpenJarvis with the new config defaults.
Operational notes:
- Updates: bash <(curl -fsSL https://raw.githubusercontent.com/posthog/posthog/HEAD/bin/upgrade-hobby)
- Logs: docker compose -f /home/posthog/posthog/docker-compose.hobby.yml logs -f
- Disk usage: df -h # bump VPS tier when /var/lib/docker > 70% full
- Backups: enable Hetzner Cloud Backups in the Hetzner console
================================================================
EOF
+25
View File
@@ -10,6 +10,31 @@ ExecStart=/opt/openjarvis/.venv/bin/jarvis serve --host 0.0.0.0 --port 8000
Restart=on-failure
RestartSec=5
Environment=HOME=/opt/openjarvis
# Binding 0.0.0.0 requires authentication. This file MUST exist and contain:
# OPENJARVIS_API_KEY=<key> (generate one: `jarvis auth generate-key`)
# It is not prefixed with "-", so the unit fails to start if the file is
# missing — preventing an accidentally unauthenticated public server.
# Keep secrets here (mode 0600, owned by root) rather than inline Environment=
# lines, which leak into `systemctl show` and the journal.
EnvironmentFile=/etc/openjarvis/env
# --- Sandboxing / hardening (#564) ---
# Conservative set: tightens the unit without blocking the server's normal I/O
# or local GPU inference. ProtectSystem=strict makes the whole filesystem
# read-only except ReadWritePaths, so $HOME (config/cache/state under
# /opt/openjarvis) stays writable.
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/openjarvis
ProtectHome=true
PrivateTmp=true
ProtectControlGroups=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
[Install]
WantedBy=multi-user.target
+126
View File
@@ -0,0 +1,126 @@
# OpenJarvis on native Windows
Phase-1 of the native-Windows-support RFC (#298). Mirrors the Linux
(`deploy/systemd/`) and macOS (`deploy/launchd/`) deployments — but for
PowerShell, without WSL2 or Docker.
## One-liner install
In an elevated-or-regular PowerShell:
```powershell
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex
```
What it does:
1. Refuses non-Windows hosts and Windows < 10 1809.
2. Checks Python 3.10 3.13 (3.14 has no numpy wheels yet — see #432).
3. Checks `git` on PATH.
4. Installs `uv` (https://astral.sh/uv) if absent.
5. Clones the OpenJarvis repository to `%LOCALAPPDATA%\OpenJarvis`
(override with `$env:OPENJARVIS_HOME`).
6. Runs `uv sync --extra desktop --group desktop-native` so the FastAPI server,
speech backend, and native extension are importable.
7. Optionally prompts to register a scheduled task that auto-starts the
server at logon.
Flags (when invoked directly rather than via `irm | iex`):
| Flag | Effect |
|------|--------|
| `-Service` | Register the scheduled task without prompting |
| `-SkipService` | Don't prompt; don't register |
| `-Force` | Re-run all steps even if already done |
`irm | iex` can't pass `param()` args into a piped script string, so
the same knobs are honored via env vars when the corresponding flag is
absent:
```powershell
$env:OPENJARVIS_SKIP_SERVICE = '1'
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex
```
The available env vars: `OPENJARVIS_SKIP_SERVICE`, `OPENJARVIS_SERVICE`,
`OPENJARVIS_FORCE`. If you need richer control, save the script first
(`irm ... -OutFile install.ps1; .\install.ps1 -Force`).
## Manual scheduled-task setup
If you skipped the prompt during install, you can register / inspect /
remove the task with `jarvis-service.ps1`:
```powershell
$srv = "$env:LOCALAPPDATA\OpenJarvis\src\deploy\windows\jarvis-service.ps1"
# install (idempotent — replaces existing)
powershell -ExecutionPolicy Bypass -File $srv install
# status
powershell -ExecutionPolicy Bypass -File $srv status
# remove
powershell -ExecutionPolicy Bypass -File $srv uninstall
```
The task runs as the current user with `LogonType=Interactive` and
`RunLevel=Limited`. It restarts up to 3 times on failure (1-minute
gap), has no execution-time limit, and starts when available (catches
up if missed).
## Loopback vs LAN-exposed
By default the scheduled task binds `127.0.0.1` — reachable only from
this machine, no API key required. This matches launchd parity (see
`deploy/launchd/com.openjarvis.plist`).
To expose on your LAN:
```powershell
# 1. Generate an API key. The server REFUSES to bind 0.0.0.0 without one.
$env:OPENJARVIS_API_KEY = (uv run jarvis auth generate-key)
# 2. Re-register the task with -ListenHost 0.0.0.0.
powershell -ExecutionPolicy Bypass -File $srv install -ListenHost 0.0.0.0
```
`jarvis-service.ps1 install` refuses `-ListenHost 0.0.0.0` if
`$env:OPENJARVIS_API_KEY` is unset — same guard as the systemd unit's
`EnvironmentFile=/etc/openjarvis/env`.
## Parity table
| Concern | systemd | launchd | Windows |
|---------|---------|---------|---------|
| Service definition | `deploy/systemd/openjarvis.service` | `deploy/launchd/com.openjarvis.plist` | `deploy/windows/jarvis-service.ps1` (cmdlet-driven) |
| Default bind | `0.0.0.0` (with API key) | `127.0.0.1` (no API key) | `127.0.0.1` (no API key) |
| Restart on failure | `Restart=on-failure RestartSec=5` | `KeepAlive=true` | `RestartCount=3 RestartInterval=PT1M` |
| Auto-start | `multi-user.target` | `RunAtLoad=true` | `AtLogOn` trigger |
## Updating
To pull the latest:
```powershell
cd "$env:LOCALAPPDATA\OpenJarvis\src"
git pull --ff-only
uv sync --extra desktop --group desktop-native
```
Or re-run the installer with `-Force`:
```powershell
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex
# (then re-run with the file directly, passing -Force)
```
## Uninstall
```powershell
powershell -ExecutionPolicy Bypass -File "$env:LOCALAPPDATA\OpenJarvis\src\deploy\windows\jarvis-service.ps1" uninstall
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\OpenJarvis"
```
Uninstalling does NOT remove `uv` (it's a separate tool — you may have
other Python projects using it).
+529
View File
@@ -0,0 +1,529 @@
<#
.SYNOPSIS
OpenJarvis native Windows installer.
.DESCRIPTION
Phase-1 of the native-Windows-support RFC (#298). Mirrors the
behavior of scripts/install/install.sh (the curl-pipe-bash installer
for Linux/WSL2/macOS) but for native Windows PowerShell - no WSL,
no Docker, no MSYS2.
Steps:
1. Refuse non-Windows / Windows < 10.
2. Check Python 3.10 - 3.13 on PATH (3.14 has no numpy wheels yet,
see #432).
3. Check git on PATH.
4. Install uv (https://astral.sh/uv) if absent.
5. Clone the OpenJarvis repository to $env:LOCALAPPDATA\OpenJarvis
(override with $env:OPENJARVIS_HOME).
6. Run `uv sync --extra desktop --group desktop-native` so the FastAPI
server, speech backend, and native extension are importable.
7. Optionally register the scheduled-task service (see
deploy/windows/jarvis-service.ps1).
Usage (one-liner):
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex
Usage (file invocation, supports flags):
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 -OutFile install.ps1
.\install.ps1 -SkipService
Flags (when running the file directly):
-SkipService Don't prompt for / install the scheduled task.
-Service Install the scheduled task without prompting.
-Force Re-run all steps even if already done.
Under `irm | iex` the param block is unreachable (Invoke-Expression
can't pass named args into a piped script string), so the same knobs
are honored via env vars when the corresponding flag is absent:
$env:OPENJARVIS_SKIP_SERVICE = '1'
$env:OPENJARVIS_SERVICE = '1'
$env:OPENJARVIS_FORCE = '1'
.NOTES
Loopback default: the scheduled-task service binds 127.0.0.1, so no
API key is needed. To expose on the LAN, edit the registered task to
pass `--host 0.0.0.0` AND set $env:OPENJARVIS_API_KEY (an
unauthenticated 0.0.0.0 server refuses to start). See
deploy/windows/README.md.
#>
[CmdletBinding()]
param(
[switch] $SkipService,
[switch] $Service,
[switch] $Force
)
$ErrorActionPreference = 'Stop'
# Env-var fallback for the `irm | iex` path, where the param block is
# unreachable (see header comment). Any explicit -switch wins; env vars
# only fill in the gaps.
if (-not $SkipService -and $env:OPENJARVIS_SKIP_SERVICE) { $SkipService = $true }
if (-not $Service -and $env:OPENJARVIS_SERVICE) { $Service = $true }
if (-not $Force -and $env:OPENJARVIS_FORCE) { $Force = $true }
# ---------------------------------------------------------------------------
# Output helpers - coloured but plain enough for Constrained Language Mode.
# ---------------------------------------------------------------------------
function Write-Info ($msg) { Write-Host "[info] $msg" -ForegroundColor Cyan }
function Write-Ok ($msg) { Write-Host "[ok] $msg" -ForegroundColor Green }
function Write-Warn2 ($msg) { Write-Host "[warn] $msg" -ForegroundColor Yellow }
function Write-Fail ($msg) {
Write-Host "[fail] $msg" -ForegroundColor Red
exit 1
}
# ---------------------------------------------------------------------------
# Shared helpers - winget bootstrap + PATH refresh
# ---------------------------------------------------------------------------
# Pull the latest Machine + User PATH from the registry into the current
# PowerShell session. Tools installed by `winget install` (Python, git,
# Ollama, etc.) update the User PATH, but the running process inherits
# the parent shell's environment - so without this refresh the just-
# installed tool stays invisible to subsequent `Get-Command` calls.
#
# CRITICAL: registry PATH entries can be REG_EXPAND_SZ (with literal
# `%VAR%` placeholders); the Python.org installer in per-user mode adds
# entries like `%LOCALAPPDATA%\Programs\Python\Python313\` unexpanded.
# `GetEnvironmentVariable` returns the raw string and PowerShell does
# NOT auto-expand on assignment to `$env:Path`, so `Get-Command python`
# would miss the just-installed binary. Expand explicitly.
function Update-PathFromRegistry {
$machinePath = [System.Environment]::GetEnvironmentVariable('Path', 'Machine')
$userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User')
$combined = "$machinePath;$userPath"
$env:Path = [System.Environment]::ExpandEnvironmentVariables($combined)
}
# Bootstrap a tool by winget id. Returns the resolved command source on
# success, $null on failure. Caller decides whether failure is fatal.
function Install-WithWinget {
param(
[string] $WingetId, # e.g. 'Python.Python.3.13'
[string] $CommandName # e.g. 'python' or 'git'
)
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
# Windows 10 pre-2004 / Windows Server / locked-down corporate
# images may not have winget. Fall back to the caller's manual
# instructions.
return $null
}
Write-Info " Installing $WingetId via winget (silent)..."
& winget install --id $WingetId --silent --accept-source-agreements --accept-package-agreements 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Warn2 " winget install $WingetId exited $LASTEXITCODE"
return $null
}
Update-PathFromRegistry
$cmd = Get-Command $CommandName -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
return $null
}
# ---------------------------------------------------------------------------
# 1. OS check
# ---------------------------------------------------------------------------
Write-Info "Checking OS..."
if ($PSVersionTable.Platform -and $PSVersionTable.Platform -ne 'Win32NT') {
Write-Fail "install.ps1 is for native Windows. On Linux/macOS use install.sh."
}
# Build number 17763 = Windows 10 1809 (the oldest LTS we test against).
$build = [System.Environment]::OSVersion.Version.Build
if ($build -lt 17763) {
Write-Fail "Windows 10 1809 (build 17763) or newer is required. Detected build $build."
}
Write-Ok "Windows build $build"
# ---------------------------------------------------------------------------
# 2. Python check
# ---------------------------------------------------------------------------
function Get-PythonCommand {
# Prefer `python3` (matches our cross-platform helper convention),
# fall back to `python` (the Windows store / python.org default).
foreach ($name in @('python3', 'python')) {
$cmd = Get-Command $name -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
}
return $null
}
Write-Info "Checking Python (3.10 - 3.13)..."
$pythonExe = Get-PythonCommand
if (-not $pythonExe) {
Write-Info "Python not on PATH - attempting auto-install via winget..."
$pythonExe = Install-WithWinget -WingetId 'Python.Python.3.13' -CommandName 'python'
if (-not $pythonExe) {
Write-Fail @"
Python 3.10 - 3.13 not found and auto-install via winget failed.
Install manually from https://python.org (check 'Add python.exe to PATH'
during install) or via winget:
winget install Python.Python.3.13
Then re-run this installer.
"@
}
}
$verRaw = & $pythonExe --version 2>&1
$verMatch = [regex]::Match($verRaw, '(\d+)\.(\d+)\.(\d+)')
if (-not $verMatch.Success) {
Write-Fail "Could not parse Python version from: $verRaw"
}
$pyMajor = [int]$verMatch.Groups[1].Value
$pyMinor = [int]$verMatch.Groups[2].Value
if ($pyMajor -ne 3 -or $pyMinor -lt 10 -or $pyMinor -gt 13) {
Write-Fail @"
Found Python $pyMajor.$pyMinor at $pythonExe, but OpenJarvis requires
3.10 - 3.13. Python 3.14 has no numpy Windows wheels yet (#432, will
re-open once numpy ships cp314).
"@
}
Write-Ok "Python $pyMajor.$pyMinor ($pythonExe)"
# ---------------------------------------------------------------------------
# 3. git check
# ---------------------------------------------------------------------------
Write-Info "Checking git..."
$gitExe = (Get-Command git -ErrorAction SilentlyContinue).Source
if (-not $gitExe) {
Write-Info "git not on PATH - attempting auto-install via winget..."
$gitExe = Install-WithWinget -WingetId 'Git.Git' -CommandName 'git'
if (-not $gitExe) {
Write-Fail @"
git not found and auto-install via winget failed.
Install manually via winget:
winget install Git.Git
or download from https://git-scm.com, then re-run this installer.
"@
}
}
Write-Ok "git ($gitExe)"
# ---------------------------------------------------------------------------
# 4. uv check / install
# ---------------------------------------------------------------------------
Write-Info "Checking uv..."
$uvExe = (Get-Command uv -ErrorAction SilentlyContinue).Source
if (-not $uvExe) {
Write-Info "Installing uv via astral.sh/uv (official PowerShell installer)..."
try {
Invoke-RestMethod -Uri 'https://astral.sh/uv/install.ps1' -UseBasicParsing | Invoke-Expression
} catch {
Write-Fail "uv install failed: $($_.Exception.Message)"
}
# The astral installer puts uv at %USERPROFILE%\.local\bin\uv.exe and
# adds that dir to the User PATH. The current process's PATH isn't
# refreshed automatically - prepend the install dir so the rest of
# this script picks it up.
$uvDir = Join-Path $env:USERPROFILE '.local\bin'
if (Test-Path (Join-Path $uvDir 'uv.exe')) {
$env:Path = "$uvDir;$env:Path"
}
$uvExe = (Get-Command uv -ErrorAction SilentlyContinue).Source
if (-not $uvExe) {
Write-Fail "uv installed but isn't on PATH. Re-open a fresh PowerShell and re-run."
}
}
Write-Ok "uv ($uvExe)"
# ---------------------------------------------------------------------------
# 5. Clone the repo
# ---------------------------------------------------------------------------
$installRoot = if ($env:OPENJARVIS_HOME) {
$env:OPENJARVIS_HOME
} else {
Join-Path $env:LOCALAPPDATA 'OpenJarvis'
}
$srcDir = Join-Path $installRoot 'src'
Write-Info "Install root: $installRoot"
if (-not (Test-Path $installRoot)) {
New-Item -ItemType Directory -Path $installRoot | Out-Null
}
$repoUrl = if ($env:OPENJARVIS_REPO_URL) {
$env:OPENJARVIS_REPO_URL
} else {
'https://github.com/open-jarvis/OpenJarvis.git'
}
if (Test-Path (Join-Path $srcDir '.git')) {
if ($Force) {
Write-Info "Force: pulling latest from $repoUrl..."
& $gitExe -C $srcDir pull --ff-only
if ($LASTEXITCODE -ne 0) { Write-Fail "git pull failed" }
} else {
Write-Ok "Repository already cloned (use -Force to update)"
}
} else {
Write-Info "Cloning $repoUrl..."
& $gitExe clone --depth 1 $repoUrl $srcDir
if ($LASTEXITCODE -ne 0) { Write-Fail "git clone failed" }
Write-Ok "Cloned to $srcDir"
}
# ---------------------------------------------------------------------------
# 6. uv sync --extra desktop --group desktop-native
# ---------------------------------------------------------------------------
Write-Info "Running 'uv sync --extra desktop --group desktop-native' in $srcDir (this can take a few minutes)..."
Push-Location $srcDir
try {
& $uvExe sync --extra desktop --group desktop-native
if ($LASTEXITCODE -ne 0) {
Write-Fail "uv sync failed with exit code $LASTEXITCODE. Check the output above."
}
} finally {
Pop-Location
}
Write-Ok "Dependencies installed"
# ---------------------------------------------------------------------------
# 7. Ollama - install + start + wait for daemon
# ---------------------------------------------------------------------------
Write-Info "Checking Ollama..."
$ollamaExe = (Get-Command ollama -ErrorAction SilentlyContinue).Source
if (-not $ollamaExe) {
Write-Info " Ollama not on PATH - downloading the official installer (~150 MB)..."
$ollamaSetup = Join-Path $env:TEMP 'OllamaSetup.exe'
# SilentlyContinue is load-bearing in PS 5.1: the default progress
# bar renderer slows Invoke-WebRequest down 30x on large downloads
# (a known PS5.1 issue), turning a 30s download into 15+ minutes.
$prevProgress = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
try {
Invoke-WebRequest `
-Uri 'https://ollama.com/download/OllamaSetup.exe' `
-OutFile $ollamaSetup `
-UseBasicParsing
} catch {
Remove-Item $ollamaSetup -ErrorAction SilentlyContinue # clean up partial download
$ProgressPreference = $prevProgress
Write-Fail "Ollama download failed: $($_.Exception.Message)`nInstall manually from https://ollama.com, then re-run."
} finally {
$ProgressPreference = $prevProgress
}
# OllamaSetup.exe is built with NSIS, whose silent-install flag is
# /S (uppercase). The Inno-Setup-style /silent would open the GUI
# and hang `Start-Process -Wait` indefinitely.
Write-Info " Running OllamaSetup.exe /S (this can take a minute)..."
Start-Process -FilePath $ollamaSetup -ArgumentList '/S' -Wait
Remove-Item $ollamaSetup -ErrorAction SilentlyContinue
Update-PathFromRegistry
$ollamaExe = (Get-Command ollama -ErrorAction SilentlyContinue).Source
if (-not $ollamaExe) {
Write-Fail "Ollama installer ran but 'ollama' isn't on PATH. Open a fresh PowerShell and re-run, or install manually from https://ollama.com."
}
}
Write-Ok "Ollama ($ollamaExe)"
# Make sure the daemon is actually responsive before pulling. The Ollama
# Windows installer launches the tray app at install time, but on a re-
# run with an existing install the daemon may not be running yet.
Write-Info "Waiting for Ollama daemon..."
$ollamaReady = $false
for ($i = 0; $i -lt 60; $i++) {
# 'ollama list' writes to stderr until the daemon is reachable; under
# $ErrorActionPreference='Stop' the 2>&1 merge surfaces that as a
# terminating NativeCommandError that would abort the whole install on
# the very first probe. Swallow it and rely on $LASTEXITCODE so the
# Start-Process serve fallback below actually runs (issue #522).
try { & $ollamaExe list 2>&1 | Out-Null } catch { }
if ($LASTEXITCODE -eq 0) {
$ollamaReady = $true
break
}
if ($i -eq 5) {
# Daemon clearly isn't auto-running - start it ourselves. Ollama
# for Windows uses the tray app `ollama app.exe`; falling back to
# `ollama serve` works headless.
Start-Process -FilePath $ollamaExe -ArgumentList 'serve' -WindowStyle Hidden -ErrorAction SilentlyContinue
}
Start-Sleep -Seconds 1
}
if (-not $ollamaReady) {
Write-Warn2 "Ollama daemon didn't become ready in 60s. Continuing - bg-orchestrator will retry later."
}
# ---------------------------------------------------------------------------
# 8. Pull a starter model (qwen3.5:2b - ~1.5 GB)
# ---------------------------------------------------------------------------
$modelPullOk = $false
if ($ollamaReady) {
Write-Info "Pulling qwen3.5:2b (~1.5 GB) so 'jarvis' works on first run..."
& $ollamaExe pull 'qwen3.5:2b'
if ($LASTEXITCODE -eq 0) {
$modelPullOk = $true
Write-Ok "Starter model ready"
} else {
Write-Warn2 "ollama pull failed; the bg-orchestrator will retry once Ollama is reachable."
}
} else {
Write-Warn2 "Skipping model pull - daemon wasn't ready."
}
# ---------------------------------------------------------------------------
# 9. jarvis.cmd shim - so bare `jarvis` works in any new PowerShell
# ---------------------------------------------------------------------------
$binDir = Join-Path $installRoot 'bin'
$shimPath = Join-Path $binDir 'jarvis.cmd'
if (-not (Test-Path $binDir)) {
New-Item -ItemType Directory -Path $binDir | Out-Null
}
# %~dp0 in a .cmd file resolves to the directory containing the script,
# so the shim is self-locating - moving %LOCALAPPDATA%\OpenJarvis won't
# break it as long as the user moves the whole tree. `uv` is resolved
# from PATH at runtime (astral installer adds it to User PATH); avoids
# pinning to the install-time uv.exe path which can shift on uv updates.
$shimContent = @"
@echo off
setlocal
set "SRC=%~dp0..\src"
uv run --project "%SRC%" jarvis %*
"@
Set-Content -Path $shimPath -Value $shimContent -Encoding ASCII
# Add %LOCALAPPDATA%\OpenJarvis\bin to User PATH if it isn't already
# there. The current process won't see it until restart - handled in the
# final banner.
#
# Compare against the EXPANDED form: a previous install may have written
# the entry as `%LOCALAPPDATA%\OpenJarvis\bin` (unexpanded) into User
# PATH, and a literal `-ieq` against the expanded `$binDir` would miss
# it and append a duplicate every re-run.
$userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User')
$pathOnUser = $false
if ($userPath) {
foreach ($entry in ($userPath -split ';')) {
$expanded = [System.Environment]::ExpandEnvironmentVariables($entry)
if ($expanded -ieq $binDir) { $pathOnUser = $true; break }
}
}
$pathNeedsRefresh = $false
if (-not $pathOnUser) {
$newUserPath = if ($userPath) { "$userPath;$binDir" } else { $binDir }
[System.Environment]::SetEnvironmentVariable('Path', $newUserPath, 'User')
$pathNeedsRefresh = $true
}
Write-Ok "jarvis shim installed at $shimPath"
# ---------------------------------------------------------------------------
# 10. Optional: register the scheduled-task service
# ---------------------------------------------------------------------------
$serviceScript = Join-Path $srcDir 'deploy\windows\jarvis-service.ps1'
$shouldInstallService = $false
# Pre-check admin if the user wants the service - Register-ScheduledTask
# requires elevation. We do this before the prompt so we don't ask "do
# you want the service?" only to fail with Access Denied after they say
# yes.
$isAdmin = ([Security.Principal.WindowsPrincipal] `
[Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ($Service -and -not $isAdmin) {
Write-Fail "-Service was requested, but this PowerShell is not elevated. Register-ScheduledTask needs admin rights - re-run from an elevated PowerShell, or drop -Service."
}
if ($Service) {
$shouldInstallService = $true
} elseif ($SkipService) {
$shouldInstallService = $false
} elseif (-not $isAdmin) {
# Default to skip-with-explanation when we can't elevate, rather
# than prompting and then failing at Register-ScheduledTask.
Write-Warn2 "Skipping scheduled-task setup - this PowerShell is not elevated."
Write-Warn2 " Register-ScheduledTask requires admin. To install the service later:"
Write-Warn2 " Right-click PowerShell -> Run as administrator, then run:"
Write-Warn2 " powershell -ExecutionPolicy Bypass -File `"$serviceScript`" install"
} else {
# Interactive prompt only when there's a real user at the keyboard
# AND stdin isn't piped. [Environment]::UserInteractive is the
# canonical PowerShell idiom for "is this a user session" (false for
# services, scheduled tasks, etc); we additionally guard against the
# `irm | iex` case where stdin is redirected.
$isInteractive = [Environment]::UserInteractive `
-and -not [System.Console]::IsInputRedirected
if ($isInteractive) {
$reply = Read-Host "Register OpenJarvis as a Windows scheduled task (auto-start at logon, loopback only)? [y/N]"
$shouldInstallService = ($reply -match '^[yY]')
} else {
Write-Warn2 "Non-interactive install - skipping scheduled-task setup."
Write-Warn2 "To register the service later, run (from an elevated PowerShell):"
Write-Warn2 " powershell -ExecutionPolicy Bypass -File `"$serviceScript`" install"
}
}
if ($shouldInstallService) {
if (-not (Test-Path $serviceScript)) {
Write-Fail "Service script not found at $serviceScript (the clone may be missing files; try -Force)."
}
Write-Info "Installing scheduled task..."
& powershell -ExecutionPolicy Bypass -File $serviceScript install -InstallRoot $installRoot
if ($LASTEXITCODE -ne 0) {
Write-Fail "Scheduled task setup failed."
}
Write-Ok "Scheduled task 'OpenJarvis' registered (loopback default)."
}
# ---------------------------------------------------------------------------
# 8. Final message
# ---------------------------------------------------------------------------
Write-Host ""
Write-Host " +----------------------------------+" -ForegroundColor Green
Write-Host " | OpenJarvis install complete |" -ForegroundColor Green
Write-Host " +----------------------------------+" -ForegroundColor Green
Write-Host ""
Write-Host " Repo: $srcDir"
# Tell the truth about what the user can run next, given (a) whether the
# starter model finished pulling and (b) whether the User-PATH update
# needs a fresh PowerShell to take effect.
$nextCmd = if ($modelPullOk) { 'jarvis' } else { 'jarvis doctor' }
if ($pathNeedsRefresh) {
Write-Host ""
Write-Host " Run it: open a NEW PowerShell, then: $nextCmd" -ForegroundColor Yellow
Write-Host " (the jarvis shim was added to your User PATH; the"
Write-Host " current PowerShell won't see it until restart)"
} else {
Write-Host " Run it: $nextCmd"
}
if (-not $modelPullOk) {
Write-Host ""
Write-Host " NOTE: the qwen3.5:2b model didn't finish downloading." -ForegroundColor Yellow
Write-Host " Chat will fail until the bg-orchestrator finishes the retry."
Write-Host " 'jarvis doctor' shows progress."
}
if ($shouldInstallService) {
Write-Host ""
Write-Host " Service: schtasks /Query /TN OpenJarvis (status)"
Write-Host " powershell -File `"$serviceScript`" uninstall (remove)"
}
Write-Host ""
Write-Host " Docs: https://open-jarvis.github.io/OpenJarvis/"
Write-Host ""
+208
View File
@@ -0,0 +1,208 @@
<#
.SYNOPSIS
Register / unregister the OpenJarvis Windows scheduled task.
.DESCRIPTION
The Windows equivalent of deploy/systemd/openjarvis.service and
deploy/launchd/com.openjarvis.plist.
Registers a per-user scheduled task named "OpenJarvis" that starts
`jarvis serve` at logon and restarts on failure. Loopback default
(127.0.0.1) so no API key is required — matches launchd parity.
Subcommands:
install — create or replace the task
uninstall — remove the task
status — show task state
Arguments (install only):
-InstallRoot <path> default: %LOCALAPPDATA%\OpenJarvis (matches
install.ps1's default)
-ListenHost <addr> default: 127.0.0.1 (loopback). Set to 0.0.0.0
ONLY if you also set $env:OPENJARVIS_API_KEY
— the server refuses to start unauthenticated
on a non-loopback bind.
-ListenPort <int> default: 8000
Usage:
powershell -ExecutionPolicy Bypass -File jarvis-service.ps1 install
powershell -ExecutionPolicy Bypass -File jarvis-service.ps1 uninstall
powershell -ExecutionPolicy Bypass -File jarvis-service.ps1 status
#>
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[ValidateSet('install', 'uninstall', 'status')]
[string] $Command = 'status',
[string] $InstallRoot,
[string] $ListenHost = '127.0.0.1',
[int] $ListenPort = 8000
)
$ErrorActionPreference = 'Stop'
$TaskName = 'OpenJarvis'
function Write-Info ($msg) { Write-Host "[info] $msg" -ForegroundColor Cyan }
function Write-Ok ($msg) { Write-Host "[ok] $msg" -ForegroundColor Green }
function Write-Warn2 ($msg) { Write-Host "[warn] $msg" -ForegroundColor Yellow }
function Write-Fail ($msg) {
Write-Host "[fail] $msg" -ForegroundColor Red
exit 1
}
function Get-DefaultInstallRoot {
# Use $script: prefix so this is robust to being called from any
# function scope (PowerShell's default dynamic lookup would also
# work today, but $script: is the explicit contract).
if ($script:InstallRoot) { return $script:InstallRoot }
if ($env:OPENJARVIS_HOME) { return $env:OPENJARVIS_HOME }
return (Join-Path $env:LOCALAPPDATA 'OpenJarvis')
}
# ---------------------------------------------------------------------------
# install
# ---------------------------------------------------------------------------
function Install-Task {
$root = Get-DefaultInstallRoot
$srcDir = Join-Path $root 'src'
if (-not (Test-Path $srcDir)) {
Write-Fail "OpenJarvis source not found at $srcDir. Run install.ps1 first."
}
$uvCmd = Get-Command uv -ErrorAction SilentlyContinue
if (-not $uvCmd) {
$uvFallback = Join-Path $env:USERPROFILE '.local\bin\uv.exe'
if (Test-Path $uvFallback) {
$uvPath = $uvFallback
} else {
Write-Fail "uv.exe not found on PATH or at $uvFallback. Re-run install.ps1."
}
} else {
$uvPath = $uvCmd.Source
}
# Safety: refuse to register a non-loopback bind without an API key.
# Mirrors deploy/systemd/openjarvis.service's EnvironmentFile guard.
$isLoopback = ($ListenHost -eq '127.0.0.1' -or $ListenHost -eq 'localhost')
if (-not $isLoopback -and -not $env:OPENJARVIS_API_KEY) {
Write-Fail @"
ListenHost is $ListenHost (non-loopback) but `$env:OPENJARVIS_API_KEY is
not set. An unauthenticated non-loopback bind is refused by jarvis serve
and would also create a security hole. Set the env var first:
`$env:OPENJARVIS_API_KEY = (uv run jarvis auth generate-key)
then re-run with -ListenHost 0.0.0.0.
"@
}
# CRITICAL: scheduled tasks do NOT inherit the registering session's
# environment. If we registered the task now and stopped here, the
# task would launch at logon with a clean env, find no API key, and
# `jarvis serve` would refuse to bind 0.0.0.0 — failing silently every
# logon. Persist the key to the User env scope so the task's logon
# session picks it up. (Loopback path doesn't need the key, so this
# only runs for the explicit LAN-exposed case.)
if (-not $isLoopback) {
Write-Info "Persisting OPENJARVIS_API_KEY to User environment so the scheduled task can read it at logon."
[System.Environment]::SetEnvironmentVariable(
'OPENJARVIS_API_KEY',
$env:OPENJARVIS_API_KEY,
'User'
)
}
Write-Info "Registering scheduled task '$TaskName'..."
Write-Info " Working dir : $srcDir"
Write-Info " Listen : $ListenHost`:$ListenPort"
Write-Info " User : $env:USERNAME"
# If a previous task exists, remove it first (idempotent install).
$existing = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($existing) {
Write-Info "Existing task found — replacing."
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
}
$action = New-ScheduledTaskAction `
-Execute $uvPath `
-Argument "run jarvis serve --host $ListenHost --port $ListenPort" `
-WorkingDirectory $srcDir
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-RestartCount 3 `
-RestartInterval (New-TimeSpan -Minutes 1) `
-ExecutionTimeLimit (New-TimeSpan -Seconds 0)
$principal = New-ScheduledTaskPrincipal `
-UserId $env:USERNAME `
-LogonType Interactive `
-RunLevel Limited
Register-ScheduledTask `
-TaskName $TaskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Description 'OpenJarvis API server (loopback default — see deploy/windows/README.md)' | Out-Null
Write-Ok "Task '$TaskName' registered."
Write-Info "It will start automatically at next logon."
Write-Info "To start it now: Start-ScheduledTask -TaskName $TaskName"
}
# ---------------------------------------------------------------------------
# uninstall
# ---------------------------------------------------------------------------
function Uninstall-Task {
$existing = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if (-not $existing) {
Write-Warn2 "Task '$TaskName' is not registered — nothing to remove."
return
}
Write-Info "Stopping '$TaskName' (if running)..."
Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
Write-Info "Unregistering '$TaskName'..."
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
Write-Ok "Task '$TaskName' removed."
}
# ---------------------------------------------------------------------------
# status
# ---------------------------------------------------------------------------
function Show-Status {
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if (-not $task) {
Write-Host "Task '$TaskName' is not registered."
Write-Host "Install it with:"
Write-Host " powershell -ExecutionPolicy Bypass -File `"$PSCommandPath`" install"
return
}
$info = Get-ScheduledTaskInfo -TaskName $TaskName
Write-Host "Task : $TaskName"
Write-Host "State : $($task.State)"
Write-Host "LastRun : $($info.LastRunTime)"
Write-Host "LastRes : 0x$('{0:X8}' -f $info.LastTaskResult)"
Write-Host "NextRun : $($info.NextRunTime)"
}
# ---------------------------------------------------------------------------
# dispatch
# ---------------------------------------------------------------------------
switch ($Command) {
'install' { Install-Task }
'uninstall' { Uninstall-Task }
'status' { Show-Status }
}
-108
View File
@@ -1,108 +0,0 @@
# OpenJarvis Desktop
Tauri 2.0 native desktop application for OpenJarvis with auto-updates, energy monitoring, trace debugging, and learning visualization.
## Development Setup
```bash
# Prerequisites: Node.js 22+, Rust stable, system deps (see below)
cd desktop
npm install
cargo tauri dev # Hot-reload development mode
cargo tauri build # Production build
```
### Linux System Dependencies
```bash
sudo apt-get install -y \
libwebkit2gtk-4.1-dev libgtk-3-dev libappindicator3-dev \
librsvg2-dev patchelf libxdo-dev
```
## Auto-Update Architecture
Every push to `main` (touching `desktop/` or the workflow) triggers a CI pipeline that:
1. Validates TypeScript + Rust (`validate` job)
2. Builds for Linux, macOS (ARM + Intel), and Windows (`build-and-release` job)
3. Creates/updates a `desktop-latest` pre-release on GitHub Releases
4. Uploads platform installers and a signed `latest.json` manifest
The desktop app checks `latest.json` on startup and every 30 minutes. When a newer version is found, it shows a banner prompting the user to download and relaunch.
```
Push to main -> CI builds -> desktop-latest release -> latest.json
|
Desktop app checks periodically <-------------------------+
-> "Update available" banner
-> Download in background
-> "Relaunch now" prompt
```
## Releases
### Rolling (Nightly)
Automatic on every push to `main`. Users on the desktop app receive updates seamlessly.
### Stable (Versioned)
```bash
# Bump version in all 3 config files
./scripts/bump-desktop-version.sh 1.0.1
# Commit and tag
git add desktop/package.json desktop/src-tauri/tauri.conf.json desktop/src-tauri/Cargo.toml
git commit -m "chore(desktop): bump version to 1.0.1"
git tag desktop-v1.0.1
git push origin main --tags
```
CI creates a versioned GitHub Release (e.g., `desktop-v1.0.1`) with full installers.
## Code Signing
### Update Signing (Required for Auto-Updates)
Generate a key pair for signing update manifests:
```bash
cargo tauri signer generate -w ~/.tauri/openjarvis.key
```
Set the public key in `src-tauri/tauri.conf.json` under `plugins.updater.pubkey`, then add these GitHub Secrets:
| Secret | Description |
|--------|-------------|
| `TAURI_SIGNING_PRIVATE_KEY` | Contents of the `.key` file |
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | Password used during generation |
### macOS Notarization (Optional)
| Secret | Description |
|--------|-------------|
| `APPLE_CERTIFICATE` | Base64-encoded `.p12` certificate |
| `APPLE_CERTIFICATE_PASSWORD` | Certificate password |
| `APPLE_SIGNING_IDENTITY` | e.g., `Developer ID Application: Name (TEAMID)` |
| `APPLE_ID` | Apple ID email |
| `APPLE_PASSWORD` | App-specific password |
| `APPLE_TEAM_ID` | 10-character team ID |
### Windows Authenticode (Optional)
| Secret | Description |
|--------|-------------|
| `WINDOWS_CERTIFICATE` | Base64-encoded `.pfx` certificate |
| `WINDOWS_CERTIFICATE_PASSWORD` | Certificate password |
All signing is optional — unsigned builds work without any secrets configured.
## Dashboard Panels
- **Energy** — Real-time power monitoring (recharts)
- **Traces** — Timeline inspection with step-type color coding
- **Learning** — Policy visualization (GRPO/bandit stats)
- **Memory** — Search and stats for memory backends
- **Admin** — Health checks, agent management, server control
-12
View File
@@ -1,12 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenJarvis Desktop</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
-2491
View File
File diff suppressed because it is too large Load Diff
-32
View File
@@ -1,32 +0,0 @@
{
"name": "openjarvis-desktop",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-notification": "^2",
"@tauri-apps/plugin-shell": "^2",
"@tauri-apps/plugin-global-shortcut": "^2",
"@tauri-apps/plugin-autostart": "^2",
"@tauri-apps/plugin-updater": "^2",
"@tauri-apps/plugin-process": "^2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"recharts": "^2.15.0"
},
"devDependencies": {
"@tauri-apps/cli": "^2",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.4",
"typescript": "~5.7.0",
"vite": "^6.0.0"
}
}
-27
View File
@@ -1,27 +0,0 @@
[package]
name = "openjarvis-desktop"
version = "1.0.0"
description = "OpenJarvis Desktop — Native AI assistant with energy monitoring, trace debugging, and learning visualization"
edition = "2021"
license = "MIT"
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = ["tray-icon"] }
tauri-plugin-notification = "2"
tauri-plugin-shell = "2"
tauri-plugin-global-shortcut = "2"
tauri-plugin-autostart = "2"
tauri-plugin-updater = "2"
tauri-plugin-single-instance = "2"
tauri-plugin-process = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
{}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 B

-208
View File
@@ -1,208 +0,0 @@
use serde::Serialize;
use tauri::Manager;
use tauri_plugin_autostart::MacosLauncher;
/// Fetch health status from the OpenJarvis API server.
#[tauri::command]
async fn check_health(api_url: String) -> Result<serde_json::Value, String> {
let url = format!("{}/health", api_url);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch energy monitoring data from the API.
#[tauri::command]
async fn fetch_energy(api_url: String) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/telemetry/energy", api_url);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch telemetry statistics from the API.
#[tauri::command]
async fn fetch_telemetry(api_url: String) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/telemetry/stats", api_url);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch recent traces from the API.
#[tauri::command]
async fn fetch_traces(api_url: String, limit: u32) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/traces?limit={}", api_url, limit);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch a single trace by ID.
#[tauri::command]
async fn fetch_trace(api_url: String, trace_id: String) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/traces/{}", api_url, trace_id);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch learning system statistics.
#[tauri::command]
async fn fetch_learning_stats(api_url: String) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/learning/stats", api_url);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch learning policy configuration.
#[tauri::command]
async fn fetch_learning_policy(api_url: String) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/learning/policy", api_url);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch memory backend statistics.
#[tauri::command]
async fn fetch_memory_stats(api_url: String) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/memory/stats", api_url);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Search memory for relevant chunks.
#[tauri::command]
async fn search_memory(
api_url: String,
query: String,
top_k: u32,
) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/memory/search", api_url);
let client = reqwest::Client::new();
let resp = client
.post(&url)
.json(&serde_json::json!({"query": query, "top_k": top_k}))
.send()
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Fetch list of available agents.
#[tauri::command]
async fn fetch_agents(api_url: String) -> Result<serde_json::Value, String> {
let url = format!("{}/v1/agents", api_url);
let resp = reqwest::get(&url)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| format!("Invalid response: {}", e))?;
Ok(body)
}
/// Launch the `jarvis` CLI command via shell.
#[tauri::command]
async fn run_jarvis_command(args: Vec<String>) -> Result<String, String> {
let output = tokio::process::Command::new("jarvis")
.args(&args)
.output()
.await
.map_err(|e| format!("Failed to launch jarvis: {}", e))?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
Err(String::from_utf8_lossy(&output.stderr).to_string())
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
Some(vec!["--hidden"]),
))
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
// Focus the main window if another instance is launched
if let Some(window) = app.get_webview_window("main") {
let _ = window.set_focus();
}
}))
.setup(|app| {
// Set up system tray menu
let _tray = app.tray_by_id("main");
Ok(())
})
.invoke_handler(tauri::generate_handler![
check_health,
fetch_energy,
fetch_telemetry,
fetch_traces,
fetch_trace,
fetch_learning_stats,
fetch_learning_policy,
fetch_memory_stats,
search_memory,
fetch_agents,
run_jarvis_command,
])
.run(tauri::generate_context!())
.expect("error while running OpenJarvis Desktop");
}
+450
View File
@@ -0,0 +1,450 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body,:root{
background:transparent !important;
background-color:transparent !important;
font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text",sans-serif;
color:#fff;height:100%;overflow:hidden;
}
#container{
display:flex;flex-direction:column;
height:100%;padding:8px 12px;
}
#messages{
flex:1;overflow-y:auto;
display:flex;flex-direction:column;gap:6px;
margin-bottom:8px;padding:12px;
border-radius:16px;
background:rgba(30,30,30,0.88);
border:1px solid rgba(255,255,255,0.20);
}
#messages:empty{display:none}
#messages::-webkit-scrollbar{width:6px}
#messages::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.15);border-radius:3px}
#messages::-webkit-scrollbar-track{background:transparent}
.msg{
padding:8px 12px;border-radius:12px;
font-size:13px;line-height:1.55;max-width:90%;
white-space:pre-wrap;word-wrap:break-word;
-webkit-user-select:text;user-select:text;
}
.msg.user{
align-self:flex-end;
background:rgba(59,130,246,0.35);
color:rgba(255,255,255,0.95);
}
.msg.assistant{
align-self:flex-start;
background:rgba(255,255,255,0.20);
color:rgba(255,255,255,0.88);
white-space:normal;
}
.msg.assistant > p{margin:0 0 6px 0;white-space:pre-wrap}
.msg.assistant > p:last-child{margin-bottom:0}
.msg.assistant h1,.msg.assistant h2,.msg.assistant h3{
font-size:14px;font-weight:600;margin:6px 0 4px 0;
}
.msg.assistant ul,.msg.assistant ol{margin:2px 0 6px 18px;padding:0}
.msg.assistant li{margin:1px 0}
.msg.assistant a{color:#93c5fd;text-decoration:underline}
.msg.assistant code{
background:rgba(0,0,0,0.35);
padding:1px 5px;border-radius:4px;
font-family:"SF Mono",Menlo,Monaco,monospace;
font-size:12px;
}
.msg.assistant pre{
background:rgba(0,0,0,0.40);
padding:8px 10px;border-radius:8px;
margin:4px 0;overflow-x:auto;
border:1px solid rgba(255,255,255,0.08);
}
.msg.assistant pre code{
background:transparent;padding:0;border-radius:0;
font-size:11.5px;line-height:1.4;white-space:pre;
}
.msg.assistant strong{font-weight:600;color:#fff}
.msg.assistant em{font-style:italic}
.msg.assistant del{opacity:0.6;text-decoration:line-through}
/* Streaming caret — pulses at the end of the in-progress bubble */
.caret{
display:inline-block;width:6px;height:13px;
vertical-align:text-bottom;margin-left:2px;
background:rgba(255,255,255,0.85);
animation:caret-blink 1s steps(1) infinite;
}
@keyframes caret-blink{50%{opacity:0}}
/* Thinking dots — shown while waiting for the first token */
.thinking{
display:inline-flex;gap:4px;align-items:center;padding:2px 0;
}
.thinking span{
width:6px;height:6px;border-radius:50%;
background:rgba(255,255,255,0.65);
animation:thinking-bounce 1.2s ease-in-out infinite;
}
.thinking span:nth-child(2){animation-delay:0.15s}
.thinking span:nth-child(3){animation-delay:0.30s}
@keyframes thinking-bounce{
0%,60%,100%{transform:translateY(0);opacity:0.4}
30%{transform:translateY(-4px);opacity:1}
}
#input-bar{
display:flex;align-items:center;gap:6px;
border-radius:16px;padding:6px 8px;
background:rgba(30,30,30,0.88);
border:1px solid rgba(255,255,255,0.20);
flex-shrink:0;
}
#model-wrap{
position:relative;flex-shrink:0;
}
#model-select{
background:rgba(255,255,255,0.08);color:rgba(255,255,255,0.6);
border:none;border-radius:8px;padding:4px 22px 4px 8px;
font-size:11px;outline:none;cursor:pointer;
max-width:140px;
-webkit-appearance:none;appearance:none;
}
#model-select:hover{background:rgba(255,255,255,0.14);color:#fff}
#model-wrap .arrow{
position:absolute;right:7px;top:50%;transform:translateY(-50%);
pointer-events:none;color:rgba(255,255,255,0.35);
}
#model-select option,#model-select optgroup{
background:#1e1e1e;color:#eee;
}
#input{
flex:1;background:transparent;border:none;outline:none;
color:#fff;font-size:14px;padding:6px 10px;
}
#input::placeholder{color:rgba(255,255,255,0.35)}
.btn{
display:flex;align-items:center;justify-content:center;
width:30px;height:30px;border-radius:50%;border:none;
background:rgba(255,255,255,0.10);
color:rgba(255,255,255,0.6);cursor:pointer;
transition:background .15s,color .15s;flex-shrink:0;
}
.btn:hover{background:rgba(255,255,255,0.20);color:#fff}
.btn:disabled{opacity:0.25;cursor:default}
.btn:disabled:hover{background:rgba(255,255,255,0.10)}
</style>
</head>
<body>
<div id="container">
<div id="messages"></div>
<div id="input-bar">
<button id="new-btn" class="btn" title="New conversation">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
</button>
<div id="model-wrap">
<select id="model-select"><option>loading...</option></select>
<svg class="arrow" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<input id="input" type="text" placeholder="Ask Jarvis anything..." autofocus>
<button id="send-btn" class="btn" disabled title="Send">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>
</button>
</div>
</div>
<script type="application/json" id="saved-data">__SAVED_MESSAGES__</script>
<script type="application/json" id="cloud-data">__CLOUD_MODELS__</script>
<script>
const input=document.getElementById('input');
const sendBtn=document.getElementById('send-btn');
const messagesEl=document.getElementById('messages');
let streaming=false,abort=null,model='qwen3.5:4b';
let convId='', convTitle='Overlay chat', convCreated=Date.now();
let messages=[];
function genId(){return Date.now().toString(36)+Math.random().toString(36).slice(2,8)}
// Restore previous conversation
try{
const raw=document.getElementById('saved-data').textContent.trim();
if(raw&&raw!=='__SAVED_PLACEHOLDER__'){
const saved=JSON.parse(raw);
if(saved.id){convId=saved.id;convTitle=saved.title||convTitle;convCreated=saved.createdAt||convCreated;messages=saved.messages||[]}
else if(Array.isArray(saved)){messages=saved}
}
}catch{}
if(!convId) convId=genId();
if(messages.length) renderAll();
// Build model dropdown: local (installed) + cloud (keyed)
const modelSelect=document.getElementById('model-select');
let cloudModels=[];
try{
const cd=document.getElementById('cloud-data').textContent.trim();
if(cd&&cd!=='__CLOUD_PLACEHOLDER__') cloudModels=JSON.parse(cd);
}catch{}
fetch('/v1/models').then(r=>r.json()).then(d=>{
const local=(Array.isArray(d)?d:(d.data||d.models||[])).map(m=>m.id||m.name).filter(Boolean);
while(modelSelect.firstChild) modelSelect.removeChild(modelSelect.firstChild);
if(local.length){
const g=document.createElement('optgroup');g.label='Local';
local.forEach(id=>{const o=document.createElement('option');o.value=id;o.textContent=id;g.appendChild(o)});
modelSelect.appendChild(g);
}
if(cloudModels.length){
const g=document.createElement('optgroup');g.label='Cloud';
cloudModels.forEach(id=>{const o=document.createElement('option');o.value=id;o.textContent=id;g.appendChild(o)});
modelSelect.appendChild(g);
}
// Restore saved model or pick first available
const saved=messages.length&&messages[0].model;
if(saved&&modelSelect.querySelector('option[value="'+CSS.escape(saved)+'"]')){modelSelect.value=saved}
model=modelSelect.value||model;
}).catch(()=>{});
modelSelect.addEventListener('change',()=>{model=modelSelect.value});
const SEND='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>';
const STOP='<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>';
function renderAll(){
while(messagesEl.firstChild) messagesEl.removeChild(messagesEl.firstChild);
for(const m of messages) bubble(m.role,m.content);
scroll();
}
function bubble(role,text){
const d=document.createElement('div');
d.className='msg '+(role==='user'?'user':'assistant');
if(role==='assistant') setHtml(d, md(text||''));
else d.textContent=text;
messagesEl.appendChild(d);
return d;
}
function scroll(){messagesEl.scrollTop=messagesEl.scrollHeight}
// Safe HTML injection helper. All incoming LLM/user text is escaped
// via escHtml() before any markdown transformations, so the string
// reaching this function only contains tags from our controlled
// regex replacements. We use Range.createContextualFragment which is
// the W3C-recommended way to construct a DocumentFragment from HTML.
function setHtml(el,html){
while(el.firstChild) el.removeChild(el.firstChild);
const range=document.createRange();
range.selectNodeContents(el);
el.appendChild(range.createContextualFragment(html));
}
// --- Minimal markdown renderer (inline, no deps) ---
// Handles: fenced code, inline code, headings, bold, italic,
// strikethrough, links, ordered/unordered lists, paragraphs.
function escHtml(s){
return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/"/g,'&quot;').replace(/'/g,'&#39;');
}
function md(src){
if(!src)return '';
const blocks=[],inlines=[];
// 1. Extract fenced code blocks first so their contents aren't touched.
src=src.replace(/```(\w*)\n?([\s\S]*?)```/g,(_,lang,code)=>{
blocks.push({lang,code});
return '\u0000CB'+(blocks.length-1)+'\u0000';
});
// 2. Extract inline code (single-line backticks).
src=src.replace(/`([^`\n]+)`/g,(_,c)=>{
inlines.push(c);
return '\u0000IC'+(inlines.length-1)+'\u0000';
});
// 3. Escape everything else.
src=escHtml(src);
// 4. Headings.
src=src.replace(/^###\s+(.+)$/gm,'<h3>$1</h3>')
.replace(/^##\s+(.+)$/gm,'<h2>$1</h2>')
.replace(/^#\s+(.+)$/gm,'<h1>$1</h1>');
// 5. Bold / italic / strikethrough.
src=src.replace(/\*\*([^*\n]+)\*\*/g,'<strong>$1</strong>')
.replace(/__([^_\n]+)__/g,'<strong>$1</strong>')
.replace(/(^|[^*\w])\*([^*\n]+)\*(?!\*)/g,'$1<em>$2</em>')
.replace(/(^|[^_\w])_([^_\n]+)_(?!_)/g,'$1<em>$2</em>')
.replace(/~~([^~\n]+)~~/g,'<del>$1</del>');
// 6. Links — url is escaped above, so quotes are safe.
src=src.replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g,'<a href="$2" target="_blank" rel="noopener">$1</a>');
// 7. Unordered + ordered lists.
src=src.replace(/(?:^[-*]\s+.+(?:\n|$))+/gm,block=>{
const items=block.trim().split('\n')
.map(l=>'<li>'+l.replace(/^[-*]\s+/,'')+'</li>').join('');
return '<ul>'+items+'</ul>';
});
src=src.replace(/(?:^\d+\.\s+.+(?:\n|$))+/gm,block=>{
const items=block.trim().split('\n')
.map(l=>'<li>'+l.replace(/^\d+\.\s+/,'')+'</li>').join('');
return '<ol>'+items+'</ol>';
});
// 8. Paragraphs: split on blank lines, wrap non-block chunks in <p>.
src=src.split(/\n{2,}/).map(chunk=>{
const t=chunk.trim();
if(!t)return '';
if(/^<(h\d|ul|ol|pre|blockquote)/.test(t))return t;
if(t.startsWith('\u0000CB'))return t;
return '<p>'+t.replace(/\n/g,'<br>')+'</p>';
}).join('');
// 9. Restore inline code.
src=src.replace(/\u0000IC(\d+)\u0000/g,(_,i)=>'<code>'+escHtml(inlines[+i])+'</code>');
// 10. Restore fenced code blocks.
src=src.replace(/\u0000CB(\d+)\u0000/g,(_,i)=>{
const b=blocks[+i];
const cls=b.lang?' class="lang-'+escHtml(b.lang)+'"':'';
return '<pre><code'+cls+'>'+escHtml(b.code)+'</code></pre>';
});
return src;
}
const THINKING='<span class="thinking"><span></span><span></span><span></span></span>';
const CARET='<span class="caret"></span>';
const CLOUD_PFX=['gpt-','o1-','o3-','o4-','claude-','gemini-','openrouter/','chatgpt-'];
function save(){
const conv={id:convId,title:convTitle,createdAt:convCreated,updatedAt:Date.now(),model,
messages:messages.map((m,i)=>{
const o={id:convId+'_'+i,role:m.role,content:m.content,timestamp:m.timestamp||Date.now()};
if(m.usage)o.usage=m.usage;
if(m.telemetry)o.telemetry=m.telemetry;
return o;
})};
try{window.webkit.messageHandlers.overlay.postMessage('save:'+JSON.stringify(conv))}catch{}
}
input.addEventListener('input',()=>{sendBtn.disabled=!input.value.trim()||streaming});
input.addEventListener('keydown',e=>{
if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send()}
});
document.addEventListener('keydown',e=>{
if(e.key==='Escape'){
if(streaming){abort&&abort.abort()}
else{try{window.webkit.messageHandlers.overlay.postMessage('hide')}catch{}}
}
});
sendBtn.addEventListener('click',()=>{if(streaming){abort&&abort.abort()}else send()});
document.getElementById('new-btn').addEventListener('click',()=>{
convId=genId();convTitle='Overlay chat';convCreated=Date.now();
messages=[];
while(messagesEl.firstChild) messagesEl.removeChild(messagesEl.firstChild);
save();input.focus();
});
async function send(){
const text=input.value.trim();
if(!text||streaming)return;
input.value='';sendBtn.disabled=true;
messages.push({role:'user',content:text,timestamp:Date.now()});
if(messages.length===1) convTitle=text.slice(0,50)+(text.length>50?'...':'');
bubble('user',text);scroll();save();
streaming=true;abort=new AbortController();
setHtml(sendBtn,STOP);sendBtn.disabled=false;
const b=bubble('assistant','');
// Show thinking dots until the first token arrives.
setHtml(b,THINKING);
scroll();
let acc='',usage=null,complexity=null,ttft=0;
const t0=Date.now();
// Throttle markdown re-renders to ~30fps so tight streams don't
// rebuild the DOM on every single token.
let pending=false;
const render=()=>{
if(pending)return;
pending=true;
requestAnimationFrame(()=>{
pending=false;
setHtml(b, md(acc)+CARET);
scroll();
});
};
try{
const r=await fetch('/v1/chat/completions',{
method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({model,messages,stream:true}),
signal:abort.signal
});
if(!r.ok)throw new Error(r.status);
const reader=r.body.getReader(),dec=new TextDecoder();
let buf='';
for(;;){
const{done,value}=await reader.read();
if(done)break;
buf+=dec.decode(value,{stream:true});
const lines=buf.split('\n');buf=lines.pop()||'';
for(const ln of lines){
if(!ln.startsWith('data: '))continue;
const d=ln.slice(6);if(d==='[DONE]')break;
try{
const p=JSON.parse(d);
if(p.usage)usage=p.usage;
if(p.complexity)complexity=p.complexity;
const c=p.choices?.[0]?.delta?.content;
if(c){if(!ttft)ttft=Date.now()-t0;acc+=c;render()}
}catch{}
}
}
}catch(e){
if(e.name!=='AbortError'){
acc='Could not get a response. Is the backend running?';
b.textContent=acc;
}
}finally{
streaming=false;abort=null;
setHtml(sendBtn,SEND);sendBtn.disabled=!input.value.trim();
input.focus();
// Final render without the caret.
if(acc) setHtml(b, md(acc));
else if(b.querySelector('.thinking')) b.textContent='';
}
if(acc){
const totalMs=Date.now()-t0;
const engine=CLOUD_PFX.some(p=>model.startsWith(p))?'cloud':'ollama';
const telem={engine,model_id:model,total_ms:totalMs,ttft_ms:ttft||undefined,
tokens_per_sec:usage?.completion_tokens?usage.completion_tokens/(totalMs/1000):undefined,
complexity_score:complexity?.score,complexity_tier:complexity?.tier,
suggested_max_tokens:complexity?.suggested_max_tokens};
messages.push({role:'assistant',content:acc,timestamp:Date.now(),usage:usage||undefined,telemetry:telem});
save();
}
}
window.addEventListener('focus',()=>input.focus());
// --- Drag-to-move: click anywhere to drag, with threshold so
// clicks on inputs/buttons still work normally ---
(function(){
let down=false,dragging=false,sx=0,sy=0;
const THRESH=3;
const INTERACTIVE='input,select,button,textarea,a,label,[contenteditable="true"],[role="button"]';
document.addEventListener('mousedown',e=>{
if(e.target.closest('select'))return;
down=true;dragging=false;sx=e.screenX;sy=e.screenY;
});
// macOS native select menus swallow the mouseup — reset on change too
document.getElementById('model-select').addEventListener('mousedown',()=>{down=false;dragging=false});
document.getElementById('model-select').addEventListener('change',()=>{down=false;dragging=false});
document.addEventListener('mousemove',e=>{
if(!down)return;
// If the mouse button is no longer pressed (e.g. the user released
// it over a native menu that swallowed mouseup), abort the drag.
if(e.buttons===0){down=false;dragging=false;return;}
const dx=e.screenX-sx,dy=e.screenY-sy;
if(!dragging){
if(Math.abs(dx)+Math.abs(dy)<THRESH)return;
dragging=true;
}
sx=e.screenX;sy=e.screenY;
try{window.webkit.messageHandlers.overlay.postMessage('drag:'+dx+','+dy)}catch{}
});
const reset=()=>{down=false;dragging=false};
document.addEventListener('mouseup',reset);
window.addEventListener('blur',reset);
document.addEventListener('mouseleave',reset);
})();
</script>
</body>
</html>
-107
View File
@@ -1,107 +0,0 @@
import React, { useState } from 'react';
import { UpdateChecker } from './components/UpdateChecker';
import { EnergyDashboard } from './components/EnergyDashboard';
import { TraceDebugger } from './components/TraceDebugger';
import { LearningCurve } from './components/LearningCurve';
import { MemoryBrowser } from './components/MemoryBrowser';
import { AdminPanel } from './components/AdminPanel';
type TabId = 'energy' | 'traces' | 'learning' | 'memory' | 'admin';
interface Tab {
id: TabId;
label: string;
}
const TABS: Tab[] = [
{ id: 'energy', label: 'Energy' },
{ id: 'traces', label: 'Traces' },
{ id: 'learning', label: 'Learning' },
{ id: 'memory', label: 'Memory' },
{ id: 'admin', label: 'Admin' },
];
const API_URL = 'http://localhost:8000';
export function App() {
const [activeTab, setActiveTab] = useState<TabId>('energy');
return (
<div style={styles.container}>
<header style={styles.header}>
<h1 style={styles.title}>OpenJarvis Desktop</h1>
<nav style={styles.nav}>
{TABS.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
style={{
...styles.tabButton,
...(activeTab === tab.id ? styles.activeTab : {}),
}}
>
{tab.label}
</button>
))}
</nav>
</header>
<UpdateChecker />
<main style={styles.main}>
{activeTab === 'energy' && <EnergyDashboard apiUrl={API_URL} />}
{activeTab === 'traces' && <TraceDebugger apiUrl={API_URL} />}
{activeTab === 'learning' && <LearningCurve apiUrl={API_URL} />}
{activeTab === 'memory' && <MemoryBrowser apiUrl={API_URL} />}
{activeTab === 'admin' && <AdminPanel apiUrl={API_URL} />}
</main>
</div>
);
}
const styles: Record<string, React.CSSProperties> = {
container: {
minHeight: '100vh',
backgroundColor: '#1e1e2e',
color: '#cdd6f4',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
},
header: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '12px 24px',
borderBottom: '1px solid #313244',
backgroundColor: '#181825',
},
title: {
fontSize: '18px',
fontWeight: 600,
margin: 0,
color: '#89b4fa',
},
nav: {
display: 'flex',
gap: '4px',
},
tabButton: {
padding: '8px 16px',
border: 'none',
borderRadius: '6px',
backgroundColor: 'transparent',
color: '#a6adc8',
cursor: 'pointer',
fontSize: '14px',
fontWeight: 500,
transition: 'all 0.15s ease',
},
activeTab: {
backgroundColor: '#313244',
color: '#cdd6f4',
},
main: {
padding: '24px',
height: 'calc(100vh - 60px)',
overflow: 'auto',
},
};
-9
View File
@@ -1,9 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { App } from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
-21
View File
@@ -1,21 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}
-1
View File
@@ -1 +0,0 @@
{"root":["./src/App.tsx","./src/main.tsx","./src/components/AdminPanel.tsx","./src/components/EnergyDashboard.tsx","./src/components/LearningCurve.tsx","./src/components/MemoryBrowser.tsx","./src/components/TraceDebugger.tsx","./src/hooks/useTauriApi.ts"],"version":"5.7.3"}
-20
View File
@@ -1,20 +0,0 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist',
emptyOutDir: true,
},
server: {
port: 5173,
strictPort: true,
proxy: {
'/v1': 'http://localhost:8000',
'/health': 'http://localhost:8000',
},
},
clearScreen: false,
envPrefix: ['VITE_', 'TAURI_'],
});
-15
View File
@@ -1,15 +0,0 @@
# ROCm GPU override — use with:
# docker compose -f docker-compose.yml -f docker-compose.gpu.rocm.yml up
version: "3.9"
services:
jarvis:
build:
context: .
dockerfile: Dockerfile.gpu.rocm
devices:
- /dev/kfd
- /dev/dri
group_add:
- video
- render
-26
View File
@@ -1,26 +0,0 @@
version: "3.9"
services:
jarvis:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
environment:
- OPENJARVIS_ENGINE_DEFAULT=ollama
- OPENJARVIS_OLLAMA_HOST=http://ollama:11434
depends_on:
- ollama
restart: unless-stopped
ollama:
image: ollama/ollama
ports:
- "11434:11434"
volumes:
- ollama-models:/root/.ollama
restart: unless-stopped
volumes:
ollama-models:
-115
View File
@@ -1,115 +0,0 @@
# OpenJarvis Roadmap Progress
Last updated: 2026-02-27
## Phase 17: Production Tool Parity
### Core Tools
- [x] `FileWriteTool``src/openjarvis/tools/file_write.py` + `tests/tools/test_file_write.py` (16 tests)
- [x] `ApplyPatchTool``src/openjarvis/tools/apply_patch.py` + `tests/tools/test_apply_patch.py` (13 tests)
- [x] `ShellExecTool``src/openjarvis/tools/shell_exec.py` + `tests/tools/test_shell_exec.py` (19 tests)
- [x] `GitTool` (4 ops) — `src/openjarvis/tools/git_tool.py` + `tests/tools/test_git_tool.py` (41 tests)
- [x] `HttpRequestTool``src/openjarvis/tools/http_request.py` + `tests/tools/test_http_request.py` (21 tests)
- [x] `DatabaseQueryTool``src/openjarvis/tools/db_query.py` + `tests/tools/test_db_query.py` (26 tests)
### Inter-Agent Tools
- [x] `AgentSpawnTool``src/openjarvis/tools/agent_tools.py` + `tests/tools/test_agent_tools.py`
- [x] `AgentSendTool` — (same file)
- [x] `AgentListTool` — (same file)
- [x] `AgentKillTool` — (same file, 22 tests total)
### Browser Automation
- [x] `BrowserNavigateTool``src/openjarvis/tools/browser.py` + `tests/tools/test_browser.py`
- [x] `BrowserClickTool` — (same file)
- [x] `BrowserTypeTool` — (same file)
- [x] `BrowserScreenshotTool` — (same file)
- [x] `BrowserExtractTool` — (same file, 71 tests total)
### Media Tools
- [x] `ImageGenerateTool``src/openjarvis/tools/image_tool.py` + `tests/tools/test_image_tool.py` (12 tests)
- [x] `AudioTranscribeTool``src/openjarvis/tools/audio_tool.py` + `tests/tools/test_audio_tool.py` (17 tests)
- [x] `PDFExtractTool``src/openjarvis/tools/pdf_tool.py` + `tests/tools/test_pdf_tool.py` (19 tests)
### Security Hardening
- [x] SSRF protection — `src/openjarvis/security/ssrf.py` + `tests/security/test_ssrf.py` (18 tests)
- [x] Subprocess sandbox — `src/openjarvis/security/subprocess_sandbox.py` + `tests/security/test_subprocess_sandbox.py` (11 tests)
- [x] Prompt injection scanner — `src/openjarvis/security/injection_scanner.py` + `tests/security/test_injection_scanner.py` (10 tests)
- [x] Rate limiting — `src/openjarvis/security/rate_limiter.py` + `tests/security/test_rate_limiter.py` (12 tests)
- [x] Security headers middleware — `src/openjarvis/server/middleware.py` + `tests/server/test_middleware.py` (4 tests)
### Config & Integration
- [x] New extras in `pyproject.toml`: `browser`, `media`, `pdf`, channel extras for Phase 21
- [x] `ToolsConfig.browser` section added to `config.py` (`BrowserConfig`: headless, timeout_ms, viewport)
- [x] `SecurityConfig.ssrf_protection`, `rate_limit_enabled`, `rate_limit_rpm`, `rate_limit_burst` fields
## Phase 18: CLI & API Expansion
### CLI Commands
- [x] `jarvis start/stop/restart/status``src/openjarvis/cli/daemon_cmd.py` + `tests/cli/test_daemon_cmd.py` (7 tests)
- [x] `jarvis chat``src/openjarvis/cli/chat_cmd.py` + `tests/cli/test_chat_cmd.py` (6 tests)
- [x] `jarvis agent``src/openjarvis/cli/agent_cmd.py` + `tests/cli/test_agent_cmd.py` (3 tests)
- [x] `jarvis workflow``src/openjarvis/cli/workflow_cmd.py` + `tests/cli/test_workflow_cmd.py` (4 tests)
- [x] `jarvis skill``src/openjarvis/cli/skill_cmd.py` + `tests/cli/test_skill_cmd.py` (4 tests)
- [x] `jarvis vault``src/openjarvis/cli/vault_cmd.py` + `tests/cli/test_vault_cmd.py` (6 tests)
- [x] `jarvis add``src/openjarvis/cli/add_cmd.py` + `tests/cli/test_add_cmd.py` (5 tests)
### API Endpoints
- [x] Agent API endpoints — `src/openjarvis/server/api_routes.py`
- [x] Workflow API endpoints — (same file)
- [x] Memory API endpoints — (same file)
- [x] Traces API endpoints — (same file)
- [x] Telemetry API endpoints — (same file)
- [x] Skills API endpoints — (same file)
- [x] Sessions API endpoints — (same file)
- [x] Budget API endpoints — (same file)
- [x] Prometheus metrics — (same file)
- [x] Security headers middleware wired into app — `src/openjarvis/server/app.py`
- [x] All routes tested — `tests/server/test_api_routes.py` (11 tests)
- [x] WebSocket streaming endpoint — `WS /v1/chat/stream` + `tests/server/test_websocket.py` (9 tests)
## Phase 19: Learning System Productionization
- [x] GRPO Router — `src/openjarvis/learning/grpo_policy.py` (replaced stub) + `tests/learning/test_grpo_policy.py` (13 tests)
- [x] Multi-Armed Bandit Router — `src/openjarvis/learning/bandit_router.py` + `tests/learning/test_bandit_router.py` (13 tests)
- [x] Closed-Loop Skill Discovery — `src/openjarvis/learning/skill_discovery.py` + `tests/learning/test_skill_discovery.py` (10 tests)
- [x] Auto-Apply ICL Updates — modified `src/openjarvis/learning/icl_updater.py` + `tests/learning/test_icl_updates.py` (13 tests)
- [x] Learning Dashboard API — `GET /v1/learning/stats`, `GET /v1/learning/policy` + `tests/learning/test_learning_api.py` (8 tests)
## Phase 20: Desktop App (Tauri 2.0)
- [x] Tauri scaffold in `desktop/``src-tauri/`, `package.json`, `vite.config.ts`, `tsconfig.json`
- [x] Tauri Rust backend — `src-tauri/src/lib.rs` with 11 commands (health, energy, telemetry, traces, learning, memory, agents, jarvis CLI)
- [x] Tauri plugins — notification, shell, global-shortcut, autostart, updater, single-instance
- [x] Energy dashboard — `src/components/EnergyDashboard.tsx` (recharts line chart, auto-refresh)
- [x] Trace debugger — `src/components/TraceDebugger.tsx` (dual-panel, color-coded step timeline)
- [x] Learning curve visualization — `src/components/LearningCurve.tsx` (GRPO/bandit/ICL stats)
- [x] Memory browser — `src/components/MemoryBrowser.tsx` (search + stats)
- [x] Admin panel — `src/components/AdminPanel.tsx` (health, agents, server control)
- [x] Build successful — `.deb`, `.rpm`, `.AppImage` bundles produced
- [x] CI workflow — `.github/workflows/desktop.yml` (Linux/macOS/Windows matrix)
## Phase 21: Channels
- [x] LINE — `src/openjarvis/channels/line_channel.py`
- [x] Viber — `src/openjarvis/channels/viber_channel.py`
- [x] Facebook Messenger — `src/openjarvis/channels/messenger_channel.py`
- [x] Reddit — `src/openjarvis/channels/reddit_channel.py`
- [x] Mastodon — `src/openjarvis/channels/mastodon_channel.py`
- [x] XMPP — `src/openjarvis/channels/xmpp_channel.py`
- [x] Rocket.Chat — `src/openjarvis/channels/rocketchat_channel.py`
- [x] Zulip — `src/openjarvis/channels/zulip_channel.py`
- [x] Twitch — `src/openjarvis/channels/twitch_channel.py`
- [x] Nostr — `src/openjarvis/channels/nostr_channel.py`
- [x] All channels tested — `tests/channels/test_channels_phase21.py` (103 tests)
## Test Summary
| Phase | New Tests | Cumulative |
|-------|-----------|------------|
| Pre-existing | ~2,447 | 2,447 |
| Phase 17 | ~300 | ~2,747 |
| Phase 18 | ~56 | ~2,803 |
| Phase 19 | ~49 | ~2,852 |
| Phase 21 | ~103 | ~2,923 |
| WebSocket + Learning API | ~17 | ~2,940 |
| **Verified total** | | **2,997 passed, 42 skipped** |
## CLAUDE.md
- [x] Updated with all new tools, CLI commands, API endpoints, channels, learning policies, desktop app, config fields, and phase table
-92
View File
@@ -1,92 +0,0 @@
# Agents Module
The agents module implements the agentic logic pillar. All agents implement
the `BaseAgent` ABC with a `run()` method. Agents handle queries by
coordinating tool calls, memory retrieval, and inference engine interactions.
The module also includes the OpenClaw infrastructure for interoperating with
external agent frameworks via HTTP or subprocess transport.
## Abstract Base Classes and Context
### BaseAgent
::: openjarvis.agents._stubs.BaseAgent
options:
show_source: true
members_order: source
### ToolUsingAgent
::: openjarvis.agents._stubs.ToolUsingAgent
options:
show_source: true
members_order: source
### AgentContext
::: openjarvis.agents._stubs.AgentContext
options:
show_source: true
members_order: source
### AgentResult
::: openjarvis.agents._stubs.AgentResult
options:
show_source: true
members_order: source
---
## Agent Implementations
### SimpleAgent
::: openjarvis.agents.simple.SimpleAgent
options:
show_source: true
members_order: source
### OrchestratorAgent
::: openjarvis.agents.orchestrator.OrchestratorAgent
options:
show_source: true
members_order: source
### NativeReActAgent
::: openjarvis.agents.native_react.NativeReActAgent
options:
show_source: true
members_order: source
### NativeOpenHandsAgent
::: openjarvis.agents.native_openhands.NativeOpenHandsAgent
options:
show_source: true
members_order: source
### RLMAgent
::: openjarvis.agents.rlm.RLMAgent
options:
show_source: true
members_order: source
### OpenHandsAgent
::: openjarvis.agents.openhands.OpenHandsAgent
options:
show_source: true
members_order: source
!!! note "OpenClaw Infrastructure"
The OpenClaw protocol, transport, and plugin modules (`openclaw_protocol.py`,
`openclaw_transport.py`, `openclaw_plugin.py`, `openclaw.py`) are part of the
OpenClaw agent infrastructure and require the `openjarvis[openclaw]` extra.
See the [architecture documentation](../architecture/agents.md#openclaw-infrastructure)
for protocol and transport details.
-48
View File
@@ -1,48 +0,0 @@
# Benchmarks Module
The benchmarks module provides a framework for measuring inference engine
performance. All benchmarks implement the `BaseBenchmark` ABC and are
registered via `BenchmarkRegistry`. The `BenchmarkSuite` runner executes
a collection of benchmarks and aggregates results into JSONL or summary
format.
## Abstract Base Class and Runner
### BaseBenchmark
::: openjarvis.bench._stubs.BaseBenchmark
options:
show_source: true
members_order: source
### BenchmarkResult
::: openjarvis.bench._stubs.BenchmarkResult
options:
show_source: true
members_order: source
### BenchmarkSuite
::: openjarvis.bench._stubs.BenchmarkSuite
options:
show_source: true
members_order: source
---
## Benchmark Implementations
### LatencyBenchmark
::: openjarvis.bench.latency.LatencyBenchmark
options:
show_source: true
members_order: source
### ThroughputBenchmark
::: openjarvis.bench.throughput.ThroughputBenchmark
options:
show_source: true
members_order: source
-128
View File
@@ -1,128 +0,0 @@
# API Reference: Channels
The `openjarvis.channels` package provides the channel messaging abstraction and the OpenClaw gateway bridge. All public classes and types are documented below.
For usage examples, CLI commands, and configuration, see the [Channels user guide](../user-guide/channels.md). For the architectural design and listener loop internals, see [Channels architecture](../architecture/channels.md).
---
## Types and Enums
### ChannelStatus
Connection status values for a channel. Returned by `BaseChannel.status()`.
::: openjarvis.channels._stubs.ChannelStatus
options:
show_source: true
show_root_heading: true
heading_level: 4
### ChannelMessage
Dataclass representing a message received from or sent to a channel. All fields correspond to the JSON payload exchanged with the gateway.
::: openjarvis.channels._stubs.ChannelMessage
options:
show_source: true
show_root_heading: true
heading_level: 4
### ChannelHandler
Type alias for message handler callbacks:
```python
ChannelHandler = Callable[[ChannelMessage], Optional[str]]
```
Handlers are called synchronously from the listener thread when a message arrives. The optional `str` return value is reserved for future auto-reply routing and has no effect in the current implementation.
!!! warning "Thread safety"
Handlers run on the listener thread, not the caller thread. Protect shared state with locks or `queue.Queue`.
::: openjarvis.channels._stubs.ChannelHandler
options:
show_source: true
show_root_heading: true
heading_level: 4
---
## BaseChannel
Abstract base class for all channel implementations. Subclasses must implement all six abstract methods and register via `@ChannelRegistry.register("name")`.
```python title="custom_channel.py"
from openjarvis.channels._stubs import BaseChannel, ChannelMessage, ChannelStatus
from openjarvis.core.registry import ChannelRegistry
@ChannelRegistry.register("my-channel")
class MyChannel(BaseChannel):
channel_id = "my-channel"
def connect(self) -> None: ...
def disconnect(self) -> None: ...
def send(self, channel, content, *, conversation_id="", metadata=None) -> bool: ...
def status(self) -> ChannelStatus: ...
def list_channels(self) -> list[str]: ...
def on_message(self, handler) -> None: ...
```
::: openjarvis.channels._stubs.BaseChannel
options:
show_source: true
show_root_heading: true
heading_level: 3
---
## OpenClawChannelBridge
`OpenClawChannelBridge` connects to the OpenClaw gateway over WebSocket, with automatic HTTP fallback when the `websockets` package is not installed or a WebSocket send fails. It is registered as `"openclaw"` in `ChannelRegistry`.
```python title="bridge_example.py"
from openjarvis.channels.openclaw_bridge import OpenClawChannelBridge
from openjarvis.channels._stubs import ChannelMessage
from openjarvis.core.events import EventBus
bus = EventBus()
bridge = OpenClawChannelBridge(
gateway_url="ws://127.0.0.1:18789/ws",
reconnect_interval=5.0,
bus=bus,
)
def on_message(msg: ChannelMessage) -> None:
print(f"[{msg.channel}] {msg.sender}: {msg.content}")
bridge.on_message(on_message)
bridge.connect()
bridge.send("notifications", "Hello!")
channels = bridge.list_channels()
bridge.disconnect()
```
### Constructor Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `gateway_url` | `str` | `ws://127.0.0.1:18789/ws` | WebSocket URL of the OpenClaw gateway |
| `reconnect_interval` | `float` | `5.0` | Seconds to wait before reconnecting after a disconnect |
| `bus` | `EventBus` | `None` | Event bus for publishing `CHANNEL_MESSAGE_RECEIVED` and `CHANNEL_MESSAGE_SENT` events |
### Events Published
| Event | When |
|-------|------|
| `CHANNEL_MESSAGE_RECEIVED` | Message received from gateway WebSocket |
| `CHANNEL_MESSAGE_SENT` | Message successfully delivered via WebSocket or HTTP |
!!! note "Optional dependency"
`OpenClawChannelBridge` requires the `openjarvis[openclaw]` extra.
See the [Channels architecture](../architecture/channels.md) for design details.

Some files were not shown because too many files have changed in this diff Show More