Skip to content

Latest commit

 

History

3,664 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

haskell-agent

An independent agent harness, written in Haskell.

Screenshot 2026-08-23 at 10 43 49 PM

Try it out

nix run --accept-flake-config "github:digitallyinduced/haskell-agent"

Steering and queued prompts

While a fullscreen terminal turn is running, plain text prompts steer the current turn. Use /steer <prompt> to do this explicitly, or /queue <prompt> to wait until the current turn finishes. Bare /queue lists waiting prompts. When idle, either prompt command starts a new turn.

Supported LLM Providers

  • OpenAI (Subscription)
  • xAI (Subscription)
  • Claude (Subscription)
  • OpenRouter (API billing)
  • Google Gemini (Google account or AI Studio API billing)

What is distinctive

Most agent harnesses are effectively untyped imperative programming environments. A model emits loosely structured commands that mutate files, processes, conversation state, and other shared resources. Correctness depends on conventions enforced at runtime, often after effects have already begun.

haskell-agent is an exploration in a different direction. Model output is treated as untrusted input at the boundary. Accepted actions are decoded into typed values, state changes are expressed as pure transformations where possible, and effects are interpreted explicitly by the runtime. The model remains probabilistic; the environment in which its actions execute does not have to be.

  • A functional agent runtime: protocol states, tool policies, transport ownership, UI transitions, and agent lifecycles are modeled with algebraic data types. Pure transformations are separated from effectful boundaries, while STM coordinates shared concurrent state.
  • GHCi as part of the agent architecture: every model gets a persistent typed workspace. The harness distinguishes pure expressions from effectful actions, preserves bindings across calls, and recovers or restarts GHCi when interruption makes its state uncertain.
  • First-class model dialects: providers own authentication, billing, and transport, while dialects own the model-facing prompt, tool surface, schema conventions, project-instruction formatting, and subagent protocol. This keeps Codex-style and Grok Build behavior intact even when a transport such as OpenRouter serves models from several families.
  • Cross-provider state and billing policy: provider transitions preserve the pending turn and durable session state. Credential failover understands account cooldowns and prevents automatic fallback from silently converting subscription usage into API-credit spending. When a usage window is exhausted and no other provider can take over, an interactive session waits for the provider's reset time (Esc cancels) and then resumes on its own: an interrupted turn continues with a real user message explaining the interruption, while a turn that never started is simply submitted again.
  • Explicit response ownership: reusable WebSocket requests carry generation-scoped ownership. If an exchange is interrupted, malformed, or returned before its terminal frame, the connection is poisoned rather than risking old frames entering a later response.
  • Types as a path toward safer agency: typed tool decoding, approval rules, and execution policies are the current foundation for deeper work with LLMs, ADTs, type checkers, effect systems, and program verification.

Features

  • Choice of models and billing: use OpenAI/Codex, xAI/Grok, OpenRouter, Google Gemini, or Claude Code through subscriptions or API keys, and add local or hosted Responses-compatible models through the user model catalog.
  • Interactive terminal workflow: choose between fullscreen and inline interfaces with streaming Markdown, live todo progress, one-shot operation, and image attachments from files or the clipboard.
  • PostgreSQL-backed memory and portable sessions: persist conversations and scoped learned guidance, resume or search past work, compact long histories, and switch supported providers without losing the pending turn or durable session state.
  • Discoverable structured memory: sessions receive a catalog of existing user, repository, and checkout tables with their PostgreSQL comments, without loading records. The catalog is limited to 8,000 characters across all scopes, prioritizes names over descriptions, and reports omitted tables with guidance to inspect the complete catalog. It refreshes on resume, after compaction, and after table-name or description changes; schema and records remain available on demand.
  • Efficient long-running agents: page persisted history on demand and virtualize TUI scrolling to bound memory and rendering work as conversations grow.
  • Parallel agents and isolated work: delegate to persisted subagents with a configurable concurrency limit and create fresh sessions in managed Git worktrees.
  • Built-in coding tools: run shell commands, opt into a persistent GHCi workspace, search the web, and connect local MCP servers. Approval policies keep mutating operations under user control.
  • Natural-language Meta Console: press Cmd+K (Alt+K on terminals that report it that way), or use /meta <request>, to preview and apply typed model, account, MCP, web-fetch, LSP, shell, and concurrency configuration changes without adding the request to the coding conversation.
  • Guided agent workflows: use plan mode, reusable skills, and scoped learned guidance for repeatable tasks and project or user preferences.
  • Multimodal input and live voice dictation: attach images and files, or press Ctrl+R on macOS to stream microphone audio to OpenAI or xAI and insert the transcript into the prompt.
  • Telegram access: run a durable, allowlisted Telegram gateway with per-conversation sessions, multimodal messages, approvals, retries, and bounded concurrent processing.

