Metadata-Version: 2.4
Name: koda-agent-runtime
Version: 0.1.0
Summary: Reusable runtime, config loader and bootstrap for KODA-ecosystem A2A agents on Amazon Bedrock AgentCore.
Project-URL: Homepage, https://bitbucket.org/kushki/koda-agent-runtime
Project-URL: Repository, https://bitbucket.org/kushki/koda-agent-runtime
Author: Kushki
License-Expression: MIT
License-File: LICENSE
Keywords: a2a,agent,agentcore,bedrock,koda,strands
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.110.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: bootstrap
Requires-Dist: bedrock-agentcore>=0.1.0; extra == 'bootstrap'
Requires-Dist: boto3>=1.35.0; extra == 'bootstrap'
Requires-Dist: strands-agents[a2a]>=0.1.0; extra == 'bootstrap'
Requires-Dist: uvicorn>=0.30.0; extra == 'bootstrap'
Provides-Extra: cdk
Requires-Dist: aws-cdk-aws-bedrock-agentcore-alpha; extra == 'cdk'
Requires-Dist: aws-cdk-lib>=2.150.0; extra == 'cdk'
Requires-Dist: constructs>=10.0.0; extra == 'cdk'
Description-Content-Type: text/markdown

# koda-agent-runtime

Runtime compartido para agentes Koda A2A sobre Amazon Bedrock AgentCore.
Extrae en un solo paquete versionado las piezas que cada `koda-agent-*`
repite: constantes de custom headers, middleware FastAPI, cliente HTTP
hacia el registry, decoradores de tools, loader del `agent.yaml`, y un
bootstrap `build_agent_app(cfg)` que ensambla Strands + A2A + FastAPI
en una llamada.

Ver `KODA_AGENT_RUNTIME_ARCHITECTURE.md` en el monorepo `koda-platform`
para el diseño completo, precedente (`koda-mcp-access`), roadmap y
compatibilidad.

## Instalación

Elige el `extra` según lo que consuma el agente:

```toml
# El mínimo — headers, middleware, registry client, config loader.
"koda-agent-runtime>=0.1.0a0"

# El agente típico — ademas Strands + A2A + Bedrock para build_agent_app.
"koda-agent-runtime[bootstrap]>=0.1.0a0"

# El stack CDK del agente — Construct KodaAgentRuntime.
"koda-agent-runtime[cdk]>=0.1.0a0"
```

## Uso — un agente en 10 líneas

`src/agent.py`:

```python
from pathlib import Path
import uvicorn
from koda_agent_runtime.config import load_agent_config
from koda_agent_runtime.app import build_agent_app

_BASE = Path(__file__).resolve().parent.parent
app = build_agent_app(load_agent_config(_BASE / "agent.yaml"), base_dir=_BASE)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=9000)
```

`agent.yaml`:

```yaml
name: koda-agent-example
version: 0.1.0
description: |
  Un agente de ejemplo.

model:
  provider: bedrock
  model_id: us.amazon.nova-lite-v1:0
  temperature: 0.3
  max_tokens: 4000

tools:
  - discover_skills
  - fetch_registry_skill
  - invoke_agent
```

## Módulos

| Módulo | Responsabilidad |
|---|---|
| `koda_agent_runtime.headers` | Constantes `KODA_*_HEADER` + `ALLOWLISTED_HEADERS` (para `agentcore configure --request-header-allowlist`). |
| `koda_agent_runtime.context` | Module-level `_state` con `bearer` y `request_id`; `set_context`, `get_bearer`, `get_request_id`. |
| `koda_agent_runtime.middleware` | `koda_headers_middleware(app)` — captura los headers Koda-* que koda-proxy inyecta al invocar el runtime. Salta `/ping` para no borrar el state con health probes. |
| `koda_agent_runtime.registry` | `discover_agents`, `discover_skills`, `fetch_skill_bundle`, `invoke_peer` — hablan con el registry usando el bearer del state. |
| `koda_agent_runtime.tools` | Decoradores `@strands.tools.tool` sobre los helpers de `registry`. `build_tools(cfg)` filtra por lo declarado en `agent.yaml.tools`. |
| `koda_agent_runtime.config` | `load_agent_config(path)` → dataclass tipado `AgentConfig`. |
| `koda_agent_runtime.app` | `build_agent_app(cfg, base_dir)` → `FastAPI` con Strands `Agent` + `A2AServer` + middleware montados. Requiere `[bootstrap]` extra. |

## Contrato de headers Koda-*

koda-proxy (broker del registry) inyecta 8 custom headers al invocar
tu runtime. AgentCore solo forwardea headers con prefix
`X-Amzn-Bedrock-AgentCore-Runtime-Custom-`.

| Header | Contenido | Estable en cadenas A→B→C |
|---|---|---|
| `Koda-Authorization` | Bearer JWT vigente (Okta o self-signed 8h). | No — se refresca cuando expira. |
| `Koda-Sub` | Usuario humano original. | Sí. |
| `Koda-Email` | Email del usuario. | Sí. |
| `Koda-Roles` | Groups Okta del usuario. | Sí. |
| `Koda-Chain-Depth` | Hops desde el usuario. | +1 por hop. |
| `Koda-Request-Id` | uuid para trace end-to-end. | Sí. |
| `Koda-Agent-Path` | Path del agente que recibe. | N/A. |
| `Koda-Caller-Type` | `user` \| `agent`. | Depende del hop. |

Todos los strings viven en `koda_agent_runtime.headers` como constantes.
Nunca los repitas en tu código.

## Anti-patrones

- **No** uses `contextvars.ContextVar` para propagar el bearer entre
  middleware y tools: Strands corre tools en threads distintos al
  request handler; el ContextVar aparece vacío. Usa `set_context()`.
- **No** firmes SigV4 desde el agente para invocar peers. Delega via
  `invoke_peer(<path>, <msg>)` — el registry firma por ti.
- **No** llames `set_context()` en el middleware si no hay bearer. Los
  `/ping` de AgentCore te borrarían el state que otra request iba a
  usar.

## Desarrollo

```bash
uv sync --dev
uv run pytest
uv run ruff check src/
uv build
```

Release: bump `pyproject.toml`, tag `vX.Y.Z`, push tag.

## Versionado

- **v0.x** — alpha, breaking changes tolerados. Todos los agentes en
  migración inicial.
- **v1.0.0** — cuando la API sea estable y >=3 agentes la consuman en
  producción. A partir de acá, SemVer estricto.
