diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b397f6..9aef12b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/README.md b/README.md index 646bf43..9d988a4 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 | |---------|-------------| @@ -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 @@ -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 ``` @@ -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 diff --git a/config/.env.example b/config/.env.example index 6c74c37..ec1c9f6 100644 --- a/config/.env.example +++ b/config/.env.example @@ -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= diff --git a/config/settings.yaml.example b/config/settings.yaml.example index 475da14..c3f6246 100644 --- a/config/settings.yaml.example +++ b/config/settings.yaml.example @@ -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 diff --git a/sidechannel/config.py b/sidechannel/config.py index 9762df4..25480aa 100644 --- a/sidechannel/config.py +++ b/sidechannel/config.py @@ -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.""" @@ -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") @@ -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 @@ -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 @@ -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 diff --git a/sidechannel/sidechannel_runner.py b/sidechannel/sidechannel_runner.py index 23e87ee..2f9df15 100644 --- a/sidechannel/sidechannel_runner.py +++ b/sidechannel/sidechannel_runner.py @@ -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 @@ -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__( @@ -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()