diff --git a/src/main/broker.ts b/src/main/broker.ts index 1a0ad219..452a8eff 100644 --- a/src/main/broker.ts +++ b/src/main/broker.ts @@ -1280,7 +1280,7 @@ function preflightSpawnCli(input: SpawnPtyInput): SpawnPtyInput { } const label = spawnCliLabel(input.cli) - if (!['claude', 'codex', 'opencode'].includes(label)) return input + if (!['claude', 'codex', 'opencode', 'grok'].includes(label)) return input const resolved = resolveCommandWithAugmentedPath(input.cli) if (!resolved) { throw new Error( diff --git a/src/main/proactive-agent.bundle.ts b/src/main/proactive-agent.bundle.ts index 50da3692..3e7923ea 100644 --- a/src/main/proactive-agent.bundle.ts +++ b/src/main/proactive-agent.bundle.ts @@ -12,7 +12,7 @@ export type ProactiveAgentDraft = { name: string description?: string cloudAgentId: string - harness: 'claude' | 'codex' | 'opencode' + harness: 'claude' | 'codex' | 'opencode' | 'grok' model: string systemPrompt: string integrations: Record> diff --git a/src/main/proactive-agent.ts b/src/main/proactive-agent.ts index a3877d04..a133ac0f 100644 --- a/src/main/proactive-agent.ts +++ b/src/main/proactive-agent.ts @@ -43,7 +43,7 @@ type PersonaKitModule = { const PERSONA_ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/ const WATCH_EVENTS = new Set(['created', 'updated', 'deleted']) -const HARNESSES = new Set(['claude', 'codex', 'opencode']) +const HARNESSES = new Set(['claude', 'codex', 'opencode', 'grok']) const RUN_MODES = new Set(['cloud', 'local']) const MEMORY_SCOPES = new Set(['workspace', 'project', 'persona']) const REASONING_LEVELS = new Set(['low', 'medium', 'high']) @@ -390,7 +390,7 @@ export class ProactiveAgentManager { throw new Error('Agent id must be kebab-case') } if (!HARNESSES.has(draft.harness)) { - throw new Error('Harness must be one of claude, codex, or opencode') + throw new Error('Harness must be one of claude, codex, opencode, or grok') } if (!RUN_MODES.has(draft.runMode || 'cloud')) { throw new Error('Run mode must be cloud or local') diff --git a/src/main/proactive-agent.types.ts b/src/main/proactive-agent.types.ts index 13b683b5..09570217 100644 --- a/src/main/proactive-agent.types.ts +++ b/src/main/proactive-agent.types.ts @@ -1,4 +1,4 @@ -export type ProactiveAgentHarness = 'claude' | 'codex' | 'opencode' +export type ProactiveAgentHarness = 'claude' | 'codex' | 'opencode' | 'grok' export type ProactiveAgentStatus = 'draft' | 'warming' | 'active' | 'paused' | 'error' export type ProactiveAgentRunStatus = 'running' | 'succeeded' | 'failed' export type ProactiveAgentRunMode = 'cloud' | 'local' diff --git a/src/renderer/src/components/agents/CloudAgentPicker.tsx b/src/renderer/src/components/agents/CloudAgentPicker.tsx index 95233e96..6cad0c4e 100644 --- a/src/renderer/src/components/agents/CloudAgentPicker.tsx +++ b/src/renderer/src/components/agents/CloudAgentPicker.tsx @@ -57,7 +57,8 @@ export type CloudAgentPickerProps = { const HARNESS_OPTIONS = [ { value: 'claude', label: 'Claude', defaultModel: 'claude-opus-4-7' }, { value: 'codex', label: 'Codex', defaultModel: 'gpt-5.2' }, - { value: 'opencode', label: 'OpenCode', defaultModel: 'claude-sonnet-4-6' } + { value: 'opencode', label: 'OpenCode', defaultModel: 'claude-sonnet-4-6' }, + { value: 'grok', label: 'Grok', defaultModel: 'grok-build-0.1' } ] function getErrorMessage(error: unknown): string { @@ -140,9 +141,10 @@ function cloudWorkerName(agent: CloudAgentRecord): string { function cloudWorkerCli(agent: CloudAgentRecord): string { const harness = agent.harness.trim().toLowerCase() - if (harness === 'claude' || harness === 'codex' || harness === 'opencode') return harness + if (harness === 'claude' || harness === 'codex' || harness === 'opencode' || harness === 'grok') return harness if (harness === 'anthropic') return 'claude' if (harness === 'openai' || harness === 'gpt' || harness === 'chatgpt') return 'codex' + if (harness === 'xai' || harness === 'x.ai') return 'grok' return 'claude' } diff --git a/src/renderer/src/components/common/AgentIcons.tsx b/src/renderer/src/components/common/AgentIcons.tsx index 8ab3bdd4..9abc1ed7 100644 --- a/src/renderer/src/components/common/AgentIcons.tsx +++ b/src/renderer/src/components/common/AgentIcons.tsx @@ -70,12 +70,24 @@ export function GeminiIcon({ className }: AgentIconProps): React.ReactNode { ) } +export function GrokIcon({ className }: AgentIconProps): React.ReactNode { + return ( + + ) +} + const AGENT_ICON_COMPONENTS = { claude: ClaudeIcon, codex: CodexIcon, copilot: CopilotIcon, opencode: OpenCodeIcon, - gemini: GeminiIcon + gemini: GeminiIcon, + grok: GrokIcon } satisfies Record> export function getAgentIcon(cli?: string): React.ComponentType | null { diff --git a/src/renderer/src/components/common/AgentToolbar.tsx b/src/renderer/src/components/common/AgentToolbar.tsx index 500702f9..2906ecf2 100644 --- a/src/renderer/src/components/common/AgentToolbar.tsx +++ b/src/renderer/src/components/common/AgentToolbar.tsx @@ -1,6 +1,6 @@ import type React from 'react' import { Settings } from 'lucide-react' -import { ClaudeIcon, CodexIcon, CopilotIcon, GeminiIcon, OpenCodeIcon } from '@/components/common/AgentIcons' +import { ClaudeIcon, CodexIcon, CopilotIcon, GeminiIcon, GrokIcon, OpenCodeIcon } from '@/components/common/AgentIcons' import { useUIStore } from '@/stores/ui-store' const AGENTS = [ @@ -8,7 +8,8 @@ const AGENTS = [ { cli: 'codex', label: 'codex', Icon: CodexIcon }, { cli: 'copilot', label: 'copilot', Icon: CopilotIcon }, { cli: 'opencode', label: 'opencode', Icon: OpenCodeIcon }, - { cli: 'gemini', label: 'gemini', Icon: GeminiIcon } + { cli: 'gemini', label: 'gemini', Icon: GeminiIcon }, + { cli: 'grok', label: 'grok', Icon: GrokIcon } ] export function AgentToolbar(): React.ReactNode { diff --git a/src/renderer/src/components/proactive/ProactiveAgentCard.tsx b/src/renderer/src/components/proactive/ProactiveAgentCard.tsx index fa6ab6e4..9475c3b8 100644 --- a/src/renderer/src/components/proactive/ProactiveAgentCard.tsx +++ b/src/renderer/src/components/proactive/ProactiveAgentCard.tsx @@ -17,7 +17,7 @@ export type ProactiveAgentDraft = { name: string description?: string cloudAgentId: string - harness: 'claude' | 'codex' | 'opencode' + harness: 'claude' | 'codex' | 'opencode' | 'grok' model: string systemPrompt: string integrations: Record> diff --git a/src/renderer/src/components/proactive/ProactiveAgentEditor.tsx b/src/renderer/src/components/proactive/ProactiveAgentEditor.tsx index 9508707c..0ba601ee 100644 --- a/src/renderer/src/components/proactive/ProactiveAgentEditor.tsx +++ b/src/renderer/src/components/proactive/ProactiveAgentEditor.tsx @@ -90,7 +90,8 @@ type KeyValueRow = { const MODEL_OPTIONS: Record = { claude: ['claude-opus-4-7', 'claude-sonnet-4-6', 'claude-haiku-4-5'], codex: ['gpt-5.2', 'gpt-5.1-codex', 'gpt-5.1-codex-mini'], - opencode: ['claude-sonnet-4-6', 'gpt-5.2', 'qwen3-coder'] + opencode: ['claude-sonnet-4-6', 'gpt-5.2', 'qwen3-coder'], + grok: ['grok-build-0.1', 'grok-code-fast-1', 'grok-code-fast'] } const DEPLOY_PHASES: DeployPhase[] = [ @@ -926,6 +927,7 @@ export function ProactiveAgentEditor({ + diff --git a/src/renderer/src/components/sidebar/SpawnAgentDialog.tsx b/src/renderer/src/components/sidebar/SpawnAgentDialog.tsx index 0e990495..afc5b5c4 100644 --- a/src/renderer/src/components/sidebar/SpawnAgentDialog.tsx +++ b/src/renderer/src/components/sidebar/SpawnAgentDialog.tsx @@ -1,7 +1,7 @@ import type React from 'react' import { useCallback, useEffect, useRef, useState } from 'react' import { Loader2, X } from 'lucide-react' -import { ClaudeIcon, CodexIcon } from '@/components/common/AgentIcons' +import { ClaudeIcon, CodexIcon, GrokIcon } from '@/components/common/AgentIcons' import { listProjectPersonas, spawnProjectAgent, spawnProjectPersona, type SpawnAgentCli } from '@/lib/spawn-agent' import type { WorkforcePersona } from '@/lib/ipc' import { useProjectStore, type ProjectRoot } from '@/stores/project-store' @@ -9,7 +9,8 @@ import { useUIStore } from '@/stores/ui-store' const AGENT_OPTIONS: Array<{ cli: SpawnAgentCli; label: string; Icon: typeof ClaudeIcon }> = [ { cli: 'claude', label: 'Claude', Icon: ClaudeIcon }, - { cli: 'codex', label: 'Codex', Icon: CodexIcon } + { cli: 'codex', label: 'Codex', Icon: CodexIcon }, + { cli: 'grok', label: 'Grok', Icon: GrokIcon } ] export function SpawnAgentDialog(): React.ReactNode { @@ -207,7 +208,7 @@ export function SpawnAgentDialog(): React.ReactNode { className="h-9 w-full rounded-md border border-[var(--pear-border-subtle)] bg-[var(--pear-bg)] px-3 text-sm text-[var(--pear-text)] outline-none placeholder:text-[var(--pear-text-faint)] focus:border-[var(--pear-accent-dim)] disabled:opacity-50" /> -
+
{AGENT_OPTIONS.map(({ cli, label, Icon }) => ( +
) : (