These are important product features, but not the core differentiation.

Install

macOS without Nix

Apple silicon Macs running macOS 14 or newer can use the standalone release archive:

(
set -e
archive=haskell-agent-macos-arm64
download_dir="$(mktemp -d)"
curl -fL \
  "https://github.com/digitallyinduced/haskell-agent/releases/latest/download/$archive.tar.gz" \
  -o "$download_dir/$archive.tar.gz"
curl -fL \
  "https://github.com/digitallyinduced/haskell-agent/releases/latest/download/$archive.tar.gz.sha256" \
  -o "$download_dir/$archive.tar.gz.sha256"
(cd "$download_dir" && shasum -a 256 -c "$archive.tar.gz.sha256")
mkdir -p "$HOME/.local/opt" "$HOME/.local/bin"
rm -rf "$HOME/.local/opt/haskell-agent-macos-arm64"
tar -xzf "$download_dir/$archive.tar.gz" -C "$HOME/.local/opt"
rm -rf "$download_dir"
ln -sfn \
  "$HOME/.local/opt/haskell-agent-macos-arm64/bin/agent-cli" \
  "$HOME/.local/bin/agent-cli"
"$HOME/.local/bin/agent-cli" --version
)

Add $HOME/.local/bin to PATH if it is not already present. Keep the extracted directory intact: agent-cli discovers its bundled libraries, data files, PostgreSQL runtime, and helper tools relative to that directory. The symlink may live anywhere.

macOS requires its system libraries and frameworks to remain dynamically linked. The archive statically links the Haskell dependency graph and carries all non-system dynamic libraries alongside the executable, so it has no Nix runtime dependency.

Nix

  1. Install Determinate Nix by following its platform-specific installation instructions.

  2. Copy this prompt to your coding agent to install:

    Install haskell-agent by running `nix profile add --accept-flake-config github:digitallyinduced/haskell-agent`. If Nix says `haskell-agent` is already added, update it with `nix profile upgrade --refresh --accept-flake-config haskell-agent` instead. Verify the packaged runtime by running `agent-cli storage start`, `agent-cli storage doctor`, and `agent-cli storage stop`.
    

    Or install it yourself:

    nix profile add --accept-flake-config github:digitallyinduced/haskell-agent

    --accept-flake-config enables the public IHP binary cache declared by the flake.

Update

nix profile add does not replace an existing profile entry. Update an existing installation with:

nix profile upgrade --refresh --accept-flake-config haskell-agent

If the profile entry cannot be upgraded, remove and reinstall it:

nix profile remove haskell-agent
nix profile add --refresh --accept-flake-config github:digitallyinduced/haskell-agent

Verify that the packaged PostgreSQL runtime is available:

agent-cli storage start
agent-cli storage doctor
agent-cli storage stop

Run

Start an interactive session:

agent-cli

On Linux and macOS, supported OpenAI sessions can use the local desktop by default in an interactive terminal. One-shot and non-interactive runs keep the tool hidden unless --computer-use is supplied explicitly. Computer-use requests require separate approval, including under --yolo; choose Always allow this tool this session to let the workflow continue without prompting for every action. A provider safety check still requires fresh approval. /computer-use toggles the capability, while /computer-use on and /computer-use off set it explicitly. Disabling or re-enabling clears the workflow approval. On macOS, grant Screen Recording and Accessibility access to the terminal application in System Settings → Privacy & Security before using it. Start with agent-cli --no-computer-use to hide the tool from the model, then enable it later with /computer-use.

To embed the runtime through a local REST and Server-Sent Events API, run nix run .#agent-server. It provides durable session management, concurrent turn supervision, approvals, cancellation, and an OpenAPI 3.1 document. See the agent server guide.

The provider's Bash/shell execution tool is enabled by default. Enable the persistent run_ghci tool when needed:

agent-cli --ghci

The GHCi tool is optional and uses a ghci executable from PATH. Run the agent with a Nix-provided GHC when enabling it:

nix shell nixpkgs#ghc -c agent-cli --ghci

