jivamai 0.1.4-dev.edce8f4 → 0.2.0-dev.08fad0b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index--KfyNhBy.js +31 -0
- package/dist/assets/index-BZnT-rRg.css +10 -0
- package/dist/index.html +2 -2
- package/dist-server/index.js +1 -292
- package/package.json +4 -2
- package/dist/assets/index-CisS1YJo.css +0 -10
- package/dist/assets/index-DftBqc_W.js +0 -510
- package/dist-server/index.js.map +0 -1
- package/docs/README.md +0 -55
- package/docs/architecture/cloud-mode.md +0 -204
- package/docs/architecture/code-agent-integration.md +0 -276
- package/docs/architecture/ipc-contract.md +0 -162
- package/docs/architecture/jiva-core-integration.md +0 -147
- package/docs/architecture/overview.md +0 -87
- package/docs/architecture/startup-flow.md +0 -108
- package/docs/architecture/state-management.md +0 -153
- package/docs/guides/adding-features.md +0 -196
- package/docs/guides/design-guide.md +0 -308
- package/docs/release_notes/.gitkeep +0 -0
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# IPC Contract
|
|
2
|
-
|
|
3
|
-
All communication between the renderer and the main process uses Electron's `ipcRenderer.invoke` / `ipcMain.handle` (request-response) or `ipcRenderer.on` / `webContents.send` (push events).
|
|
4
|
-
|
|
5
|
-
The preload script (`electron/preload.ts`) exposes these as typed methods on `window.electron`. The full TypeScript interface is in `src/types/electron.d.ts`.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Setup
|
|
10
|
-
|
|
11
|
-
| Channel | Direction | Description |
|
|
12
|
-
|---------|-----------|-------------|
|
|
13
|
-
| `setup:check` | invoke | Pre-flight check: Node.js, jiva-core, config |
|
|
14
|
-
|
|
15
|
-
**`setup:check` return:**
|
|
16
|
-
```typescript
|
|
17
|
-
{
|
|
18
|
-
nodejs: { ok: boolean; version?: string }
|
|
19
|
-
jivaCore: { ok: boolean; version?: string }
|
|
20
|
-
config: { ok: boolean; path: string }
|
|
21
|
-
platform: string // 'win32' | 'darwin' | 'linux'
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Jiva Server Lifecycle
|
|
28
|
-
|
|
29
|
-
| Channel | Direction | Description |
|
|
30
|
-
|---------|-----------|-------------|
|
|
31
|
-
| `jiva:server:start` | invoke | Initialize JivaRunner (load jiva-core, start DualAgent) |
|
|
32
|
-
| `jiva:server:stop` | invoke | Cleanup agent and MCP servers |
|
|
33
|
-
| `jiva:server:restart` | invoke | Stop then start |
|
|
34
|
-
| `jiva:server:status` | invoke | Returns `{ status: ServerStatus; port: number }` |
|
|
35
|
-
| `jiva:server:status-changed` | push | Renderer receives status updates from runner |
|
|
36
|
-
| `jiva:phase-update` | push | Agent phase updates during inference (`thinking`, `calling-tool`, etc.) |
|
|
37
|
-
|
|
38
|
-
**`ServerStatus`:** `'stopped' | 'starting' | 'running' | 'error'`
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## Chat / Messaging
|
|
43
|
-
|
|
44
|
-
| Channel | Direction | Description |
|
|
45
|
-
|---------|-----------|-------------|
|
|
46
|
-
| `jiva:send-message` | invoke | Send a prompt; returns full result when complete |
|
|
47
|
-
| `jiva:reset-conversation` | invoke | Start a new conversation (clears agent memory) |
|
|
48
|
-
| `jiva:load-conversation` | invoke | Load a saved conversation by ID into the agent |
|
|
49
|
-
|
|
50
|
-
**`jiva:send-message` payload:** `(prompt: string, persona?: string)`
|
|
51
|
-
**`jiva:send-message` return:** `{ success: boolean; result?: AgentResult; conversationId?: string; error?: string }`
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## Conversations
|
|
56
|
-
|
|
57
|
-
| Channel | Direction | Description |
|
|
58
|
-
|---------|-----------|-------------|
|
|
59
|
-
| `conversations:list` | invoke | List saved conversations from `~/.jiva/conversations/` |
|
|
60
|
-
| `conversations:load` | invoke | Read a conversation JSON by ID |
|
|
61
|
-
|
|
62
|
-
**`conversations:list` return:**
|
|
63
|
-
```typescript
|
|
64
|
-
Array<{
|
|
65
|
-
id: string
|
|
66
|
-
summary: string // title from metadata, or first user message (truncated to 60 chars)
|
|
67
|
-
messageCount: number
|
|
68
|
-
lastModified: number // ms timestamp
|
|
69
|
-
}>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Personas
|
|
75
|
-
|
|
76
|
-
| Channel | Direction | Description |
|
|
77
|
-
|---------|-----------|-------------|
|
|
78
|
-
| `personas:list` | invoke | List all persona directories from `~/.jiva/personas/` |
|
|
79
|
-
| `personas:activate` | invoke | Activate a persona and switch the agent's context |
|
|
80
|
-
| `personas:active` | invoke | Get the currently active persona name |
|
|
81
|
-
|
|
82
|
-
**`personas:list` return:** `PersonaInfo[]` (see `electron/persona-manager.ts`)
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## Configuration
|
|
87
|
-
|
|
88
|
-
| Channel | Direction | Description |
|
|
89
|
-
|---------|-----------|-------------|
|
|
90
|
-
| `config:read` | invoke | Read `JivaConfig` from the platform-specific path |
|
|
91
|
-
| `config:write` | invoke | Write `JivaConfig` to the platform-specific path |
|
|
92
|
-
|
|
93
|
-
Config is read from / written to `getJivaConfigPath()` — see `electron/config-manager.ts`.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## MCP Server Management
|
|
98
|
-
|
|
99
|
-
| Channel | Direction | Description |
|
|
100
|
-
|---------|-----------|-------------|
|
|
101
|
-
| `mcp:list-status` | invoke | List all MCP servers with runtime connection status |
|
|
102
|
-
| `mcp:get-tools` | invoke | Get all tools exposed by connected MCP servers |
|
|
103
|
-
| `mcp:add-server` | invoke | Add a new MCP server (stdio or HTTP) to config and runtime |
|
|
104
|
-
| `mcp:remove-server` | invoke | Remove an MCP server from config and runtime |
|
|
105
|
-
| `mcp:toggle-server` | invoke | Enable or disable an MCP server |
|
|
106
|
-
| `mcp:reconnect-server` | invoke | Reconnect a specific MCP server |
|
|
107
|
-
|
|
108
|
-
**`mcp:list-status` return item:**
|
|
109
|
-
```typescript
|
|
110
|
-
{
|
|
111
|
-
name: string
|
|
112
|
-
enabled: boolean
|
|
113
|
-
connected: boolean
|
|
114
|
-
toolCount: number
|
|
115
|
-
command: string // stdio servers
|
|
116
|
-
args: string[]
|
|
117
|
-
env: Record<string, string>
|
|
118
|
-
url?: string // HTTP servers
|
|
119
|
-
type: 'stdio' | 'http'
|
|
120
|
-
error?: string
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Workspace / File Browser
|
|
127
|
-
|
|
128
|
-
| Channel | Direction | Description |
|
|
129
|
-
|---------|-----------|-------------|
|
|
130
|
-
| `workspace:get-dir` | invoke | Get the configured workspace directory |
|
|
131
|
-
| `workspace:set-dir` | invoke | Set the workspace directory (persisted to config) |
|
|
132
|
-
| `workspace:pick-dir` | invoke | Open native folder picker dialog |
|
|
133
|
-
| `workspace:list-files` | invoke | List files in a directory (restricted to `$HOME`) |
|
|
134
|
-
| `workspace:read-file` | invoke | Read a file's text content (max 500 KB) |
|
|
135
|
-
| `workspace:open-external` | invoke | Reveal a file in the native file manager |
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## Window Controls
|
|
140
|
-
|
|
141
|
-
| Channel | Direction | Description |
|
|
142
|
-
|---------|-----------|-------------|
|
|
143
|
-
| `window:minimize` | invoke | Minimize the window |
|
|
144
|
-
| `window:maximize` | invoke | Toggle maximize/restore |
|
|
145
|
-
| `window:close` | invoke | Close the window |
|
|
146
|
-
| `window:isMaximized` | invoke | Returns `boolean` |
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
## System / Native Events
|
|
151
|
-
|
|
152
|
-
| Channel | Direction | Description |
|
|
153
|
-
|---------|-----------|-------------|
|
|
154
|
-
| `native-theme-changed` | push | System light/dark mode changed; payload: `isDark: boolean` |
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Notes
|
|
159
|
-
|
|
160
|
-
- All invoke handlers return `{ success: boolean; error?: string }` on failure unless otherwise specified.
|
|
161
|
-
- Push events use `ipcRenderer.on` in the preload and are exposed as `onXxx(callback)` methods on `window.electron`.
|
|
162
|
-
- The `persona` parameter on `jiva:send-message` is accepted but intentionally ignored to avoid destroying conversation history mid-chat. Use `personas:activate` for explicit persona switches.
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# jiva-core Integration
|
|
2
|
-
|
|
3
|
-
Jivam does not bundle jiva-core. It dynamically loads the globally installed npm package at runtime. This means:
|
|
4
|
-
|
|
5
|
-
- Users must have `npm install -g jiva-core` before Jivam can start
|
|
6
|
-
- Updating jiva-core globally is sufficient — no Jivam rebuild needed
|
|
7
|
-
- The integration layer is entirely in `electron/jiva-runner.ts`
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Runtime Resolution
|
|
12
|
-
|
|
13
|
-
### `augmentPath()`
|
|
14
|
-
|
|
15
|
-
In a packaged Electron app, the OS launches the process with a minimal PATH that often excludes npm's global bin directory. `augmentPath()` fixes this by spawning the user's login shell:
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
const loginPath = execSync(`${shell} -l -c 'echo $PATH'`).toString().trim()
|
|
19
|
-
process.env.PATH = loginPath
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
This runs before any `execSync` or `import()` calls that depend on npm being on PATH.
|
|
23
|
-
|
|
24
|
-
### `resolveJivaCoreEntryPath()`
|
|
25
|
-
|
|
26
|
-
Finds the jiva-core ESM entry point:
|
|
27
|
-
|
|
28
|
-
1. `npm root -g` → e.g. `/usr/local/lib/node_modules` → checks for `jiva-core/package.json`
|
|
29
|
-
2. Fallback: `~/.npm-global/lib/node_modules/jiva-core/package.json` (common Linux setup)
|
|
30
|
-
3. Fallback: `./node_modules/jiva-core/package.json` (development / local install)
|
|
31
|
-
|
|
32
|
-
Reads `package.json` → follows the `"exports"` or `"main"` field to get the entry file path.
|
|
33
|
-
|
|
34
|
-
### Dynamic ESM Import
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
const { DualAgent, ConversationManager, MCPServerManager } = await import(entryPath)
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
This is a true ESM dynamic import — no CommonJS `require()`. jiva-core is an ESM-only package.
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Configuration
|
|
45
|
-
|
|
46
|
-
Config is read via `readConfig()` in `electron/config-manager.ts`, which reads from `getJivaConfigPath()` — the platform-specific location where jiva-core stores its `config.json`.
|
|
47
|
-
|
|
48
|
-
The config shape relevant to jiva-core:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
{
|
|
52
|
-
models: {
|
|
53
|
-
reasoning: {
|
|
54
|
-
provider?: string // e.g. 'anthropic', 'openai'
|
|
55
|
-
apiKey?: string
|
|
56
|
-
endpoint?: string
|
|
57
|
-
model?: string
|
|
58
|
-
useHarmonyFormat?: boolean
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
mcpServers?: Record<string, MCPServerConfig>
|
|
62
|
-
workspaceDir?: string
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## DualAgent
|
|
69
|
-
|
|
70
|
-
`DualAgent` is jiva-core's primary agent class. JivaRunner wraps it:
|
|
71
|
-
|
|
72
|
-
| JivaRunner method | DualAgent call | Notes |
|
|
73
|
-
|-------------------|----------------|-------|
|
|
74
|
-
| `initialize()` | `new DualAgent(config)` | Constructs with model config, persona, directive |
|
|
75
|
-
| `chat(prompt, onPhase)` | `agent.chat(prompt)` | Streams phase events via callback |
|
|
76
|
-
| `resetConversation()` | `agent.resetConversation()` | Clears in-memory conversation state |
|
|
77
|
-
| `loadConversation(id)` | `agent.loadConversation(id)` | Loads from `~/.jiva/conversations/<id>.json` |
|
|
78
|
-
| `saveConversation()` | `agent.saveConversation()` | Saves to `~/.jiva/conversations/` |
|
|
79
|
-
| `switchPersona(name)` | `agent.cleanup()` + `new DualAgent(...)` | Full re-init with new persona dir |
|
|
80
|
-
| `cleanup()` | `agent.cleanup()` | Closes MCP connections, frees resources |
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## Directive Injection
|
|
85
|
-
|
|
86
|
-
Before each agent initialization, `electron/directive-manager.ts` writes a directive file to `~/.jiva/jiva-directive.md`. This file contains:
|
|
87
|
-
|
|
88
|
-
- Today's date (so the agent always knows the current date)
|
|
89
|
-
- Any custom user instructions configured for the active persona
|
|
90
|
-
|
|
91
|
-
jiva-core reads this file as a system-level context injection, so the directive takes effect on every conversation without needing to be in the message history.
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Persona System
|
|
96
|
-
|
|
97
|
-
Personas live in `~/.jiva/personas/<name>/`. Each persona directory may contain:
|
|
98
|
-
|
|
99
|
-
- `.jiva-plugin/plugin.json` — manifest with `name`, `description`, `tags`
|
|
100
|
-
- `CLAUDE.md` or `SKILL.md` — persona instructions read by jiva-core
|
|
101
|
-
- Custom tools, skills, or resource files
|
|
102
|
-
|
|
103
|
-
`electron/persona-manager.ts` handles:
|
|
104
|
-
- `listPersonas()` — scans `~/.jiva/personas/`, reads manifests, returns `PersonaInfo[]`
|
|
105
|
-
- `activatePersona(name)` — writes `~/.jiva/active-persona.txt` (fallback if CLI unavailable)
|
|
106
|
-
- `getActivePersona()` — reads from config's `activePersona` field, falls back to `active-persona.txt`
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## MCP Server Management
|
|
111
|
-
|
|
112
|
-
MCP (Model Context Protocol) servers extend the agent with external tools. They are configured in `config.mcpServers`:
|
|
113
|
-
|
|
114
|
-
**Stdio server** (local process):
|
|
115
|
-
```json
|
|
116
|
-
{
|
|
117
|
-
"myServer": {
|
|
118
|
-
"command": "node",
|
|
119
|
-
"args": ["/path/to/server.js"],
|
|
120
|
-
"env": { "API_KEY": "..." },
|
|
121
|
-
"enabled": true
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**HTTP server** (remote):
|
|
127
|
-
```json
|
|
128
|
-
{
|
|
129
|
-
"remoteServer": {
|
|
130
|
-
"url": "http://localhost:8080/mcp",
|
|
131
|
-
"enabled": true
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
`MCPServerManager` (jiva-core) manages connections. JivaRunner exposes these live operations:
|
|
137
|
-
|
|
138
|
-
| Method | Description |
|
|
139
|
-
|--------|-------------|
|
|
140
|
-
| `getMCPServerStatus()` | Current connection state + tool count for each server |
|
|
141
|
-
| `getMCPTools()` | All tools grouped by server name |
|
|
142
|
-
| `addMCPServer(name, config)` | Add and connect a new server at runtime |
|
|
143
|
-
| `removeMCPServer(name)` | Disconnect and remove a server at runtime |
|
|
144
|
-
| `toggleMCPServer(name, enabled)` | Enable/disable without removing |
|
|
145
|
-
| `reconnectMCPServer(name)` | Force reconnect a failed server |
|
|
146
|
-
|
|
147
|
-
Config changes are written to disk immediately via `writeConfig()` so they persist across restarts.
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# Architecture Overview
|
|
2
|
-
|
|
3
|
-
## Three-Process Model
|
|
4
|
-
|
|
5
|
-
Electron enforces strict process separation. Jivam uses all three Electron processes:
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
9
|
-
│ Main Process (Node.js) │
|
|
10
|
-
│ electron/main.ts │
|
|
11
|
-
│ · Creates BrowserWindow │
|
|
12
|
-
│ · Loads jiva-core via dynamic import │
|
|
13
|
-
│ · Registers IPC handlers (ipc-handlers.ts) │
|
|
14
|
-
│ · Manages JivaRunner lifecycle │
|
|
15
|
-
└──────────────────────┬──────────────────────────────────────┘
|
|
16
|
-
│ contextBridge (context isolation ON)
|
|
17
|
-
┌──────────────────────▼──────────────────────────────────────┐
|
|
18
|
-
│ Preload Script (Node.js, sandboxed) │
|
|
19
|
-
│ electron/preload.ts │
|
|
20
|
-
│ · Exposes window.electron API to renderer │
|
|
21
|
-
│ · Wraps ipcRenderer.invoke / ipcRenderer.on │
|
|
22
|
-
└──────────────────────┬──────────────────────────────────────┘
|
|
23
|
-
│ window.electron.*
|
|
24
|
-
┌──────────────────────▼──────────────────────────────────────┐
|
|
25
|
-
│ Renderer Process (Chromium, no Node access) │
|
|
26
|
-
│ src/ (React + TypeScript + Vite) │
|
|
27
|
-
│ · All UI components and Zustand stores │
|
|
28
|
-
│ · Calls window.electron.* for all system operations │
|
|
29
|
-
└─────────────────────────────────────────────────────────────┘
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Context isolation is **enabled** — the renderer has no direct Node.js or Electron API access. All privileged operations go through the preload bridge.
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Key Directories
|
|
37
|
-
|
|
38
|
-
| Directory | Process | Purpose |
|
|
39
|
-
|-----------|---------|---------|
|
|
40
|
-
| `electron/` | Main | IPC handlers, jiva-core runner, config, persona, directive managers |
|
|
41
|
-
| `src/` | Renderer | React components, Zustand stores, types |
|
|
42
|
-
| `src/components/` | Renderer | UI component tree |
|
|
43
|
-
| `src/store/` | Renderer | Zustand state stores |
|
|
44
|
-
| `src/types/` | Shared | TypeScript interfaces including `ElectronAPI` |
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Data Persistence
|
|
49
|
-
|
|
50
|
-
| Data | Location | Manager |
|
|
51
|
-
|------|----------|---------|
|
|
52
|
-
| AI model config / API keys | Platform-specific (see below) | `electron/config-manager.ts` |
|
|
53
|
-
| Conversations | `~/.jiva/conversations/*.json` | jiva-core's ConversationManager |
|
|
54
|
-
| Personas | `~/.jiva/personas/<name>/` | `electron/persona-manager.ts` |
|
|
55
|
-
| Active persona | `~/.jiva/active-persona.txt` | `electron/persona-manager.ts` |
|
|
56
|
-
| Directive | `~/.jiva/jiva-directive.md` | `electron/directive-manager.ts` |
|
|
57
|
-
| Workspace dir preference | Inside config.json | `electron/config-manager.ts` |
|
|
58
|
-
| UI preferences (theme) | `localStorage` | `src/store/settings.store.ts` |
|
|
59
|
-
|
|
60
|
-
### Platform-Specific Config Paths
|
|
61
|
-
|
|
62
|
-
jiva-core stores `config.json` at OS-appropriate locations, **not** `~/.jiva/config.json`. `getJivaConfigPath()` in `electron/config-manager.ts` returns the correct path:
|
|
63
|
-
|
|
64
|
-
| Platform | Path |
|
|
65
|
-
|----------|------|
|
|
66
|
-
| Windows | `%APPDATA%\jiva-nodejs\Config\config.json` |
|
|
67
|
-
| macOS | `~/Library/Preferences/jiva-nodejs/config.json` |
|
|
68
|
-
| Linux | `$XDG_CONFIG_HOME/jiva-nodejs/config.json` (default: `~/.config/jiva-nodejs/config.json`) |
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Build System
|
|
73
|
-
|
|
74
|
-
| Tool | Role |
|
|
75
|
-
|------|------|
|
|
76
|
-
| `vite` | Bundles the renderer (React app) |
|
|
77
|
-
| `vite-plugin-electron` | Co-builds main + preload alongside renderer |
|
|
78
|
-
| `electron-builder` | Packages the app for distribution |
|
|
79
|
-
| `build.sh` | Wrapper script: cleans, builds, packages all platforms |
|
|
80
|
-
|
|
81
|
-
**Packaging targets:**
|
|
82
|
-
|
|
83
|
-
| Platform | Formats |
|
|
84
|
-
|----------|---------|
|
|
85
|
-
| macOS | DMG (installer) + ZIP (portable) |
|
|
86
|
-
| Windows | NSIS installer + portable EXE |
|
|
87
|
-
| Linux | AppImage + `.deb` |
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# Startup Flow
|
|
2
|
-
|
|
3
|
-
This document traces the full boot sequence from process launch to a fully interactive UI.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Phase 1 — Electron Main Process Starts
|
|
8
|
-
|
|
9
|
-
**File:** `electron/main.ts`
|
|
10
|
-
|
|
11
|
-
1. Electron creates the `BrowserWindow` with `contextIsolation: true` and `nodeIntegration: false`.
|
|
12
|
-
2. The preload script (`electron/preload.ts`) is loaded into the window context.
|
|
13
|
-
3. The window loads the renderer: dev server URL in development, `dist/index.html` in production.
|
|
14
|
-
4. `setupIpcHandlers(jivaRunner, getWindow)` is called, registering all `ipcMain.handle()` listeners.
|
|
15
|
-
5. `JivaRunner` instance is created but **not yet initialized** — it waits for the renderer to request it.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Phase 2 — Renderer Mounts, Pre-flight Check
|
|
20
|
-
|
|
21
|
-
**File:** `src/App.tsx`
|
|
22
|
-
|
|
23
|
-
1. React mounts. `setupDone` state is initialized to `null` (pre-flight in progress).
|
|
24
|
-
2. `window.electron.setup.check()` is invoked — this calls `setup:check` IPC.
|
|
25
|
-
|
|
26
|
-
**Main process handles `setup:check`** (`electron/ipc-handlers.ts`):
|
|
27
|
-
|
|
28
|
-
- Runs `npm --version` (ok flag) + `node --version` (display version)
|
|
29
|
-
- Checks `npm root -g` for `jiva-core/package.json`
|
|
30
|
-
- Checks `getJivaConfigPath()` for a config file with a non-empty `apiKey`
|
|
31
|
-
- Returns `{ nodejs, jivaCore, config, platform }`
|
|
32
|
-
|
|
33
|
-
3. If all three checks pass → `setupDone = true` → proceed to Phase 3.
|
|
34
|
-
4. If any fail → `setupDone = false` → `<SetupScreen>` renders with the check results.
|
|
35
|
-
- SetupScreen auto-polls every 3 seconds until all checks pass.
|
|
36
|
-
- User can configure the AI model in-app via the Settings overlay.
|
|
37
|
-
- Once all pass, user clicks "Continue" → `setupDone = true`.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Phase 3 — Jiva Agent Initialization
|
|
42
|
-
|
|
43
|
-
**File:** `src/store/jiva.store.ts` → `startServer()` → `window.electron.jiva.server.start()`
|
|
44
|
-
|
|
45
|
-
**Main process handles `jiva:server:start`** (`electron/ipc-handlers.ts`):
|
|
46
|
-
|
|
47
|
-
1. Calls `jivaRunner.initialize()`.
|
|
48
|
-
|
|
49
|
-
**`JivaRunner.initialize()`** (`electron/jiva-runner.ts`):
|
|
50
|
-
|
|
51
|
-
1. `augmentPath()` — spawns `$SHELL -l -c 'echo $PATH'` to capture the login shell's PATH (needed in packaged apps where the process PATH is minimal).
|
|
52
|
-
2. `resolveJivaCoreEntryPath()` — runs `npm root -g` to locate the global `jiva-core` install. Falls back to `~/.npm-global` and local `node_modules`.
|
|
53
|
-
3. Dynamic ESM import: `const { DualAgent, ConversationManager, MCPServerManager } = await import(entryPath)`
|
|
54
|
-
4. Reads config via `readConfig()` → `getJivaConfigPath()`.
|
|
55
|
-
5. `directive-manager.ts` writes a date-aware `~/.jiva/jiva-directive.md` (injected as system context).
|
|
56
|
-
6. `persona-manager.ts` resolves the active persona directory.
|
|
57
|
-
7. `DualAgent` is constructed with the config, persona, directive, and MCP server config.
|
|
58
|
-
8. MCP servers from `config.mcpServers` are started via `MCPServerManager`.
|
|
59
|
-
9. Runner emits `status-changed: 'ready'`.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Phase 4 — Status Events and UI Ready
|
|
64
|
-
|
|
65
|
-
**File:** `electron/ipc-handlers.ts`
|
|
66
|
-
|
|
67
|
-
The runner's `status-changed` event is forwarded to the renderer via `win.webContents.send('jiva:server:status-changed', ...)`.
|
|
68
|
-
|
|
69
|
-
**File:** `src/store/jiva.store.ts`
|
|
70
|
-
|
|
71
|
-
`initPhaseListener()` registers `window.electron.jiva.onStatusChange()` and `window.electron.jiva.onPhaseUpdate()`. Status updates flow:
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
JivaRunner emits 'status-changed'
|
|
75
|
-
→ ipcMain forwards to renderer
|
|
76
|
-
→ jiva.store sets serverStatus
|
|
77
|
-
→ App.tsx reacts: setShowSplash(false)
|
|
78
|
-
→ AppShell renders
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## Phase 5 — App Shell Renders
|
|
84
|
-
|
|
85
|
-
**File:** `src/components/layout/AppShell.tsx`
|
|
86
|
-
|
|
87
|
-
- `loadPersonas()` is called from `App.tsx` once `showSplash` clears.
|
|
88
|
-
- `AppShell` renders the sidebar, topbar, and active tab panel.
|
|
89
|
-
- The user can now chat, switch personas, browse conversations, manage MCP servers, and browse files.
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## Sequence Summary
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
main.ts → BrowserWindow, setupIpcHandlers
|
|
97
|
-
preload.ts → window.electron exposed
|
|
98
|
-
App.tsx mounts → setup:check IPC
|
|
99
|
-
[SetupScreen if needed]
|
|
100
|
-
setupDone = true
|
|
101
|
-
jiva:server:start → JivaRunner.initialize()
|
|
102
|
-
augmentPath → resolveJivaCoreEntryPath → import(jiva-core)
|
|
103
|
-
DualAgent created, MCP servers started
|
|
104
|
-
status-changed: 'ready'
|
|
105
|
-
showSplash = false
|
|
106
|
-
AppShell renders
|
|
107
|
-
loadPersonas()
|
|
108
|
-
```
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# State Management
|
|
2
|
-
|
|
3
|
-
Jivam uses [Zustand](https://github.com/pmndrs/zustand) for all renderer-side state. There are six stores, each with a single responsibility.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Data Flow Pattern
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
User action
|
|
11
|
-
→ Component calls store action
|
|
12
|
-
→ Store action calls window.electron.* (IPC invoke)
|
|
13
|
-
→ Main process handler executes
|
|
14
|
-
→ Returns result
|
|
15
|
-
→ Store updates state
|
|
16
|
-
→ Component re-renders
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Push events (server status, phase updates) follow the reverse path: main process sends via `webContents.send` → preload forwards via `ipcRenderer.on` → store listener updates state.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Stores
|
|
24
|
-
|
|
25
|
-
### `useJivaStore` — `src/store/jiva.store.ts`
|
|
26
|
-
|
|
27
|
-
Manages the jiva-core agent lifecycle and runtime state.
|
|
28
|
-
|
|
29
|
-
| State | Type | Description |
|
|
30
|
-
|-------|------|-------------|
|
|
31
|
-
| `serverStatus` | `'stopped' \| 'starting' \| 'running' \| 'error'` | Current agent status |
|
|
32
|
-
| `currentPhase` | `string \| null` | Agent's current reasoning phase |
|
|
33
|
-
| `isInitializing` | `boolean` | True while `startServer()` is in progress |
|
|
34
|
-
|
|
35
|
-
| Action | Description |
|
|
36
|
-
|--------|-------------|
|
|
37
|
-
| `startServer()` | Invokes `jiva:server:start`, transitions status through `starting → running` |
|
|
38
|
-
| `stopServer()` | Invokes `jiva:server:stop` |
|
|
39
|
-
| `restartServer()` | Invokes `jiva:server:restart` |
|
|
40
|
-
| `setServerStatus(status)` | Direct status setter (used by push event listener) |
|
|
41
|
-
| `initPhaseListener()` | Registers listeners for `jiva:server:status-changed` and `jiva:phase-update` push events |
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
### `useChatStore` — `src/store/chat.store.ts`
|
|
46
|
-
|
|
47
|
-
Manages the chat message history and streaming state for the active session.
|
|
48
|
-
|
|
49
|
-
| State | Type | Description |
|
|
50
|
-
|-------|------|-------------|
|
|
51
|
-
| `messages` | `ChatMessage[]` | Array of user and assistant messages |
|
|
52
|
-
| `isStreaming` | `boolean` | True while awaiting agent response |
|
|
53
|
-
| `streamError` | `string \| null` | Error from the last failed send |
|
|
54
|
-
|
|
55
|
-
| Action | Description |
|
|
56
|
-
|--------|-------------|
|
|
57
|
-
| `sendMessage(prompt, persona?)` | Appends user message, invokes `jiva:send-message`, appends assistant response |
|
|
58
|
-
| `clearMessages()` | Clears the message array (used when starting a new conversation) |
|
|
59
|
-
| `setMessages(messages)` | Bulk-replaces messages (used when loading a saved conversation) |
|
|
60
|
-
|
|
61
|
-
**`ChatMessage` shape:**
|
|
62
|
-
```typescript
|
|
63
|
-
{
|
|
64
|
-
id: string
|
|
65
|
-
role: 'user' | 'assistant'
|
|
66
|
-
content: string
|
|
67
|
-
timestamp: number
|
|
68
|
-
phase?: string // reasoning phase tag, shown on assistant messages
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
### `useConversationStore` — `src/store/conversation.store.ts`
|
|
75
|
-
|
|
76
|
-
Manages the conversation list in the sidebar and the active conversation ID.
|
|
77
|
-
|
|
78
|
-
| State | Type | Description |
|
|
79
|
-
|-------|------|-------------|
|
|
80
|
-
| `conversations` | `ConversationSummary[]` | Sidebar list, sorted newest-first |
|
|
81
|
-
| `activeId` | `string \| null` | ID of the currently loaded conversation |
|
|
82
|
-
| `isLoading` | `boolean` | True while fetching the list |
|
|
83
|
-
|
|
84
|
-
| Action | Description |
|
|
85
|
-
|--------|-------------|
|
|
86
|
-
| `loadConversations()` | Invokes `conversations:list`, populates `conversations` |
|
|
87
|
-
| `selectConversation(id)` | Invokes `conversations:load` + `jiva:load-conversation`, sets `activeId`, loads messages into `useChatStore` |
|
|
88
|
-
| `newConversation()` | Invokes `jiva:reset-conversation`, clears messages, sets `activeId = null` |
|
|
89
|
-
| `refreshConversations()` | Re-fetches the list (called after a chat completes to capture new titles) |
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
### `usePersonaStore` — `src/store/persona.store.ts`
|
|
94
|
-
|
|
95
|
-
Manages the persona list and active persona.
|
|
96
|
-
|
|
97
|
-
| State | Type | Description |
|
|
98
|
-
|-------|------|-------------|
|
|
99
|
-
| `personas` | `PersonaInfo[]` | All discovered personas from `~/.jiva/personas/` |
|
|
100
|
-
| `activePersona` | `string \| null` | Name of the currently active persona |
|
|
101
|
-
| `isLoading` | `boolean` | True while fetching |
|
|
102
|
-
|
|
103
|
-
| Action | Description |
|
|
104
|
-
|--------|-------------|
|
|
105
|
-
| `loadPersonas()` | Invokes `personas:list` and `personas:active`, populates state |
|
|
106
|
-
| `activatePersona(name)` | Invokes `personas:activate`, updates `activePersona` |
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
### `useSettingsStore` — `src/store/settings.store.ts`
|
|
111
|
-
|
|
112
|
-
Manages UI preferences. Persisted to `localStorage` via Zustand's `persist` middleware.
|
|
113
|
-
|
|
114
|
-
| State | Type | Description |
|
|
115
|
-
|-------|------|-------------|
|
|
116
|
-
| `theme` | `'light' \| 'dark' \| 'system'` | Active color theme |
|
|
117
|
-
| `modelConfig` | `ModelConfig \| null` | Cached model configuration for the Settings UI |
|
|
118
|
-
|
|
119
|
-
| Action | Description |
|
|
120
|
-
|--------|-------------|
|
|
121
|
-
| `setTheme(theme)` | Updates theme, persisted automatically |
|
|
122
|
-
| `loadModelConfig()` | Invokes `config:read`, populates `modelConfig` |
|
|
123
|
-
| `saveModelConfig(config)` | Invokes `config:write` with the updated config |
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
### `useFilesStore` — `src/store/files.store.ts`
|
|
128
|
-
|
|
129
|
-
Manages the workspace file browser state.
|
|
130
|
-
|
|
131
|
-
| State | Type | Description |
|
|
132
|
-
|-------|------|-------------|
|
|
133
|
-
| `workspaceDir` | `string` | Current workspace root directory |
|
|
134
|
-
| `entries` | `FileEntry[]` | Files and subdirectories in the current directory |
|
|
135
|
-
| `selectedFile` | `FileEntry \| null` | File currently open in the preview panel |
|
|
136
|
-
| `fileContent` | `string \| null` | Text content of the selected file |
|
|
137
|
-
| `isLoading` | `boolean` | True while listing or reading |
|
|
138
|
-
|
|
139
|
-
| Action | Description |
|
|
140
|
-
|--------|-------------|
|
|
141
|
-
| `loadWorkspaceDir()` | Invokes `workspace:get-dir`, sets `workspaceDir` |
|
|
142
|
-
| `listFiles(dirPath)` | Invokes `workspace:list-files`, populates `entries` |
|
|
143
|
-
| `selectFile(entry)` | Invokes `workspace:read-file`, sets `selectedFile` and `fileContent` |
|
|
144
|
-
| `pickDirectory()` | Invokes `workspace:pick-dir` (native dialog), then `workspace:set-dir` |
|
|
145
|
-
| `openExternal(path)` | Invokes `workspace:open-external` to reveal file in native file manager |
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## Store Composition Rules
|
|
150
|
-
|
|
151
|
-
- Stores are independent — no cross-store imports. Components compose multiple stores.
|
|
152
|
-
- `useChatStore` and `useConversationStore` are coupled by convention: `selectConversation()` in the conversation store calls `setMessages()` on the chat store internally via the `window.electron` conversation load.
|
|
153
|
-
- `useSettingsStore` is the only store with persistence (localStorage). All others are session-only.
|