Commit Graph
673 Commits
Author SHA1 Message Date
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
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
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
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 Ceyssatandkrypticmouse 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
tomaiooandkrypticmouse 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-FalconandGitHub 4a7817509b docs(index): align homepage with canonical surfaces (#361) 2026-05-19 18:52:30 -07:00
Jon Saad-FalconandGitHub 2b0e6e7130 Update README.md 2026-05-19 18:18:26 -07:00
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
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 BhathalandGitHub 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-FalconandGitHub fea8d3e872 release: v1.0.1 (#357) v1.0.1 2026-05-18 20:19:37 -07:00
Jon Saad-FalconandGitHub 5b847cd081 Update README.md 2026-05-18 18:41:48 -07:00
Jon Saad-FalconandGitHub a5029e297f Update README.md 2026-05-18 18:40:54 -07:00
Robby ManihaniandGitHub 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 BhathalandGitHub 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-FalconandGitHub e97088f199 release: v1.0.0 (#349) v1.0.0 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-FalconandGitHub 9540e85dfb ci: fix env-dependent test failures (gated datasets, server extra, router prefix) (#348) 2026-05-15 21:31:13 -07:00
Andrew ParkandGitHub a7b8597856 hybrid: evaluate six local+cloud paradigms (minions, conductor, archon, advisors, skillorchestra, toolorchestra) (#344) 2026-05-15 20:51:19 -07:00
Jon Saad-FalconandGitHub 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-FalconandGitHub 44815e8544 feat(evals): add TerminalBench V2.1 benchmark (#345) 2026-05-14 20:23:14 -07:00
Avanika NarayanandGitHub 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 BhathalandGitHub 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-FalconandGitHub 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-FalconandGitHub b8245136db fix(security): block IPv4-mapped IPv6 addresses in SSRF check (#327) 2026-05-07 14:55:52 -07:00
Jon Saad-FalconandGitHub 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 HamiltonandGitHub 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
4935053ac7 feat(mining): register ScalingIntelligence Pearl models (#324)
Co-authored-by: krypticmouse <herumbshandilya123@gmail.com>
2026-05-06 22:20:18 -07:00
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 NarayanandGitHub 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 NarayanandGitHub 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
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-FalconandGitHub 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