speexor 0.1.1 → 0.2.0

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.
Files changed (46) hide show
  1. package/API-REFERENCE.md +96 -1
  2. package/ARCHITECTURE.md +83 -32
  3. package/BENCHMARKS.md +52 -0
  4. package/CHANGELOG.md +35 -4
  5. package/CODE-OF-CONDUCT.md +83 -83
  6. package/CONTRIBUTING.md +98 -98
  7. package/FAQ.md +105 -105
  8. package/GLOSSARY.md +33 -0
  9. package/LICENSE.md +21 -21
  10. package/PUBLISH.md +77 -77
  11. package/README.md +219 -5
  12. package/REFACTOR-LOG.md +40 -40
  13. package/ROADMAP.md +37 -15
  14. package/SECURITY-DEFAULTS.md +118 -0
  15. package/SECURITY.md +79 -79
  16. package/SUMMARY.md +31 -8
  17. package/TESTING.md +140 -140
  18. package/dist/{agent-5D3BVWNK.js → agent-D4BRWEOZ.js} +4 -4
  19. package/dist/agent-D4BRWEOZ.js.map +1 -0
  20. package/dist/{chunk-2F66BZYJ.js → chunk-2DX54KIM.js} +2 -2
  21. package/dist/chunk-2DX54KIM.js.map +1 -0
  22. package/dist/{chunk-B7WLHC4W.js → chunk-7VZHDGRQ.js} +2 -2
  23. package/dist/chunk-7VZHDGRQ.js.map +1 -0
  24. package/dist/{chunk-SXALZEOJ.js → chunk-AOFWQZWY.js} +2 -2
  25. package/dist/chunk-AOFWQZWY.js.map +1 -0
  26. package/dist/cli/index.js +4 -4
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/core/index.js +1 -1
  29. package/dist/index.js +3 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/plugins/index.js +1 -1
  32. package/docs/SETUP.md +94 -94
  33. package/docs/TROUBLESHOOTING.md +113 -113
  34. package/docs/adr/0001-record-architecture-decisions.md +44 -0
  35. package/docs/adr/0002-plugin-architecture.md +53 -0
  36. package/docs/adr/0003-recursive-task-decomposition.md +57 -0
  37. package/docs/adr/0004-local-first-security.md +58 -0
  38. package/docs/adr/0005-data-directory-layout.md +69 -0
  39. package/examples/basic.yaml +61 -61
  40. package/package.json +103 -102
  41. package/schema/config.schema.json +119 -119
  42. package/speexor.config.yaml.example +30 -30
  43. package/dist/agent-5D3BVWNK.js.map +0 -1
  44. package/dist/chunk-2F66BZYJ.js.map +0 -1
  45. package/dist/chunk-B7WLHC4W.js.map +0 -1
  46. package/dist/chunk-SXALZEOJ.js.map +0 -1