On Linux, native X11 uses xrandr, maim, and xdotool. Native Wayland uses the standard ScreenCast and RemoteDesktop portals with PipeWire. The Nix package includes the required command-line and GStreamer dependencies. See the computer-use guide for permissions, session behavior, non-Nix prerequisites, and desktop-specific checks.

For GHCi-only operation, disable Bash explicitly:

agent-cli --ghci --no-bash

During an interactive session, switch the available shell tools without restarting:

/shell ghci
/shell bash

Use /shell to show the current selection. /shell both and /shell none are also supported.

Run a one-shot task:

agent-cli -p \
  "inspect this Cabal project, explain its architecture, and run its tests"

Start in an isolated Git worktree:

agent-cli --worktree

By default, managed worktrees fetch and branch from the selected remote's latest default commit. Repositories without remotes instead branch from the current local HEAD. To disable fetching, add this to ~/.haskell-agent/config.json:

{
  "version": 1,
  "worktree": {
    "fetchLatestUpstream": false
  }
}

This policy applies to --worktree, /worktree, and subagent worktrees. The remote is selected from the current branch's configured remote, then upstream, origin, or the repository's sole remote. When a remote exists, a fetch failure aborts worktree creation rather than falling back to a stale commit.

Default-branch discovery first uses Git's local refs/remotes/<remote>/HEAD symbolic reference, shared across linked worktrees. The server is queried only when that reference is missing or invalid, or when fetching the cached branch reports that it no longer exists. Successful discovery and fetching refresh the local reference; unrelated fetch failures are not retried. If the server changes its default but retains the old branch, fetch the new default branch into its remote-tracking reference before refreshing the cache. For a new default named main on origin:

git fetch origin refs/heads/main:refs/remotes/origin/main &&
git remote set-head origin --auto

Substitute the selected remote and its new default branch name. set-head --auto requires the new remote-tracking reference to exist; the agent's isolated fetches do not create it.

Organization gateway model routing

Connected organization gateways must include provider and protocol for every entry in /v1/models. Supported pairs are openai/responses, xai/responses, and anthropic/anthropic. Upgrade the gateway before the client; missing or incompatible metadata fails closed.

The catalog selects the native provider transport, not the model name or a local dialect override. OpenAI uses the OpenAI transport, Grok uses xAI with its native compaction, and Claude uses the Claude gateway integration. Requests retain the organization's model alias and gateway credential/endpoint; provider environment variables cannot redirect gateway xAI requests to a personal endpoint.

Startup, resume, model switching, and child model selection use this catalog. Legacy top-level gateway sessions re-resolve their saved alias to its current provider. In-process subagents support OpenAI and xAI; Claude requires a separate child session.

Worktree recovery and cleanup

Managed worktrees are collected only when clean, incorporated into another branch, and inactive for at least 24 hours (configurable to a longer interval). Dirty checkouts and unmerged work never expire merely because they are old. Inactivity means saved-session idle time, not commit age or time since merge. Ownership, active-session, protection and safety checks still apply.

Local proof uses exact commit ancestry into another surviving local or remote-tracking branch, excluding the checkout's own branch and upstream. An equal-tip copy alone is insufficient, except for the resolved default branch (origin/HEAD, or local main/master when it is unavailable). For squash/rebase merges, authenticated GitHub API evidence must identify a merged PR with the checkout's exact HEAD. Its merge commit must exist locally and be reachable from the target branch, with exact final content and modes preserved for every branch-changed path. Missing or ambiguous evidence retains the checkout. GC does not fetch; cached refs may miss recent merges.

Clean recovery reuses existing Git trees rather than launching a hashing process for every file. Recovery refs also preserve checkout reflog commits, REBASE_HEAD commits, and AUTO_MERGE trees. Active merge/rebase operations still prevent collection. Conversation history and historical recovery snapshots are retained, and resuming a collected session restores its checkout.

Recognized, explicitly ignored build/cache directories are disposable and are not restored (for example node_modules, dist-newstyle, and .venv). An ignored result symlink directly into the Nix store is also disposable. Known ignored agent settings (.haskell-agent/settings.json) and generated OpenAI provider files (packages/agent-openai/data/models.json and prompt.md) are permitted only when byte-identical regular copies remain in the primary checkout; these duplicate copies are not restored. Additional files under .haskell-agent or differing contents prevent collection. Other ignored data, including .env files and local databases, prevents collection. Protect a checkout if its build/cache directories contain irreplaceable data.

