Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
758eb00
feat(core): add persistent local adapter lifecycle
AjayThorve Jul 17, 2026
05cae8b
feat(adapters): persist Claude and Codex SDK runtimes
AjayThorve Jul 17, 2026
933c3fd
feat(adapters): persist Deep Agents and Hermes runtimes
AjayThorve Jul 17, 2026
d1843a4
docs: document persistent adapter runtime support
AjayThorve Jul 17, 2026
713a41d
docs: expand adapter capability matrix
AjayThorve Jul 17, 2026
679a265
fix(runtime): align persistent Relay lifecycle
AjayThorve Jul 17, 2026
7e9ab4a
test: validate persistent adapter lifecycles
AjayThorve Jul 17, 2026
8874a3b
fix(runtime): harden persistent host failures
AjayThorve Jul 17, 2026
cab3dcc
test(adapters): tighten persistent lifecycle coverage
AjayThorve Jul 17, 2026
1144e76
docs: align persistent lifecycle guidance
AjayThorve Jul 17, 2026
0e9250a
fix(hermes): retry failed relay flush
AjayThorve Jul 18, 2026
9b2a236
fix(runtime): serialize timed-out host invalidation
AjayThorve Jul 18, 2026
0b33bd5
docs: clarify compatibility adapter continuity
AjayThorve Jul 18, 2026
00e5ca4
test(runtime): restore typed local-host protocol coverage
AjayThorve Jul 20, 2026
f882a8f
fix(cli): align persistent runtime integration
AjayThorve Jul 20, 2026
f53e304
test(codex): cover persistent normalized capabilities
AjayThorve Jul 20, 2026
4a58d13
docs: refresh persistent adapter capabilities
AjayThorve Jul 20, 2026
07f301e
docs: clarify Codex MCP and skills support
AjayThorve Jul 20, 2026
aa4646c
fix: harden persistent adapter lifecycle
AjayThorve Jul 21, 2026
7829fa7
fix: surface Codex cleanup failures
AjayThorve Jul 21, 2026
ab056d7
fix: preserve Codex adapter errors during cleanup
AjayThorve Jul 21, 2026
8dd24ae
fix: remove local-host protocol versioning
AjayThorve Jul 21, 2026
f1b6d6f
docs: regenerate local-host API reference
AjayThorve Jul 21, 2026
c3cda2c
feat(runtime): make local adapter hosting persistent-only
AjayThorve Jul 21, 2026
aebea96
docs: document persistent-only adapter execution
AjayThorve Jul 21, 2026
c284643
refactor(runtime): remove per-invocation adapter paths
AjayThorve Jul 21, 2026
0e34be8
docs: align persistent host contract
AjayThorve Jul 21, 2026
405a2fc
test: annotate lifecycle protocol doubles
AjayThorve Jul 21, 2026
95ccfef
test: address adapter lifecycle review feedback
AjayThorve Jul 21, 2026
db8c33b
docs: split harness and adapter compatibility guides
AjayThorve Jul 21, 2026
3caa8d7
test: use SDK mocks for Claude lifecycle coverage
AjayThorve Jul 21, 2026
bbc0984
docs: clarify Codex blocked-tool rejection
AjayThorve Jul 21, 2026
54d85f4
test: type Claude mock options explicitly
AjayThorve Jul 21, 2026
a729367
fix: classify broken local-host pipes as crashes
AjayThorve Jul 21, 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
37 changes: 26 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ under `examples/code_review_agent/artifacts/hermes/`. Its complete base
config and clone-based variants live in
`examples/code_review_agent/config.py`.

## Supported Agent Harnesses

Choose a bundled agent harness based on your model ecosystem and application
needs. Every harness supports persistent multi-turn local runtimes through the
same Fabric lifecycle and returns normalized results, artifacts, and telemetry
references.

| Agent harness | Choose it for | Model ecosystem | Key capabilities | Observability |
| --- | --- | --- | --- | --- |
| Claude | Claude-native coding and tool-use workflows | Anthropic and NVIDIA-hosted Anthropic Messages-compatible models | Tool guardrails, MCP, skills, and persistent Claude sessions | NeMo Relay |
| Codex | Codex-native coding workflows | OpenAI and NVIDIA-hosted Responses-compatible models | MCP, skills, and persistent Codex threads | NeMo Relay and native OpenTelemetry |
| LangChain Deep Agents | Composable LangChain and LangGraph agents | LangChain model providers | Built-in and MCP tools, guardrails, skills, and local subagents | NeMo Relay and native OpenTelemetry/OpenInference |
| Hermes Agent | Hermes workflows with custom model endpoints | Configurable provider, model, and base URL | Toolsets, guardrails, MCP, skills, and persistent conversation history | NeMo Relay |

For package names, exact compatibility and limitations, runtime ownership, and
individual harness guides, refer to the
[adapter compatibility reference](adapters/README.md).

## Claude Adapter

Build the local wheels and install Fabric with the independent Claude adapter:
Expand All @@ -154,8 +172,8 @@ python -m pip install --find-links dist "nemo-fabric[claude]"
```

Refer to the [Claude adapter guide](adapters/claude/README.md) for
typed configuration, normalized tools, MCP and skills, multi-turn resume,
authentication, and execution details.
typed configuration, normalized tools, MCP and skills, persistent multi-turn
runtimes, authentication, and execution details.

## Core Concepts

Expand All @@ -179,12 +197,10 @@ authentication, and execution details.
`disallowed_tools`, Deep Agents enforces them with middleware, and adapters
without a native deny mechanism route the policy as unsupported.
- **Adapters:** harness-specific integrations selected by `harness.adapter_id`.
The Hermes adapter lives under `adapters/hermes/`; the Codex SDK
adapter lives under `adapters/codex/`; the
[Claude adapter](adapters/claude/README.md)
lives under `adapters/claude/`; the LangChain Deep Agents adapter lives under
`adapters/deepagents/`. Harness-specific extensions belong under
`harness.settings` so the normalized contract can remain stable.
Harness-specific extensions belong under `harness.settings` so the normalized
contract can remain stable. Refer to the
[adapter compatibility reference](adapters/README.md) for bundled package and
implementation details.
- **Artifacts:** normalized output, logs, patches, and telemetry references
returned through an `ArtifactManifest`.

Expand All @@ -211,9 +227,8 @@ the [Python SDK guide](docs/sdk/python.mdx). Exact signatures are in the
- [Harbor examples](examples/harbor/README.md): validate the integration with a
deterministic, credential-free calculator smoke, optionally run the same
task with Hermes or Claude, and evaluate real coding tasks with SWE-Bench.
- Adapter guides: [Hermes](adapters/hermes/README.md),
[Codex SDK](adapters/codex/README.md), and
[Deep Agents](adapters/deepagents/README.md).
- [Adapter compatibility and guides](adapters/README.md): compare bundled
harness support, runtime ownership, telemetry integration, and package guides.

## Tests

Expand Down
63 changes: 63 additions & 0 deletions adapters/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!--
SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->

# NVIDIA NeMo Fabric Agent Harness Adapters

NeMo Fabric adapters translate the normalized Fabric contract into
harness-native models, tools, sessions, and telemetry. Use this reference to
compare the bundled adapters and then open the linked package guide for
installation, authentication, and configuration details.

The adapter descriptor selected in `RunPlan` is authoritative for normalized
configuration and telemetry support.

## Bundled Adapter Packages

| Agent Harness | Adapter ID | Python Package | Supported Python |
| --- | --- | --- | --- |
| [Claude](claude/README.md) | `nvidia.fabric.claude` | `nemo-fabric-adapters-claude` | 3.11+ |
| [Codex](codex/README.md) | `nvidia.fabric.codex` | `nemo-fabric-adapters-codex` | 3.11+ |
| [LangChain Deep Agents](deepagents/README.md) | `nvidia.fabric.langchain.deepagents` | `nemo-fabric-adapters-deepagents` | 3.11+ |
| [Hermes Agent](hermes/README.md) | `nvidia.fabric.hermes` | `nemo-fabric-adapters-hermes` | 3.11-3.13 |

## Configuration Compatibility

| Agent Harness | Models | Tools / Blocked Tools | MCP | Skills | Subagents |
| --- | --- | --- | --- | --- | --- |
| [Claude](claude/README.md) | Anthropic and NVIDIA-hosted Anthropic Messages-compatible models | `allowed_tools` adapter setting / normalized `tools.blocked` | Normalized: stdio, HTTP, streamable HTTP, and SSE | Normalized `skills.paths` | Not exposed |
| [Codex](codex/README.md) | OpenAI; NVIDIA Responses-compatible models without Relay | Codex-native tools / configuring `tools.blocked` is unsupported and raises `UnsupportedToolsPolicy` | Normalized: stdio, HTTP, and streamable HTTP | Normalized `SKILL.md` directories | Not exposed |
| [LangChain Deep Agents](deepagents/README.md) | LangChain model providers | Built-ins and MCP / normalized middleware block list | Normalized through `langchain-mcp-adapters` | Normalized | Constrained declarative local delegation |
| [Hermes Agent](hermes/README.md) | Normalized provider, model, and base URL | Toolsets / normalized disabled toolsets | Normalized | Normalized | Not exposed |

"Normalized" means that the adapter accepts the corresponding `FabricConfig`
field. "Not exposed" does not mean that the underlying harness lacks the
feature; it means that Fabric does not provide a portable configuration surface
for it. Fabric normalizes a blocked-tool list, not a portable tool-definition
catalog. Deep Agents subagents are limited to declarative local subagents that
inherit the parent agent's capabilities.

## Runtime and Observability Compatibility

All bundled adapters use one persistent Python adapter host with an ordered
`start` → `invoke*` → `stop` protocol.

NeMo Relay records raw events in Agent Trajectory Observability Format (ATOF)
and produces normalized trajectories in Agent Trajectory Interchange Format
(ATIF).

| Agent Harness | State Retained Across Turns | Relay Integration | Per-Turn Behavior | Stop Behavior | Remote Service |
| --- | --- | --- | --- | --- | --- |
| [Claude](claude/README.md) | `ClaudeSDKClient` and Claude session ID | Runtime-owned Relay CLI gateway and generated Claude hooks | Calls `client.query()`, validates the session ID, and collects ATOF and ATIF | Disconnects the client, stops the gateway, and removes the generated plugin | Not implemented |
| [Codex](codex/README.md) | `AsyncCodex` app-server client and SDK thread | Runtime-owned Relay CLI gateway and Codex SDK hooks | Reuses the SDK thread and persists its thread ID | Closes the SDK client and app server, then stops the gateway | Not implemented |
| [LangChain Deep Agents](deepagents/README.md) | Compiled LangGraph agent, checkpointer, and thread ID | NeMo Relay Python SDK integration added when the agent is compiled | Creates a fresh Relay request scope and callback for each invocation | Closes the checkpointer; no gateway process | Not implemented |
| [Hermes Agent](hermes/README.md) | `AIAgent`, `SessionDB`, and conversation history | Hermes NeMo Relay plugin context | Finalizes and flushes Relay after each invocation | Closes the agent and database, then exits the plugin context | Not implemented |

Telemetry output names use the descriptor contract values. Claude, Codex, and
Hermes can emit NeMo Relay ATIF, OpenTelemetry, and OpenInference output. Deep
Agents supports the same Relay outputs plus native OpenTelemetry and
OpenInference; Codex also supports native OpenTelemetry.

Shared lifecycle, Relay gateway, hook, and payload helpers are documented in
the [adapter utilities guide](common/README.md).
42 changes: 25 additions & 17 deletions adapters/claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ that Claude Code and the Claude Agent SDK consume. This includes
and `ANTHROPIC_IDENTITY_TOKEN` or `ANTHROPIC_IDENTITY_TOKEN_FILE`. Fabric reads
selected environment values and forwards them to the Claude runtime, but it
does not persist or log them in configuration or artifacts. Authentication is
validated when Claude starts the invocation.
validated when the Claude runtime starts.

Unset unused `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` variables before
using WIF. Anthropic credential resolution treats an empty variable as selected,
Expand All @@ -71,11 +71,18 @@ for other supported installation methods.

## Execution Model

Each `invoke` starts a fresh adapter process. The adapter persists the terminal
Claude session ID under the Fabric artifact root, keyed by `runtime_id`, and
passes it as `ClaudeAgentOptions.resume` on the next invocation. One Fabric
runtime therefore maps to one Claude session even though no adapter process
stays resident.
The Claude adapter implements Fabric's persistent local-host wire protocol.
`Fabric.start_runtime(...)` launches one adapter host, creates one
`ClaudeSDKClient`, and connects it once. Every `Runtime.invoke(...)` reuses that
client and its event loop; `Runtime.stop()` disconnects the client and exits the
host. `Fabric.run(...)` uses the same lifecycle around one invocation.

One Fabric runtime maps to one live Claude session. The adapter records the
terminal Claude session ID under the Fabric artifact root for correlation, but
does not silently recreate a crashed host or replay an invocation. Start a new
Fabric runtime when the host or SDK connection becomes unusable. Runtime
hosting is adapter-declared; consumers do not configure a runtime strategy in
`FabricConfig` or `harness.settings`.

## Configuration

Expand All @@ -90,7 +97,7 @@ Configure portable capabilities through the normalized `FabricConfig` fields:
- `mcp` configures stdio, HTTP, streamable HTTP, or SSE servers. For stdio,
Fabric parses `url` as a command plus arguments.
- `skills.paths` names skill directories that contain `SKILL.md`. The adapter
stages these directories as a local Claude plugin for the invocation.
stages these directories as a local Claude plugin for the runtime.

Only Claude-specific controls belong in `harness.settings`:

Expand Down Expand Up @@ -121,12 +128,13 @@ config.enable_relay(
)
```

For each Relay-enabled invocation, Fabric starts one `nemo-relay` gateway,
waits for its health endpoint, and stops it after Claude succeeds, fails, times
out, or is canceled. Fabric passes the gateway URL to Claude Code through
`ANTHROPIC_BASE_URL` and `NEMO_RELAY_GATEWAY_URL`. It also stages an
invocation-scoped Claude plugin that forwards lifecycle hooks with
`nemo-relay hook-forward claude`.
For each Relay-enabled Claude runtime, Fabric starts one `nemo-relay` gateway,
waits for its health endpoint, and stops it with the runtime. Fabric passes the
gateway URL to the connected Claude Code process through `ANTHROPIC_BASE_URL`
and `NEMO_RELAY_GATEWAY_URL`. It also stages a runtime-scoped Claude plugin that
forwards lifecycle hooks with `nemo-relay hook-forward claude`.
`Fabric.run(...)` starts the same runtime, invokes it once, and stops it, so the
gateway has the same lifecycle as that single invocation.

The Fabric result includes `relay_runtime.gateway_config_path`,
`relay_runtime.gateway_log_path`, and the collected `relay_artifacts`. Relay
Expand Down Expand Up @@ -194,7 +202,7 @@ config = FabricConfig(
fabric = Fabric()
```

## One-Shot Run
## Single Invocation

```python
result = await fabric.run(
Expand All @@ -218,6 +226,6 @@ assert first.runtime_id == second.runtime_id
assert first.output["session_id"] == second.output["session_id"]
```

Resume requires the same workspace and Claude state directory on the same host.
The Fabric-to-Claude correlation record alone is insufficient if Claude's
underlying transcript store is removed.
The runtime must remain on the same local host for its lifetime. A persisted
Fabric-to-Claude correlation record is not an attach token and cannot recover a
stopped or crashed local host.
3 changes: 1 addition & 2 deletions adapters/claude/fabric-adapter.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"harness": "claude",
"adapter_kind": "python",
"runner": {
"module": "nemo_fabric_adapters.claude.adapter",
"callable": "run"
"module": "nemo_fabric_adapters.claude.adapter"
},
"config": {
"accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"]
Expand Down
Loading
Loading