Skip to content

Add optional Jarvis bridge integration and autonomy status UI - #5

Open
Dickes007 wants to merge 10 commits into
ASHR12:mainfrom
Dickes007:main
Open

Dickes007 wants to merge 10 commits into
ASHR12:mainfrom
Dickes007:main

Conversation

@Dickes007

Copy link
Copy Markdown

Summary

Adds an optional bridge between Iris and an external Jarvis backend, plus UI surfaces for real Jarvis context and autonomous engineering status.

Included

  • Jarvis bridge client and Electron IPC/preload wiring
  • Personal focus/context integration
  • Work Stream support for real goal/job lifecycle events
  • Autonomy state such as worker, attempts, verification, and approval status
  • Bridge smoke tests and client tests
  • Routing-policy updates needed for the integration

Behavior

  • Iris remains the visible shell/UI.
  • Jarvis remains the external source of truth for goals, jobs, verification, and approval state.
  • Existing Iris voice behavior remains optional; the Jarvis bridge can operate without GEMINI_API_KEY.
  • No direct Jarvis job-store access is introduced in the renderer; data is consumed through the bridge.

The fork is 2 commits ahead of upstream/main and 0 behind.

…nal OS reads

Preserves prior uncommitted work: voice-turn routing to Jarvis's real
askJarvis pipeline (routingPolicy-gated), Work Stream/PersonalFocus panels
backed by getTasks/getTopFocus/getCurrentContext, and voice-state publish —
all via an in-process require() of Jarvis-Desktop's adapter/iris-bridge.cjs,
never a second retrieval/model/memory pipeline.
…, approval)

Consumes the new Jarvis-Desktop iris-bridge.cjs getActiveGoal()/
getLatestEngineeringJob() via two new jarvisBridge:* IPC channels — Iris
never reads Jarvis's job/goal files directly, no second job system.

- PersonalFocusPanel: new "Mission" section shows the active goal + next
  action.
- WorkStream: new "Autonomie" block shows the latest job's status/worker/
  attempts/approval banner (BEREIT ZUR FREIGABE / MENSCHLICHE ENTSCHEIDUNG
  NÖTIG, verbatim from Jarvis's own CommandCenter.jsx labels) and its last
  5 Work Stream events.

Read-only throughout — no merge/push/promote control in Iris, matching
Jarvis's own approval boundary. Reuses existing pos-block/focus-section
CSS and --mint/--amber tone tokens; no redesign, no new dashboard.
electron/main.mjs's dev-mode fallback hardcoded 127.0.0.1:5173. When that
port was already taken by an unrelated process, Vite auto-selected a
different port but "wait-on tcp:5173" just waited for anything to answer
on 5173 and Electron connected to that wrong renderer instead.

dev-electron.mjs starts Vite via its Node API, reads the port it actually
bound to, and passes that exact URL to Electron via VITE_DEV_SERVER_URL —
no polling a fixed port, no probing/reusing whatever else is listening
there. package.json's "dev" script now runs it directly, replacing the
concurrently+wait-on composition. Prod scripts (start/start:prod/electron)
are untouched.

Verified: target port free -> binds there, loads there. Target port
occupied by another process -> Vite auto-shifts, Electron follows exactly,
old port's occupant never rendered.
@Dickes007

Copy link
Copy Markdown
Author

@ASHR12 could you review this PR when you get a chance?

Dickes007 and others added 7 commits August 21, 2026 20:15
…ndering

Adds a always-available text composer to CommsPanel (independent of
voice/sidecar/Gemini-key state) and stops forcing the onboarding
wizard or auto-loading demo data on boot — a missing Gemini key is now
a normal, fully text-usable state. CenterStage/TopBar reflect
voice-configured state truthfully instead of implying voice is always
available. SessionSwitcher hides when there are no real Hermes
sessions instead of exposing an internal default id.

Renders a completed engineering job's real job.result.text in
WorkStream (the actual worker output, not just event log lines).
Localizes the remaining English UI copy to German for consistency
with the rest of the shell.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a compact, read-only Jarvis connections readout to the top bar: a
popover listing Personal OS, Google Drive, Google Calendar, GitHub, Web
Research, Mail (GMX) and Claude Worker with a connected count, using the
existing deck-panel/status-dot design language.

The status comes verbatim from Jarvis's published snapshot via the
existing bridge (getConnectionsStatus); Iris never computes it, because
outside Jarvis's own process the credential-backed checks would report
false negatives. Not-yet-loaded, bridge-unavailable and
nothing-published all render as distinct honest states, never demo data.

Iris also owns the backend lifecycle now: jarvisBackend.mjs resolves
Jarvis (dev checkout, installed Jarvis.app, or JARVIS_APP_PATH) and
starts it with --headless-backend on ready, stopping it again on quit,
so Iris stays the only visible shell and no orphan backend survives.
Jarvis's own single-instance lock keeps it to exactly one process; a
missing or unstartable Jarvis is non-fatal and Iris still boots.

Also fixes a z-index regression: .deck-top needed its own stacking level
so top-bar popovers are not painted over by the right-hand column.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A natural request typed into the existing Ask Jarvis composer can now
become a real Jarvis write action, approved by a human in Iris and
executed by the one running Jarvis backend process.

Iris owns none of it. jarvisActionClient.mjs resolves the running Jarvis
backend's loopback Action endpoint (re-read per call, since Jarvis
restarts on a new port and token) and forwards propose/approve/
secondaryApprove/cancel there. No previewId list, no approval stage, no
credentials and no execution state live in this process — a previewId is
an opaque handle into Jarvis's own Action Service. Every failure (no
Jarvis running, dead port, rejected token, unreadable response, hang)
surfaces as an honest {ok:false, error}; none can look like a successful
approval.

askJarvisText now asks Jarvis first whether the text is an action. If it
is, the proposal is rendered for approval and nothing is executed; if it
is not, the text falls through to the unchanged Ask Jarvis answer path.
The classification is Jarvis's own — Iris parses nothing.

JarvisActionApproval renders inside the existing CommsPanel rather than
as a new panel, so the approval sits next to the sentence that caused
it. It decides nothing: a destructive action that Jarvis has escalated
to secondary_approval_required gets a separate, differently labeled
second button, never rendered until Jarvis itself asks for it.

test:jarvis-action-smoke drives the whole chain unstubbed against a
throwaway vault, runtime dir and audit log, guarded by a pre-flight that
asks Jarvis's own modules where they would write and aborts before
Electron launches if any of them resolves to real user data.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the sibling-source-checkout resolution in jarvisBridgeClient.mjs and
jarvisActionClient.mjs with jarvisEndpoint.mjs, a resolver that finds Jarvis's
loopback Action endpoint from the OS Application Support dir instead of a
Jarvis-Desktop source directory. A packaged Iris.app has no such directory
(repoRoot is inside app.asar), so Ask Jarvis, Connections Status and every
Personal OS read were silently dead in the packaged app while working in dev.
Reads now travel the same loopback endpoint, token and transport as the
existing P2.5 write actions.

Adds scripts/test-packaged-runtime-smoke.mjs, an end-to-end smoke against the
real packaged Iris.app + Jarvis.app binaries (one window, one headless
backend, Connections Status, Ask Jarvis, Action/Approval/Result, clean
teardown), isolated to a throwaway vault/audit log/runtime dir.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
test:packaged-runtime-smoke always injected JARVIS_APP_PATH into the spawned
Iris process, defaulting to the dist-electron sibling build. That meant it
could never actually exercise the real production fallback chain end-to-end
— only an isolated resolver check did. JARVIS_APP_PATH is now forwarded only
when the caller sets it; otherwise the spawned Iris resolves Jarvis exactly
like a real installed app, landing on /Applications/Jarvis.app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
stopJarvisBackend() only ever sent SIGTERM with no exit check or
fallback, so a hung backend (e.g. stuck in a synchronous Keychain
call) could survive Iris quitting and orphan the Jarvis
single-instance lock, blocking the next restart. Mirrors the existing
SIGTERM->SIGKILL escalation pattern already used for the Hermes
gateway child in this repo (hermesGatewayClient.mjs).

Also extends the packaged-runtime e2e smoke test with empirical
coverage for double-start (AC2), endpoint-file cleanup on quit
(AC3b), and restart-after-shutdown (AC4) - none of which had any
coverage before.

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

natea commented Sep 19, 2026

Copy link
Copy Markdown

Would this allow you to bypass Gemini and use a local voice model as an alternative to Gemini Live?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants