From 3ac8b50f5ecfd5df4d5e359591e3e15379a227a5 Mon Sep 17 00:00:00 2001 From: dagelf Date: Mon, 16 Feb 2026 14:51:01 +0200 Subject: [PATCH 1/2] Used qodercli to add qodercli support --- README.md | 4 +- lib/setup-wizard.sh | 238 ++++++++++++++++++++++++++++++----------- src/lib/config.ts | 14 ++- src/lib/invoke.ts | 23 +++- src/lib/types.ts | 13 ++- src/queue-processor.ts | 3 +- 6 files changed, 222 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 88de727..a5dc8b3 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ - ✅ **Multi-team collaboration** - Agents hand off work to teammates via chain execution and fan-out - ✅ **Multi-channel** - Discord, WhatsApp, and Telegram - ✅ **Team Observation** - You can observe agent teams conversations via `tinyclaw team visualize` -- ✅ **Multiple AI providers** - Anthropic Claude and OpenAI Codex using existing subscriptions without breaking ToS +- ✅ **Multiple AI providers** - Anthropic Claude, OpenAI Codex, and Qoder using existing subscriptions without breaking ToS - ✅ **Parallel processing** - Agents process messages concurrently - ✅ **Live TUI dashboard** - Real-time team visualizer for monitoring agent chains - ✅ **Persistent sessions** - Conversation context maintained across restarts @@ -80,7 +80,7 @@ The setup wizard will guide you through: 2. **Bot tokens** - Enter tokens for enabled channels 3. **Workspace setup** - Name your workspace directory 4. **Default agent** - Configure your main AI assistant -5. **AI provider** - Select Anthropic (Claude) or OpenAI +5. **AI provider** - Select Anthropic (Claude), OpenAI, or Qoder 6. **Model selection** - Choose model (e.g., Sonnet, Opus, GPT-5.3) 7. **Heartbeat interval** - Set proactive check-in frequency diff --git a/lib/setup-wizard.sh b/lib/setup-wizard.sh index c66abb7..0f899e5 100755 --- a/lib/setup-wizard.sh +++ b/lib/setup-wizard.sh @@ -79,60 +79,112 @@ for ch in "${ENABLED_CHANNELS[@]}"; do done # Provider selection -echo "Which AI provider?" -echo "" -echo " 1) Anthropic (Claude) (recommended)" -echo " 2) OpenAI (Codex/GPT)" -echo "" -read -rp "Choose [1-2]: " PROVIDER_CHOICE - -case "$PROVIDER_CHOICE" in - 1) PROVIDER="anthropic" ;; - 2) PROVIDER="openai" ;; - *) - echo -e "${RED}Invalid choice${NC}" - exit 1 - ;; -esac -echo -e "${GREEN}✓ Provider: $PROVIDER${NC}" -echo "" - -# Model selection based on provider -if [ "$PROVIDER" = "anthropic" ]; then - echo "Which Claude model?" +PROVIDER="" +while [ -z "$PROVIDER" ]; do + echo "Which AI provider?" echo "" - echo " 1) Sonnet (fast, recommended)" - echo " 2) Opus (smartest)" + echo " 1) Anthropic (Claude) (recommended)" + echo " 2) OpenAI (Codex/GPT)" + echo " 3) Qoder" + echo " s) Skip (will use defaults)" echo "" - read -rp "Choose [1-2]: " MODEL_CHOICE - - case "$MODEL_CHOICE" in - 1) MODEL="sonnet" ;; - 2) MODEL="opus" ;; + read -rp "Choose [1-3, s]: " PROVIDER_CHOICE + + case "$PROVIDER_CHOICE" in + 1) PROVIDER="anthropic" ;; + 2) PROVIDER="openai" ;; + 3) PROVIDER="qoder" ;; + [sS]) + echo -e "${YELLOW}Skipping provider selection (will use defaults)${NC}" + PROVIDER="anthropic" + break + ;; *) - echo -e "${RED}Invalid choice${NC}" - exit 1 + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" ;; esac +done + +echo -e "${GREEN}✓ Provider: $PROVIDER${NC}" +echo "" + +# Model selection based on provider +MODEL="" +if [ "$PROVIDER" = "anthropic" ]; then + while [ -z "$MODEL" ]; do + echo "Which Claude model?" + echo "" + echo " 1) Sonnet (fast, recommended)" + echo " 2) Opus (smartest)" + echo " s) Skip (use default)" + echo "" + read -rp "Choose [1-2, s]: " MODEL_CHOICE + + case "$MODEL_CHOICE" in + 1) MODEL="sonnet" ;; + 2) MODEL="opus" ;; + [sS]) + echo -e "${YELLOW}Using default model: sonnet${NC}" + MODEL="sonnet" + break + ;; + *) + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done echo -e "${GREEN}✓ Model: $MODEL${NC}" echo "" -else - # OpenAI models - echo "Which OpenAI model?" - echo "" - echo " 1) GPT-5.3 Codex (recommended)" - echo " 2) GPT-5.2" +elif [ "$PROVIDER" = "qoder" ]; then + while [ -z "$MODEL" ]; do + echo "Which Qoder model?" + echo "" + echo " 1) Qoder (default)" + echo " s) Skip (use default)" + echo "" + read -rp "Choose [1, s]: " MODEL_CHOICE + + case "$MODEL_CHOICE" in + 1) MODEL="qoder" ;; + [sS]) + echo -e "${YELLOW}Using default model: qoder${NC}" + MODEL="qoder" + break + ;; + *) + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + echo -e "${GREEN}✓ Model: $MODEL${NC}" echo "" - read -rp "Choose [1-2]: " MODEL_CHOICE - - case "$MODEL_CHOICE" in - 1) MODEL="gpt-5.3-codex" ;; - 2) MODEL="gpt-5.2" ;; - *) - echo -e "${RED}Invalid choice${NC}" - exit 1 - ;; - esac +elif [ "$PROVIDER" = "openai" ]; then + while [ -z "$MODEL" ]; do + echo "Which OpenAI model?" + echo "" + echo " 1) GPT-5.3 Codex (recommended)" + echo " 2) GPT-5.2" + echo " s) Skip (use default)" + echo "" + read -rp "Choose [1-2, s]: " MODEL_CHOICE + + case "$MODEL_CHOICE" in + 1) MODEL="gpt-5.3-codex" ;; + 2) MODEL="gpt-5.2" ;; + [sS]) + echo -e "${YELLOW}Using default model: gpt-5.3-codex${NC}" + MODEL="gpt-5.3-codex" + break + ;; + *) + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done echo -e "${GREEN}✓ Model: $MODEL${NC}" echo "" fi @@ -216,27 +268,81 @@ if [[ "$SETUP_AGENTS" =~ ^[yY] ]]; then read -rp " Display name: " NEW_AGENT_NAME [ -z "$NEW_AGENT_NAME" ] && NEW_AGENT_NAME="$NEW_AGENT_ID" - echo " Provider: 1) Anthropic 2) OpenAI" - read -rp " Choose [1-2, default: 1]: " NEW_PROVIDER_CHOICE - case "$NEW_PROVIDER_CHOICE" in - 2) NEW_PROVIDER="openai" ;; - *) NEW_PROVIDER="anthropic" ;; - esac + NEW_PROVIDER="" + while [ -z "$NEW_PROVIDER" ]; do + echo " Provider: 1) Anthropic 2) OpenAI 3) Qoder" + echo " s) Skip (use default: anthropic)" + read -rp " Choose [1-3, s, default: 1]: " NEW_PROVIDER_CHOICE + case "$NEW_PROVIDER_CHOICE" in + 2) NEW_PROVIDER="openai" ;; + 3) NEW_PROVIDER="qoder" ;; + [sS]) + echo -e " ${YELLOW}Using default provider: anthropic${NC}" + NEW_PROVIDER="anthropic" + break + ;; + ""|1) NEW_PROVIDER="anthropic" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + NEW_MODEL="" if [ "$NEW_PROVIDER" = "anthropic" ]; then - echo " Model: 1) Sonnet 2) Opus" - read -rp " Choose [1-2, default: 1]: " NEW_MODEL_CHOICE - case "$NEW_MODEL_CHOICE" in - 2) NEW_MODEL="opus" ;; - *) NEW_MODEL="sonnet" ;; - esac - else - echo " Model: 1) GPT-5.3 Codex 2) GPT-5.2" - read -rp " Choose [1-2, default: 1]: " NEW_MODEL_CHOICE - case "$NEW_MODEL_CHOICE" in - 2) NEW_MODEL="gpt-5.2" ;; - *) NEW_MODEL="gpt-5.3-codex" ;; - esac + while [ -z "$NEW_MODEL" ]; do + echo " Model: 1) Sonnet 2) Opus s) Skip (use default: sonnet)" + read -rp " Choose [1-2, s, default: 1]: " NEW_MODEL_CHOICE + case "$NEW_MODEL_CHOICE" in + 2) NEW_MODEL="opus" ;; + [sS]) + echo -e " ${YELLOW}Using default model: sonnet${NC}" + NEW_MODEL="sonnet" + break + ;; + ""|1) NEW_MODEL="sonnet" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + elif [ "$NEW_PROVIDER" = "qoder" ]; then + while [ -z "$NEW_MODEL" ]; do + echo " Model: 1) Qoder s) Skip (use default: qoder)" + read -rp " Choose [1, s, default: 1]: " NEW_MODEL_CHOICE + case "$NEW_MODEL_CHOICE" in + [sS]) + echo -e " ${YELLOW}Using default model: qoder${NC}" + NEW_MODEL="qoder" + break + ;; + ""|1) NEW_MODEL="qoder" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + elif [ "$NEW_PROVIDER" = "openai" ]; then + while [ -z "$NEW_MODEL" ]; do + echo " Model: 1) GPT-5.3 Codex 2) GPT-5.2 s) Skip (use default: gpt-5.3-codex)" + read -rp " Choose [1-2, s, default: 1]: " NEW_MODEL_CHOICE + case "$NEW_MODEL_CHOICE" in + 2) NEW_MODEL="gpt-5.2" ;; + [sS]) + echo -e " ${YELLOW}Using default model: gpt-5.3-codex${NC}" + NEW_MODEL="gpt-5.3-codex" + break + ;; + ""|1) NEW_MODEL="gpt-5.3-codex" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done fi NEW_AGENT_DIR="$WORKSPACE_PATH/$NEW_AGENT_ID" @@ -270,6 +376,8 @@ TELEGRAM_TOKEN="${TOKENS[telegram]:-}" # Use jq to build valid JSON to avoid escaping issues with agent prompts if [ "$PROVIDER" = "anthropic" ]; then MODELS_SECTION='"models": { "provider": "anthropic", "anthropic": { "model": "'"${MODEL}"'" } }' +elif [ "$PROVIDER" = "qoder" ]; then + MODELS_SECTION='"models": { "provider": "qoder", "qoder": { "model": "'"${MODEL}"'" } }' else MODELS_SECTION='"models": { "provider": "openai", "openai": { "model": "'"${MODEL}"'" } }' fi diff --git a/src/lib/config.ts b/src/lib/config.ts index de370c0..322a603 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; import { jsonrepair } from 'jsonrepair'; -import { Settings, AgentConfig, TeamConfig, CLAUDE_MODEL_IDS, CODEX_MODEL_IDS } from './types'; +import { Settings, AgentConfig, TeamConfig, CLAUDE_MODEL_IDS, CODEX_MODEL_IDS, QODER_MODEL_IDS } from './types'; export const SCRIPT_DIR = path.resolve(__dirname, '../..'); const _localTinyclaw = path.join(SCRIPT_DIR, '.tinyclaw'); @@ -52,6 +52,9 @@ export function getSettings(): Settings { } else if (settings?.models?.anthropic) { if (!settings.models) settings.models = {}; settings.models.provider = 'anthropic'; + } else if (settings?.models?.qoder) { + if (!settings.models) settings.models = {}; + settings.models.provider = 'qoder'; } } @@ -70,6 +73,8 @@ export function getDefaultAgentFromModels(settings: Settings): AgentConfig { let model = ''; if (provider === 'openai') { model = settings?.models?.openai?.model || 'gpt-5.3-codex'; + } else if (provider === 'qoder') { + model = settings?.models?.qoder?.model || 'qoder'; } else { model = settings?.models?.anthropic?.model || 'sonnet'; } @@ -118,3 +123,10 @@ export function resolveClaudeModel(model: string): string { export function resolveCodexModel(model: string): string { return CODEX_MODEL_IDS[model] || model || ''; } + +/** + * Resolve the model ID for QoderCLI. + */ +export function resolveQoderModel(model: string): string { + return QODER_MODEL_IDS[model] || model || ''; +} diff --git a/src/lib/invoke.ts b/src/lib/invoke.ts index 471eaf2..10449ee 100644 --- a/src/lib/invoke.ts +++ b/src/lib/invoke.ts @@ -2,7 +2,7 @@ import { spawn } from 'child_process'; import fs from 'fs'; import path from 'path'; import { AgentConfig, TeamConfig } from './types'; -import { SCRIPT_DIR, resolveClaudeModel, resolveCodexModel } from './config'; +import { SCRIPT_DIR, resolveClaudeModel, resolveCodexModel, resolveQoderModel } from './config'; import { log } from './logging'; import { ensureAgentDirectory, updateAgentTeammates } from './agent-setup'; @@ -112,6 +112,27 @@ export async function invokeAgent( } return response || 'Sorry, I could not generate a response from Codex.'; + } else if (provider === 'qoder') { + // QoderCLI provider + log('INFO', `Using QoderCLI provider (agent: ${agentId})`); + + const continueConversation = !shouldReset; + + if (shouldReset) { + log('INFO', `🔄 Resetting conversation for agent: ${agentId}`); + } + + const modelId = resolveQoderModel(agent.model); + const qoderArgs = ['-w', workingDir]; + if (modelId && modelId !== 'qoder') { + qoderArgs.push('--model', modelId); + } + if (continueConversation) { + qoderArgs.push('-c'); + } + qoderArgs.push('-p', message); + + return await runCommand('qodercli', qoderArgs, workingDir); } else { // Default to Claude (Anthropic) log('INFO', `Using Claude provider (agent: ${agentId})`); diff --git a/src/lib/types.ts b/src/lib/types.ts index f0b2e40..6a07856 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,7 +1,7 @@ export interface AgentConfig { name: string; - provider: string; // 'anthropic' or 'openai' - model: string; // e.g. 'sonnet', 'opus', 'gpt-5.3-codex' + provider: string; // 'anthropic', 'openai', or 'qoder' + model: string; // e.g. 'sonnet', 'opus', 'gpt-5.3-codex', 'qoder' working_directory: string; } @@ -28,13 +28,16 @@ export interface Settings { whatsapp?: {}; }; models?: { - provider?: string; // 'anthropic' or 'openai' + provider?: string; // 'anthropic', 'openai', or 'qoder' anthropic?: { model?: string; }; openai?: { model?: string; }; + qoder?: { + model?: string; + }; }; agents?: Record; teams?: Record; @@ -103,3 +106,7 @@ export const CODEX_MODEL_IDS: Record = { 'gpt-5.2': 'gpt-5.2', 'gpt-5.3-codex': 'gpt-5.3-codex', }; + +export const QODER_MODEL_IDS: Record = { + 'qoder': 'qoder', +}; diff --git a/src/queue-processor.ts b/src/queue-processor.ts index e89b989..9ab129e 100644 --- a/src/queue-processor.ts +++ b/src/queue-processor.ts @@ -347,7 +347,8 @@ async function processMessage(messageFile: string): Promise { response = await invokeAgent(agent, agentId, message, workspacePath, shouldReset, agents, teams); } catch (error) { const provider = agent.provider || 'anthropic'; - log('ERROR', `${provider === 'openai' ? 'Codex' : 'Claude'} error (agent: ${agentId}): ${(error as Error).message}`); + const providerName = provider === 'openai' ? 'Codex' : provider === 'qoder' ? 'Qoder' : 'Claude'; + log('ERROR', `${providerName} error (agent: ${agentId}): ${(error as Error).message}`); response = "Sorry, I encountered an error processing your request. Please check the queue logs."; } From fc744c7b665b392bd44bc3172138885a07f247c8 Mon Sep 17 00:00:00 2001 From: dagelf Date: Mon, 16 Feb 2026 20:22:19 +0200 Subject: [PATCH 2/2] added qodercli models --- README.md | 4 +- lib/setup-wizard.sh | 264 +++++++++++++++++++++++++++++++---------- src/lib/config.ts | 14 ++- src/lib/invoke.ts | 23 +++- src/lib/types.ts | 21 +++- src/queue-processor.ts | 3 +- 6 files changed, 256 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 88de727..a5dc8b3 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ - ✅ **Multi-team collaboration** - Agents hand off work to teammates via chain execution and fan-out - ✅ **Multi-channel** - Discord, WhatsApp, and Telegram - ✅ **Team Observation** - You can observe agent teams conversations via `tinyclaw team visualize` -- ✅ **Multiple AI providers** - Anthropic Claude and OpenAI Codex using existing subscriptions without breaking ToS +- ✅ **Multiple AI providers** - Anthropic Claude, OpenAI Codex, and Qoder using existing subscriptions without breaking ToS - ✅ **Parallel processing** - Agents process messages concurrently - ✅ **Live TUI dashboard** - Real-time team visualizer for monitoring agent chains - ✅ **Persistent sessions** - Conversation context maintained across restarts @@ -80,7 +80,7 @@ The setup wizard will guide you through: 2. **Bot tokens** - Enter tokens for enabled channels 3. **Workspace setup** - Name your workspace directory 4. **Default agent** - Configure your main AI assistant -5. **AI provider** - Select Anthropic (Claude) or OpenAI +5. **AI provider** - Select Anthropic (Claude), OpenAI, or Qoder 6. **Model selection** - Choose model (e.g., Sonnet, Opus, GPT-5.3) 7. **Heartbeat interval** - Set proactive check-in frequency diff --git a/lib/setup-wizard.sh b/lib/setup-wizard.sh index c66abb7..f5190d0 100755 --- a/lib/setup-wizard.sh +++ b/lib/setup-wizard.sh @@ -79,60 +79,128 @@ for ch in "${ENABLED_CHANNELS[@]}"; do done # Provider selection -echo "Which AI provider?" -echo "" -echo " 1) Anthropic (Claude) (recommended)" -echo " 2) OpenAI (Codex/GPT)" -echo "" -read -rp "Choose [1-2]: " PROVIDER_CHOICE - -case "$PROVIDER_CHOICE" in - 1) PROVIDER="anthropic" ;; - 2) PROVIDER="openai" ;; - *) - echo -e "${RED}Invalid choice${NC}" - exit 1 - ;; -esac -echo -e "${GREEN}✓ Provider: $PROVIDER${NC}" -echo "" - -# Model selection based on provider -if [ "$PROVIDER" = "anthropic" ]; then - echo "Which Claude model?" +PROVIDER="" +while [ -z "$PROVIDER" ]; do + echo "Which AI provider?" echo "" - echo " 1) Sonnet (fast, recommended)" - echo " 2) Opus (smartest)" + echo " 1) Anthropic (Claude) (recommended)" + echo " 2) OpenAI (Codex/GPT)" + echo " 3) Qoder" + echo " s) Skip (will use defaults)" echo "" - read -rp "Choose [1-2]: " MODEL_CHOICE - - case "$MODEL_CHOICE" in - 1) MODEL="sonnet" ;; - 2) MODEL="opus" ;; + read -rp "Choose [1-3, s]: " PROVIDER_CHOICE + + case "$PROVIDER_CHOICE" in + 1) PROVIDER="anthropic" ;; + 2) PROVIDER="openai" ;; + 3) PROVIDER="qoder" ;; + [sS]) + echo -e "${YELLOW}Skipping provider selection (will use defaults)${NC}" + PROVIDER="anthropic" + break + ;; *) - echo -e "${RED}Invalid choice${NC}" - exit 1 + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" ;; esac +done + +echo -e "${GREEN}✓ Provider: $PROVIDER${NC}" +echo "" + +# Model selection based on provider +MODEL="" +if [ "$PROVIDER" = "anthropic" ]; then + while [ -z "$MODEL" ]; do + echo "Which Claude model?" + echo "" + echo " 1) Sonnet (fast, recommended)" + echo " 2) Opus (smartest)" + echo " s) Skip (use default)" + echo "" + read -rp "Choose [1-2, s]: " MODEL_CHOICE + + case "$MODEL_CHOICE" in + 1) MODEL="sonnet" ;; + 2) MODEL="opus" ;; + [sS]) + echo -e "${YELLOW}Using default model: sonnet${NC}" + MODEL="sonnet" + break + ;; + *) + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done echo -e "${GREEN}✓ Model: $MODEL${NC}" echo "" -else - # OpenAI models - echo "Which OpenAI model?" - echo "" - echo " 1) GPT-5.3 Codex (recommended)" - echo " 2) GPT-5.2" +elif [ "$PROVIDER" = "qoder" ]; then + while [ -z "$MODEL" ]; do + echo "Which Qoder model?" + echo "" + echo " 1) auto (automatic selection)" + echo " 2) efficient (fast, cost-effective)" + echo " 3) gmodel" + echo " 4) kmodel" + echo " 5) lite (lightweight)" + echo " 6) mmodel" + echo " 7) performance (balanced)" + echo " 8) qmodel" + echo " 9) ultimate (most capable)" + echo " s) Skip (use default: auto)" + echo "" + read -rp "Choose [1-9, s]: " MODEL_CHOICE + + case "$MODEL_CHOICE" in + 1) MODEL="auto" ;; + 2) MODEL="efficient" ;; + 3) MODEL="gmodel" ;; + 4) MODEL="kmodel" ;; + 5) MODEL="lite" ;; + 6) MODEL="mmodel" ;; + 7) MODEL="performance" ;; + 8) MODEL="qmodel" ;; + 9) MODEL="ultimate" ;; + [sS]) + echo -e "${YELLOW}Using default model: auto${NC}" + MODEL="auto" + break + ;; + *) + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + echo -e "${GREEN}✓ Model: $MODEL${NC}" echo "" - read -rp "Choose [1-2]: " MODEL_CHOICE - - case "$MODEL_CHOICE" in - 1) MODEL="gpt-5.3-codex" ;; - 2) MODEL="gpt-5.2" ;; - *) - echo -e "${RED}Invalid choice${NC}" - exit 1 - ;; - esac +elif [ "$PROVIDER" = "openai" ]; then + while [ -z "$MODEL" ]; do + echo "Which OpenAI model?" + echo "" + echo " 1) GPT-5.3 Codex (recommended)" + echo " 2) GPT-5.2" + echo " s) Skip (use default)" + echo "" + read -rp "Choose [1-2, s]: " MODEL_CHOICE + + case "$MODEL_CHOICE" in + 1) MODEL="gpt-5.3-codex" ;; + 2) MODEL="gpt-5.2" ;; + [sS]) + echo -e "${YELLOW}Using default model: gpt-5.3-codex${NC}" + MODEL="gpt-5.3-codex" + break + ;; + *) + echo -e "${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done echo -e "${GREEN}✓ Model: $MODEL${NC}" echo "" fi @@ -216,27 +284,91 @@ if [[ "$SETUP_AGENTS" =~ ^[yY] ]]; then read -rp " Display name: " NEW_AGENT_NAME [ -z "$NEW_AGENT_NAME" ] && NEW_AGENT_NAME="$NEW_AGENT_ID" - echo " Provider: 1) Anthropic 2) OpenAI" - read -rp " Choose [1-2, default: 1]: " NEW_PROVIDER_CHOICE - case "$NEW_PROVIDER_CHOICE" in - 2) NEW_PROVIDER="openai" ;; - *) NEW_PROVIDER="anthropic" ;; - esac + NEW_PROVIDER="" + while [ -z "$NEW_PROVIDER" ]; do + echo " Provider: 1) Anthropic 2) OpenAI 3) Qoder" + echo " s) Skip (use default: anthropic)" + read -rp " Choose [1-3, s, default: 1]: " NEW_PROVIDER_CHOICE + case "$NEW_PROVIDER_CHOICE" in + 2) NEW_PROVIDER="openai" ;; + 3) NEW_PROVIDER="qoder" ;; + [sS]) + echo -e " ${YELLOW}Using default provider: anthropic${NC}" + NEW_PROVIDER="anthropic" + break + ;; + ""|1) NEW_PROVIDER="anthropic" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + NEW_MODEL="" if [ "$NEW_PROVIDER" = "anthropic" ]; then - echo " Model: 1) Sonnet 2) Opus" - read -rp " Choose [1-2, default: 1]: " NEW_MODEL_CHOICE - case "$NEW_MODEL_CHOICE" in - 2) NEW_MODEL="opus" ;; - *) NEW_MODEL="sonnet" ;; - esac - else - echo " Model: 1) GPT-5.3 Codex 2) GPT-5.2" - read -rp " Choose [1-2, default: 1]: " NEW_MODEL_CHOICE - case "$NEW_MODEL_CHOICE" in - 2) NEW_MODEL="gpt-5.2" ;; - *) NEW_MODEL="gpt-5.3-codex" ;; - esac + while [ -z "$NEW_MODEL" ]; do + echo " Model: 1) Sonnet 2) Opus s) Skip (use default: sonnet)" + read -rp " Choose [1-2, s, default: 1]: " NEW_MODEL_CHOICE + case "$NEW_MODEL_CHOICE" in + 2) NEW_MODEL="opus" ;; + [sS]) + echo -e " ${YELLOW}Using default model: sonnet${NC}" + NEW_MODEL="sonnet" + break + ;; + ""|1) NEW_MODEL="sonnet" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + elif [ "$NEW_PROVIDER" = "qoder" ]; then + while [ -z "$NEW_MODEL" ]; do + echo " Model: 1) auto 2) efficient 3) gmodel 4) kmodel 5) lite" + echo " 6) mmodel 7) performance 8) qmodel 9) ultimate" + echo " s) Skip (use default: auto)" + read -rp " Choose [1-9, s, default: 1]: " NEW_MODEL_CHOICE + case "$NEW_MODEL_CHOICE" in + 2) NEW_MODEL="efficient" ;; + 3) NEW_MODEL="gmodel" ;; + 4) NEW_MODEL="kmodel" ;; + 5) NEW_MODEL="lite" ;; + 6) NEW_MODEL="mmodel" ;; + 7) NEW_MODEL="performance" ;; + 8) NEW_MODEL="qmodel" ;; + 9) NEW_MODEL="ultimate" ;; + [sS]) + echo -e " ${YELLOW}Using default model: auto${NC}" + NEW_MODEL="auto" + break + ;; + ""|1) NEW_MODEL="auto" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done + elif [ "$NEW_PROVIDER" = "openai" ]; then + while [ -z "$NEW_MODEL" ]; do + echo " Model: 1) GPT-5.3 Codex 2) GPT-5.2 s) Skip (use default: gpt-5.3-codex)" + read -rp " Choose [1-2, s, default: 1]: " NEW_MODEL_CHOICE + case "$NEW_MODEL_CHOICE" in + 2) NEW_MODEL="gpt-5.2" ;; + [sS]) + echo -e " ${YELLOW}Using default model: gpt-5.3-codex${NC}" + NEW_MODEL="gpt-5.3-codex" + break + ;; + ""|1) NEW_MODEL="gpt-5.3-codex" ;; + *) + echo -e " ${RED}Invalid choice, please try again${NC}" + echo "" + ;; + esac + done fi NEW_AGENT_DIR="$WORKSPACE_PATH/$NEW_AGENT_ID" @@ -270,6 +402,8 @@ TELEGRAM_TOKEN="${TOKENS[telegram]:-}" # Use jq to build valid JSON to avoid escaping issues with agent prompts if [ "$PROVIDER" = "anthropic" ]; then MODELS_SECTION='"models": { "provider": "anthropic", "anthropic": { "model": "'"${MODEL}"'" } }' +elif [ "$PROVIDER" = "qoder" ]; then + MODELS_SECTION='"models": { "provider": "qoder", "qoder": { "model": "'"${MODEL}"'" } }' else MODELS_SECTION='"models": { "provider": "openai", "openai": { "model": "'"${MODEL}"'" } }' fi diff --git a/src/lib/config.ts b/src/lib/config.ts index de370c0..d711941 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; import { jsonrepair } from 'jsonrepair'; -import { Settings, AgentConfig, TeamConfig, CLAUDE_MODEL_IDS, CODEX_MODEL_IDS } from './types'; +import { Settings, AgentConfig, TeamConfig, CLAUDE_MODEL_IDS, CODEX_MODEL_IDS, QODER_MODEL_IDS } from './types'; export const SCRIPT_DIR = path.resolve(__dirname, '../..'); const _localTinyclaw = path.join(SCRIPT_DIR, '.tinyclaw'); @@ -52,6 +52,9 @@ export function getSettings(): Settings { } else if (settings?.models?.anthropic) { if (!settings.models) settings.models = {}; settings.models.provider = 'anthropic'; + } else if (settings?.models?.qoder) { + if (!settings.models) settings.models = {}; + settings.models.provider = 'qoder'; } } @@ -70,6 +73,8 @@ export function getDefaultAgentFromModels(settings: Settings): AgentConfig { let model = ''; if (provider === 'openai') { model = settings?.models?.openai?.model || 'gpt-5.3-codex'; + } else if (provider === 'qoder') { + model = settings?.models?.qoder?.model || 'auto'; } else { model = settings?.models?.anthropic?.model || 'sonnet'; } @@ -118,3 +123,10 @@ export function resolveClaudeModel(model: string): string { export function resolveCodexModel(model: string): string { return CODEX_MODEL_IDS[model] || model || ''; } + +/** + * Resolve the model ID for QoderCLI. + */ +export function resolveQoderModel(model: string): string { + return QODER_MODEL_IDS[model] || model || ''; +} diff --git a/src/lib/invoke.ts b/src/lib/invoke.ts index 471eaf2..e9e0547 100644 --- a/src/lib/invoke.ts +++ b/src/lib/invoke.ts @@ -2,7 +2,7 @@ import { spawn } from 'child_process'; import fs from 'fs'; import path from 'path'; import { AgentConfig, TeamConfig } from './types'; -import { SCRIPT_DIR, resolveClaudeModel, resolveCodexModel } from './config'; +import { SCRIPT_DIR, resolveClaudeModel, resolveCodexModel, resolveQoderModel } from './config'; import { log } from './logging'; import { ensureAgentDirectory, updateAgentTeammates } from './agent-setup'; @@ -112,6 +112,27 @@ export async function invokeAgent( } return response || 'Sorry, I could not generate a response from Codex.'; + } else if (provider === 'qoder') { + // QoderCLI provider + log('INFO', `Using QoderCLI provider (agent: ${agentId})`); + + const continueConversation = !shouldReset; + + if (shouldReset) { + log('INFO', `🔄 Resetting conversation for agent: ${agentId}`); + } + + const modelId = resolveQoderModel(agent.model); + const qoderArgs = ['-w', workingDir]; + if (modelId) { + qoderArgs.push('--model', modelId); + } + if (continueConversation) { + qoderArgs.push('-c'); + } + qoderArgs.push('-p', message); + + return await runCommand('qodercli', qoderArgs, workingDir); } else { // Default to Claude (Anthropic) log('INFO', `Using Claude provider (agent: ${agentId})`); diff --git a/src/lib/types.ts b/src/lib/types.ts index f0b2e40..eb7b80f 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,7 +1,7 @@ export interface AgentConfig { name: string; - provider: string; // 'anthropic' or 'openai' - model: string; // e.g. 'sonnet', 'opus', 'gpt-5.3-codex' + provider: string; // 'anthropic', 'openai', or 'qoder' + model: string; // e.g. 'sonnet', 'opus', 'gpt-5.3-codex', 'qoder' working_directory: string; } @@ -28,13 +28,16 @@ export interface Settings { whatsapp?: {}; }; models?: { - provider?: string; // 'anthropic' or 'openai' + provider?: string; // 'anthropic', 'openai', or 'qoder' anthropic?: { model?: string; }; openai?: { model?: string; }; + qoder?: { + model?: string; + }; }; agents?: Record; teams?: Record; @@ -103,3 +106,15 @@ export const CODEX_MODEL_IDS: Record = { 'gpt-5.2': 'gpt-5.2', 'gpt-5.3-codex': 'gpt-5.3-codex', }; + +export const QODER_MODEL_IDS: Record = { + 'auto': 'auto', + 'efficient': 'efficient', + 'gmodel': 'gmodel', + 'kmodel': 'kmodel', + 'lite': 'lite', + 'mmodel': 'mmodel', + 'performance': 'performance', + 'qmodel': 'qmodel', + 'ultimate': 'ultimate', +}; diff --git a/src/queue-processor.ts b/src/queue-processor.ts index e89b989..9ab129e 100644 --- a/src/queue-processor.ts +++ b/src/queue-processor.ts @@ -347,7 +347,8 @@ async function processMessage(messageFile: string): Promise { response = await invokeAgent(agent, agentId, message, workspacePath, shouldReset, agents, teams); } catch (error) { const provider = agent.provider || 'anthropic'; - log('ERROR', `${provider === 'openai' ? 'Codex' : 'Claude'} error (agent: ${agentId}): ${(error as Error).message}`); + const providerName = provider === 'openai' ? 'Codex' : provider === 'qoder' ? 'Qoder' : 'Claude'; + log('ERROR', `${providerName} error (agent: ${agentId}): ${(error as Error).message}`); response = "Sorry, I encountered an error processing your request. Please check the queue logs."; }