Existing agent worktrees are automatically adopted only when their managed-root location, reciprocal linked-Git metadata, and saved-session provenance verify ownership. Their latest saved-session activity (including archived sessions and sessions in checkout subdirectories) initializes inactivity; adoption does not reset old worktrees to today. Missing or ambiguous ownership/activity retains the checkout. Database errors or incompatible session metadata defer adoption; stale legacy JSON and directory names/mtime are not activity fallbacks. Review the simulated adoption and retention reasons first:

agent-cli worktree gc --dry-run
agent-cli worktree enroll /absolute/path/to/managed/worktree
agent-cli worktree protect /absolute/path/to/managed/worktree
agent-cli worktree unprotect /absolute/path/to/managed/worktree
agent-cli worktree restore /absolute/path/to/managed/worktree

gc --dry-run reports eligibility, retained reasons, and per-checkout and total estimated bytes without writing registry entries or snapshots or collecting checkouts. Estimates are gross apparent checkout bytes, excluding snapshot overhead and filesystem/APFS sharing, not guaranteed net reclaimed disk space. Adoption reads the existing session database without starting it, migrating it, or importing old sessions. Manual enroll remains available for a checkout whose provenance cannot be established and starts its inactivity clock now. Manual gc examines every candidate and prints progress, with a per-checkout deadline. Background maintenance retains its short pass budget and rotates its starting point. Not-examined worktrees and failed operations are reported separately from retained worktrees. Active leases, explicit protection, unsupported Git state, incomplete snapshots, or detected concurrent edits prevent deletion. Restoration refuses to overwrite an existing directory and never resets a branch that moved after collection. Restored checkouts use a detached HEAD. Recovery requires the original shared Git repository and the registry under ~/.haskell-agent/worktrees/.registry; these local snapshots are not an off-machine backup. An interrupted restore that leaves a directory requires manual recovery rather than overwriting it. External editors do not participate in agent leases: protect a checkout while using it outside the agent, since edits after the final verification can race with removal. Existing saved-session lifetime and turn locks are also probed without creating lock files; an active or unverifiable lock retains its checkout even if its last saved activity is old. Stop pre-upgrade agents before explicit collection: their locks can be observed, but an old binary starting after the final probe does not participate in the new worktree lease protocol.

Configure a positive whole number of days with "worktree": {"inactivityDays": 14} in ~/.haskell-agent/config.json, or use worktree gc --inactivity-days 14 for one pass. Saving a conversation does not protect a checkout forever; use worktree protect for long-lived work.

Use --provider openai, --provider xai, --provider openrouter, --provider gemini, or --provider claude-code to override automatic provider detection. Claude Code is selected explicitly rather than by auto-detection.

Open /model and choose a Gemini model such as gemini-3.7-flash. If no Gemini account is connected, the CLI opens Google sign-in in your browser and stores the OAuth credential in the same managed credential store used by the other providers:

/model

No API key is required for this Google-account flow. For Google AI Studio API billing instead, set GOOGLE_API_KEY (preferred) or GEMINI_API_KEY, then run agent-cli --provider gemini --model gemini-3.7-flash.

Telegram

Create a bot with BotFather, then run:

agent-telegram setup --provider openai --cwd /path/to/project \
  --allowed-user 123456789
agent-telegram start
agent-telegram status

The gateway supports durable per-conversation sessions, allowlists, multimodal messages, approvals, and concurrent chats. See the Telegram guide for setup, groups, commands, and NixOS deployment. You can also ask the agent to “set up a Telegram agent”.

Model catalog and local models

Add local, hosted, or custom models through:

~/.haskell-agent/models.json

The built-in add-model skill can configure it for you. See the model catalog guide for the schema, local-server example, dialects, authentication, and compaction metadata.

Session titles use a cheap auxiliary model by default (Haiku on Claude, not Fable). /title-model pins a catalog model in ~/.haskell-agent/settings.json; /title-model --auto restores the automatic choice.

The built-in learn-about-user skill can derive consent-reviewed technical defaults from a confirmed public GitHub profile. Invoke it with /learn-about-user, $learn-about-user, or a natural-language request.

The built-in skill-installer skill installs Agent Skills from a GitHub repository, gist, URL, or local path into ~/.haskell-agent/skills or the project's .haskell-agent/skills directory. Invoke it with /skill-installer, $skill-installer, or a natural-language request such as “install this skill”.

Authentication

