seihou-cli-0.10.0.0: src-exe/Seihou/CLI/AgentLaunchExec.hs
module Seihou.CLI.AgentLaunchExec
( launchConfiguredAgent,
launchConfiguredAgentAddingDirs,
launchConfiguredAgentWith,
)
where
import Baikai.Agent (AgentRenderError, renderAgentRenderError)
import Baikai.Interactive
( CodexApprovalPolicy (CodexApprovalOnRequest),
CodexSandboxMode (CodexWorkspaceWrite),
InteractiveLaunchResult (..),
InteractiveSafety (ClaudeAllowedTools, CodexSandbox),
interactiveLaunchRequest,
)
import Baikai.Kit.Session qualified as KitSession
import Baikai.Provider.Claude.Interactive
( defaultClaudeInteractiveConfig,
launchClaudeInteractive,
)
import Baikai.Provider.OpenAI.Interactive
( defaultCodexInteractiveConfig,
launchCodexInteractive,
)
import Baikai.ThinkingLevel (ThinkingLevel)
import Data.Generics.Labels ()
import Data.Text qualified as T
import Data.Text.IO qualified as TIO
import Seihou.CLI.AgentCompletion (AgentModelConfig (..), AgentProvider (..))
import Seihou.CLI.Kit (seihouKitConfig)
import Seihou.Prelude
import System.Directory (findExecutable, getCurrentDirectory)
import System.Exit (ExitCode (..), exitFailure)
launchConfiguredAgent :: AgentModelConfig -> [String] -> Bool -> Text -> Maybe Text -> IO ExitCode
launchConfiguredAgent = launchConfiguredAgentAddingDirs []
launchConfiguredAgentAddingDirs :: [FilePath] -> AgentModelConfig -> [String] -> Bool -> Text -> Maybe Text -> IO ExitCode
launchConfiguredAgentAddingDirs extraDirs modelConfig tools debug systemPrompt initialPrompt = do
sessionDirs <- KitSession.agentDirsForSession seihouKitConfig
launchConfiguredAgentWith (sessionDirs <> extraDirs) modelConfig tools debug systemPrompt initialPrompt
launchConfiguredAgentWith :: [FilePath] -> AgentModelConfig -> [String] -> Bool -> Text -> Maybe Text -> IO ExitCode
launchConfiguredAgentWith addDirs modelConfig tools debug systemPrompt initialPrompt
| debug = do
TIO.putStr systemPrompt
pure ExitSuccess
| otherwise =
case modelConfig ^. #provider of
AgentProviderClaudeCli ->
launchClaude addDirs tools (modelConfig ^. #model) (modelConfig ^. #effort) systemPrompt initialPrompt
AgentProviderCodexCli ->
launchCodex addDirs (modelConfig ^. #model) (modelConfig ^. #effort) systemPrompt initialPrompt
AgentProviderAnthropic ->
unsupportedInteractiveProvider "anthropic"
AgentProviderOpenAI ->
unsupportedInteractiveProvider "openai"
launchClaude :: [FilePath] -> [String] -> Maybe Text -> Maybe ThinkingLevel -> Text -> Maybe Text -> IO ExitCode
launchClaude addDirs tools model effortLevel systemPrompt initialPrompt = do
claudePath <- findExecutable "claude"
case claudePath of
Nothing -> do
TIO.putStrLn "Error: 'claude' CLI (Claude Code) not found on PATH."
TIO.putStrLn "Install it from: https://docs.anthropic.com/en/docs/claude-code"
exitFailure
Just _ -> do
cwd <- getCurrentDirectory
launchExitCode
=<< launchClaudeInteractive
defaultClaudeInteractiveConfig
( interactiveLaunchRequest (promptOrEmpty initialPrompt)
& #systemPrompt
.~ Just systemPrompt
& #modelId
.~ model
& #effort
.~ effortLevel
& #workingDir
.~ Just cwd
& #extraDirs
.~ addDirs
& #safety
.~ ClaudeAllowedTools (map T.pack tools)
)
launchCodex :: [FilePath] -> Maybe Text -> Maybe ThinkingLevel -> Text -> Maybe Text -> IO ExitCode
launchCodex addDirs model effortLevel systemPrompt initialPrompt = do
codexPath <- findExecutable "codex"
case codexPath of
Nothing -> do
TIO.putStrLn "Error: 'codex' CLI not found on PATH."
TIO.putStrLn "Install and authenticate Codex CLI, then retry."
exitFailure
Just _ -> do
cwd <- getCurrentDirectory
sessionArgs <- KitSession.codexSessionArgs seihouKitConfig
launchExitCode
=<< launchCodexInteractive
defaultCodexInteractiveConfig
( interactiveLaunchRequest (promptOrEmpty initialPrompt)
& #systemPrompt
.~ Just systemPrompt
& #modelId
.~ model
& #effort
.~ effortLevel
& #workingDir
.~ Just cwd
& #extraDirs
.~ addDirs
& #extraArgs
.~ sessionArgs
& #safety
.~ CodexSandbox CodexWorkspaceWrite CodexApprovalOnRequest
)
-- | The launched session's exit code. A 'Left' means baikai refused to render
-- the launch (the provider cannot honor the requested safety policy), so no
-- process ran.
launchExitCode :: Either AgentRenderError InteractiveLaunchResult -> IO ExitCode
launchExitCode = \case
Right InteractiveLaunchResult {exitCode} -> pure exitCode
Left err -> do
TIO.putStrLn ("Error: " <> renderAgentRenderError err)
exitFailure
promptOrEmpty :: Maybe Text -> Text
promptOrEmpty = maybe "" id
unsupportedInteractiveProvider :: Text -> IO ExitCode
unsupportedInteractiveProvider providerName = do
TIO.putStrLn $
"Error: provider '"
<> providerName
<> "' is an API provider and cannot start an interactive local agent session."
TIO.putStrLn "Use --provider claude-cli or --provider codex-cli for interactive agent sessions."
exitFailure