Skip to content
Closed
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
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ sidechannel/
├── bot.py # Main bot: message routing, command dispatch
├── claude_runner.py # Claude CLI subprocess with retry and error classification
├── config.py # YAML/env configuration loader
├── sidechannel_runner.py # sidechannel AI assistant (OpenAI / Grok provider)
├── sidechannel_runner.py # sidechannel AI assistant (OpenAI / Grok / Morpheus provider)
├── security.py # Auth, rate limiting, path validation, input sanitization
├── project_manager.py # Multi-project management
├── plugin_base.py # Plugin base class and PluginContext API
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Most AI coding tools require you to sit at your computer. Sidechannel lets you m
- **Delegate complex projects** - Describe what you want built, and Sidechannel breaks it into a full PRD with stories and tasks, then executes them autonomously with parallel workers
- **Never lose context** - Episodic memory with vector embeddings means Sidechannel remembers your conversations, project preferences, and past decisions across sessions
- **Trust the output** - Every autonomous task is independently verified by a separate Claude context using a fail-closed security model. Code that introduces security issues or logic errors is rejected automatically
- **Powered by Claude** - All code analysis, generation, and autonomous tasks run through Claude (via Claude CLI). Optionally add OpenAI or Grok as lightweight quick-response assistants for general questions that don't need project access
- **Powered by Claude** - All code analysis, generation, and autonomous tasks run through Claude (via Claude CLI). Optionally add OpenAI, Grok, or Morpheus as lightweight quick-response assistants for general questions that don't need project access
- **Stay secure** - Phone number allowlist, end-to-end encryption via Signal, rate limiting, path validation hardening, and no message content logging

### Key Benefits
Expand Down Expand Up @@ -301,7 +301,7 @@ The memory system gives Sidechannel persistent context across sessions. Conversa

### Sidechannel AI Assistant (Optional)

All code commands (`/ask`, `/do`, `/complex`) are powered by **Claude** via Claude CLI. Separately, you can enable a lightweight quick-response assistant backed by OpenAI (GPT-4o) or Grok for general knowledge questions that don't need project file access. This is optional — Claude handles all the real work.
All code commands (`/ask`, `/do`, `/complex`) are powered by **Claude** via Claude CLI. Separately, you can enable a lightweight quick-response assistant backed by OpenAI (GPT-4o), Grok, or Morpheus (decentralized AI compute) for general knowledge questions that don't need project file access. This is optional — Claude handles all the real work.

| Command | Description |
|---------|-------------|
Expand All @@ -321,7 +321,7 @@ sidechannel what's the best way to handle JWT refresh tokens?
→ Practical advice on token refresh patterns
```

The provider is auto-detected from your API keys. If only `OPENAI_API_KEY` is set, it uses OpenAI. If only `GROK_API_KEY` is set, it uses Grok. You can also set it explicitly in config.
The provider is auto-detected from your API keys. If only `OPENAI_API_KEY` is set, it uses OpenAI. If only `GROK_API_KEY` is set, it uses Grok. If only `MORPHEUS_API_KEY` is set, it uses Morpheus. You can also set it explicitly in config.

### Auto-Update

Expand Down Expand Up @@ -382,11 +382,11 @@ autonomous:
# Rate Limiting
# Currently hardcoded to 30 requests per 60-second window per user.

# Optional: sidechannel AI assistant (supports OpenAI and Grok)
# Optional: sidechannel AI assistant (supports OpenAI, Grok, and Morpheus)
sidechannel_assistant:
enabled: false
# provider: "openai" # or "grok" — auto-detected from API keys if omitted
# model: "gpt-4o" # Default: gpt-4o (OpenAI) or grok-3-latest (Grok)
# provider: "openai" # or "grok" or "morpheus" — auto-detected from API keys if omitted
# model: "gpt-4o" # Default: gpt-4o (OpenAI), grok-3-latest (Grok), or glm-5 (Morpheus)
# max_tokens: 1024
```

Expand All @@ -401,9 +401,10 @@ claude login
### Environment Variables (.env)

```bash
# Optional (for sidechannel AI assistant) — set one or both
# Optional (for sidechannel AI assistant) — set one or more
OPENAI_API_KEY=sk-...
GROK_API_KEY=xai-...
MORPHEUS_API_KEY=sk-...

# Optional: Override Signal API URL (takes precedence over settings.yaml)
# SIGNAL_API_URL=http://127.0.0.1:8080
Expand Down
5 changes: 4 additions & 1 deletion config/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
# Only needed if running Claude outside the CLI
# ANTHROPIC_API_KEY=

# Optional: sidechannel AI assistant — set one (or both) of these
# Optional: sidechannel AI assistant — set one (or more) of these
# OpenAI: https://platform.openai.com/api-keys
# OPENAI_API_KEY=

# Grok: https://x.ai
# GROK_API_KEY=

# Morpheus (decentralized AI compute): https://app.mor.org
# MORPHEUS_API_KEY=
9 changes: 6 additions & 3 deletions config/settings.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ autonomous:
# Currently hardcoded to 30 requests per 60-second window per user.
# Configurable rate limiting is planned for a future release.

# Optional: sidechannel AI assistant (supports OpenAI and Grok)
# Optional: sidechannel AI assistant (supports OpenAI, Grok, and Morpheus)
sidechannel_assistant:
enabled: false
# provider: "openai" # or "grok" — auto-detected from API keys if omitted
# model: "gpt-4o" # Default: gpt-4o (OpenAI) or grok-3-latest (Grok)
# provider: "openai" # or "grok" or "morpheus" — auto-detected from API keys if omitted
# model: "gpt-4o" # Default: gpt-4o (OpenAI), grok-3-latest (Grok), or glm-5 (Morpheus)
# max_tokens: 1024
# Morpheus offers 30+ models. Browse available models at https://app.mor.org
# or query the API: curl https://api.mor.org/api/v1/models -H "Authorization: Bearer $MORPHEUS_API_KEY"
# Tip: Most models have a ":web" variant (e.g. "glm-5:web") that can search the internet

# Legacy nova / grok config sections are still recognized for backward compatibility

Expand Down
35 changes: 24 additions & 11 deletions sidechannel/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def claude_path(self) -> str:
return str(home_local)
return "claude" # Hope it's in PATH

# sidechannel AI assistant configuration (supports OpenAI and Grok providers)
# sidechannel AI assistant configuration (supports OpenAI, Grok, and Morpheus providers)
@property
def sidechannel_assistant_enabled(self) -> bool:
"""Whether sidechannel AI assistant is enabled."""
Expand All @@ -136,14 +136,15 @@ def grok_enabled(self) -> bool:

@property
def sidechannel_assistant_provider(self) -> str:
"""Detect which provider to use: 'openai' or 'grok'.
"""Detect which provider to use: 'openai', 'grok', or 'morpheus'.

Priority:
1. Explicit sidechannel_assistant.provider setting
2. Auto-detect from env: only OPENAI_API_KEY -> 'openai'
3. Auto-detect from env: only GROK_API_KEY -> 'grok'
4. Both keys present -> 'grok' (backward compat)
5. Neither key -> 'grok' (will fail gracefully at call time)
2. Auto-detect from env: only MORPHEUS_API_KEY -> 'morpheus'
3. Auto-detect from env: only OPENAI_API_KEY -> 'openai'
4. Auto-detect from env: only GROK_API_KEY -> 'grok'
5. Multiple keys present -> 'grok' (backward compat)
6. No keys -> 'grok' (will fail gracefully at call time)
"""
sc_config = self.settings.get("sidechannel_assistant", {})
explicit = sc_config.get("provider")
Expand All @@ -157,17 +158,23 @@ def sidechannel_assistant_provider(self) -> str:

has_openai = bool(os.environ.get("OPENAI_API_KEY"))
has_grok = bool(os.environ.get("GROK_API_KEY"))
has_morpheus = bool(os.environ.get("MORPHEUS_API_KEY"))

if has_openai and not has_grok:
if has_morpheus and not has_openai and not has_grok:
return "morpheus"
if has_openai and not has_grok and not has_morpheus:
return "openai"
# grok for: only grok, both, or neither
# grok for: only grok, multiple keys, or neither
return "grok"

@property
def sidechannel_assistant_api_key(self) -> str:
"""Return the API key for the active provider."""
if self.sidechannel_assistant_provider == "openai":
provider = self.sidechannel_assistant_provider
if provider == "openai":
return os.environ.get("OPENAI_API_KEY", "")
if provider == "morpheus":
return os.environ.get("MORPHEUS_API_KEY", "")
return os.environ.get("GROK_API_KEY", "")

@property
Expand All @@ -182,8 +189,11 @@ def sidechannel_assistant_api_url(self) -> str:
custom_url = nova_config.get("api_url")
if custom_url:
return custom_url
if self.sidechannel_assistant_provider == "openai":
provider = self.sidechannel_assistant_provider
if provider == "openai":
return "https://api.openai.com/v1/chat/completions"
if provider == "morpheus":
return "https://api.mor.org/api/v1/chat/completions"
return "https://api.x.ai/v1/chat/completions"

@property
Expand All @@ -203,8 +213,11 @@ def sidechannel_assistant_model(self) -> str:
if model:
return model
# Provider default
if self.sidechannel_assistant_provider == "openai":
provider = self.sidechannel_assistant_provider
if provider == "openai":
return "gpt-4o"
if provider == "morpheus":
return "glm-5"
return "grok-3-latest"

@property
Expand Down
11 changes: 6 additions & 5 deletions sidechannel/sidechannel_runner.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""sidechannel AI assistant runner with configurable provider (OpenAI or Grok)."""
"""sidechannel AI assistant runner with configurable provider (OpenAI, Grok, or Morpheus)."""

import asyncio
from typing import Optional, Tuple
Expand All @@ -11,15 +11,16 @@

GROK_API_URL = "https://api.x.ai/v1/chat/completions"
OPENAI_API_URL = "https://api.openai.com/v1/chat/completions"
MORPHEUS_API_URL = "https://api.mor.org/api/v1/chat/completions"

ALLOWED_API_HOSTS = {"api.openai.com", "api.x.ai"}
ALLOWED_API_HOSTS = {"api.openai.com", "api.x.ai", "api.mor.org"}


class SidechannelRunner:
"""Manages API execution for sidechannel AI assistant.

Supports both OpenAI and Grok (X.AI) as backend providers.
Both use the identical /v1/chat/completions schema.
Supports OpenAI, Grok (X.AI), and Morpheus (mor.org) as backend providers.
All use the OpenAI-compatible /v1/chat/completions schema.
"""

def __init__(
Expand Down Expand Up @@ -75,7 +76,7 @@ async def ask_jarvis(
Tuple of (success, response)
"""
if not self.api_key:
return False, "sidechannel assistant is not configured. Set OPENAI_API_KEY or GROK_API_KEY in your .env file."
return False, "sidechannel assistant is not configured. Set OPENAI_API_KEY, GROK_API_KEY, or MORPHEUS_API_KEY in your .env file."

# Clean the message - remove sidechannel prefix variations
clean_message = message.strip()
Expand Down