Works with your Codex, Grok, Google, and Claude accounts, plus provider API keys. Gemini can be connected interactively from /model or /account; GOOGLE_API_KEY (or GEMINI_API_KEY) remains an optional AI Studio fallback.

Voice dictation

Press Ctrl+R in the prompt composer, speak, and press Enter to stop (or Esc to cancel). Recording stays in the TUI; it does not suspend or close the session. On macOS, the resulting transcript is inserted at the cursor. Dictation follows the active model provider: OpenAI models use OpenAI and Grok models use xAI. Claude models have no transcription API, so they borrow a locally configured OpenAI account and fall back to an xAI account when no OpenAI credential exists. ChatGPT/Codex OAuth uses the subscription-backed streaming protocol used by the official desktop app and falls back to its buffered ChatGPT transcription route with the same recording if streaming fails. API keys use the public OpenAI Realtime API. Both OpenAI paths can update the composer while recording. Subscription auth is preferred when both OpenAI credential types are configured. OpenAI credentials can come from CODEX_ACCESS_TOKEN, CODEX_AUTH_JSON, $CODEX_HOME/auth.json (defaulting to ~/.codex/auth.json), OPENAI_API_KEY, CODEX_API_KEY, or a managed OpenAI account. For Grok models, dictation uses the configured xAI subscription or API-key credential; set XAI_STT_LANGUAGE to override xAI's default en. When an organization gateway is connected, the recording is sent only to the gateway's authenticated /v1/audio/transcriptions endpoint. The gateway uses its organization-managed transcription pool and streams partial transcripts into the composer while recording. Compatible older gateways and interrupted streams use the final-only upload on the same endpoint with the already captured recording; dictation never falls back to local provider credentials. Dictation is currently unavailable for OpenRouter and Gemini models.

Claude Code subscription

Install Claude Code, authenticate it with a first-party Claude subscription, and select the provider:

claude auth login
agent-cli --provider claude-code --model sonnet

The integration keeps a claude -p process alive through the reusable claude-agent-sdk-haskell package. Claude Code executes its built-in tools; complementary harness tools are exposed through an in-process MCP bridge and use the same host approval policy as other providers. The harness renders events, persists the session, and performs isolated local-summary compaction before restarting the Claude continuation. --yolo auto-approves ordinary calls, but host catastrophic command and Plan Mode safeguards remain enforced.

Anthropic's June 15, 2026 subscription-policy update says that Claude Agent SDK, claude -p, and third-party app usage currently draw from Claude subscription usage limits. Anthropic's current Agent SDK documentation also says third-party developers need prior approval to offer Claude.ai login or subscription rate limits in their products. Technical availability does not replace that approval requirement; consult the linked documents for current terms. See packages/agent-claude/README.md for details.

MCP servers

Use /mcp to add, authorize, enable, or remove MCP servers from the session UI, or configure them in ~/.haskell-agent/config.json. Paste a remote https://… URL or a local stdio command; HTTP servers that need OAuth can be authorized with i. agent-cli mcp list, add, enable, and disable manage the same catalog from the command line. See the MCP guide for the configuration schema, startup strategies, and tool exposure rules.

Meta Console

Press Cmd+K to open a compact configuration prompt over the current session, then describe a change such as “add the MCP server at https://example.com/mcp” or “connect my Grok account”. /meta <request> is the keyboard-independent fallback.

Meta Console uses a private, tool-free planner with no coding transcript. Its typed plan is validated and previewed before execution, and the normal approval policy still applies. Secrets are requested only through masked host-owned prompts and are never returned to the planner. See the Meta Console guide for supported actions and safety details.

Secret entry

The built-in ask_secret tool reads secrets through a masked prompt and gives the model only a private temporary-file path, keeping values out of chat and tool arguments. Files are removed when the tool runtime closes.

Inline images

The built-in show_image tool displays an image file (PNG, JPEG, GIF, BMP, TIFF) inline in the conversation next to the tool call: Kitty, Ghostty, WezTerm, and iTerm2 draw the bitmap natively, other terminals get a true-colour text approximation. The image is shown to the user only; it is not added to the model context.

To inspect a local image, Codex and Grok Build expose view_image. That tool attaches PNG, JPEG, WebP, and non-animated GIF files to the next model request. read_file remains text-only.

Inline charts

In interactive terminal sessions, render_chart displays static line, bar, area, and scatter charts from the same validated chart documents used by the macOS app. Ghostty and Kitty display PNGs directly; other terminals retain a readable text summary. No plotting program or browser is required. Fullscreen conversation history recreates cached previews from saved chart documents. Hover and zoom are not supported in the terminal.

