A-gent 0.11.0.5 → 0.11.0.6
raw patch · 13 files changed
+1286/−206 lines, 13 filesdep ~containerssetup-changedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: containers
API changes (from Hackage documentation)
- Agent.IO.Effects: class LlmChatConf m => LlmChatCurl (m :: Type -> Type)
- Agent.IO.Effects: llmChatCurl :: LlmChatCurl m => String -> m (Either String String)
- Agent.IO.Restricted: instance (Agent.IO.Effects.LlmChatConf Agent.IO.Restricted.RIO, Agent.IO.Restricted.ReadProc Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmChatCurl Agent.IO.Restricted.RIO
- Agent.IO.Restricted: instance (Agent.IO.Effects.LlmChatConf Agent.IO.Restricted.RIO, Agent.IO.Restricted.SysEnv Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmChatConf Agent.IO.Restricted.RIO
- Agent.IO.Restricted: instance Agent.IO.Restricted.ReadProc Agent.IO.Restricted.RIO
- Agent.IO.Restricted: instance Agent.IO.Restricted.SysEnv Agent.IO.Restricted.RIO
- Agent.IO.Restricted: llmChatCurl :: LlmChatCurl m => String -> m (Either String String)
- Internal.GaloisInc.Text.JSON: Error :: String -> Result a
- Internal.GaloisInc.Text.JSON: JSArray :: [JSValue] -> JSValue
- Internal.GaloisInc.Text.JSON: JSBool :: !Bool -> JSValue
- Internal.GaloisInc.Text.JSON: JSNull :: JSValue
- Internal.GaloisInc.Text.JSON: JSObject :: JSObject JSValue -> JSValue
- Internal.GaloisInc.Text.JSON: JSRational :: Bool -> !Rational -> JSValue
- Internal.GaloisInc.Text.JSON: JSString :: JSString -> JSValue
- Internal.GaloisInc.Text.JSON: Ok :: a -> Result a
- Internal.GaloisInc.Text.JSON: class JSKey a
- Internal.GaloisInc.Text.JSON: class JSON a
- Internal.GaloisInc.Text.JSON: data JSObject e
- Internal.GaloisInc.Text.JSON: data JSString
- Internal.GaloisInc.Text.JSON: data JSValue
- Internal.GaloisInc.Text.JSON: data Result a
- Internal.GaloisInc.Text.JSON: decJSDict :: (JSKey a, JSON b) => String -> JSValue -> Result [(a, b)]
- Internal.GaloisInc.Text.JSON: decode :: JSON a => String -> Result a
- Internal.GaloisInc.Text.JSON: decodeStrict :: JSON a => String -> Result a
- Internal.GaloisInc.Text.JSON: encJSDict :: (JSKey a, JSON b) => [(a, b)] -> JSValue
- Internal.GaloisInc.Text.JSON: encode :: JSON a => a -> String
- Internal.GaloisInc.Text.JSON: encodeStrict :: JSON a => a -> String
- Internal.GaloisInc.Text.JSON: fromJSKey :: JSKey a => String -> Maybe a
- Internal.GaloisInc.Text.JSON: fromJSObject :: JSObject e -> [(String, e)]
- Internal.GaloisInc.Text.JSON: fromJSString :: JSString -> String
- Internal.GaloisInc.Text.JSON: instance (GHC.Classes.Ord a, Internal.GaloisInc.Text.JSON.JSON a) => Internal.GaloisInc.Text.JSON.JSON (Data.Set.Internal.Set a)
- Internal.GaloisInc.Text.JSON: instance (GHC.Classes.Ord a, Internal.GaloisInc.Text.JSON.JSON a, Internal.GaloisInc.Text.JSON.JSON b) => Internal.GaloisInc.Text.JSON.JSON (Data.Map.Internal.Map a b)
- Internal.GaloisInc.Text.JSON: instance (GHC.Ix.Ix i, Internal.GaloisInc.Text.JSON.JSON i, Internal.GaloisInc.Text.JSON.JSON e) => Internal.GaloisInc.Text.JSON.JSON (GHC.Arr.Array i e)
- Internal.GaloisInc.Text.JSON: instance (Internal.GaloisInc.Text.JSON.JSON a, Internal.GaloisInc.Text.JSON.JSON b) => Internal.GaloisInc.Text.JSON.JSON (Data.Either.Either a b)
- Internal.GaloisInc.Text.JSON: instance (Internal.GaloisInc.Text.JSON.JSON a, Internal.GaloisInc.Text.JSON.JSON b) => Internal.GaloisInc.Text.JSON.JSON (a, b)
- Internal.GaloisInc.Text.JSON: instance (Internal.GaloisInc.Text.JSON.JSON a, Internal.GaloisInc.Text.JSON.JSON b, Internal.GaloisInc.Text.JSON.JSON c) => Internal.GaloisInc.Text.JSON.JSON (a, b, c)
- Internal.GaloisInc.Text.JSON: instance (Internal.GaloisInc.Text.JSON.JSON a, Internal.GaloisInc.Text.JSON.JSON b, Internal.GaloisInc.Text.JSON.JSON c, Internal.GaloisInc.Text.JSON.JSON d) => Internal.GaloisInc.Text.JSON.JSON (a, b, c, d)
- Internal.GaloisInc.Text.JSON: instance Control.Monad.Fail.MonadFail Internal.GaloisInc.Text.JSON.Result
- Internal.GaloisInc.Text.JSON: instance GHC.Base.Alternative Internal.GaloisInc.Text.JSON.Result
- Internal.GaloisInc.Text.JSON: instance GHC.Base.Applicative Internal.GaloisInc.Text.JSON.Result
- Internal.GaloisInc.Text.JSON: instance GHC.Base.Functor Internal.GaloisInc.Text.JSON.Result
- Internal.GaloisInc.Text.JSON: instance GHC.Base.Monad Internal.GaloisInc.Text.JSON.Result
- Internal.GaloisInc.Text.JSON: instance GHC.Base.MonadPlus Internal.GaloisInc.Text.JSON.Result
- Internal.GaloisInc.Text.JSON: instance GHC.Classes.Eq a => GHC.Classes.Eq (Internal.GaloisInc.Text.JSON.Result a)
- Internal.GaloisInc.Text.JSON: instance GHC.Show.Show a => GHC.Show.Show (Internal.GaloisInc.Text.JSON.Result a)
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSKey GHC.Base.String
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSKey GHC.Types.Int
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSKey Internal.GaloisInc.Text.JSON.Types.JSString
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON ()
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON Data.IntSet.Internal.IntSet
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Int.Int16
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Int.Int32
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Int.Int64
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Int.Int8
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Num.Integer.Integer
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Types.Bool
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Types.Char
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Types.Double
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Types.Float
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Types.Int
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Types.Ordering
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Types.Word
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Word.Word16
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Word.Word32
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Word.Word64
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON GHC.Word.Word8
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON Internal.GaloisInc.Text.JSON.Types.JSString
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON Internal.GaloisInc.Text.JSON.Types.JSValue
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON a => Internal.GaloisInc.Text.JSON.JSON (Data.IntMap.Internal.IntMap a)
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON a => Internal.GaloisInc.Text.JSON.JSON (GHC.Maybe.Maybe a)
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON a => Internal.GaloisInc.Text.JSON.JSON (Internal.GaloisInc.Text.JSON.Types.JSObject a)
- Internal.GaloisInc.Text.JSON: instance Internal.GaloisInc.Text.JSON.JSON a => Internal.GaloisInc.Text.JSON.JSON [a]
- Internal.GaloisInc.Text.JSON: makeObj :: [(String, JSValue)] -> JSValue
- Internal.GaloisInc.Text.JSON: readJSArray :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON: readJSBool :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON: readJSNull :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON: readJSON :: JSON a => JSValue -> Result a
- Internal.GaloisInc.Text.JSON: readJSONs :: JSON a => JSValue -> Result [a]
- Internal.GaloisInc.Text.JSON: readJSObject :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON: readJSRational :: GetJSON Rational
- Internal.GaloisInc.Text.JSON: readJSString :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON: readJSValue :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON: resultToEither :: Result a -> Either String a
- Internal.GaloisInc.Text.JSON: showJSArray :: [JSValue] -> ShowS
- Internal.GaloisInc.Text.JSON: showJSBool :: Bool -> ShowS
- Internal.GaloisInc.Text.JSON: showJSNull :: ShowS
- Internal.GaloisInc.Text.JSON: showJSON :: JSON a => a -> JSValue
- Internal.GaloisInc.Text.JSON: showJSONs :: JSON a => [a] -> JSValue
- Internal.GaloisInc.Text.JSON: showJSObject :: JSObject JSValue -> ShowS
- Internal.GaloisInc.Text.JSON: showJSRational :: Rational -> ShowS
- Internal.GaloisInc.Text.JSON: showJSRational' :: Bool -> Rational -> ShowS
- Internal.GaloisInc.Text.JSON: showJSValue :: JSValue -> ShowS
- Internal.GaloisInc.Text.JSON: toJSKey :: JSKey a => a -> String
- Internal.GaloisInc.Text.JSON: toJSObject :: [(String, a)] -> JSObject a
- Internal.GaloisInc.Text.JSON: toJSString :: String -> JSString
- Internal.GaloisInc.Text.JSON: valFromObj :: JSON a => String -> JSObject JSValue -> Result a
- Internal.GaloisInc.Text.JSON.Generic: class Typeable a => Data a
- Internal.GaloisInc.Text.JSON.Generic: class Typeable (a :: k)
- Internal.GaloisInc.Text.JSON.Generic: decodeJSON :: Data a => String -> a
- Internal.GaloisInc.Text.JSON.Generic: encodeJSON :: Data a => a -> String
- Internal.GaloisInc.Text.JSON.Generic: fromJSON :: Data a => JSValue -> Result a
- Internal.GaloisInc.Text.JSON.Generic: fromJSON_generic :: Data a => JSValue -> Result a
- Internal.GaloisInc.Text.JSON.Generic: toJSON :: Data a => a -> JSValue
- Internal.GaloisInc.Text.JSON.Generic: toJSON_generic :: Data a => a -> JSValue
- Internal.GaloisInc.Text.JSON.String: data GetJSON a
- Internal.GaloisInc.Text.JSON.String: instance Control.Monad.Fail.MonadFail Internal.GaloisInc.Text.JSON.String.GetJSON
- Internal.GaloisInc.Text.JSON.String: instance GHC.Base.Applicative Internal.GaloisInc.Text.JSON.String.GetJSON
- Internal.GaloisInc.Text.JSON.String: instance GHC.Base.Functor Internal.GaloisInc.Text.JSON.String.GetJSON
- Internal.GaloisInc.Text.JSON.String: instance GHC.Base.Monad Internal.GaloisInc.Text.JSON.String.GetJSON
- Internal.GaloisInc.Text.JSON.String: readJSArray :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON.String: readJSBool :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON.String: readJSNull :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON.String: readJSObject :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON.String: readJSRational :: GetJSON Rational
- Internal.GaloisInc.Text.JSON.String: readJSString :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON.String: readJSTopType :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON.String: readJSValue :: GetJSON JSValue
- Internal.GaloisInc.Text.JSON.String: runGetJSON :: GetJSON a -> String -> Either String a
- Internal.GaloisInc.Text.JSON.String: showJSArray :: [JSValue] -> ShowS
- Internal.GaloisInc.Text.JSON.String: showJSBool :: Bool -> ShowS
- Internal.GaloisInc.Text.JSON.String: showJSNull :: ShowS
- Internal.GaloisInc.Text.JSON.String: showJSObject :: JSObject JSValue -> ShowS
- Internal.GaloisInc.Text.JSON.String: showJSRational :: Rational -> ShowS
- Internal.GaloisInc.Text.JSON.String: showJSRational' :: Bool -> Rational -> ShowS
- Internal.GaloisInc.Text.JSON.String: showJSTopType :: JSValue -> ShowS
- Internal.GaloisInc.Text.JSON.String: showJSValue :: JSValue -> ShowS
- Internal.GaloisInc.Text.JSON.Types: JSArray :: [JSValue] -> JSValue
- Internal.GaloisInc.Text.JSON.Types: JSBool :: !Bool -> JSValue
- Internal.GaloisInc.Text.JSON.Types: JSNull :: JSValue
- Internal.GaloisInc.Text.JSON.Types: JSONObject :: [(String, e)] -> JSObject e
- Internal.GaloisInc.Text.JSON.Types: JSONString :: String -> JSString
- Internal.GaloisInc.Text.JSON.Types: JSObject :: JSObject JSValue -> JSValue
- Internal.GaloisInc.Text.JSON.Types: JSRational :: Bool -> !Rational -> JSValue
- Internal.GaloisInc.Text.JSON.Types: JSString :: JSString -> JSValue
- Internal.GaloisInc.Text.JSON.Types: [fromJSObject] :: JSObject e -> [(String, e)]
- Internal.GaloisInc.Text.JSON.Types: [fromJSString] :: JSString -> String
- Internal.GaloisInc.Text.JSON.Types: data JSValue
- Internal.GaloisInc.Text.JSON.Types: get_field :: JSObject a -> String -> Maybe a
- Internal.GaloisInc.Text.JSON.Types: instance Data.String.IsString Internal.GaloisInc.Text.JSON.Types.JSString
- Internal.GaloisInc.Text.JSON.Types: instance Data.String.IsString Internal.GaloisInc.Text.JSON.Types.JSValue
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Classes.Eq Internal.GaloisInc.Text.JSON.Types.JSString
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Classes.Eq Internal.GaloisInc.Text.JSON.Types.JSValue
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Classes.Eq e => GHC.Classes.Eq (Internal.GaloisInc.Text.JSON.Types.JSObject e)
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Classes.Ord Internal.GaloisInc.Text.JSON.Types.JSString
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Classes.Ord Internal.GaloisInc.Text.JSON.Types.JSValue
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Classes.Ord e => GHC.Classes.Ord (Internal.GaloisInc.Text.JSON.Types.JSObject e)
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Read.Read Internal.GaloisInc.Text.JSON.Types.JSString
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Read.Read Internal.GaloisInc.Text.JSON.Types.JSValue
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Read.Read e => GHC.Read.Read (Internal.GaloisInc.Text.JSON.Types.JSObject e)
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Show.Show Internal.GaloisInc.Text.JSON.Types.JSString
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Show.Show Internal.GaloisInc.Text.JSON.Types.JSValue
- Internal.GaloisInc.Text.JSON.Types: instance GHC.Show.Show e => GHC.Show.Show (Internal.GaloisInc.Text.JSON.Types.JSObject e)
- Internal.GaloisInc.Text.JSON.Types: newtype JSObject e
- Internal.GaloisInc.Text.JSON.Types: newtype JSString
- Internal.GaloisInc.Text.JSON.Types: set_field :: JSObject a -> String -> a -> JSObject a
- Internal.GaloisInc.Text.JSON.Types: toJSObject :: [(String, a)] -> JSObject a
- Internal.GaloisInc.Text.JSON.Types: toJSString :: String -> JSString
- Internal.GlasgowUniversity.Data.Generics.Aliases: GM :: GenericM m -> GenericM' (m :: Type -> Type)
- Internal.GlasgowUniversity.Data.Generics.Aliases: GQ :: GenericQ r -> GenericQ' r
- Internal.GlasgowUniversity.Data.Generics.Aliases: GT :: GenericT -> GenericT'
- Internal.GlasgowUniversity.Data.Generics.Aliases: Generic' :: Generic c -> Generic' (c :: Type -> Type)
- Internal.GlasgowUniversity.Data.Generics.Aliases: GenericB' :: GenericB -> GenericB'
- Internal.GlasgowUniversity.Data.Generics.Aliases: GenericR' :: GenericR m -> GenericR' (m :: Type -> Type)
- Internal.GlasgowUniversity.Data.Generics.Aliases: [unGM] :: GenericM' (m :: Type -> Type) -> GenericM m
- Internal.GlasgowUniversity.Data.Generics.Aliases: [unGQ] :: GenericQ' r -> GenericQ r
- Internal.GlasgowUniversity.Data.Generics.Aliases: [unGT] :: GenericT' -> GenericT
- Internal.GlasgowUniversity.Data.Generics.Aliases: [unGeneric'] :: Generic' (c :: Type -> Type) -> Generic c
- Internal.GlasgowUniversity.Data.Generics.Aliases: [unGenericB'] :: GenericB' -> GenericB
- Internal.GlasgowUniversity.Data.Generics.Aliases: [unGenericR'] :: GenericR' (m :: Type -> Type) -> GenericR m
- Internal.GlasgowUniversity.Data.Generics.Aliases: choiceMp :: forall (m :: Type -> Type). MonadPlus m => GenericM m -> GenericM m -> GenericM m
- Internal.GlasgowUniversity.Data.Generics.Aliases: choiceQ :: forall (m :: Type -> Type) r. MonadPlus m => GenericQ (m r) -> GenericQ (m r) -> GenericQ (m r)
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext0 :: (Typeable a, Typeable b) => c a -> c b -> c a
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext1 :: (Data a, Typeable t) => c a -> (forall d. Data d => c (t d)) -> c a
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext1B :: (Data a, Typeable t) => a -> (forall b. Data b => t b) -> a
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext1M :: (Monad m, Data d, Typeable t) => (forall e. Data e => e -> m e) -> (forall f. Data f => t f -> m (t f)) -> d -> m d
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext1Q :: (Data d, Typeable t) => (d -> q) -> (forall e. Data e => t e -> q) -> d -> q
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext1R :: (Monad m, Data d, Typeable t) => m d -> (forall e. Data e => m (t e)) -> m d
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext1T :: (Data d, Typeable t) => (forall e. Data e => e -> e) -> (forall f. Data f => t f -> t f) -> d -> d
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext2 :: (Data a, Typeable t) => c a -> (forall d1 d2. (Data d1, Data d2) => c (t d1 d2)) -> c a
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext2B :: (Data a, Typeable t) => a -> (forall d1 d2. (Data d1, Data d2) => t d1 d2) -> a
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext2M :: (Monad m, Data d, Typeable t) => (forall e. Data e => e -> m e) -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> m (t d1 d2)) -> d -> m d
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext2Q :: (Data d, Typeable t) => (d -> q) -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> q) -> d -> q
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext2R :: (Monad m, Data d, Typeable t) => m d -> (forall d1 d2. (Data d1, Data d2) => m (t d1 d2)) -> m d
- Internal.GlasgowUniversity.Data.Generics.Aliases: ext2T :: (Data d, Typeable t) => (forall e. Data e => e -> e) -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> t d1 d2) -> d -> d
- Internal.GlasgowUniversity.Data.Generics.Aliases: extB :: (Typeable a, Typeable b) => a -> b -> a
- Internal.GlasgowUniversity.Data.Generics.Aliases: extM :: (Monad m, Typeable a, Typeable b) => (a -> m a) -> (b -> m b) -> a -> m a
- Internal.GlasgowUniversity.Data.Generics.Aliases: extMp :: (MonadPlus m, Typeable a, Typeable b) => (a -> m a) -> (b -> m b) -> a -> m a
- Internal.GlasgowUniversity.Data.Generics.Aliases: extQ :: (Typeable a, Typeable b) => (a -> r) -> (b -> r) -> a -> r
- Internal.GlasgowUniversity.Data.Generics.Aliases: extR :: (Monad m, Typeable a, Typeable b) => m a -> m b -> m a
- Internal.GlasgowUniversity.Data.Generics.Aliases: extT :: (Typeable a, Typeable b) => (a -> a) -> (b -> b) -> a -> a
- Internal.GlasgowUniversity.Data.Generics.Aliases: mkM :: (Monad m, Typeable a, Typeable b) => (b -> m b) -> a -> m a
- Internal.GlasgowUniversity.Data.Generics.Aliases: mkMp :: (MonadPlus m, Typeable a, Typeable b) => (b -> m b) -> a -> m a
- Internal.GlasgowUniversity.Data.Generics.Aliases: mkQ :: (Typeable a, Typeable b) => r -> (b -> r) -> a -> r
- Internal.GlasgowUniversity.Data.Generics.Aliases: mkR :: (MonadPlus m, Typeable a, Typeable b) => m b -> m a
- Internal.GlasgowUniversity.Data.Generics.Aliases: mkT :: (Typeable a, Typeable b) => (b -> b) -> a -> a
- Internal.GlasgowUniversity.Data.Generics.Aliases: newtype Generic' (c :: Type -> Type)
- Internal.GlasgowUniversity.Data.Generics.Aliases: newtype GenericB'
- Internal.GlasgowUniversity.Data.Generics.Aliases: newtype GenericM' (m :: Type -> Type)
- Internal.GlasgowUniversity.Data.Generics.Aliases: newtype GenericQ' r
- Internal.GlasgowUniversity.Data.Generics.Aliases: newtype GenericR' (m :: Type -> Type)
- Internal.GlasgowUniversity.Data.Generics.Aliases: newtype GenericT'
- Internal.GlasgowUniversity.Data.Generics.Aliases: orElse :: Maybe a -> Maybe a -> Maybe a
- Internal.GlasgowUniversity.Data.Generics.Aliases: recoverMp :: forall (m :: Type -> Type). MonadPlus m => GenericM m -> GenericM m
- Internal.GlasgowUniversity.Data.Generics.Aliases: recoverQ :: forall (m :: Type -> Type) r. MonadPlus m => r -> GenericQ (m r) -> GenericQ (m r)
- Internal.GlasgowUniversity.Data.Generics.Aliases: type Generic (c :: Type -> Type) = forall a. Data a => a -> c a
- Internal.GlasgowUniversity.Data.Generics.Aliases: type GenericR (m :: Type -> Type) = forall a. Data a => m a
+ Agent.Control.Concurrent: class Proxy m => Async (m :: Type -> Type)
+ Agent.Control.Concurrent: data Task a
+ Agent.Control.Concurrent: fork :: Async m => m a -> m (Task a)
+ Agent.Control.Concurrent: instance Agent.Control.Concurrent.Async GHC.Types.IO
+ Agent.Control.Concurrent: instance Agent.Control.Concurrent.Proxy GHC.Types.IO
+ Agent.Control.Concurrent: join :: Async m => [Task a] -> m ()
+ Agent.Control.Concurrent: wait :: Async m => Task a -> m a
+ Agent.IO.Effects: class LlmChatConf m => LlmChatPost (m :: Type -> Type)
+ Agent.IO.Effects: class Monad m => LlmCodeMask (m :: Type -> Type)
+ Agent.IO.Effects: class (LlmConf m, LlmCodeRoot m, LlmCodeMask m) => LlmCodeRead (m :: Type -> Type)
+ Agent.IO.Effects: class Monad m => LlmCodeRoot (m :: Type -> Type)
+ Agent.IO.Effects: class (LlmConf m, LlmCodeRoot m) => LlmCodeSave (m :: Type -> Type)
+ Agent.IO.Effects: class Monad m => LlmConf (m :: Type -> Type)
+ Agent.IO.Effects: class Monad m => LlmPlanConf (m :: Type -> Type)
+ Agent.IO.Effects: class Monad m => LlmPlanMask (m :: Type -> Type)
+ Agent.IO.Effects: class (LlmConf m, LlmPlanConf m) => LlmPlanPost (m :: Type -> Type)
+ Agent.IO.Effects: class (LlmConf m, LlmPlanRoot m, LlmPlanMask m) => LlmPlanRead (m :: Type -> Type)
+ Agent.IO.Effects: class Monad m => LlmPlanRoot (m :: Type -> Type)
+ Agent.IO.Effects: llmChatWeb :: LlmChatPost m => String -> m (Either String String)
+ Agent.IO.Effects: llmCodeDir :: LlmCodeRoot m => m String
+ Agent.IO.Effects: llmCodeGet :: LlmCodeRead m => String -> m (Either String String)
+ Agent.IO.Effects: llmCodeMsk :: LlmCodeMask m => m [String]
+ Agent.IO.Effects: llmCodePut :: LlmCodeSave m => m [(String, String)] -> m [Either String ()]
+ Agent.IO.Effects: llmCodeSeq :: LlmCodeRead m => Maybe String -> m (Either String [String])
+ Agent.IO.Effects: llmPathCWD :: LlmConf m => m (Maybe String)
+ Agent.IO.Effects: llmPlanAPI :: LlmPlanConf m => m (Maybe String)
+ Agent.IO.Effects: llmPlanDir :: LlmPlanRoot m => m String
+ Agent.IO.Effects: llmPlanGet :: LlmPlanRead m => String -> m (Either String String)
+ Agent.IO.Effects: llmPlanKey :: LlmPlanConf m => m (Maybe String)
+ Agent.IO.Effects: llmPlanMsk :: LlmPlanMask m => m [String]
+ Agent.IO.Effects: llmPlanSeq :: LlmPlanRead m => Maybe String -> m (Either String [String])
+ Agent.IO.Effects: llmPlanWeb :: LlmPlanPost m => String -> m (Either String String)
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmCodeMask Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmCodeMask Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmCodeRoot Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmCodeRoot Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmCodeRoot Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmCodeMask Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmCodeRead Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmPlanConf Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmPlanConf Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmPlanConf Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmPlanPost Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmPlanMask Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmPlanMask Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmPlanRoot Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmPlanRoot Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance (Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmPlanRoot Agent.IO.Restricted.RIO, Agent.IO.Effects.LlmPlanMask Agent.IO.Restricted.RIO) => Agent.IO.Restricted.LlmPlanRead Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance Agent.IO.Effects.LlmChatConf Agent.IO.Restricted.RIO => Agent.IO.Restricted.LlmChatConf Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance Agent.IO.Effects.LlmChatConf Agent.IO.Restricted.RIO => Agent.IO.Restricted.LlmChatPost Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: instance Agent.IO.Effects.LlmConf Agent.IO.Restricted.RIO => Agent.IO.Restricted.LlmConf Agent.IO.Restricted.RIO
+ Agent.IO.Restricted: llmChatWeb :: LlmChatPost m => String -> m (Either String String)
+ Agent.IO.Restricted: llmCodeDir :: LlmCodeRoot m => m String
+ Agent.IO.Restricted: llmCodeGet :: LlmCodeRead m => String -> m (Either String String)
+ Agent.IO.Restricted: llmCodeMsk :: LlmCodeMask m => m [String]
+ Agent.IO.Restricted: llmCodeSeq :: LlmCodeRead m => Maybe String -> m (Either String [String])
+ Agent.IO.Restricted: llmPathCWD :: LlmConf m => m (Maybe String)
+ Agent.IO.Restricted: llmPlanAPI :: LlmPlanConf m => m (Maybe String)
+ Agent.IO.Restricted: llmPlanDir :: LlmPlanRoot m => m String
+ Agent.IO.Restricted: llmPlanGet :: LlmPlanRead m => String -> m (Either String String)
+ Agent.IO.Restricted: llmPlanKey :: LlmPlanConf m => m (Maybe String)
+ Agent.IO.Restricted: llmPlanMsk :: LlmPlanMask m => m [String]
+ Agent.IO.Restricted: llmPlanSeq :: LlmPlanRead m => Maybe String -> m (Either String [String])
+ Agent.IO.Restricted: llmPlanWeb :: LlmPlanPost m => String -> m (Either String String)
+ Agent.IO.Restricted: readFileStrict :: FilePath -> RIO String
+ Agent.LLM: History :: Bool -> [String] -> [String] -> History
+ Agent.LLM: [file] :: Context a -> Maybe (Index, Numbers)
+ Agent.LLM: [hist] :: Context a -> History
+ Agent.LLM: [list] :: Context a -> Maybe Filter
+ Agent.LLM: [next] :: History -> [String]
+ Agent.LLM: [prev] :: History -> [String]
+ Agent.LLM: [safe] :: Context a -> Maybe Filter
+ Agent.LLM: [view] :: History -> Bool
+ Agent.LLM: data History
+ Agent.LLM: instance GHC.Read.Read Agent.LLM.Mode
+ Agent.Utils.Code: file :: LlmCodeRead rio => Maybe String -> Int -> Bool -> rio (Either String String)
+ Agent.Utils.Code: files :: LlmCodeRead rio => Maybe String -> rio [String]
+ Agent.Utils.Common: file :: Bool -> String -> String
+ Agent.Utils.Common: files :: Bool -> [String] -> [String]
+ Agent.Utils.Common: index :: Int -> Int -> String
+ Agent.Utils.Common: leftpad :: Char -> Int -> [Char] -> [Char]
+ Agent.Utils.Plan: file :: LlmPlanRead rio => Maybe String -> Int -> Bool -> rio (Either String String)
+ Agent.Utils.Plan: files :: LlmPlanRead rio => Maybe String -> rio [String]
- Agent.IO.Restricted: getEnvVar :: SysEnv m => String -> m (Maybe String)
+ Agent.IO.Restricted: getEnvVar :: String -> RIO (Maybe String)
- Agent.IO.Restricted: input :: StdIn m => m String
+ Agent.IO.Restricted: input :: StdIn m => [String] -> [String] -> m String
- Agent.LLM: Context :: Bool -> Mode -> Load a -> Context a
+ Agent.LLM: Context :: Bool -> Mode -> Load a -> History -> Maybe Filter -> Maybe Filter -> Maybe (Index, Numbers) -> Context a
Files
- A-gent.cabal +14/−8
- CHANGELOG.md +30/−0
- Setup.hs +20/−0
- src/Agent/Control/Concurrent.hs +85/−0
- src/Agent/Control/IFC.hs +1/−1
- src/Agent/Control/MAC.hs +4/−12
- src/Agent/IO/Effects.hs +152/−9
- src/Agent/IO/Restricted.hs +565/−108
- src/Agent/JSON.hs +5/−7
- src/Agent/LLM.hs +189/−61
- src/Agent/Utils/Code.hs +70/−0
- src/Agent/Utils/Common.hs +81/−0
- src/Agent/Utils/Plan.hs +70/−0
A-gent.cabal view
@@ -9,13 +9,12 @@ build-type: Simple name: A-gent-version: 0.11.0.5+version: 0.11.0.6 synopsis: Polite & well educated LLM agent with excellent manners that always behaves well description: Polite and well educated LLM agent with excellent manners that always behaves well -homepage: https://a-gent.org-bug-reports: https://bsky.app/profile/a-gent.org+homepage: https://a-gent.org license: SSPL-1.0 OR AGPL-3.0-only license-file: LICENSE.md@@ -56,8 +55,10 @@ -- Prelude base >= 4 && < 5 -- Process+ -- NOTE: github.com/NixOS/hydra builds with stackage lastest LST (24.…)+ -- and stackage only has 'containers 0.7' while hackage has '0.8' , process >= 1.6.25.0 && < 2- , containers >= 0.8 && < 1+ , containers >= 0.7 && < 1 -- JSON , mtl >= 2.3.1 && < 3 ghc-options:@@ -139,21 +140,26 @@ base hs-source-dirs: src- exposed-modules:+ other-modules: Internal.GaloisInc.Text.JSON Internal.GaloisInc.Text.JSON.Generic Internal.GaloisInc.Text.JSON.String Internal.GaloisInc.Text.JSON.Types -- Internal.GlasgowUniversity.Data.Generics.Aliases- --- Agent.JSON- --+ exposed-modules:+ Agent.Control.Concurrent Agent.Control.IFC Agent.Control.MAC -- Agent.IO.Effects Agent.IO.Restricted+ --+ Agent.Utils.Common+ Agent.Utils.Code+ Agent.Utils.Plan+ --+ Agent.JSON -- Agent.LLM
CHANGELOG.md view
@@ -1,5 +1,35 @@ # Revision history for Λ-gent +## 0.11.0.6 -- 2026-03-20++* Added a `todo.org` file in the root folder of the project. This will allow to+ see current, but also future, status of the project.++* Expanded file capabilities for `Code` and `Plan`.++ * Files will be shown with `relative` paths. However, they will be handled+ with `absolute` paths under the hood.++* Added a few more `REPL` commands and improvements to typing.++ * **NOTE**: There is a `bug` with regard of multi-line text. Hopefully it will+ be fixed shortly.++* No longer on Bluesky and therefore, removed from `cabal` file.++* Issue with `haskellPackages` on `NixOS`. It seems that they use `stack` to+ build with the latest `LTS`. The fix is to change in the `cabal` file the+ following version contraint: `containers >= 0.7 && < 1` as `stackage` only has+ the version `0.7` of the `containers` package.+ + * **NOTE**: Fix is not verified. Well, this update will confirm (or not).++* Added the `Agent.Control.Concurrent` module to provide support for+ asynchronous tasks.++* Added `Agent.Utils` modules to help for the respective `modes`. This will help+ keep `Λ-gent` code small by providing ready to plug code bloks.+ ## 0.11.0.5 -- 2026-03-08 * `ENTER` only triggers if something is typed.
+ Setup.hs view
@@ -0,0 +1,20 @@+{-# OPTIONS_GHC -Wall -Werror #-}++--------------------------------------------------------------------------------++-- |+-- Copyright : (c) 2026 SPISE MISU ApS+-- License : SSPL-1.0 OR AGPL-3.0-only+-- Maintainer : SPISE MISU <mail+hackage@spisemisu.com>+-- Stability : experimental++--------------------------------------------------------------------------------++import Distribution.Simple+ ( defaultMain+ )++--------------------------------------------------------------------------------++main :: IO ()+main = defaultMain
+ src/Agent/Control/Concurrent.hs view
@@ -0,0 +1,85 @@+{-# OPTIONS_GHC -Wall -Werror #-}++{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-}++{-# LANGUAGE LambdaCase #-}++--------------------------------------------------------------------------------++-- |+-- Copyright : (c) 2026 SPISE MISU ApS+-- License : SSPL-1.0 OR AGPL-3.0-only+-- Maintainer : SPISE MISU <mail+hackage@spisemisu.com>+-- Stability : experimental++--------------------------------------------------------------------------------++module Agent.Control.Concurrent+ ( Async+ , Task+ , fork+ , wait+ , join+ )+where++--------------------------------------------------------------------------------++import Control.Concurrent ( ThreadId, forkFinally )+import Control.Concurrent.MVar+ ( MVar+ , newEmptyMVar+ , putMVar+ , readMVar+ )+import GHC.Exception ( SomeException, throw )++--------------------------------------------------------------------------------++data Task a = Task !ThreadId (IO (Either SomeException a))++--------------------------------------------------------------------------------++-- |+-- To prevent the users from adding instances of `Async`, we+-- provide a middle-layer (`Proxy`) between the `Monad` instance and the `Proxy`+-- (`Async`) instance.++class Monad m => Proxy m where+ new :: m (MVar a)+ put :: MVar a -> a -> m ( )+ get :: MVar a -> m a+class Proxy m => Async m where+ fork :: m a -> m (Task a)+ wait :: Task a -> m a+ join :: [ Task a ] -> m ( )++--------------------------------------------------------------------------------++instance Proxy IO where+ -- | newEmptyMVar: Create an MVar which is initially empty.+ new = newEmptyMVar+ -- | putMVar: Put a value into an MVar.+ put = putMVar+ -- |+ -- readMVar: Atomically read the contents of an MVar. If the MVar is currently+ -- empty, readMVar will wait until it is full. readMVar is guaranteed to+ -- receive the next putMVar.+ get = readMVar++instance Async IO where+ fork compute =+ new >>= \ var ->+ forkFinally compute (finally var) >>= \ tid ->+ pure $ Task tid $ get var+ where+ finally v r = put v r+ wait (Task _ mvar) =+ ( \case+ Right v -> v+ Left e -> throw e+ )+ <$> mvar+ join =+ mapM_ wait
src/Agent/Control/IFC.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -Wall -Werror #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE MultiParamTypeClasses #-}
src/Agent/Control/MAC.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -Wall -Werror #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-} -------------------------------------------------------------------------------- @@ -62,20 +62,12 @@ -------------------------------------------------------------------------------- -import Control.Monad- ( ap- , liftM- )+import Control.Monad ( ap, liftM ) -import Control.Exception- ( Exception- , SomeException- )+import Control.Exception ( Exception, SomeException ) import qualified Control.Exception as Ex -import Agent.Control.IFC- ( Flow- )+import Agent.Control.IFC ( Flow ) --------------------------------------------------------------------------------
src/Agent/IO/Effects.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -Wall -Werror #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-} -------------------------------------------------------------------------------- @@ -14,17 +14,160 @@ -------------------------------------------------------------------------------- module Agent.IO.Effects- ( -- * LLM- LlmChatConf(llmChatKey, llmChatAPI)- , LlmChatCurl(llmChatCurl)+ ( -- * LLM (Config)+ LlmConf(llmPathCWD)+ -- * LLM (Chat)+ , LlmChatConf(llmChatKey, llmChatAPI)+ , LlmChatPost(llmChatWeb)+ , -- * LLM (Code)+ LlmCodeRoot(llmCodeDir)+ , LlmCodeMask(llmCodeMsk)+ , LlmCodeRead(llmCodeSeq, llmCodeGet)+ , LlmCodeSave(llmCodePut)+ , -- * LLM (Plan)+ LlmPlanRoot(llmPlanDir)+ , LlmPlanMask(llmPlanMsk)+ , LlmPlanRead(llmPlanSeq, llmPlanGet)+ , LlmPlanConf(llmPlanKey, llmPlanAPI)+ , LlmPlanPost(llmPlanWeb) ) where -------------------------------------------------------------------------------- -class Monad m => LlmChatConf m where- llmChatAPI :: m (Maybe String)- llmChatKey :: m (Maybe String)+class+ Monad m+ => LlmConf m+ where+ llmPathCWD+ :: m (Maybe String) -class LlmChatConf m => LlmChatCurl m where- llmChatCurl :: String -> m (Either String String)+--------------------------------------------------------------------------------+--------------------------------------------------------------------------------++class+ Monad m+ => LlmChatConf m+ where+ llmChatAPI+ :: m (Maybe String)+ llmChatKey+ :: m (Maybe String)++--------------------------------------------------------------------------------++class+ LlmChatConf m+ => LlmChatPost m+ where+ llmChatWeb+ :: String+ -> m (Either String String)++--------------------------------------------------------------------------------+--------------------------------------------------------------------------------++class+ Monad m+ => LlmCodeRoot m+ where+ llmCodeDir+ :: m String -- Ex: "src"++--------------------------------------------------------------------------------++class+ Monad m+ => LlmCodeMask m+ where+ llmCodeMsk+ :: m [ String ] -- Ex: [ "*.hs", "*.lhs" ]++--------------------------------------------------------------------------------++class+ ( LlmConf m+ , LlmCodeRoot m+ , LlmCodeMask m+ )+ => LlmCodeRead m+ where+ llmCodeSeq+ :: Maybe String+ -> m (Either String [ String ])+ llmCodeGet+ :: String+ -> m (Either String String)++--------------------------------------------------------------------------------++class+ ( LlmConf m+ , LlmCodeRoot m+ )+ => LlmCodeSave m+ where+ llmCodePut+ :: m [ (String, String) ]+ -> m [ (Either String ()) ]++--------------------------------------------------------------------------------+--------------------------------------------------------------------------------++class+ Monad m+ => LlmPlanRoot m+ where+ llmPlanDir+ :: m String -- Ex: "llm"++--------------------------------------------------------------------------------++class+ Monad m+ => LlmPlanMask m+ where+ llmPlanMsk+ :: m [ String ] -- Ex: [ "plan_*.md" ]++--------------------------------------------------------------------------------++class+ ( LlmConf m+ , LlmPlanRoot m+ , LlmPlanMask m+ )+ => LlmPlanRead m+ where+ llmPlanSeq+ :: Maybe String+ -> m (Either String [ String ])+ llmPlanGet+ :: String+ -> m (Either String String)++--------------------------------------------------------------------------------++class+ Monad m+ => LlmPlanConf m+ where+ llmPlanAPI+ :: m (Maybe String)+ llmPlanKey+ :: m (Maybe String)++--------------------------------------------------------------------------------++class+ ( LlmConf m+ , LlmPlanConf m+ )+ => LlmPlanPost m+ where+ llmPlanWeb+ :: String+ -> m (Either String String)++--------------------------------------------------------------------------------+--------------------------------------------------------------------------------
src/Agent/IO/Restricted.hs view
@@ -1,13 +1,15 @@ {-# OPTIONS_GHC -Wall -Werror #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE LambdaCase #-}+ -------------------------------------------------------------------------------- -- |@@ -50,37 +52,44 @@ -- * Read/Print , input , output+ -- * LLM (Config)+ , llmPathCWD -- * LLM (Chat) , llmChatKey, llmChatAPI- , llmChatCurl+ , llmChatWeb -- * LLM (Code)+ , llmCodeDir+ , llmCodeMsk+ , llmCodeSeq, llmCodeGet+ -- * LLM (Plan)+ , llmPlanDir+ , llmPlanMsk+ , llmPlanSeq, llmPlanGet+ , llmPlanKey, llmPlanAPI+ , llmPlanWeb+ -- * Temporary+ , readFileStrict ) where -------------------------------------------------------------------------------- +import Data.Either ( partitionEithers )+import Data.List ( isInfixOf )+import Data.Maybe ( fromMaybe ) import qualified System.Environment.Blank as ENV-import System.IO- {-- ( hFlush, hPutStr, stdout- , hSetEncoding- , utf8- )- -}- ( hFlush, stdout- , hReady, stdin- ) import System.Exit- ( ExitCode- ( ExitFailure- , ExitSuccess- )+ ( ExitCode (ExitFailure, ExitSuccess) )+import System.IO ( hFlush, hReady, stdin, stdout ) import System.Process- ( readProcessWithExitCode+ ( CreateProcess (cwd)+ , proc+ , readCreateProcessWithExitCode+ , readProcessWithExitCode ) -import qualified Agent.IO.Effects as EFF+import qualified Agent.IO.Effects as EFF -------------------------------------------------------------------------------- @@ -97,25 +106,36 @@ (>>=) m f = RestrictedIO $ run m >>= run . f --------------------------------------------------------------------------------+-------------------------------------------------------------------------------- class StdIn m where- input :: m String + input+ :: [ String ]+ -> [ String ]+ -> m String class StdOut m where- output :: String -> m ()+ output+ :: String+ -> m () +--------------------------------------------------------------------------------+ instance StdIn RIO where- input =- RestrictedIO $ aux [] []+ -- BUG: Issue with multiline text+ input prev next =+ -- TODO: CTRL+U (erase from cursor to start of line) and CTRL+K (erase from+ -- cursor to end of line+ RestrictedIO $ aux prev next [] [] where- aux bcs acs =+ aux hps hns bcs acs = key >>= \ k ->- case k of+ case (k < [ '\32' ], k) of -- NOTE: To view keystrokes, use: `ghc -e getLine` (hit + ENTER)- '\008':[] ->+ (True , '\008':[]) -> case bcs of -- NOTE: Backspace (remove char if any and re-write after chars)- [ ] -> aux bcs acs+ [ ] -> aux hps hns bcs acs (_:cs) -> putChar '\008' >> hFlush stdout >> putChar '\032' >> hFlush stdout >>@@ -124,96 +144,141 @@ putChar '\008' >> hFlush stdout >> putStr acs >> hFlush stdout >> putStr rm >> hFlush stdout >>- aux cs acs+ aux hps hns cs acs where+ la = [ '\ESC', '[', 'D' ] lc = length acs es = concatMap id $ take lc $ repeat sp rm = concatMap id $ take lc $ repeat la sp = [ ' ' ]- la = [ '\ESC', '[', 'D' ]- '\010':[] ->- -- NOTE: Enter (only if something is typed)+ (True , '\010':[]) ->+ -- NOTE: ENTER (only if something is typed) case (bcs, acs) of ([], []) ->- aux bcs acs+ aux hps hns bcs acs ________ -> pure $ (++ acs) $ reverse $ bcs- '\ESC':'[':'C':[] ->- -- NOTE: Allowed escaped sequences: Arrow "->"+ (True , '\012':[]) ->+ -- NOTE: Form Feed becomes "/w" (clear screen)+ pure $ "/w"+ (True , '\ESC':'[':'A':[]) ->+ -- NOTE: Allowed escaped sequences: Arrow "↑"+ case (txt, hps, hns) of+ (_, [ ], _) -> aux hps hns bcs acs+ (_, p:ps, _) ->+ putStr rm >> hFlush stdout >>+ putStr es >> hFlush stdout >>+ putStr rm >> hFlush stdout >>+ putStr p >> hFlush stdout >>+ aux ps (p:hns) rev []+ where+ rev = reverse p+ where+ sp = [ ' ' ]+ la = [ '\ESC', '[', 'D' ]+ lc = length txt+ es = concatMap id $ take lc $ repeat sp+ rm = concatMap id $ take lc $ repeat la+ txt = (++ acs) $ reverse $ bcs+ (True , '\ESC':'[':'B':[]) ->+ -- NOTE: Allowed escaped sequences: Arrow "↓"+ case (txt, hps, hns) of+ (_, _, [ ]) -> aux hps hns bcs acs+ (_, _, p:ps) ->+ putStr rm >> hFlush stdout >>+ putStr es >> hFlush stdout >>+ putStr rm >> hFlush stdout >>+ putStr p >> hFlush stdout >>+ aux (p:hps) ps rev []+ where+ rev = reverse p+ where+ sp = [ ' ' ]+ la = [ '\ESC', '[', 'D' ]+ lc = length txt+ es = concatMap id $ take lc $ repeat sp+ rm = concatMap id $ take lc $ repeat la+ txt = (++ acs) $ reverse $ bcs+ (True , '\ESC':'[':'C':[]) ->+ -- NOTE: Allowed escaped sequences: Arrow "→" case (bcs, acs) of (_, [ ]) ->- aux bcs acs+ aux hps hns bcs acs (_, c:cs) -> putStr k >> hFlush stdout >>- aux (c:bcs) cs- '\ESC':'[':'D':[ ] ->- -- NOTE: Allowed escaped sequences: Arrow "<-"+ aux hps hns (c:bcs) cs+ (True , '\ESC':'[':'D':[ ]) ->+ -- NOTE: Allowed escaped sequences: Arrow "←" case (bcs, acs) of ([ ], _) ->- aux bcs acs+ aux hps hns bcs acs (c:cs, _) -> putStr k >> hFlush stdout >>- aux cs (c:acs)- '\ESC':'[':'H':[] ->- -- NOTE: HOME- putStr rm >> hFlush stdout >>- (aux [] ((reverse bcs) ++ acs))- where- lc = length bcs- rm = concatMap id $ take lc $ repeat la- la = [ '\ESC', '[', 'D' ]- '\ESC':'[':'1':';':'5':'C':[] ->- -- NOTE: Allowed escaped sequences: CTRL + Arrow "->"+ aux hps hns cs (c:acs)+ (True , '\ESC':'[':'1':';':'5':'C':[]) ->+ -- NOTE: Allowed escaped sequences: CTRL + Arrow "→" putStr rm >> hFlush stdout >>- (aux (ts ++ bcs) ds)+ (aux hps hns (ts ++ bcs) ds) where ds = dropWhile (== ' ') $ dropWhile (/= ' ') acs ts = drop (length ds) $ reverse acs+ la = [ '\ESC', '[', 'C' ] lc = length ts rm = concatMap id $ take lc $ repeat la- la = [ '\ESC', '[', 'C' ]- '\ESC':'[':'1':';':'5':'D':[] ->- -- NOTE: Allowed escaped sequences: CTRL + Arrow "<-"+ (True , '\ESC':'[':'1':';':'5':'D':[]) ->+ -- NOTE: Allowed escaped sequences: CTRL + Arrow "←" putStr rm >> hFlush stdout >>- (aux ds (ts ++ acs))+ (aux hps hns ds (ts ++ acs)) where ds = dropWhile (== ' ') $ dropWhile (/= ' ') bcs ts = drop (length ds) $ reverse bcs+ la = [ '\ESC', '[', 'D' ] lc = length ts rm = concatMap id $ take lc $ repeat la+ (True , '\ESC':'[':'H':[]) ->+ -- NOTE: HOME+ -- HERE:+ putStr rm >> hFlush stdout >>+ (aux hps hns [] ((reverse bcs) ++ acs))+ where la = [ '\ESC', '[', 'D' ]- '\ESC':'[':'F':[] ->+ lc = length bcs+ rm = concatMap id $ take lc $ repeat la+ (True , '\ESC':'[':'F':[]) -> -- NOTE: END putStr rm >> hFlush stdout >>- (aux ((++ bcs) $ reverse $ acs) [])+ (aux hps hns ((++ bcs) $ reverse $ acs) []) where+ la = [ '\ESC', '[', 'C' ] lc = length acs rm = concatMap id $ take lc $ repeat la- la = [ '\ESC', '[', 'C' ]- '\ESC':'[':'3':'~':[] ->+ (True , '\ESC':'[':'3':'~':[]) -> case acs of -- NOTE: Delete (remove char if any and re-write after chars)- [ ] -> aux bcs acs+ [ ] -> aux hps hns bcs acs (_:cs) -> putStr cs >> hFlush stdout >> putChar ' ' >> hFlush stdout >> putStr rm >> hFlush stdout >>- (aux bcs cs)+ (aux hps hns bcs cs) where+ la = [ '\ESC', '[', 'D' ] lc = length acs rm = concatMap id $ take lc $ repeat la- la = [ '\ESC', '[', 'D' ]- '\ESC':__ ->+ (True , '\ESC':__) -> -- NOTE: Other escaped sequences (skip)- aux bcs acs- '\DEL':[] ->+ aux hps hns bcs acs+ (True , _) ->+ -- NOTE: Skip all other unhandled control keys+ aux hps hns bcs acs+ (False, '\DEL':[]) -> -- NOTE: DELETE (skip)- aux bcs acs- _________ ->+ aux hps hns bcs acs+ (False, _) -> putStr k >> hFlush stdout >> putStr acs >> hFlush stdout >> putStr rm >> hFlush stdout >>- (aux (reverse k ++ bcs) acs)+ (aux hps hns (reverse k ++ bcs) acs) where lc = length acs rm = concatMap id $ take lc $ repeat la@@ -222,7 +287,7 @@ -- NOTE: https://stackoverflow.com/a/38553473 reverse <$> nxt [] where- nxt cs = + nxt cs = do c <- getChar m <- hReady stdin@@ -233,62 +298,454 @@ putStr x >> hFlush stdout --------------------------------------------------------------------------------+-------------------------------------------------------------------------------- -class SysEnv m where- getEnvVar :: String -> m (Maybe String)+class+ EFF.LlmConf m+ => LlmConf m+ where+ llmPathCWD+ :: m (Maybe String) -instance SysEnv RIO where- getEnvVar = RestrictedIO . ENV.getEnv+instance+ EFF.LlmConf RIO+ => LlmConf RIO+ where+ llmPathCWD = getEnvVar "LLM_PATH_CWD" --------------------------------------------------------------------------------+-------------------------------------------------------------------------------- -class ReadProc m where- withExitCode :: String -> [String] -> m (Either String String)+class+ EFF.LlmChatConf m+ => LlmChatConf m+ where+ llmChatAPI+ :: m (Maybe String)+ llmChatKey+ :: m (Maybe String) -instance ReadProc RIO where- withExitCode app fs = RestrictedIO $- do- (exitcode, out, err) <- readProcessWithExitCode app fs []- case exitcode of- ExitSuccess ->- pure $ Right out- ExitFailure _ ->+instance+ EFF.LlmChatConf RIO+ => LlmChatConf RIO+ where+ llmChatAPI = getEnvVar "LLM_CHAT_LOCALHOST_API"+ llmChatKey = pure Nothing++--------------------------------------------------------------------------------++class+ EFF.LlmChatConf m+ => LlmChatPost m+ where+ llmChatWeb+ :: String -> m (Either String String)++instance+ EFF.LlmChatConf RIO+ => LlmChatPost RIO+ where+ llmChatWeb json =+ EFF.llmChatAPI >>= \ mapi ->+ EFF.llmChatKey >>= \ mkey ->+ llmCurl json mapi mkey++--------------------------------------------------------------------------------+--------------------------------------------------------------------------------++class+ EFF.LlmConf m+ => LlmCodeRoot m+ where+ llmCodeDir+ :: m String++instance+ ( EFF.LlmConf RIO+ , EFF.LlmCodeRoot RIO+ )+ => LlmCodeRoot RIO+ where+ llmCodeDir = pure "src"++--------------------------------------------------------------------------------++class+ EFF.LlmCodeMask m+ => LlmCodeMask m+ where+ llmCodeMsk+ :: m [ String ]++instance+ ( EFF.LlmConf RIO+ , EFF.LlmCodeMask RIO+ )+ => LlmCodeMask RIO+ where+ llmCodeMsk = pure [ "*.hs" ]++--------------------------------------------------------------------------------++class+ ( EFF.LlmConf m+ , EFF.LlmCodeRoot m+ , EFF.LlmCodeMask m+ )+ => LlmCodeRead m+ where+ llmCodeSeq+ :: Maybe String+ -> m (Either String [ String ])+ llmCodeGet+ :: String+ -> m (Either String String)++instance+ ( EFF.LlmConf RIO+ , EFF.LlmCodeRoot RIO+ , EFF.LlmCodeMask RIO+ )+ => LlmCodeRead RIO+ where+ llmCodeSeq mfilter =+ EFF.llmPathCWD >>= \ ocwd ->+ EFF.llmCodeDir >>= \ cdir ->+ EFF.llmCodeMsk >>= \ cmsk ->+ realPath (fromMaybe [] ocwd ++ "/" ++ cdir) >>= \ efrp ->+ case efrp of+ Right frp ->+ ( \ case+ ([], rs) ->+ Right $ concat rs+ (ls, __) ->+ Left $ foldl1 (\ x y -> x ++ "\n" ++ y) ls+ )+ . partitionEithers+ . map+ ( \ case+ Right fns ->+ case mfilter of+ Nothing -> Right rel+ Just fil -> Right $ filter (isInfixOf fil) rel+ where+ rel = map (cdir ++ ) fns+ Left err ->+ Left err+ )+ <$> mapM (findFiles frp) cmsk+ Left err -> pure $ Left err + llmCodeGet relpath =+ EFF.llmPathCWD >>= \ ocwd ->+ realPath (fromMaybe [] ocwd) >>= \ efrp ->+ case efrp of+ Right frp ->+ Right <$> readFileStrict (frp ++ "/" ++ relpath)+ Left err ->+ pure $ Left err+ --------------------------------------------------------------------------------+-------------------------------------------------------------------------------- -class (EFF.LlmChatConf m, SysEnv m) => LlmChatConf m where- llmChatAPI :: m (Maybe String)- llmChatKey :: m (Maybe String)+class+ EFF.LlmConf m+ => LlmPlanRoot m+ where+ llmPlanDir+ :: m String -instance (EFF.LlmChatConf RIO, SysEnv RIO) => LlmChatConf RIO where- llmChatAPI = getEnvVar "LLM_CHAT_LOCALHOST_API"- llmChatKey = pure Nothing+instance+ ( EFF.LlmConf RIO+ , EFF.LlmPlanRoot RIO+ )+ => LlmPlanRoot RIO+ where+ llmPlanDir = pure "llm" -class (EFF.LlmChatConf m , ReadProc m) => LlmChatCurl m where- llmChatCurl :: String -> m (Either String String)+-------------------------------------------------------------------------------- -instance (EFF.LlmChatConf RIO, ReadProc RIO) => LlmChatCurl RIO where- llmChatCurl json =- llmChatAPI >>= \ mapi ->- llmChatKey >>= \ mkey ->- case (mapi, mkey) of- (Nothing, _ ) ->- RestrictedIO $ pure $ Left "No API address was provided"- (Just api, Nothing) ->- withExitCode "curl"- [ "--silent"+class+ EFF.LlmConf m+ => LlmPlanMask m+ where+ llmPlanMsk+ :: m [ String ]++instance+ ( EFF.LlmConf RIO+ , EFF.LlmPlanMask RIO+ )+ => LlmPlanMask RIO+ where+ llmPlanMsk = pure [ "plan_*.md" ]++--------------------------------------------------------------------------------++class+ ( EFF.LlmConf m+ , EFF.LlmPlanRoot m+ , EFF.LlmPlanMask m+ )+ => LlmPlanRead m+ where+ llmPlanSeq+ :: Maybe String+ -> m (Either String [ String ])+ llmPlanGet+ :: String+ -> m (Either String String)++instance+ ( EFF.LlmConf RIO+ , EFF.LlmPlanRoot RIO+ , EFF.LlmPlanMask RIO+ )+ => LlmPlanRead RIO+ where+ llmPlanSeq mfilter =+ EFF.llmPathCWD >>= \ ocwd ->+ EFF.llmPlanDir >>= \ pdir ->+ EFF.llmPlanMsk >>= \ pmsk ->+ realPath (fromMaybe [] ocwd ++ "/" ++ pdir) >>= \ efrp ->+ case efrp of+ Right frp ->+ ( \ case+ ([], rs) ->+ Right $ concat rs+ (ls, __) ->+ Left $ foldl1 (\ x y -> x ++ "\n" ++ y) ls+ )+ . partitionEithers+ . map+ ( \ case+ Right fns ->+ case mfilter of+ Nothing -> Right rel+ Just fil -> Right $ filter (isInfixOf fil) rel+ where+ rel = map (pdir ++ ) fns+ Left err ->+ Left err+ )+ <$> mapM (findFiles frp) pmsk+ Left err ->+ pure $ Left err++ llmPlanGet path =+ EFF.llmPathCWD >>= \ ocwd ->+ EFF.llmPlanDir >>= \ pdir ->+ realPath (fromMaybe [] ocwd ++ "/" ++ pdir) >>= \ efrp ->+ case efrp of+ Right rp ->+ if isInfixOf rp path then+ Right <$> readFileStrict path+ else+ pure $ Left $ "File: " ++ path ++ " is not in: " ++ rp+ Left err ->+ pure $ Left err++--------------------------------------------------------------------------------++class+ ( EFF.LlmConf m+ , EFF.LlmPlanConf m+ )+ => LlmPlanConf m+ where+ llmPlanAPI+ :: m (Maybe String)+ llmPlanKey+ :: m (Maybe String)++instance+ ( EFF.LlmConf RIO+ , EFF.LlmPlanConf RIO+ )+ => LlmPlanConf RIO+ where+ llmPlanAPI = getEnvVar "LLM_PLAN_LOCALHOST_API"+ llmPlanKey = pure Nothing++--------------------------------------------------------------------------------++class+ ( EFF.LlmConf m+ , EFF.LlmPlanConf m+ )+ => LlmPlanPost m+ where+ llmPlanWeb+ :: String+ -> m (Either String String)++instance+ ( EFF.LlmConf RIO+ , EFF.LlmPlanConf RIO+ )+ => LlmPlanPost RIO+ where+ llmPlanWeb json =+ EFF.llmPathCWD >>= \ ocwd ->+ realPath (fromMaybe [] ocwd) >>= \ efrp ->+ case efrp of+ Right rp ->+ gitExist rp >>= \ git ->+ if git then+ EFF.llmPlanAPI >>= \ mapi ->+ EFF.llmPlanKey >>= \ mkey ->+ llmCurl json mapi mkey+ else+ pure $ Left "No GIT repo initialized"+ Left err ->+ pure $ Left err++--------------------------------------------------------------------------------+--------------------------------------------------------------------------------++-- HELPERS (public)++getEnvVar+ :: String+ -> RIO (Maybe String)+getEnvVar =+ RestrictedIO . ENV.getEnv++--------------------------------------------------------------------------------++-- HELPERS (private)++findFiles+ :: String+ -> String+ -> RIO (Either String [String])+findFiles path mask =+ -- NOTE: Remove any '\NUL` from filepaths+ -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10110+ ( \case+ Right str ->+ Right+ $ map (filter (/= '\NUL'))+ $ map (drop (length path)) -- NOTE: Relative paths instead of full+ $ lines str+ Left err ->+ Left err+ )+ <$> withExitCodeAndCWD path "find"+ [ path+ , "-name"+ , mask+ ]++gitExist+ :: String+ -> RIO Bool+gitExist path =+ ( \case+ Right _ -> True+ Left _ -> False+ )+ <$> withExitCodeAndCWD path "git"+ [ "status"+ ]++llmCurl+ :: String+ -> Maybe String+ -> Maybe String+ -> RIO (Either String String)+llmCurl json mapi mkey =+ case (mapi, mkey) of+ (Nothing, _ ) ->+ RestrictedIO $ pure $ Left "No API address was provided"+ (Just api, Nothing) ->+ withExitCode "curl"+ ( [ "--silent" , "--show-error"- , "--header" , "\"Content-Type: application/json; charset=utf-8\""- , "--data" , json+ ]+ ++ hs +++ [ "--data", json , api ++ "/chat/completions" ]- (Just api, Just key) ->- withExitCode "curl"- [ "--silent"+ )+ (Just api, Just key) ->+ withExitCode "curl"+ ( [ "--silent" , "--show-error"- , "--header" , "\"Authorization: \"" ++ key ++ "\""- , "--header" , "\"Content-Type: application/json; charset=utf-8\""- , "--data" , json+ , "--header", "\"Authorization: \"" ++ key ++ "\""+ ]+ ++ hs +++ [ "--data", json , api ++ "/chat/completions" ]+ )+ where+ hs =+ [ "--header" , "\"Accept: application/json\""+ , "--header" , "\"Accept-Encoding: gzip, deflate, br\""+ , "--header" , "\"Content-Type: application/json; charset=utf-8\""+ , "--header" , "\"User-Agent: Λ-gent/0.11\""+ ]++readFileStrict+ :: FilePath+ -> RIO String+readFileStrict path =+ -- NOTE: «Too Many Open Files» error in Linux+ --+ -- - http://woshub.com/too-many-open-files-error-linux/+ RestrictedIO $+ readFile path >>= \bs ->+ length bs `seq` pure bs++realPath+ :: String+ -> RIO (Either String String)+realPath path =+ -- NOTE: Remove any '\NUL` from filepaths+ -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10110+ ( \case+ Right str -> Right $ filter (/= '\NUL') str+ Left err -> Left err+ )+ <$> withExitCode "realpath"+ [ "--canonicalize-existing"+ , "--logical"+ , "--physical"+ -- NOTE: End each output line with NUL, not newline+ , "--zero"+ , path+ ]++withExitCode+ :: String+ -> [String]+ -> RIO (Either String String)+withExitCode cmd args =+ RestrictedIO $+ do+ (exitcode, out, err) <- readProcessWithExitCode cmd args []+ case exitcode of+ ExitSuccess ->+ pure $ Right out+ ExitFailure _ ->+ pure $ Left err++withExitCodeAndCWD+ :: String+ -> String+ -> [String]+ -> RIO (Either String String)+withExitCodeAndCWD path cmd args =+ RestrictedIO $+ do+ (exitcode, out, err) <- readCreateProcessWithExitCode rcp []+ case exitcode of+ ExitSuccess ->+ pure $ Right out+ ExitFailure _ ->+ pure $ Left err+ where+ raw = proc cmd args+ rcp = raw { cwd = Just path }
src/Agent/JSON.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -Wall -Werror #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-} -------------------------------------------------------------------------------- @@ -25,16 +25,14 @@ -------------------------------------------------------------------------------- -import Data.Data- ( Data- )+import Data.Data ( Data ) -import Internal.GaloisInc.Text.JSON.Generic- ( Result(Error, Ok)+import Internal.GaloisInc.Text.JSON.Generic+ ( Result (Error, Ok) , encodeJSON , fromJSON )-import Internal.GaloisInc.Text.JSON.String+import Internal.GaloisInc.Text.JSON.String ( readJSValue , runGetJSON )
src/Agent/LLM.hs view
@@ -1,10 +1,12 @@ {-# OPTIONS_GHC -Wall -Werror #-} -{-# LANGUAGE Safe #-} {-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TupleSections #-}+ -------------------------------------------------------------------------------- -- |@@ -23,6 +25,7 @@ Mode(..) -- * Context , Load+ , History(..) , Context(..) -- * Paramenters , Eval@@ -34,24 +37,33 @@ -------------------------------------------------------------------------------- -import Prelude hiding (head, mod, print, read)--import Data.Char- ( toLower+import Prelude hiding+ ( head+ , mod+ , print+ , read )++import Data.Char ( toLower, toUpper )+import GHC.IO.Encoding ( setLocaleEncoding ) import System.IO- ( BufferMode(NoBuffering)- , hFlush, hSetBuffering, hSetEcho- , stderr, stdin, stdout+ ( BufferMode (NoBuffering)+ , hFlush+ , hSetBuffering+ , hSetEcho+ , stderr+ , stdin+ , stdout , utf8 )-import GHC.IO.Encoding- ( setLocaleEncoding- )+import Text.Read ( readMaybe ) +import Internal.GaloisInc.Text.JSON.Generic ( Data )+ import Agent.IO.Restricted-import Agent.JSON- ( Data+ ( RIO (..)+ , input+ , output ) --------------------------------------------------------------------------------@@ -64,27 +76,34 @@ | Echo | Plan | Test- deriving (Bounded, Enum, Eq, Show)--{- TODO: Use ↑ and ↓ to see previous and current input text-data Text =- Text- { prev :: [String]- , curr :: String- , next :: [String]- }--}+ deriving (Bounded, Enum, Eq, Read, Show) type Load a = Data a => Maybe a +data History =+ History+ { view :: Bool+ , prev :: [String]+ , next :: [String]+ }++type Index = Int++type Numbers = Bool++type Filter = String+ data Context a = Context { exit :: Bool , mode :: Mode- -- , text :: Text , load :: Load a+ , hist :: History+ , list :: Maybe Filter+ , safe :: Maybe Filter+ , file :: Maybe (Index, Numbers) } type Eval a =@@ -110,7 +129,8 @@ hSetBuffering stderr NoBuffering hSetBuffering stdin NoBuffering hSetBuffering stdout NoBuffering- hSetEcho stdin False -- NOTE: No default output when typing+ -- NOTE: No default output when typing+ hSetEcho stdin False putStrLn head >> hFlush stdout run $ loop ctx proc where@@ -119,49 +139,151 @@ { exit = False , mode = mod , load = Nothing+ , hist =+ History+ { view = False+ , prev = []+ , next = []+ }+ , list = Nothing+ , safe = Nothing+ , file = Nothing } -------------------------------------------------------------------------------- -- HELPERS +{- NOTE: Doesn't seem to work+ctrlC :: IO () -> IO ()+ctrlC task =+ -- NOTE: Catch CTRL+C+ -- https://neilmitchell.blogspot.com/2015/05/handling-control-c-in-haskell.html+ sync . (:[]) <$> fork task+-}+ loop :: Context a -> Eval a -> RIO () loop ctx eval =- if exit ctx then- return ()- else- print prompt >>- read >>= \ txt ->- case txt of- "/exit" ->- printLn [] >>- printLn "Λ-gent will shutdown" >>- loop (ctx { exit = True }) eval- "/help" ->- printLn [ ] >>- printLn help >>- loop ctx eval- "/mode chat" ->- printLn [ ] >>- printLn "Changed to chat-mode" >>- loop (ctx { mode = Chat }) eval- "/mode code" ->- printLn [ ] >>- printLn "Changed to code-mode" >>- loop (ctx { mode = Code }) eval- "/mode echo" ->- printLn [ ] >>- printLn "Changed to echo-mode" >>- loop (ctx { mode = Echo }) eval- ____________ ->- eval ctx txt >>= \ (upd, res) ->- printLn [ ] >>- printLn res >>- loop upd eval+ case ctx of+ Context { exit = True } ->+ return ()+ Context { hist = History { view = True } } ->+ eval ctx [ ] >>= \ (upd, res) ->+ printLn res >>+ loop (upd { hist = ((hist ctx) { view = False }) }) eval+ Context { list = Just _ } ->+ eval ctx [ ] >>= \ (upd, res) ->+ printLn res >>+ loop (upd { list = Nothing, safe = list ctx }) eval+ Context { file = Just _, safe = Just _ } ->+ eval ctx [ ] >>= \ (upd, res) ->+ printLn res >>+ loop (upd { file = Nothing }) eval+ __________________________________________ ->+ print prompt >>+ read hps hns >>= \ txt ->+ printLn [ ] >>+ case txt of+ '/':'m':'o':'d':'e':' ':c:cs -> caseMode txt c cs+ '/':'m' :' ':c:cs -> caseMode txt c cs+ '/':'f':'i':'l':'e' :midx -> caseFile txt midx+ '/':'f' :midx -> caseFile txt midx+ '/':'n':'u':'m':'s' :midx -> caseNums txt midx+ '/':'n' :midx -> caseNums txt midx+ '/':'l':'i':'s':'t' :mfil -> caseList txt mfil+ '/':'l' :mfil -> caseList txt mfil+ "/safe" -> caseSafe txt+ "/s" -> caseSafe txt+ "/hist" -> caseHist txt+ "/h" -> caseHist txt+ "/wipe" -> caseWipe txt+ "/w" -> caseWipe txt+ "/help" -> caseHelp txt+ "/?" -> caseHelp txt+ "/exit" -> caseExit txt+ "/e" -> caseExit txt+ '/':cmd ->+ printLn msg >>+ loop (nxt txt ctx) eval+ where+ msg = "Command not recognized: " ++ cmd+ ____________________________ ->+ eval ctx txt >>= \ (upd, res) ->+ printLn res >>+ loop (nxt txt upd) eval+ where+ his = hist ctx+ hps = prev his+ hns = next his+ nxt p c = c { hist = (hist c) { prev = p : hps } }+ -- NOTE: Enforce DRY in cases+ caseMode txt c cs =+ case mmod of+ Just mod ->+ printLn ("Changed to " ++ low ++ "-mode") >>+ loop (nxt txt (ctx { mode = mod, safe = Nothing })) eval+ where+ low = map toLower $ show mod+ Nothing ->+ printLn ("Invalid mode: " ++ c:cs) >>+ loop (nxt txt ctx) eval+ where+ mmod = readMaybe (toUpper c : map toLower cs) :: Maybe Mode+ caseFile txt midx =+ case lst of+ Just _ ->+ loop (nxt txt (ctx { file = (, False) <$> idx })) eval+ Nothing ->+ printLn ("Invalid index") >>+ loop (nxt txt ctx) eval+ where+ lst = safe ctx+ idx =+ case midx of+ [ ] -> Nothing+ ' ':cs -> readMaybe cs :: Maybe Int+ ______ -> Nothing+ caseNums txt midx =+ case lst of+ Just _ ->+ loop (nxt txt (ctx { file = (, True) <$> idx })) eval+ Nothing ->+ printLn ("Invalid index") >>+ loop (nxt txt ctx) eval+ where+ lst = safe ctx+ idx =+ case midx of+ [ ] -> Nothing+ ' ':cs -> readMaybe cs :: Maybe Int+ ______ -> Nothing+ caseList txt mfil =+ loop (nxt txt (ctx { list = fil })) eval+ where+ fil =+ case mfil of+ [ ] -> Just mfil+ ' ':cs -> Just cs+ ______ -> Nothing+ caseSafe txt =+ loop (nxt txt (ctx { list = safe ctx })) eval+ caseHist txt =+ loop (nxt txt (ctx { hist = his { view = True } })) eval+ caseWipe txt =+ -- NOTE: Clear screen & Move top-left+ printLn clear >>+ loop (nxt txt ctx) eval+ caseHelp txt =+ printLn help >>+ loop (nxt txt ctx) eval+ caseExit txt =+ printLn "Λ-gent will shutdown" >>+ loop (nxt txt (ctx { exit = True })) eval where+ clear = "\ESC[H\ESC[2J" -- NOTE: See `infocmp -x` prompt = "Λ-" ++ (map toLower $ show $ mode ctx) ++ "> " read = input print = output@@ -173,13 +295,19 @@ head :: String head =- "# Quit Λ-gent with `/exit`. For more commands, type `/help`."+ "# Exit Λ-gent with /e or /exit. For more commands, type /? or /help." help :: String help =- "# Supported commands\n" ++- "* /help : This message\n" ++- "* /exit : Quit Λ-gent\n" ++- "* /mode : Change mode to {" ++ ms ++ "}`. Ex:`/mode code`"+ "# Supported commands:\n" +++ "/? or /help | This message\n" +++ "/e or /exit | Exit Λ-gent\n" +++ "/w or /wipe | Clear screen\n" +++ "/m or /mode | Change to {" ++ ms ++ "}. Ex: /m code\n" +++ "/h or /hist | View input history\n" +++ "/s or /safe | Show stored list of files. See /list below\n" +++ "/l f or /list f | List of files, limited by mode, file masks and filter\n" +++ "/f i or /file i | Show file with the given index. See, /safe above\n" +++ "/n i or /nums i | Show file with line numbers. See /file above" where ms = foldl1 (\ x y -> x ++ "|" ++ y) $ map (map toLower . show) modes
+ src/Agent/Utils/Code.hs view
@@ -0,0 +1,70 @@+{-# OPTIONS_GHC -Wall -Werror #-}++{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-}++{-# LANGUAGE LambdaCase #-}++--------------------------------------------------------------------------------++-- |+-- Copyright : (c) 2026 SPISE MISU ApS+-- License : SSPL-1.0 OR AGPL-3.0-only+-- Maintainer : SPISE MISU <mail+hackage@spisemisu.com>+-- Stability : experimental++--------------------------------------------------------------------------------++module Agent.Utils.Code+ ( -- * Files+ files+ , file+ )+where++--------------------------------------------------------------------------------++import Data.Either ( fromRight )++import qualified Agent.IO.Effects as EFF+import qualified Agent.Utils.Common as COM++--------------------------------------------------------------------------------++files+ :: EFF.LlmCodeRead rio+ => Maybe String+ -> rio [ String ]+files mfilter =+ filesHelper mfilter True++file+ :: EFF.LlmCodeRead rio+ => Maybe String+ -> Int+ -> Bool+ -> rio (Either String String)+file mfilter index nums =+ filesHelper mfilter False >>= \ fs ->+ if length fs > index && index >= 0 then+ ( \case+ Right f -> Right $ COM.file nums f+ Left e -> Left e+ )+ <$> EFF.llmCodeGet (fs !! index)+ else+ pure $ Left msg+ where+ msg = "Index (" ++ show index ++ ") is out of bounds. See /last"++--------------------------------------------------------------------------------++-- HELPERS (private)++filesHelper+ :: EFF.LlmCodeRead rio+ => Maybe String+ -> Bool+ -> rio [ String ]+filesHelper mfilter nums =+ (COM.files nums) . fromRight [] <$> EFF.llmCodeSeq mfilter
+ src/Agent/Utils/Common.hs view
@@ -0,0 +1,81 @@+{-# OPTIONS_GHC -Wall -Werror #-}++{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-}++--------------------------------------------------------------------------------++-- |+-- Copyright : (c) 2026 SPISE MISU ApS+-- License : SSPL-1.0 OR AGPL-3.0-only+-- Maintainer : SPISE MISU <mail+hackage@spisemisu.com>+-- Stability : experimental++--------------------------------------------------------------------------------++module Agent.Utils.Common+ ( -- * Files+ files+ , file+ -- * Helpers+ , leftpad+ , index+ )+where++--------------------------------------------------------------------------------++leftpad+ :: Char+ -> Int+ -> [Char]+ -> [Char]+leftpad chr rep cs =+ replicate (rep - length xs) chr ++ xs+ where+ xs = take rep cs++index+ :: Int+ -> Int+ -> String+index rep idx =+ leftpad '0' rep $ show idx++files+ :: Bool+ -> [ String ]+ -> [ String ]+files nums fs =+ if null fs then+ []+ else+ map (\ (idx, rel) -> f idx rel)+ $ zip [0..] fs+ where+ len = length $ show $ length fs+ f i x =+ if nums then+ index len i ++ ": " ++ x+ else+ x++file+ :: Bool+ -> String+ -> String+file nums f =+ if null f then+ []+ else+ unlines+ $ map (\ (idx, rel) -> g idx rel)+ $ zip [1..] ls+ where+ ls = lines f+ len = length $ show $ length ls+ g i x =+ if nums then+ index len i ++ ": " ++ x+ else+ x
+ src/Agent/Utils/Plan.hs view
@@ -0,0 +1,70 @@+{-# OPTIONS_GHC -Wall -Werror #-}++{-# LANGUAGE NoGeneralizedNewtypeDeriving #-}+{-# LANGUAGE Safe #-}++{-# LANGUAGE LambdaCase #-}++--------------------------------------------------------------------------------++-- |+-- Copyright : (c) 2026 SPISE MISU ApS+-- License : SSPL-1.0 OR AGPL-3.0-only+-- Maintainer : SPISE MISU <mail+hackage@spisemisu.com>+-- Stability : experimental++--------------------------------------------------------------------------------++module Agent.Utils.Plan+ ( -- * Files+ files+ , file+ )+where++--------------------------------------------------------------------------------++import Data.Either ( fromRight )++import qualified Agent.IO.Effects as EFF+import qualified Agent.Utils.Common as COM++--------------------------------------------------------------------------------++files+ :: EFF.LlmPlanRead rio+ => Maybe String+ -> rio [ String ]+files mfilter =+ filesHelper mfilter True++file+ :: EFF.LlmPlanRead rio+ => Maybe String+ -> Int+ -> Bool+ -> rio (Either String String)+file mfilter index nums =+ filesHelper mfilter False >>= \ fs ->+ if length fs > index && index >= 0 then+ ( \case+ Right f -> Right $ COM.file nums f+ Left e -> Left e+ )+ <$> EFF.llmPlanGet (fs !! index)+ else+ pure $ Left msg+ where+ msg = "Index (" ++ show index ++ ") is out of bounds. See /last"++--------------------------------------------------------------------------------++-- HELPERS (private)++filesHelper+ :: EFF.LlmPlanRead rio+ => Maybe String+ -> Bool+ -> rio [ String ]+filesHelper mfilter nums =+ (COM.files nums) . fromRight [] <$> EFF.llmPlanSeq mfilter