Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
e5cc28c
fix(hermes): A1 docker smoke fixes — version pin + plugin opt-in
Jun 4, 2026
3812c14
fix(hermes): A1.2 CRD schema + gitignore for cross-compile artifacts
Jun 4, 2026
82b7fa1
fix(hermes): A1 e2e smoke — six bugs surfaced by kind cluster run
Jun 4, 2026
6aa2fb4
fix(controller): always allow operator policy-echo ingress (NP)
Jun 4, 2026
bb12435
feat(e2e): add exec-brief-hermes-single scenario (Hermes Act 1)
Jun 4, 2026
493c118
fix(hermes): A1 e2e harness wiring — dev pull policy + Hermes posture…
Jun 4, 2026
f21048d
fix(hermes): A1 e2e — Hermes runs the full exec-brief pipeline on rea…
Jun 4, 2026
f4e42d6
fix(router): operator-UX token + sandbox metrics for /v1/responses
Jun 4, 2026
343eafa
feat(mesh): Hermes Act 2 — runtime-neutral Python AGT MeshClient + 6-…
Jun 4, 2026
5a372a4
feat(mesh): kars-agt-mesh Act 2.1 — full bidirectional E2E round-trip…
Jun 4, 2026
fdf7794
feat(runtime-contract): lift env injection to be runtime-neutral + pl…
Jun 4, 2026
16eed14
feat(mesh): Hermes Act 2.2 — multi-agent mesh end-to-end with kars_spawn
Jun 4, 2026
42d00a6
chore(harness): remove stray .new file from mesh-roundtrip-hermes
Jun 4, 2026
7730bd9
feat(mesh): Hermes Act 2.3 — autonomous sub-agent auto-responder
Jun 5, 2026
100555e
fix(hermes): pre_tool_call hook signature + heartbeat-vs-app metric b…
Jun 5, 2026
c957e7a
feat(cli): wire kars connect for Hermes sandboxes
Jun 5, 2026
2edc323
feat(operator): Enter-key drops into Hermes agent TUI (full UX parity)
Jun 5, 2026
1a6e7f4
fix(mesh): align Python AGT MeshClient wire format with TS SDK (cross…
Jun 5, 2026
1ebde5d
feat(cli): include kars-runtime-hermes in 'kars push' image set
Jun 5, 2026
19ec22c
fix(mesh-plugin): emit modern did:mesh:<sha256[:32]> DID format
Jun 5, 2026
dd810e3
build(agt): move AGT pin from pallakatos fork to upstream microsoft b…
Jun 5, 2026
0764dc8
fix(agt-mesh-python): JSON-wrap payload to interop with TS SDK receiver
Jun 5, 2026
8416c0a
fix(controller,hermes,agt-mesh-python): operator panel shows mesh pee…
Jun 5, 2026
2d2131c
fix(hermes): trust score at OpenClaw-convention baseline + bidi harness
Jun 5, 2026
28704be
fix(hermes): Entra OAuth identity verification for operator-panel parity
Jun 5, 2026
496cc92
feat(aks): foundryRbac auto-grant + Entra-JWT WS connect + AKS test s…
Jun 5, 2026
09cdff3
feat(hermes): productize + document the Hermes runtime for out-of-the…
Jun 6, 2026
ac8dfed
fix(build): hermetic AGT wheel build (PEP 668) + Hermes in architectu…
Jun 6, 2026
7f783aa
test(hermes): aks_hermes_hermes_bidi.sh + matrix-symmetry proof
Jun 6, 2026
10de47b
fix(controller,cli,harness): unblock Hermes status emission + docker …
Jun 6, 2026
d68f4d8
docs(hermes): honest parity audit vs OpenClaw
Jun 6, 2026
808bcf7
feat(hermes): tool-surface parity with OpenClaw — 5 of 5 gaps closed
Jun 6, 2026
1b0f59d
fix(hermes): mesh_worker file_transfer save runs unconditionally
Jun 6, 2026
e266812
ci(hermes): cargo fmt + LOC budget bumps for Hermes-support PR
Jun 7, 2026
1d06dcf
ci: gate Hermes runtime + kars-agt-mesh pytest suites
Jun 7, 2026
77c9ec0
ci(hermes): expand coverage — ruff lint, hermes reconcile-E2E, intero…
Jun 7, 2026
8fa83bc
fix(cli): runLocalK8s auto-bootstraps AGT toolkit (fresh-machine OOTB)
Jun 8, 2026
baeee5b
fix(sandbox-images): harden every external curl with retry+backoff
Jun 8, 2026
6886415
fix(router): Copilot IDE-JWT cache must respect expires_at, not just …
Jun 8, 2026
6dca0f8
fix(cli,router): auto-emit Copilot fallback chain + visible "no chain…
Jun 8, 2026
31cf2d4
fix(router): cap forward-proxy upstream connect at 10s (was unbounded)
Jun 8, 2026
76ed437
ci: ignore RUSTSEC-2026-0173 (proc-macro-error2 unmaintained, build-t…
Jun 8, 2026
e77e8f8
feat(cli,operator): Hermes in spawn picker + Hermes channels everywhere
Jun 8, 2026
ea59a9f
fix(cli): auto-load kars-runtime-hermes into kind + helpful build hint
Jun 8, 2026
f39d425
fix(cli): kars add surfaces stuck-container errors instead of lying
Jun 8, 2026
ac28f29
fix(cli,operator): honest error reporting in kars add + operator spawn
Jun 8, 2026
93938f5
fix(cli): kars add prints CRD-refresh hint on schema-mismatch rejection
Jun 8, 2026
986c884
fix(cli): kars dev auto-builds kars-runtime-hermes image into kind
Jun 8, 2026
2c0c912
fix(cli): CRD-mismatch hint primary path = `kars dev`, not naked `hel…
Jun 8, 2026
99deca3
fix(helm): pin KARS_DEV_PROFILE=true in values-local-dev.yaml
Jun 8, 2026
a5425fa
fix(cli): CRD-mismatch hint stops recommending naked `helm template |…
Jun 8, 2026
fbf48b5
docs(proposal): kars-sre — built-in AKS SRE Hermes agent (design)
Jun 8, 2026
da213a8
fix(cli) + docs(sre): always rebuild controller+router + expand acces…
Jun 8, 2026
ca7ad67
fix(hermes): entrypoint uses `cp -r` (not `cp -a`) for staged plugin …
Jun 8, 2026
3d0f302
fix(hermes): entrypoint config.yaml honors KARS_MODEL (was hardcoding…
Jun 8, 2026
8eaee9e
fix(router): accept `role` in SpawnRequest (was 422-ing valid Hermes …
Jun 8, 2026
5853301
ci(smoke): scripts/smoke/fresh-machine-ootb.sh + README
Jun 8, 2026
d721fe2
docs(showcase): kars showcase v1 — outline + 6 diagrams + 15-slide pi…
Jun 8, 2026
7a99b99
docs(showcase): fix slide 7 (KNOCK JSON overflow) + slide 11 (runtime…
Jun 8, 2026
ace04f4
docs(showcase): commit deck builder + README
Jun 8, 2026
2971d3a
docs(showcase): v2 deck — 21 slides, architecture deep-dive + outcome…
Jun 8, 2026
84907a8
docs(showcase): v3 deck — high-level architecture diagram + network e…
Jun 8, 2026
4b56560
ci: add MS+MIT copyright header to scripts/showcase/build-deck.js
Jun 9, 2026
650c097
docs(showcase): v4 deck — add live demo flow (Act I · intermission · …
Jun 9, 2026
23d0b60
docs(showcase): make Act II demo Kubernetes-native (ImagePullBackOff)
Jun 9, 2026
876527c
docs(sre): production-grade addendum — slicing, threat model, privile…
Jun 9, 2026
41dc5d8
docs(sre): mesh disabled at the source — SRE agent is not on the mesh…
Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .cargo/audit.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,20 @@ ignore = [
# behavior; no runtime/network exposure beyond what rustls itself provides.
# TODO: drop this ignore once rustls-native-certs / tokio-rustls roll forward.
"RUSTSEC-2025-0134",
# RUSTSEC-2026-0173 — `proc-macro-error2 2.0.1` is unmaintained
# https://rustsec.org/advisories/RUSTSEC-2026-0173
#
# Severity: informational ("unmaintained"), NOT a vulnerability.
# Source: transitive build-time chain (controller-only):
# oci-client 0.16.1 / oci-client 0.15.0 → oci-spec → getset 0.1.6 (proc-macro)
# → proc-macro-error2 2.0.1
# Same rationale as RUSTSEC-2024-0370 above — this is a proc-macro crate
# that runs at compile time inside rustc, not at runtime in the controller
# binary. There is no runtime attack surface from an unmaintained
# proc-macro crate beyond the build toolchain itself. The advisory was
# published 2026-06-07 (one day before this commit); no upstream
# replacement exists yet — `getset` would need to roll forward.
# TODO: drop this ignore when `getset` switches off `proc-macro-error2`
# or when oci-client upgrades past `getset 0.1.6`.
"RUSTSEC-2026-0173",
]
68 changes: 68 additions & 0 deletions .github/skills/agt-e2e-encryption/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ description: "Kars AGT E2E encryption skill — how the Signal Protocol inter-ag
## Overview
Kars agents communicate via the official Microsoft Agent Governance Toolkit (AGT) — a decentralized, E2E encrypted messaging protocol using Signal Protocol (X3DH + Double Ratchet). Transport is provided through `@microsoft/agent-governance-sdk` (upstream) wrapped by the in-repo `@kars/mesh` provider, selected at runtime via `createMeshTransport()`.

OpenClaw runtimes go through the TypeScript SDK; **Hermes** runtimes go through `runtimes/agt-mesh-python/` (kars-agt-mesh), a Python AGT MeshClient at byte-for-byte wire-format parity with the TS SDK. The same relay/registry pair serves both. Cross-runtime bidi (`OpenClaw ↔ Hermes`) is proven on AKS and local kind — see `tests/e2e/interop/`.

> **History note:** Earlier releases used a vendored fork of `@agentmesh/sdk` with 8+ local patches under `vendor/`. All upstream-relevant fixes have landed in AGT (PRs through the toolkit's 3.x line) and the vendored tree has been removed. The bug table below is preserved as a regression checklist — if any of these symptoms reappear, treat as a regression in the official SDK.

## Protocol Flow
Expand Down Expand Up @@ -90,3 +92,69 @@ not the WS relay.
per known peer plus a 30 s heartbeat tick, but only receives back
the relay's KNOCK-ack until a real bidirectional conversation
starts. Counters live in the router process and **reset on pod restart**.

---

## Cross-runtime mesh (Hermes ↔ OpenClaw)

### Wire format

| Component | TS SDK | Python `kars_agt_mesh` |
|---|---|---|
| MESSAGE frame | `{v, type:"message", from, to, id, ts, header:{dh, pn, n}, ciphertext}` (std base64, NOT urlsafe) | identical |
| KNOCK establishment | `{ik, ek, otk?}` — **short** keys | identical |
| Plaintext payload | `JSON.parse(plaintext)` hardcoded on receive | sender JSON-wraps; receiver unwraps (`_payload_to_wire_bytes` / `_wire_bytes_to_payload`) |
| AAD inside Double Ratchet | `caller_ad || x3dh_ad` where `caller_ad = "${sender_did}|${receiver_did}"` and `x3dh_ad = IK_init || IK_resp` (32 + 32 = 64) | identical |
| Header serialization | `dh(32) || pcl(u32 BE) || mn(u32 BE)` = 40 bytes | identical |
| HKDF (X3DH) | `HKDF(salt=zero[32], info="AgentMesh_X3DH_v1", IKM=F||dhConcat, len=32)`, F = `0xFF × 32` | identical |
| Connect-frame POP | std-base64 (NOT urlsafe) `public_key` + Ed25519 sig over timestamp | identical |

Both sides are pinned to the same upstream commit via `vendor/agt/pin.json::sha` — a single source of truth across TS, Python, and Rust.

### Prekey-clobber guard (DO NOT BYPASS)

`MeshClient.connect()` takes an exclusive `fcntl.flock` on `<identity_dir>/.mesh-prekeys.lock` (`runtimes/agt-mesh-python/src/kars_agt_mesh/client.py::_acquire_prekey_writer_lock`). A second Python process attempting to start a MeshClient for the same identity raises `MeshTransportError` naming the holder PID.

**DO NOT** debug a live Hermes pod by running `kubectl exec ... python3 -c "from kars_runtime_hermes.plugin import mesh; mesh._get_or_init_client(); ..."`. The secondary process would (before the guard) generate fresh X3DH key material and `PUT` it to the registry, clobbering the daemon's bundle and causing silent `Decrypt failed for did:mesh:...` log entries with no traceback. Cost the team several hours of debugging in 2026-06; see [`docs/internal/security-audits/2026-06-06-cross-runtime-mesh-aks.md`](../../docs/internal/security-audits/2026-06-06-cross-runtime-mesh-aks.md).

**Correct ways to inspect a live daemon:**

```bash
# Identity / DID
kubectl exec <pod> -c agent -- cat /sandbox/.hermes/.agt/identity.json | jq

# Operator trust store (peers seen by the daemon's mesh_worker)
ADMIN=$(kubectl get secret -n kars-<sb> admin-token -o jsonpath='{.data.token}' | base64 -d)
kubectl exec <pod> -c agent -- sh -c "curl -sS -H 'Authorization: Bearer $ADMIN' http://127.0.0.1:8443/agt/trust"

# Daemon log
kubectl logs <pod> -c agent
```

### AKS-specific Entra requirements

Hermes pods on AKS require **two** RBAC arms for the mesh to bring up the verified tier:

1. **The pod's workload-identity client (Entra Agent App auto-provisioned per sandbox)** — must have `Azure AI User` on the resource group. Auto-granted by the controller when you set `KarsAuthConfig.spec.foundryRbac` in the sandbox namespace. The CRD field landed in commit `496cc92`.
2. **The Foundry project's MI** — needs `Azure AI User` on the resource group for Memory Store operations. This is a one-time Portal step per project (see the `Foundry Memory Store Auth` section in the repo's CLAUDE.md).

The entrypoint exchanges the projected SA token for an Entra Agent App token (audience `<app-id>/.default`) and POSTs it to `/agt/registry/v1/registry/verify`. Success surfaces as `tier=verified, verified_app_id=<guid>` on the operator panel.

### `kars push --only runtime-hermes` is hermetic

Out of the box (no manual `git clone`, no `bash runtimes/build-agt-wheels.sh`):

```bash
kars push --only runtime-hermes --apply
```

The CLI auto-clones `microsoft/agent-governance-toolkit@kars-sdk-pop-signing@<pin sha>` (via `cli/src/lib/agt-bootstrap.ts::ensureAgtRepo`) and builds `runtimes/wheels/*.whl` (via `ensureAgtWheels`) before docker build. Cached by `runtimes/wheels/.agt-sha` so re-runs are no-ops.

### End-to-end harnesses

| Scope | Harness | Expected proof |
|---|---|---|
| Local kind, Hermes ↔ OpenClaw bidi | `tests/e2e/interop/hermes_openclaw_bidi.sh` | `score ≥ 500, status=delivered_and_replied` |
| AKS production, four scenarios | `tests/e2e/interop/aks_full_suite.sh` | 4/4 PASS (single, inter-agent, multi-fanout, Entra Verified) |

If either regresses, the first thing to suspect is the AGT pin — `vendor/agt/pin.json::sha` must match upstream `microsoft/agent-governance-toolkit:kars-sdk-pop-signing`. Pre-existing image layers may have a stale SDK; `kars push --only runtime-hermes --apply` (or `runtime-*` per affected runtime) refreshes them.
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,53 @@ jobs:
python -m ruff check .
python -m pytest

hermes-runtime:
name: Hermes Runtime (Python) Build & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Install kars-agt-mesh (sibling dep) + hermes (+dev extras)
run: |
python -m pip install --upgrade pip
# hermes plugin imports kars_agt_mesh at module load, so the sibling
# in-repo package must be installed first; matches the order in
# sandbox-images/hermes/Dockerfile.
pip install -e runtimes/agt-mesh-python
pip install -e "runtimes/hermes[dev]"
- name: Run hermes ruff lint
run: |
cd runtimes/hermes
python -m ruff check .
- name: Run hermes pytest
run: |
cd runtimes/hermes
python -m pytest -v

kars-agt-mesh-python:
name: kars-agt-mesh (Python AGT mesh transport) Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Install kars-agt-mesh (+test extras)
run: |
cd runtimes/agt-mesh-python
python -m pip install --upgrade pip
pip install -e ".[test]"
- name: Run kars_agt_mesh ruff lint
run: |
cd runtimes/agt-mesh-python
python -m ruff check .
- name: Run kars_agt_mesh pytest
run: |
cd runtimes/agt-mesh-python
python -m pytest -v

bicep-validate:
name: Bicep Validation
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,10 @@ FodyWeavers.xsd
# Rust
target/
target-linux-*/
# Cross-compile artifacts produced by docker run -v $PWD:/work …
# (e.g. `docker run … rust:1.89 cargo build` with CARGO_HOME=/work/.cargo-docker
# to keep the host's ~/.cargo cache unpolluted by container-arch builds).
.cargo-docker/
Cargo.lock
# Vendor lockfiles needed for reproducible Docker builds
!vendor/*/Cargo.lock
Expand Down
33 changes: 17 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 23 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ futures = "0.3"
# HTTP
axum = { version = "0.8", features = ["ws"] }
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json", "stream", "gzip", "brotli", "deflate"] }
# rustls 0.23 with aws-lc-rs is the only provider in our dep graph
# (transitively via reqwest + oci-client). We declare it as a direct
# workspace dep so the router's main() can install the provider
# explicitly — otherwise kube-client's first TLS handshake panics
# with "Could not automatically determine the process-level
# CryptoProvider" when multiple feature flags resolve.
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
tower = { version = "0.5", features = ["limit"] }
tower-http = { version = "0.6", features = ["trace", "cors"] }
hyper = "1"
Expand Down Expand Up @@ -101,24 +108,27 @@ lto = true
codegen-units = 1
strip = true

# ─── Temporary git override for AGT pre-release (POP-aware SDK) ───
# Pinned to pallakatos/agent-governance-toolkit@bdea1097 — fork-branch
# that adds the TypeScript SDK side of proof-of-possession registration
# and connect-frame signing. Upstream PR:
# https://github.com/microsoft/agent-governance-toolkit/pull/2772
# ─── Temporary git override for AGT pre-release fixes ───
# Pinned to microsoft/agent-governance-toolkit@3322175d on the
# `kars-sdk-pop-signing` feature branch (upstream repo, not a fork).
# This branch carries two pre-release fixes kars depends on:
# 1. TypeScript SDK proof-of-possession registration + connect-frame
# signing (upstream PR #2772, in review).
# 2. X3DH KDF spec-compliance per Signal §2.2 (F prefix in IKM,
# zero salt — separate upstream PR pending).
#
# The Rust agentmesh crate is unchanged between upstream `bae5de3` and
# our `bdea1097` (only the TypeScript SDK was touched); we still point
# the Rust [patch.crates-io] override at the fork SHA so the workspace
# stays at one consistent toolkit revision and `ci/check-agt-released.sh`
# The Rust agentmesh crate is unchanged between upstream main and our
# pin (only the TypeScript SDK was touched); we still point the Rust
# [patch.crates-io] override at the branch SHA so the workspace stays
# at one consistent toolkit revision and `ci/check-agt-released.sh`
# can track a single SHA.
#
# Remove this whole [patch.crates-io] block when:
# 1. PR #2772 merges to microsoft/agent-governance-toolkit main, AND
# 2. AGT cuts a release containing it (track via the daily check).
# 1. Both PRs merge to microsoft/agent-governance-toolkit main, AND
# 2. AGT cuts a release containing them (track via the daily check).
# At that point also drop the `vendor/agt/microsoft-agent-governance-sdk-*.tgz`
# tarball + the file: dep in mesh-plugin/package.json and switch to the
# published npm release.
[patch.crates-io]
agentmesh = { git = "https://github.com/pallakatos/agent-governance-toolkit", rev = "bdea10970f641fa695bb6dce7f8cee425f8aebd6", package = "agentmesh" }
agentmesh-mcp = { git = "https://github.com/pallakatos/agent-governance-toolkit", rev = "bdea10970f641fa695bb6dce7f8cee425f8aebd6", package = "agentmesh-mcp" }
agentmesh = { git = "https://github.com/microsoft/agent-governance-toolkit", rev = "3322175d88baf61e8ceab8e392e29fa2bf9b580a", package = "agentmesh" }
agentmesh-mcp = { git = "https://github.com/microsoft/agent-governance-toolkit", rev = "3322175d88baf61e8ceab8e392e29fa2bf9b580a", package = "agentmesh-mcp" }
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ It is built for three audiences:
- **A prompt-injected agent cannot leak your Azure credentials** — because the agent process runs under a different UID than the Rust router that holds them, on a different network, and never sees the bearer token. (iptables + NetworkPolicy back this up; they are not the primary control.)
- **Your security and platform teams review YAML, not Python.** Approval gates, rate limits, tool allowlists, content-safety floors, token budgets, and trust topology are declarative Kubernetes resources — commit them to a repo, reconcile with Argo / Flux, audit with `git log`. No out-of-band config store, no per-agent code review for policy.
- **Two agents that talk cannot be eavesdropped by you, by us, or by the relay.** Agent-to-agent messaging is end-to-end encrypted with Signal Protocol (X3DH + Double Ratchet) and KNOCK trust gating. The relay sees only ciphertext.
- **You are not locked to one runtime or one model provider.** Seven first-class runtimes (OpenClaw, OpenAI Agents SDK, Microsoft Agent Framework, LangGraph in Python and TypeScript, Anthropic, Pydantic-AI) plus BYO. GitHub Copilot, Azure AI Foundry, Azure OpenAI, and GitHub Models as backends — switching is a one-field CRD change, not a re-architecture. Native Anthropic-shape passthrough for Claude.
- **You are not locked to one runtime or one model provider.** Eight first-class runtimes (OpenClaw, Hermes, OpenAI Agents SDK, Microsoft Agent Framework, LangGraph in Python and TypeScript, Anthropic, Pydantic-AI) plus BYO. GitHub Copilot, Azure AI Foundry, Azure OpenAI, and GitHub Models as backends — switching is a one-field CRD change, not a re-architecture. Native Anthropic-shape passthrough for Claude.
- **What runs on your laptop is what runs in production.** `kars dev` and `kars up` share the same router binary, the same audit chain, the same governance profile. The dev-to-prod jump is one CLI command — no separate stack to learn or debug.

---
Expand Down Expand Up @@ -258,6 +258,7 @@ You pick the runtime via `KarsSandbox.spec.runtime.kind`. The router, governance
| Runtime | Language | Image dir | Status |
|---|---|---|---|
| **OpenClaw** (default) | Python | `sandbox-images/openclaw/` | ✅ |
| **Hermes** (Nous Research) | Python | `sandbox-images/hermes/` | ✅ |
| **OpenAI Agents SDK** | Python | `sandbox-images/openai-agents/` | ✅ |
| **Microsoft Agent Framework** | Python | `sandbox-images/maf-python/` | ✅ (`.NET` deferred) |
| **LangGraph** | Python | `sandbox-images/langgraph/` | ✅ |
Expand Down
Loading
Loading