Ideas and direction

Why an independent harness matters, why code and Haskell are useful foundations, and how types, effects, and verification could make agents safer are discussed in IDEAS.md.

Architecture

             agent-cli / agent-telegram / future clients
                              |
                       agent-runtime
       sessions | model catalog | turn execution | gateways
                 /            |
        agent-accounts   agent-tools
                              |
                   provider-neutral events
                              |
     +------------------- agent-core -------------------+
     | loop | tool contracts | approvals | agents | state |
     +-------------------------+------------------------+
                               |
                    canonical Responses model
                               |
       +---------------+---------------+---------------+---------------+---------------+
       |               |               |               |               |
 agent-openai      agent-xai    agent-openrouter  agent-gemini    agent-claude
       |               |               |               |               |
OpenAI / ChatGPT       xAI          OpenRouter      Gemini         Claude Code

The provider-neutral loop sees typed turns, tool calls, tool results, usage, and streamed events. Provider packages own wire formats, authentication, transport, and provider-specific continuation. Presentation consumes the same events through renderer-independent state.

agent-connectivity wraps individual provider submissions with replay-safe retry policy. On macOS it also uses NWPathMonitor to wake an interrupted submission as soon as the network path recovers; other platforms retain the portable polling fallback.

agent-runtime replaces agent-cli-runtime as the headless support library shared by the terminal CLI, server, and gateways. Its Agent.Runtime.* modules own session persistence and lifecycle primitives, model configuration, provider-neutral request construction and turn execution, database and MCP support, shared process resources, gateway support, and neutral host contracts. Product-level provider assembly, tool registration, and session orchestration still live in Agent.CLI.Runtime.Orchestration.*; extracting this support library does not make it a complete frontend-independent composition root. Interactive parsing, rendering, and TTY state also remain in agent-cli, so Cabal builds of gateway libraries do not depend on or rebuild the terminal frontend. The packaged Telegram service still carries the agent-cli executable as a runtime dependency because managed child sessions launch that executable.

agent-core owns the provider-neutral loop, tool contracts, scheduling, approvals, and shared state, but not concrete tool implementations. agent-tools implements filesystem, shell, GHCi/code-mode, image, and multi-agent tools against those contracts; core never depends on it. agent-accounts owns credential storage, account selection, authentication, and gateway credentials. agent-computer-use owns desktop input, accessibility, and platform backends used by the CLI and native integration; the runtime does not depend on it. These packages and the runtime have no CLI/TUI dependency or frontend module imports.

Repository review/delivery and process-hardening code lives in the independent agent-repository package. Native administration helpers and the Darwin foreign-library bridge live in agent-native-bridge. Its ordinary Haskell library uses the headless runtime and does not depend on the CLI. The Darwin foreign library still uses the CLI turn runner and legacy composition entry points; migrating that FFI boundary is deliberately deferred. The production CLI does not depend on native-only integration code. The resulting production rebuild change is recorded in the package-split benchmark.

scripts/check-package-boundaries.sh checks source ownership and invokes the Python 3 package-graph guard. The graph conservatively includes all components and conditional dependencies, including the deferred Darwin FFI edge, and rejects missing local packages and cycles.

Discovery and bounded import of Codex, Claude, Cursor, and Grok histories lives in agent-external-session. The CLI re-exports its public facade while keeping the provider-specific parsers independently testable and reusable by future frontends.

agent-claude delegates its generic process transport, protocol decoding, and session client to claude-agent-sdk-haskell, leaving subscription policy and Agent.Loop translation in the provider adapter.

Model targets resolve independently to a provider transport and a model-facing dialect. OpenAI models use the Codex dialect, xAI models use the Grok Build dialect, Gemini uses the portable Responses dialect over Google's native Code Assist or GenerateContent API, and OpenRouter selects Codex, Grok Build, or a portable Responses dialect from the model family.

Development

All compiler and package dependencies come from the pinned Nix flake.

nix develop
cabal test all

From the development shell, repl opens the agent under GHCi. Edit the harness, leave the running agent, reload the changed modules, and resume the same session without rebuilding the executable.

See AGENTS.md for the complete development workflow, including multi-package GHCi sessions, Nix package maintenance, and CLI testing.

License

MIT. See LICENSE.

About

an agent harness is just a monoid in the category of endofunctors

Resources

Stars

31 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages