Metadata-Version: 2.3
Name: karta-runtime
Version: 0.1.0
Summary: Karta agent runtime: harness adapters, dev serve, and the platform serve layer
License: MIT
Keywords: agents,ai,framework,orchestration
Author: Karta
Author-email: team@karta.sh
Requires-Python: >=3.9,<4.0
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Provides-Extra: dev-reload
Provides-Extra: gitmerge
Provides-Extra: otel
Provides-Extra: postgres
Provides-Extra: s3
Requires-Dist: anyio (>=4.8.0,<5.0.0)
Requires-Dist: boto3 (>=1.35.92,<2.0.0) ; extra == "s3"
Requires-Dist: claude-agent-sdk (>=0.2.82,<0.3.0) ; python_version >= "3.10"
Requires-Dist: fastapi (>=0.115.6,<0.116.0)
Requires-Dist: httpx (>=0.28.1,<0.29.0)
Requires-Dist: opentelemetry-api (>=1.27.0,<2.0.0) ; extra == "otel"
Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.27.0,<2.0.0) ; extra == "otel"
Requires-Dist: opentelemetry-instrumentation-asgi (>=0.48b0,<0.49) ; extra == "otel"
Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.48b0,<0.49) ; extra == "otel"
Requires-Dist: opentelemetry-instrumentation-httpx (>=0.48b0,<0.49) ; extra == "otel"
Requires-Dist: opentelemetry-sdk (>=1.27.0,<2.0.0) ; extra == "otel"
Requires-Dist: psycopg[binary] (>=3.2.3,<4.0.0) ; extra == "postgres"
Requires-Dist: pydantic (>=2.10.4,<3.0.0)
Requires-Dist: pygit2 (>=1.18.0,<2.0.0) ; (python_version >= "3.11") and (extra == "gitmerge")
Requires-Dist: python-frontmatter (>=1.1.0,<2.0.0)
Requires-Dist: rich (>=13.0.0,<16.0.0)
Requires-Dist: typer (>=0.21.0,<0.22.0)
Requires-Dist: uvicorn[standard] (>=0.32.1,<0.40.0)
Requires-Dist: watchfiles (>=1.0.0,<2.0.0) ; extra == "dev-reload"
Project-URL: Documentation, https://docs.karta.sh
Project-URL: Homepage, https://karta.sh
Project-URL: Repository, https://github.com/karta-sh/karta
Description-Content-Type: text/markdown

<div align="center">
  <h1>karta-runtime</h1>
  <p><strong>The Karta runtime: harness adapters, sessions, and the consumer session API on agent harnesses like Claude Code and OpenCode</strong></p>
</div>

<div align="center">

[![GitHub License](https://img.shields.io/github/license/karta/karta?logo=github)](https://github.com/karta/karta/blob/main/LICENSE)
[![PyPI Version](https://img.shields.io/pypi/v/karta-runtime?logo=pypi&color=blue)](https://pypi.org/project/karta-runtime/)
[![Python Version](https://img.shields.io/badge/Python-3.9--3.12-blue?logo=python)](https://www.python.org/)
[![GitHub issues](https://img.shields.io/github/issues/karta/karta)](https://github.com/karta/karta/issues)

</div>

> **Looking for the `karta` command?** The public CLI is
> [`@karta.sh/cli`](../../sdks/typescript-cli/) (`npm install -g @karta.sh/cli`).
> This package is `karta-runtime` - the internal engine that CLI bootstraps
> for `karta dev`, and the data plane the platform serves (RFC 0013,
> [docs/rfcs/0013-unified-cli.md](../../docs/rfcs/0013-unified-cli.md)). Its
> own CLI verbs are internal (see [CLI](#cli-internal)); use it directly as a
> Python SDK/library, exactly as documented below.

## Why Karta

Agent frameworks like [LangGraph](https://github.com/langchain-ai/langgraph), [CrewAI](https://github.com/crewAIInc/crewAI), [AutoGen](https://github.com/microsoft/autogen), and the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) build agents from scratch — defining their own tool systems, context management, memory, and execution loops.

**Karta takes the opposite approach.** Agent harnesses like [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and [OpenCode](https://opencode.ai) already solved the hard problems — tools, MCP servers, context management, memory, skills, and agentic execution. They're battle-tested, deeply integrated with real development workflows, and improving fast.

Karta builds **on top** of these harnesses in two tiers: a **thin core** that delegates all agent execution and conversation persistence to the harness, plus a **platform layer** that adds the multi-tenant SaaS machinery production applications need. Together they provide **multi-user sessions**, **multi-agent routing**, **participant-aware messaging**, **cross-instance communication**, **HTTP deployment**, **multi-tenancy with durable tenant isolation**, **BYOK key management**, **usage metering**, **policy enforcement**, and **lifecycle hooks** — without reinventing agent capabilities from scratch.

```
┌──────────────────────────────────────────────────────────┐
│  Other frameworks          │  Karta                      │
│                            │                              │
│  Build agents from scratch │  Build ON existing harnesses │
│  Own tool system           │  Harness tools (MCP, etc.)   │
│  Own memory/context        │  Harness memory/context      │
│  Own execution loop        │  Harness execution loop      │
│  + Multi-agent orchestr.   │  + Multi-user sessions       │
│                            │  + Multi-agent routing       │
│                            │  + Gateway & fan-out         │
│                            │  + Multi-tenancy             │
│                            │  + HTTP API & CLI            │
│                            │  + Hooks & policies          │
└──────────────────────────────────────────────────────────┘
```

## What Karta provides

| Feature | Description |
|---------|-------------|
| **Multi-agent routing** | Discover and route to specialist agents defined in your harness's native format |
| **Multi-user sessions** | Route conversations by metadata (customer ID, channel, etc.) with automatic persistence |
| **Participant model** | Multiple humans and AI agents in the same session with message attribution |
| **Gateway & fan-out** | Unified event ingestion with fan-out delivery to all session participants |
| **Cross-instance messaging** | Agents on different Karta instances communicate via gateway HTTP POST |
| **Multi-tenancy** | Workspace-per-user isolation with `KartaHub` orchestrator |
| **Session persistence** | SQLite (default), PostgreSQL, or S3 backends |
| **Lifecycle hooks** | Events on `message.received`, `message.completed`, `agent.handoff`, `session.created` |
| **Policy enforcement** | Validate messages against configurable policies (length limits, message counts, keyword gates) |
| **HTTP API** | Production-ready FastAPI server with REST endpoints and SSE streaming |
| **Internal CLI** | `karta-runtime dev/dev-serve/serve` - streaming terminal REPL and the local/self-host serve planes (the public CLI is `@karta.sh/cli`) |
| **Typed streaming** | Structured events: `text`, `tool_use`, `reasoning`, `step_start`, `step_finish`, `error`, `input_required` |

## Install

```bash
pip install karta-runtime
```

With optional backends:

```bash
pip install karta-runtime[postgres]   # PostgreSQL sessions
pip install karta-runtime[s3]         # S3 sessions
```

You also need a coding agent harness installed:

- **Claude Code**: `npm install -g @anthropic-ai/claude-code` ([docs](https://docs.anthropic.com/en/docs/claude-code))
- **OpenCode**: `curl -fsSL https://opencode.ai/install | bash` ([docs](https://opencode.ai))

## Quickstart

### Zero-config hello world

```python
from karta import Karta

app = Karta()
response = app.send_sync("Hello!")
print(response.text)
```

Karta auto-detects your harness from the project directory (`.claude/` → Claude Code, `.opencode/` → OpenCode).

### Multi-turn sessions

```python
from karta import Karta

app = Karta()

session = app.session(metadata={"customer_id": "abc123"})
response = session.send_sync("I need help with my order")
response = session.send_sync("Order #12345")

# Resume later by looking up the session
session = app.session(metadata={"customer_id": "abc123"})
response = session.send_sync("Any updates?")
```

### Multi-agent routing

Define specialist agents in your harness's native format (`.claude/agents/*.md` or `.opencode/agents/*.md`), then route:

```python
from karta import Karta

app = Karta()

# Route to a specific agent
response = app.send_sync("Audit my billing", agent="billing-specialist")

# Agent handoff within a session
session = app.session(metadata={"customer_id": "abc123"})
session.send_sync("I need help with my order")         # → default agent
session.current_agent = app.agents["billing"]           # handoff
session.send_sync("Check invoice #789")                 # → billing agent
```

### Multi-participant sessions

```python
from karta import Karta, HumanAgent

app = Karta()
session = app.session()

alice = HumanAgent(name="alice", display_name="Alice Chen")
bob = HumanAgent(name="bob", display_name="Bob Park")

# Messages are attributed to the sending participant
session.send_sync("I need help with deployment", participant=alice)
session.send_sync("I can help — what's the error?", participant=bob)
```

### Streaming

```python
import asyncio
from karta import Karta

async def main():
    app = Karta()
    async for event in app.stream("Explain quicksort"):
        if event.type == "text":
            print(event.text, end="", flush=True)

asyncio.run(main())
```

### HTTP API

```python
from karta import Karta
from karta.server import create_fastapi_app
import uvicorn

app = Karta()
fastapi_app = create_fastapi_app(app)
uvicorn.run(fastapi_app, host="0.0.0.0", port=8000)
```

This exposes:

- `POST /v1/send` — send a message, get a response
- `POST /v1/stream` — send a message, get SSE stream
- `POST /v1/sessions` — create a session
- `GET /v1/sessions` — list/lookup sessions
- `POST /v1/sessions/{id}/messages` — send within a session
- `POST /v1/sessions/{id}/input/respond` — respond to approval prompts
- `POST /v1/gateway/submit` — submit a gateway event
- `POST /v1/gateway/deliver` — deliver to a local participant
- `GET /healthz` — health check

### Multi-tenancy

```python
from karta import KartaHub

hub = KartaHub("/path/to/karta-root")

# Each tenant/user gets an isolated workspace
session = hub.session("acme", "alice", metadata={"topic": "billing"})
response = session.send_sync("Help with invoice")
```

### Lifecycle hooks

```python
app = Karta()

@app.on("message.completed")
async def log_response(event):
    print(f"Session {event.session.id}: {event.message.text}")

@app.on("agent.handoff")
async def track_handoff(event):
    print(f"Handoff: {event.payload['from']} → {event.payload['to']}")
```

### CLI (internal)

The customer-facing CLI is `@karta.sh/cli`; this package's verbs are internal
(local platform dev, self-host):

```bash
karta-runtime dev <path>         # Hot-reload REPL against a folder
karta-runtime dev-serve <path>   # Serve a folder behind the consumer session API
                                 # (what the unified CLI's `karta dev` spawns)
karta-runtime serve              # The platform serve plane (self-host / dev-infra)
```

This package installs no `karta` script at all - that name belongs to
the unified CLI (`npm install -g @karta.sh/cli`). It was dropped rather
than shimmed: karta-python never shipped to PyPI, so there is no
installed base to point anywhere.

## Architecture

```
                 External Clients / Other Karta Instances
                              │
                    ┌─────────▼──────────┐
                    │     Gateway         │  event ingestion, fan-out delivery
                    │  Local · HTTP       │  cross-instance messaging
                    └─────────┬──────────┘
                              │
                    ┌─────────▼──────────┐
                    │   Client Layer      │  HTTP API · CLI · Python SDK
                    └─────────┬──────────┘
                              │
                    ┌─────────▼──────────┐
                    │   Multi-Tenancy     │  KartaHub → WorkspaceManager
                    └─────────┬──────────┘
                              │
                    ┌─────────▼──────────┐
                    │    Karta Core       │  sessions, agents, policies,
                    │                     │  hooks, participants
                    └─────────┬──────────┘
                              │
                    ┌─────────▼──────────┐
                    │   Harness Layer     │  Claude Code (SDK) or
                    │                     │  OpenCode (CLI subprocess)
                    └─────────┬──────────┘
                              │
                    ┌─────────▼──────────┐
                    │   Coding Agent      │  tools, MCP, context,
                    │                     │  memory, skills
                    └──────────────────────┘
```

### Design principles

- **Convention over configuration** — follows your harness's folder conventions
- **Progressive disclosure** — start with 3 lines, add features as you need them
- **Harness-native** — agents are defined in `.claude/agents/*.md` or `.opencode/agents/*.md`, not reinvented
- **Zero abstraction tax** — examples from harness docs work as-is inside Karta

## Progressive examples

The [`examples/`](examples/) directory walks through increasing levels of complexity:

| Level | Directory | What it demonstrates |
|-------|-----------|---------------------|
| 0 | `level-0-zero-config/` | No config needed — just `app.send()` |
| 1 | `level-1-project-context/` | Project context with `AGENTS.md` / `.opencode` |
| 1 | `level-1-project-context-claude-code/` | Same, using Claude Code |
| 2 | `level-2-custom-agent/` | Single agent with a custom skill |
| 2 | `level-2-custom-agent-claude-code/` | Same, using Claude Code |
| 3 | `level-3-multiple-agents/` | Multi-agent routing |
| 4 | `level-4-multi-turn-sessions/` | Session persistence and metadata lookup |
| 5 | `level-5-hooks-policies/` | Lifecycle hooks and policy enforcement |

## Configuration

Karta uses your harness's native agent definitions as the source of truth. Optional Karta-specific settings go in `karta.jsonc`:

```jsonc
{
  "cli": {
    "hidden_event_types": ["system"],
    "input_required_policy": "prompt"
  },
  "harness": {
    "claude": {
      "idle_timeout_seconds": 600,
      "permission_intercept": false
    }
  }
}
```

See [`examples/configurations/`](examples/configurations/) for copy-ready templates.

## Supported harnesses

| Harness | Adapter | Detection |
|---------|---------|-----------|
| Claude Code | `ClaudeAdapter` (via Claude SDK) | `.claude/` directory or `CLAUDE.md` |
| OpenCode | `OpenCodeAdapter` (via `opencode run`) | `.opencode/` directory |

Adding a new harness means implementing one `HarnessAdapter` class.

## Documentation

Design documents and implementation specs live in [`dev/`](dev/):

- [Architecture](dev/ARCHITECTURE.md) — system architecture reference
- [Design document](dev/2026-02-12-karta-design-doc.md) — architecture and design philosophy
- [Implementation spec](dev/2026-02-13-karta-implementation-spec.md) — detailed implementation reference
- [CLI spec](dev/2026-02-13-karta-cli.md) — CLI commands and options
- [HTTP API spec](dev/2026-02-13-karta-http-api.md) — REST endpoint reference
- [Gateway implementation plan](dev/2026-02-14-gateway-implementation-plan.md) — gateway system design
- [Multi-tenancy architecture](dev/2026-02-14-multi-tenancy-architecture.md) — workspace isolation deep dive
- [Phase status](dev/phase-status.md) — implementation progress tracking

## Development

```bash
# Install dependencies
poetry install --all-extras

# Run tests
poetry run pytest

# Lint
poetry run ruff check src/ tests/

# Type check
poetry run mypy src/karta/
```

## License

This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.