package/docs/SETUP.md CHANGED
@@ -1,94 +1,94 @@
1
- # Speexor Setup Guide
2
-
3
- ## Prerequisites
4
-
5
- - **Node.js** >= 18.0.0
6
- - **pnpm** (recommended) or npm
7
- - **Git** >= 2.30 (for `git worktree` support)
8
- - **GitHub CLI** (`gh`) — for tracker & SCM plugins (optional for local-only mode)
9
- - **tmux** >= 3.0 — for tmux runtime (macOS/Linux, optional with process fallback)
10
- - One or more AI coding agent CLIs:
11
- - [OpenCode CLI](https://github.com/superdevids/opencode)
12
- - [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
13
- - [Aider](https://aider.chat/)
14
- - [Codex CLI](https://github.com/openai/codex)
15
-
16
- ## Installation
17
-
18
- ```bash
19
- # Via npm
20
- npm install -g speexor
21
-
22
- # Or via pnpm
23
- pnpm add -g speexor
24
-
25
- # Or run from the monorepo
26
- cd speexjs
27
- pnpm install
28
- pnpm --filter speexor build
29
- ```
30
-
31
- ## Quick Start
32
-
33
- ### 1. Initialize a project
34
-
35
- ```bash
36
- cd /path/to/your/project
37
- speexor start https://github.com/username/repo.git
38
- ```
39
-
40
- This will:
41
- - Create `speexor.config.yaml` with default configuration
42
- - Create `.speexor/` directory for worktrees and logs
43
- - Start the dashboard at `http://localhost:3000`
44
-
45
- ### 2. Spawn an agent for a task
46
-
47
- ```bash
48
- # Using a GitHub issue ID
49
- speexor agent spawn --task 42 --agent opencode
50
-
51
- # Using a custom task ID
52
- speexor agent spawn --task "feature-auth" --agent claude-code
53
- ```
54
-
55
- ### 3. Monitor progress
56
-
57
- ```bash
58
- # Open dashboard in browser
59
- open http://localhost:3000
60
-
61
- # List active sessions
62
- speexor list
63
-
64
- # View agent logs
65
- speexor logs <session-id>
66
- ```
67
-
68
- ### 4. Stop a session
69
-
70
- ```bash
71
- speexor stop <session-id>
72
- ```
73
-
74
- ## Configuration
75
-
76
- See `speexor config-help` for full schema reference, or refer to `examples/basic.yaml` in the package.
77
-
78
- ## Plugin Architecture
79
-
80
- Speexor uses a 7-slot plugin architecture:
81
-
82
- | Slot | Purpose | Default Plugin |
83
- |------|---------|---------------|
84
- | **Agent** | AI coding agent adapter | OpenCode, Claude Code, Aider, Codex |
85
- | **Runtime** | Process execution environment | tmux (Unix), Process (Windows) |
86
- | **Workspace** | Code isolation strategy | Git Worktree |
87
- | **Tracker** | Task/issue source | GitHub Issues |
88
- | **SCM** | Git/PR operations | GitHub (gh CLI) |
89
- | **Notifier** | Alert channel | Desktop notifications |
90
- | **Terminal** | Live session viewer | Web (dashboard) |
91
-
92
- ## Troubleshooting
93
-
94
- See [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for common issues.
1
+ # Speexor Setup Guide
2
+
3
+ ## Prerequisites
4
+
5
+ - **Node.js** >= 18.0.0
6
+ - **pnpm** (recommended) or npm
7
+ - **Git** >= 2.30 (for `git worktree` support)
8
+ - **GitHub CLI** (`gh`) — for tracker & SCM plugins (optional for local-only mode)
9
+ - **tmux** >= 3.0 — for tmux runtime (macOS/Linux, optional with process fallback)
10
+ - One or more AI coding agent CLIs:
11
+ - [OpenCode CLI](https://github.com/superdevids/opencode)
12
+ - [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
13
+ - [Aider](https://aider.chat/)
14
+ - [Codex CLI](https://github.com/openai/codex)
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Via npm
20
+ npm install -g speexor
21
+
22
+ # Or via pnpm
23
+ pnpm add -g speexor
24
+
25
+ # Or run from the monorepo
26
+ cd speexjs
27
+ pnpm install
28
+ pnpm --filter speexor build
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ### 1. Initialize a project
34
+
35
+ ```bash
36
+ cd /path/to/your/project
37
+ speexor start https://github.com/username/repo.git
38
+ ```
39
+
40
+ This will:
41
+ - Create `speexor.config.yaml` with default configuration
42
+ - Create `.speexor/` directory for worktrees and logs
43
+ - Start the dashboard at `http://localhost:3000`
44
+
45
+ ### 2. Spawn an agent for a task
46
+
47
+ ```bash
48
+ # Using a GitHub issue ID
49
+ speexor agent spawn --task 42 --agent opencode
50
+
51
+ # Using a custom task ID
52
+ speexor agent spawn --task "feature-auth" --agent claude-code
53
+ ```
54
+
55
+ ### 3. Monitor progress
56
+
57
+ ```bash
58
+ # Open dashboard in browser
59
+ open http://localhost:3000
60
+
61
+ # List active sessions
62
+ speexor list
63
+
64
+ # View agent logs
65
+ speexor logs <session-id>
66
+ ```
67
+
68
+ ### 4. Stop a session
69
+
70
+ ```bash
71
+ speexor stop <session-id>
72
+ ```
73
+
74
+ ## Configuration
75
+
76
+ See `speexor config-help` for full schema reference, or refer to `examples/basic.yaml` in the package.
77
+
78
+ ## Plugin Architecture
79
+
80
+ Speexor uses a 7-slot plugin architecture:
81
+
82
+ | Slot | Purpose | Default Plugin |
83
+ |------|---------|---------------|
84
+ | **Agent** | AI coding agent adapter | OpenCode, Claude Code, Aider, Codex |
85
+ | **Runtime** | Process execution environment | tmux (Unix), Process (Windows) |
86
+ | **Workspace** | Code isolation strategy | Git Worktree |
87
+ | **Tracker** | Task/issue source | GitHub Issues |
88
+ | **SCM** | Git/PR operations | GitHub (gh CLI) |
89
+ | **Notifier** | Alert channel | Desktop notifications |
90
+ | **Terminal** | Live session viewer | Web (dashboard) |
91
+
92
+ ## Troubleshooting
93
+
94
+ See [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for common issues.
@@ -1,113 +1,113 @@
1
- # Speexor Troubleshooting Guide
2
-
3
- ## Common Issues
4
-
5
- ### "speexor.config.yaml not found"
6
-
7
- **Cause:** You ran `speexor list` or `speexor agent spawn` without initializing a project first.
8
-
9
- **Fix:** Run `speexor start <repo-url>` to create the config file, or manually create `speexor.config.yaml` in your project root.
10
-
11
- ### "Not a git repository"
12
-
13
- **Cause:** You're running `speexor` outside a git repository.
14
-
15
- **Fix:** Navigate to a git repository or run `git init` first.
16
-
17
- ### "tmux not available"
18
-
19
- **Cause:** tmux is not installed on your system.
20
-
21
- **Fix (macOS):**
22
- ```bash
23
- brew install tmux
24
- ```
25
-
26
- **Fix (Linux):**
27
- ```bash
28
- sudo apt install tmux # Debian/Ubuntu
29
- sudo dnf install tmux # Fedora
30
- ```
31
-
32
- **Fix (Windows):** Speexor will automatically fall back to the Process runtime on Windows.
33
-
34
- ### "GitHub CLI (gh) not found"
35
-
36
- **Cause:** The `gh` CLI is not installed but required for GitHub tracker/SCM plugins.
37
-
38
- **Fix:**
39
- ```bash
40
- # macOS
41
- brew install gh
42
-
43
- # Linux (Debian/Ubuntu)
44
- sudo apt install gh
45
-
46
- # Windows (winget)
47
- winget install GitHub.cli
48
-
49
- # Or manual: https://cli.github.com/
50
- ```
51
-
52
- ### Agent spawn fails
53
-
54
- **Cause:** The specified agent CLI is not installed or not in PATH.
55
-
56
- **Fix:** Ensure the agent CLI is installed and accessible:
57
- ```bash
58
- # Verify
59
- opencode --version
60
- claude --version
61
- aider --version
62
- codex --version
63
- ```
64
-
65
- ### "Worktree already exists"
66
-
67
- **Cause:** A worktree for the same branch already exists, possibly from a previous interrupted session.
68
-
69
- **Fix:**
70
- ```bash
71
- # List worktrees
72
- git worktree list
73
-
74
- # Remove stale worktree
75
- speexor stop <session-id>
76
- # Or manually:
77
- git worktree remove --force .speexor/worktrees/<task-id>
78
- ```
79
-
80
- ### Dashboard not showing
81
-
82
- **Cause:** Port 3000 might be in use, or the dashboard was not started.
83
-
84
- **Fix:**
85
- ```bash
86
- # Specify a different port
87
- speexor start --port 4000
88
-
89
- # Or start dashboard only (if already initialized)
90
- speexor start
91
- ```
92
-
93
- ## Windows-Specific Issues
94
-
95
- ### ConPTY Fallback
96
-
97
- On Windows, tmux is not available. Speexor automatically uses the Process runtime instead. This works for most cases but lacks live terminal streaming.
98
-
99
- ### Shell Path
100
-
101
- If you use PowerShell, the default shell path detection should work. To customize:
102
-
103
- ```yaml
104
- # In speexor.config.yaml
105
- plugins:
106
- runtime: process
107
- ```
108
-
109
- ## Getting Help
110
-
111
- - Open an issue: https://github.com/superdevids/speexjs/issues
112
- - Check the PRD: [PRD01.md](./PRD01.md)
113
- - Ask in the SpeexJS community
1
+ # Speexor Troubleshooting Guide
2
+
3
+ ## Common Issues
4
+
5
+ ### "speexor.config.yaml not found"
6
+
7
+ **Cause:** You ran `speexor list` or `speexor agent spawn` without initializing a project first.
8
+
9
+ **Fix:** Run `speexor start <repo-url>` to create the config file, or manually create `speexor.config.yaml` in your project root.
10
+
11
+ ### "Not a git repository"
12
+
13
+ **Cause:** You're running `speexor` outside a git repository.
14
+
15
+ **Fix:** Navigate to a git repository or run `git init` first.
16
+
17
+ ### "tmux not available"
18
+
19
+ **Cause:** tmux is not installed on your system.
20
+
21
+ **Fix (macOS):**
22
+ ```bash
23
+ brew install tmux
24
+ ```
25
+
26
+ **Fix (Linux):**
27
+ ```bash
28
+ sudo apt install tmux # Debian/Ubuntu
29
+ sudo dnf install tmux # Fedora
30
+ ```
31
+
32
+ **Fix (Windows):** Speexor will automatically fall back to the Process runtime on Windows.
33
+
34
+ ### "GitHub CLI (gh) not found"
35
+
36
+ **Cause:** The `gh` CLI is not installed but required for GitHub tracker/SCM plugins.
37
+
38
+ **Fix:**
39
+ ```bash
40
+ # macOS
41
+ brew install gh
42
+
43
+ # Linux (Debian/Ubuntu)
44
+ sudo apt install gh
45
+
46
+ # Windows (winget)
47
+ winget install GitHub.cli
48
+
49
+ # Or manual: https://cli.github.com/
50
+ ```
51
+
52
+ ### Agent spawn fails
53
+
54
+ **Cause:** The specified agent CLI is not installed or not in PATH.
55
+
56
+ **Fix:** Ensure the agent CLI is installed and accessible:
57
+ ```bash
58
+ # Verify
59
+ opencode --version
60
+ claude --version
61
+ aider --version
62
+ codex --version
63
+ ```
64
+
65
+ ### "Worktree already exists"
66
+
67
+ **Cause:** A worktree for the same branch already exists, possibly from a previous interrupted session.
68
+
69
+ **Fix:**
70
+ ```bash
71
+ # List worktrees
72
+ git worktree list
73
+
74
+ # Remove stale worktree
75
+ speexor stop <session-id>
76
+ # Or manually:
77
+ git worktree remove --force .speexor/worktrees/<task-id>
78
+ ```
79
+
80
+ ### Dashboard not showing
81
+
82
+ **Cause:** Port 3000 might be in use, or the dashboard was not started.
83
+
84
+ **Fix:**
85
+ ```bash
86
+ # Specify a different port
87
+ speexor start --port 4000
88
+
89
+ # Or start dashboard only (if already initialized)
90
+ speexor start
91
+ ```
92
+
93
+ ## Windows-Specific Issues
94
+
95
+ ### ConPTY Fallback
96
+
97
+ On Windows, tmux is not available. Speexor automatically uses the Process runtime instead. This works for most cases but lacks live terminal streaming.
98
+
99
+ ### Shell Path
100
+
101
+ If you use PowerShell, the default shell path detection should work. To customize:
102
+
103
+ ```yaml
104
+ # In speexor.config.yaml
105
+ plugins:
106
+ runtime: process
107
+ ```
108
+
109
+ ## Getting Help
110
+
111
+ - Open an issue: https://github.com/superdevids/speexjs/issues
112
+ - Check the PRD: [PRD01.md](./PRD01.md)
113
+ - Ask in the SpeexJS community
@@ -0,0 +1,44 @@
1
+ # ADR-0001: Use Architecture Decision Records
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Context
8
+
9
+ Speexor is a plugin-based, agent-agnostic orchestrator for multi-AI coding agents. As the project grows, contributors and maintainers need a clear historical record of why architectural choices were made. Without this, future developers may reverse decisions without understanding the original rationale, leading to inconsistent architecture.
10
+
11
+ ## Decision
12
+
13
+ We will use Architecture Decision Records (ADRs) in `docs/adr/` to document all significant architectural decisions. Each ADR follows this template:
14
+
15
+ ```markdown
16
+ # ADR-NNNN: Title
17
+
18
+ ## Status
19
+
20
+ [Proposed | Accepted | Deprecated | Superseded by ADR-NNNN]
21
+
22
+ ## Context
23
+
24
+ The background, constraints, and forces that led to this decision.
25
+
26
+ ## Decision
27
+
28
+ The architectural choice we made and how it addresses the context.
29
+
30
+ ## Consequences
31
+
32
+ The trade-offs, benefits, and costs of this decision.
33
+ ```
34
+
35
+ - ADRs are numbered sequentially (0001, 0002, ...).
36
+ - ADRs are written in the present tense as of the decision date.
37
+ - ADRs are never deleted; deprecated ADRs link to their replacement.
38
+ - ADRs are committed alongside the code changes they describe.
39
+
40
+ ## Consequences
41
+
42
+ - **Positive:** Clear rationale trail for future contributors; easier onboarding; architectural consistency enforced by explicit record-keeping.
43
+ - **Negative:** Overhead of writing and maintaining ADRs; risk of falling behind if decisions are not documented promptly.
44
+ - **Neutral:** ADRs become a permanent part of the codebase in `docs/adr/`.
@@ -0,0 +1,53 @@
1
+ # ADR-0002: 7-Slot Plugin Architecture with EventBus
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Context
8
+
9
+ Speexor must support diverse capabilities — agent adapters, runtime backends, workspace management, issue tracking, SCM operations, notifications, and terminal I/O — without coupling these concerns in the core lifecycle. The architecture must allow:
10
+
11
+ 1. New plugins to be added without modifying core code.
12
+ 2. Multiple implementations per slot (e.g., tmux and Process for runtime).
13
+ 3. Loose communication between plugins and the dashboard.
14
+ 4. Graceful degradation when a plugin dependency (e.g., `tmux`, `gh` CLI) is unavailable.
15
+
16
+ ## Decision
17
+
18
+ ### Seven Plugin Slots
19
+
20
+ We define exactly seven plugin slots, each with a dedicated TypeScript interface:
21
+
22
+ | Slot | Interface | Purpose |
23
+ |------------|---------------------|---------------------------------------|
24
+ | agent | `AgentPlugin` | Spawn, communicate with, kill agents |
25
+ | runtime | `RuntimePlugin` | Create/destroy terminal sessions |
26
+ | workspace | `WorkspacePlugin` | Manage isolated git worktrees |
27
+ | tracker | `TrackerPlugin` | Fetch issues, subscribe to events |
28
+ | scm | `SCMPlugin` | Branch, commit, PR, CI operations |
29
+ | notifier | `NotifierPlugin` | Desktop notifications |
30
+ | terminal | `TerminalPlugin` | Interactive terminal attach/detach |
31
+
32
+ ### EventBus over Direct Calls
33
+
34
+ All inter-module communication flows through an EventBus (EventEmitter3 wrapper) rather than direct method calls. This means:
35
+
36
+ - The dashboard subscribes to lifecycle events without lifecycle knowing about the dashboard.
37
+ - Plugins emit events (e.g., `session:created`, `worktree:created`) without importing other modules.
38
+ - New observers (e.g., logging, metrics) can be added without modifying existing code.
39
+
40
+ ### getFirstPlugin() Resolution
41
+
42
+ When the lifecycle needs a plugin for a slot, it calls `getFirstPlugin<T>(slot)` which returns the first registered implementation. This allows:
43
+
44
+ - Multiple implementations per slot (e.g., both TmuxRuntime and ProcessRuntime).
45
+ - Implicit priority ordering by registration order.
46
+ - Graceful fallback: if the primary plugin fails initialization, the next in the list serves.
47
+
48
+ ## Consequences
49
+
50
+ - **Positive:** Loose coupling; plugins are independently testable; new capabilities slot in without core changes.
51
+ - **Positive:** The `getFirstPlugin()` pattern enables natural fallback (ProcessRuntime when tmux is absent).
52
+ - **Negative:** Event-based flow is harder to trace than direct calls during debugging.
53
+ - **Negative:** Seven slots are a fixed set — adding a new slot requires a core type change and a new interface definition.
@@ -0,0 +1,57 @@
1
+ # ADR-0003: DAG-Based Recursive Task Decomposition with LLM Planner
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Context
8
+
9
+ Speexor must decompose complex tasks (e.g., "implement feature X across the full stack") into smaller, parallel-executable units that can be distributed across multiple agents. Two core design questions arise:
10
+
11
+ 1. **Representation:** Should the task structure be a flat list, a tree, or a directed acyclic graph (DAG)?
12
+ 2. **Planner:** Should the decomposition algorithm be rule-based (deterministic) or LLM-driven (probabilistic)?
13
+
14
+ The representation must handle dependency ordering (task B depends on task A, task C depends on both) and allow parallel execution of independent sub-tasks. The planner must adapt to arbitrary repo structures and technologies without hardcoded rules.
15
+
16
+ ## Decision
17
+
18
+ ### DAG-Based Task Graph
19
+
20
+ We represent decomposed tasks as a **directed acyclic graph (DAG)** where:
21
+
22
+ - Each **Task Node** represents one atomic unit of work.
23
+ - Edges represent **depends-on** relationships (a node cannot execute until all predecessors complete).
24
+ - Nodes with no edges between them are eligible for parallel execution.
25
+ - The graph supports dynamic refinement: a node in progress can be further decomposed into sub-DAGs at runtime.
26
+
27
+ This choice over a flat list (which cannot express dependencies) or a tree (which cannot express cross-branch dependencies like "both frontend and backend depend on the shared schema change").
28
+
29
+ ### LLM-Based Planner over Algorithmic Decomposition
30
+
31
+ We use an LLM-based planner (configurable per project, defaulting to `deepseek-reasoner`) to decompose tasks rather than a rule-based algorithm. Rationale:
32
+
33
+ - **Arbitrary tech stacks:** The planner reads the repo structure and task description, then generates a decomposition customized to the actual codebase — no hardcoded "microservice decomposition" rules needed.
34
+ - **Context-aware granularity:** The LLM decides how fine-grained each sub-task should be based on complexity, rather than a fixed heuristic.
35
+ - **Adaptive refinement:** If the initial decomposition is too coarse, the planner can further decompose a node mid-execution using the same LLM.
36
+ - **Human-readable plans:** The LLM generates natural language descriptions for each node, which feed into the approval UI and decision log.
37
+
38
+ ### Configuration
39
+
40
+ ```yaml
41
+ decomposition:
42
+ maxTaskGraphDepth: 3 # Max depth of the Task Graph (node depth)
43
+ maxAgentSpawnDepth: 3 # Max levels of subagent spawning
44
+ maxNodesPerGraph: 50 # Safety limit on graph size
45
+ plannerProvider: opencode # Which agent backend to use for planning
46
+ plannerModel: deepseek-reasoner # Model for the planner LLM call
47
+ ```
48
+
49
+ The two depth limits (`maxTaskGraphDepth` and `maxAgentSpawnDepth`) are tracked separately — a deep task graph does not force deep agent spawning if the planner assigns shallow agents to deep nodes.
50
+
51
+ ## Consequences
52
+
53
+ - **Positive:** DAG enables maximum parallelism — independent sub-tasks execute concurrently.
54
+ - **Positive:** LLM planner adapts to any codebase without rule maintenance.
55
+ - **Negative:** LLM planner calls add latency and cost to the decomposition phase.
56
+ - **Negative:** DAG complexity requires a scheduler with dependency resolution (no simple FIFO queue).
57
+ - **Neutral:** The `maxTaskGraphDepth`/`maxAgentSpawnDepth` split prevents confusion between graph depth and agent hierarchy depth (per FR-89).
@@ -0,0 +1,58 @@
1
+ # ADR-0004: Local-First Security with Two-Layer Defense
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Context
8
+
9
+ Speexor manages AI agents that write code, execute commands, and interact with git providers. This introduces two distinct security surfaces:
10
+
11
+ 1. **Third-party extensions** (installed via the future Marketplace) that can access the file system, shell, and network.
12
+ 2. **Runtime agent actions** — file edits, git operations, PR creation, CI interactions — some of which are irreversible (e.g., force-push to main).
13
+
14
+ The architecture must ensure that a malicious or buggy extension cannot compromise the host system, and that high-risk agent actions require explicit human approval.
15
+
16
+ ## Decision
17
+
18
+ ### Local-First Architecture
19
+
20
+ All data, credentials, and execution remain on the user's machine. There is no cloud relay, no telemetry by default, and no remote control plane. This means:
21
+
22
+ - Secrets are stored in the OS keychain (via `conf` with `encryptionKey`), never in plaintext config files.
23
+ - The dashboard runs on localhost only (`127.0.0.1`) by default.
24
+ - The decision log and session state never leave the `~/.speexor/` directory.
25
+
26
+ ### Two-Layer Defense: Extension Permissions + Action Risk Tiers
27
+
28
+ These are two independent, complementary layers documented in `SECURITY-DEFAULTS.md`:
29
+
30
+ **Layer 1 — Extension Permissions (install-time capability gating):**
31
+
32
+ Defined by `extensions.permissionsMode` in config (`strict` | `permissive`). Each extension declares capabilities (`shell`, `network`, `fileSystem`, `clipboard`) at install time. In `strict` mode, the user must explicitly approve each capability; in `permissive` mode, all declared capabilities are auto-granted. This layer gates what an extension *can ever do* — set once, at install.
33
+
34
+ Extensions with `shell: none` and `network: none` run in `isolated-vm` (true V8 isolate, no access to Node built-ins). Extensions requiring `fileSystem`/`shell`/`network` run as separate OS processes with minimum privileges and a permission-enforcing proxy layer intercepting `fs`/`net`/`child_process` calls.
35
+
36
+ `worker_threads` is explicitly **not** used as a security boundary — it is a performance-only primitive for orchestrator-internal CPU-bound work (per FR-85).
37
+
38
+ **Layer 2 — Action Risk Tiers (runtime action gating):**
39
+
40
+ Defined by `riskPolicy` in config. Every action an agent or extension takes is classified into a risk tier. Actions in `requireApproval` tiers (e.g., `irreversible-high-stakes`) block until the user approves. Actions in `autoApprove` tiers (e.g., `reversible-low`) execute autonomously. Unknown actions default to `high-stakes` (safe default).
41
+
42
+ This layer gates what *any* action (from any already-permitted extension or core agent) *does right now* — evaluated every time, separate from the install-time capability grant.
43
+
44
+ ### Sandboxing: isolated-vm over worker_threads
45
+
46
+ | Mechanism | Security Boundary | Use Case |
47
+ |-----------------|-------------------|----------------------------------|
48
+ | `isolated-vm` | True V8 isolate | Extensions with no shell/network |
49
+ | OS process | OS-level | Extensions with shell/network |
50
+ | `worker_threads`| None (same proc) | Orchestrator-internal CPU work |
51
+
52
+ ## Consequences
53
+
54
+ - **Positive:** Two layers provide defense-in-depth — an extension with "write files" capability still cannot force-push to main without risk-tier approval.
55
+ - **Positive:** Local-first ensures no external dependency for security; no cloud outage can leak secrets.
56
+ - **Negative:** `isolated-vm` is a native dependency that complicates cross-platform builds and installs.
57
+ - **Negative:** Two-layer model requires clear documentation (covered by `SECURITY-DEFAULTS.md`).
58
+ - **Neutral:** `worker_threads` reclassification from v4's proposal removes a false sense of security.