Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 101 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
A local-first, permission-controlled desktop assistant for Windows. The
assistant is named **JARVIS** by default; the product is **Local Agent**.

> **Status: Phase 1 complete. Phase 2, Milestone 4 (provider completion and
> streaming) in progress.**
> **Status: Phase 1 complete. Phase 2, Milestone 6 (controlled coding
> actions) in progress.**
> Phase 1 delivered the hardened desktop shell, non-secret settings storage,
> an audit-log foundation, the permission-policy runtime, persisted
> emergency-stop state, first-run onboarding, an encrypted secret store, and
Expand Down Expand Up @@ -33,15 +33,47 @@ assistant is named **JARVIS** by default; the product is **Local Agent**.
> refused rather than sent — and reads no credential at all. A streamed
> fragment is a preview, never a message: the reply committed to the
> conversation is still the whole, separately validated one. `anthropic`,
> `openai`, `claude` and `gemini` remain absent. See
> `openai`, `claude` and `gemini` remain absent. Milestone 5 adds the
> **read-only coding workspace**: the user approves one project directory in a
> native picker the main process owns — the renderer cannot name a directory,
> because `workspace.select` takes no argument at all — and the application can
> then list, read, search and prepare a coding plan inside it, and nothing
> else. Every path is contained twice (lexically, then again after `realpath`,
> so a symbolic link cannot lead out); dependencies, build output and
> credential files are never opened; every listing, file and search is
> bounded; and **nothing in this milestone can modify a file**. That is
> structural rather than a policy: no action type, no IPC channel, no preload
> function and no schema can express a modification. A generated plan is
> inert — its `diff` is pinned to `null`, it always awaits explicit approval,
> and approving it unlocks nothing, because there is nothing to unlock. See
> [docs/phase-2-chat-architecture.md](docs/phase-2-chat-architecture.md),
> [docs/phase-2-provider-architecture.md](docs/phase-2-provider-architecture.md),
> [docs/phase-2-real-provider-architecture.md](docs/phase-2-real-provider-architecture.md)
> and
> [docs/phase-2-real-provider-architecture.md](docs/phase-2-real-provider-architecture.md),
> [docs/phase-2-provider-completion.md](docs/phase-2-provider-completion.md)
> for the full design and for what remains explicitly deferred (tool/action
> execution from model output, memory, and everything else Phase 2 has not
> reached yet).
> and
> [docs/phase-2-coding-workspace.md](docs/phase-2-coding-workspace.md)
> for the full design.
>
> Milestone 6 adds **controlled coding actions** — the first three things this
> application can change outside its own data directory, each behind a native
> confirmation the renderer cannot forge or answer. It can **overwrite files**
> inside the approved project: a change is proposed once, shown as a diff, and
> then applied by _identifier_ — `workspace:apply` carries no path and no
> content, so the bytes written are necessarily the bytes that were reviewed.
> A backup is taken outside the project first, the latest change can be undone,
> and nothing can create or delete a file. It can **run one of five commands**
> (`test`, `lint`, `typecheck`, `build`, `format:check`) that the project
> itself declares — named by an enum member, never a command string, with no
> shell, no terminal, no inherited secrets, bounded time and output, and a kill
> that reaches the whole process tree and is triggered by the emergency stop.
> And it can read **Git status and diff** and create **one checkpoint commit**
> on the branch already checked out; there is no reset, checkout, branch,
> push or remote anywhere in the codebase, and a repository's own hooks are
> disabled for every invocation. See
> [docs/phase-2-coding-actions.md](docs/phase-2-coding-actions.md) for the
> design, the bounds, and what remains explicitly deferred (file creation and
> deletion, arbitrary commands, dependency installation, browser and Windows
> automation, memory, and everything else Phase 2 has not reached yet).

All rights reserved. No licence has been granted for this project.

Expand Down Expand Up @@ -83,12 +115,23 @@ postponed.
- The preload bridge exposes only narrow, explicitly named, typed functions —
`localAgent.health`, `localAgent.settings.{get,update}`,
`localAgent.secrets.{status,write,clear}`,
`localAgent.chat.{send,cancel,onChunk}` — never `ipcRenderer` itself, never
a generic invoke-any-channel function, and never a generic
`localAgent.chat.{send,cancel,onChunk}`,
`localAgent.workspace.{status,select,tree,file,search,plan,propose,apply,rollback,changes}`,
`localAgent.command.{list,run,cancel}`,
`localAgent.git.{status,diff,checkpoint}` — never `ipcRenderer` itself,
never a generic invoke-any-channel function, and never a generic
listen-to-any-channel one: `chat.onChunk` subscribes to a single fixed,
one-way streaming channel whose payloads are schema-validated in the
preload before any renderer code sees them. None of them can return a
plaintext API key.
one-way streaming channel whose payloads are schema-validated in the preload
before any renderer code sees them. None of them can return a plaintext API
key. Of the ones that change something: `workspace.select` does not accept a
path, since the directory is chosen by the user in a native dialog the main
process owns; `workspace.apply` accepts only a change _identifier_, so the
bytes written are necessarily the ones already diffed and shown;
`command.run` accepts an identifier from a five-value enum, never a command
string; and `git.checkpoint` accepts no argument at all. There is no
function anywhere on the bridge that can create a file, delete one, run an
arbitrary command, or perform a destructive Git operation — asserted against
the real built bridge by an end-to-end test.
- A strict Content-Security-Policy blocks remote script and network access
outright; navigation, `window.open` and `<webview>` are all denied.
- An action with no matching policy rule is **denied** — enforced both by the
Expand Down Expand Up @@ -135,13 +178,27 @@ postponed.
anything: there is no path from a chat message, typed, streamed or
received, to `main/executor.ts` or to an action proposal.

- The coding workspace (Phase 2, Milestone 5) is **read-only, and structurally
so**. The user approves one directory through a native picker the main
process owns; the renderer cannot name a directory, and no action type, IPC
channel, preload function or schema in this milestone can express a
modification. Every renderer-supplied path is validated lexically and then
re-checked after `realpath`, so a symbolic link cannot lead outside the
approved project. Dependencies, build output and credential files are never
opened. Every listing, file read and search is bounded and reports when a
bound stopped it. A generated coding plan is inert: it always awaits explicit
approval, and approving it unlocks nothing. The approved path lives in memory
for one application run and is never written to disk.

Full detail, including known limitations, is in
[docs/security-model.md](docs/security-model.md); the chat-specific design is
in [docs/phase-2-chat-architecture.md](docs/phase-2-chat-architecture.md),
[docs/phase-2-provider-architecture.md](docs/phase-2-provider-architecture.md),
[docs/phase-2-real-provider-architecture.md](docs/phase-2-real-provider-architecture.md)
and
[docs/phase-2-provider-completion.md](docs/phase-2-provider-completion.md).
[docs/phase-2-provider-completion.md](docs/phase-2-provider-completion.md);
the workspace design is in
[docs/phase-2-coding-workspace.md](docs/phase-2-coding-workspace.md).

## Where your data lives

Expand Down Expand Up @@ -211,8 +268,14 @@ src/shared/ Pure schemas, types and constants, plus chat/ (the
deterministic mock provider, the approved-provider registry
with its fail-closed adapters, the composable timeout
decorator, and the local-endpoint classifier that keeps
Ollama local). No I/O, no Electron, no network — safe to
import from any process, including the renderer.
Ollama local), plus workspace/ (the lexical path-safety rules,
the exclusion lists, the normalized error vocabulary, and the
pure coding-plan builder), plus agent/ (the fixed tool
registry and its capability ceiling, the profile registry with
its built-ins and fail-closed resolution, the normalized error
vocabulary, and the pure bounded-orchestration decider). No
I/O, no Electron, no network — safe to import from any
process, including the renderer.
src/main/ Privileged Electron main process. Owns the BrowserWindow,
the Content-Security-Policy, navigation/window-open/webview
hardening, non-secret settings storage (paths.ts,
Expand All @@ -231,9 +294,17 @@ src/main/ Privileged Electron main process. Owns the BrowserWindow,
ollama-provider.ts — the last enforced local), the
main-process provider registry that resolves them from
settings and the encrypted secret store
(chat-provider-registry.ts), and the eight registered IPC
channels (ipc.ts), of which chat:chunk is the only
main-to-renderer event.
(chat-provider-registry.ts), the read-only coding workspace
(directory-picker.ts — the native project picker;
workspace-paths.ts — canonical path containment;
workspace-session.ts — the approved project, in memory only;
workspace-inspector.ts — the only module that reads a user's
file; workspace-planner.ts), agent profiles and the bounded
orchestrator (agent-profiles.ts — fail-safe, atomic storage
that never persists a built-in; agent-orchestrator.ts — the
run loop, which decides nothing about permissions), and the
registered IPC channels (ipc.ts), of which chat:chunk is the
only main-to-renderer event.
src/preload/ The single contextBridge. Exposes a narrow, explicitly
enumerated, typed API — never ipcRenderer, never a generic
invoke-any-channel function. Bundled into one file: a
Expand All @@ -246,9 +317,17 @@ src/renderer/ React interface: App.tsx gates on onboardingCompleted,
provider is active — and, since Milestone 3,
ipc-chat-provider.ts, the one file in this directory
permitted to call window.localAgent, reaching the real
provider through chat.send/chat.cancel). No Node, no
Electron, no direct filesystem or network access anywhere
else in this directory — only the bridge at window.localAgent.
provider through chat.send/chat.cancel), and workspace/ is the
Milestone 5 read-only coding surface (Workspace.tsx, the
framework-independent workspace-controller.ts, useWorkspace.ts,
and ipc-workspace-client.ts — the second file permitted to
call window.localAgent), and agent/ is the Milestone 7 profile
and run surface (Agents.tsx, the framework-independent
agent-controller.ts, useAgent.ts, and ipc-agent-client.ts —
the third and last file permitted to call window.localAgent).
No Node, no Electron, no direct filesystem or network access
anywhere else in this directory — only the bridge at
window.localAgent.
tests/unit/ Unit tests. `npm test`.
tests/e2e/ Playwright + Electron smoke test against the built app.
`npm run test:e2e`.
Expand Down
Loading
Loading