Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15,499 changes: 13,625 additions & 1,874 deletions ATTRIBUTIONS-Rust.md

Large diffs are not rendered by default.

820 changes: 820 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ repository = "https://github.com/NVIDIA/nemo-fabric"
nemo-fabric-core = { path = "crates/fabric-core", version = "0.1.0" }

clap = { version = "4", default-features = false, features = ["derive", "std", "help", "usage", "error-context", "suggestions"] }
# Validate adapter-owned JSON Schema in Rust while preserving precise instance paths.
# `schemars` only generates schemas, and delegating validation to Python would cross
# the language-neutral core boundary. The smaller validator evaluated for this path
# misreported array indices after `prefixItems`, making `jsonschema` the narrowest
# option with correct diagnostics. Keep remote and local `$ref` retrieval disabled for
# untrusted adapter schemas.
jsonschema = { version = "0.49", default-features = false }
schemars = { version = "1", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ print(result.output.response)

`HarnessConfig.adapter_id` selects the Hermes Agent adapter. To use another
supported harness, install its package extra and set the corresponding adapter
ID. Pass harness-specific options through `HarnessConfig.settings`.
ID. Pass harness-specific options through `HarnessConfig.settings` only when
the selected adapter descriptor declares them in `settings_schema`.

For a guided version of this example, refer to the
[`01_quickstart.ipynb` notebook](examples/notebooks/01_quickstart.ipynb). The
Expand Down
18 changes: 13 additions & 5 deletions adapters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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.
configuration, its adapter-owned settings schema, and telemetry support.

## Descriptor Discovery

Expand All @@ -27,6 +27,12 @@ precedence:
NeMo Fabric resolves multi-component relative `ADAPTER_PYTHON` paths from
`<base_dir>`. It resolves bare command names through `PATH`.

The winning descriptor supplies its runner metadata and `settings_schema`
atomically. Planning validates `harness.settings` against that exact schema.
An agent-local descriptor therefore replaces an installed schema rather than
merging with it. Settings schemas must be self-contained; NeMo Fabric does not
resolve HTTP or file references from adapter descriptors.

This scan only discovers installed metadata. It is not the final registry
contract for resolving or installing third-party adapters. Installed and
agent-local descriptors both currently report `source: local`; a registry
Expand All @@ -47,16 +53,18 @@ provider should expose more precise provenance.
| --- | --- | --- | --- | --- | --- |
| [Claude](claude/README.md) | Native Anthropic or a configured Anthropic Messages-compatible provider | `tools.enabled` selects built-ins; a pre-tool hook enforces enabled and blocked names across built-in, MCP, and plugin tools | Normalized: stdio, HTTP, streamable HTTP, and SSE | Normalized `skills.paths` | Not exposed |
| [Codex](codex/README.md) | Native OpenAI or a configured Responses-compatible provider | `tools.enabled` and `tools.blocked` unsupported | Normalized: stdio, HTTP, and streamable HTTP | Normalized `SKILL.md` directories | Not exposed |
| [LangChain Deep Agents](deepagents/README.md) | LangChain model providers | Middleware enforces `tools.enabled` and `tools.blocked` across built-ins, MCP, and local subagents | Normalized through `langchain-mcp-adapters` | Normalized | Constrained declarative local delegation |
| [LangChain Deep Agents](deepagents/README.md) | LangChain model providers | Middleware enforces `tools.enabled` and `tools.blocked` across built-ins, MCP, and local delegation | Normalized through `langchain-mcp-adapters` | Normalized | Built-in, declarative, and Agent Protocol |
| [Hermes Agent](hermes/README.md) | Configurable provider, model, and base URL | `tools.enabled` and `tools.blocked` map to Hermes native toolset selectors | 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 NeMo Fabric does not provide a portable configuration
surface for it. Tool values are adapter-native selectors; NeMo Fabric does not
define a cross-harness tool-name catalog. Planning fails when the selected
adapter cannot enforce a configured policy. Deep Agents subagents are limited
to declarative local subagents that inherit the parent agent's capabilities.
adapter cannot enforce a configured policy. Deep Agents supports its built-in
subagent and descriptor-validated caller-defined subagents. A configured tools
policy permits only local declarative subagents because the adapter cannot gate
remote Agent Protocol tools.

`RunPlan.capability_plan.routes` records execution ownership, not network
routing. `harness_native` assigns a capability to the selected adapter,
Expand All @@ -77,7 +85,7 @@ and additive extension maps because their support does not vary by adapter:
| `schema_version` | Core | Core | Core | Core |
| `metadata.name`, `.description` | Core | Core | Core | Core |
| `harness.adapter_id`, `.resolution` | Core | Core | Core | Core |
| `harness.settings` | Adapter-owned escape hatch | Adapter-owned escape hatch | Adapter-owned escape hatch | Adapter-owned escape hatch |
| `harness.settings` | Closed adapter schema | Closed adapter schema | Closed adapter schema | Closed adapter schema |
| `models.<role>.provider` | `anthropic` uses native auth; custom names require an Anthropic Messages-compatible `base_url` and `api_key_env` | `openai` uses native auth; custom names require a Responses-compatible `base_url` and `api_key_env` | Dynamic LangChain provider; custom OpenAI-compatible endpoints require `base_url` and `api_key_env` | Dynamic Hermes provider |
| `models.<role>.model` | Yes | Yes | Yes | Yes |
| `models.<role>.api_key_env` | Yes | Yes | Yes | Yes |
Expand Down
23 changes: 16 additions & 7 deletions adapters/claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ The following table shows which components each installation provides:
| `pip install "nemo-fabric-adapters-claude[harness]"` | No | Yes | Yes | No |
| `pip install nemo-fabric-adapters-claude` | No | Yes | No | No |

For split runtime and adapter environments, configure `ADAPTER_PYTHON` or
`harness.settings.python` and use matching NeMo Fabric release versions. Refer
to the [installation guide](https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric/getting-started/install#install-an-adapter-and-harness-without-the-runtime).
For split runtime and adapter environments, configure `ADAPTER_PYTHON` and use
matching NeMo Fabric release versions. Refer to the
[installation guide](https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric/getting-started/install#install-an-adapter-and-harness-without-the-runtime).

The `full` extra is equivalent to `harness`. Relay is optional for ordinary
runs. Relay telemetry and `Runtime.invoke_stream()` require the external CLI
Expand Down Expand Up @@ -90,7 +90,9 @@ Configure portable capabilities through the normalized `FabricConfig` fields:
- `environment.workspace` sets the Claude working directory, and
`environment.env` supplies explicit harness-visible variables.
- `tools.enabled` selects Claude built-in tools. `None` preserves the Claude
default, while an empty list disables every tool.
default, while an empty list disables every tool. With `permission_mode` set
to `dontAsk`, explicitly enabled tools are also pre-approved so headless runs
can invoke them.
- `tools.blocked` maps to Claude `disallowed_tools`. A pre-tool hook enforces
both lists across built-in, MCP, and plugin tools.
- `mcp` configures stdio, HTTP, streamable HTTP, or SSE servers. For stdio,
Expand All @@ -100,9 +102,16 @@ Configure portable capabilities through the normalized `FabricConfig` fields:

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

- `allowed_tools` and `permission_mode`
- `max_budget_usd`
- `setting_sources` (defaults to `[]` for deterministic isolation)
| Setting | Type | Required | Static default |
| --- | --- | --- | --- |
| `permission_mode` | One of `default`, `acceptEdits`, `bypassPermissions`, `plan`, `dontAsk`, or `auto` | No | No default |
| `max_budget_usd` | Number greater than `0` | No | No default |
| `setting_sources` | Array containing `user`, `project`, or `local` | No | `[]` |

Planning validates these settings against the schema in the resolved Claude
descriptor. Unknown keys and invalid values fail before the adapter starts.
Schema defaults are documentation only; planning preserves the supplied settings
without adding `setting_sources`.

The adapter filters the inherited environment before launching Claude Code.
It retains portable OS/config variables, the selected model's `api_key_env`,
Expand Down
34 changes: 34 additions & 0 deletions adapters/claude/fabric-adapter.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,40 @@
"runner": {
"module": "nemo_fabric_adapters.claude.adapter"
},
"settings_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"setting_sources": {
"type": "array",
"items": {
"type": "string",
"enum": ["user", "project", "local"]
},
"default": [],
"description": "Claude settings scopes to load."
},
"max_budget_usd": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Maximum amount in US dollars that Claude may spend during one invocation."
},
"permission_mode": {
"type": "string",
"enum": [
"default",
"acceptEdits",
"bypassPermissions",
"plan",
"dontAsk",
"auto"
],
"description": "Claude permission handling mode."
}
},
"required": [],
"additionalProperties": false
},
"config": {
"accepts": [
"models",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,11 @@ def build_options(

system_prompt = common_utils.system_instruction(payload)
enabled_tools = common_utils.enabled_tools(payload)
allowed_tools = (
enabled_tools
if permission_mode == "dontAsk" and enabled_tools is not None
else []
)
plugins = _stage_skill_plugin(payload)
has_skill_plugin = bool(plugins)
if relay is not None:
Expand All @@ -551,7 +556,7 @@ def build_options(
model=selected_model(payload),
system_prompt=system_prompt,
tools=enabled_tools,
allowed_tools=_string_list(settings.get("allowed_tools"), name="allowed_tools"),
allowed_tools=allowed_tools,
disallowed_tools=common_utils.blocked_tools(payload),
hooks=tool_policy_hooks(payload),
permission_mode=permission_mode,
Expand Down
40 changes: 25 additions & 15 deletions adapters/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ The following table shows which components each installation provides:
| `pip install nemo-fabric-adapters-codex` | No | Yes | No | No |

For an environment-managed SDK, use `openai-codex==0.144.4`. For split runtime
and adapter environments, configure `ADAPTER_PYTHON` or
`harness.settings.python` and use matching NeMo Fabric release versions. Refer
to the [installation guide](https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric/getting-started/install#install-an-adapter-and-harness-without-the-runtime).
and adapter environments, configure `ADAPTER_PYTHON` and use matching NeMo
Fabric release versions. Refer to the
[installation guide](https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric/getting-started/install#install-an-adapter-and-harness-without-the-runtime).

The `full` extra is equivalent to `harness`. Relay is optional for ordinary
runs. Relay telemetry and `Runtime.invoke_stream()` require the external CLI
Expand Down Expand Up @@ -108,26 +108,36 @@ deny boundary for built-in, local, MCP, and hosted tools. NeMo Fabric therefore
routes normalized blocked-tool policy as unsupported instead of applying a
partial policy.

Codex-specific controls belong in `harness.settings`:

- `sandbox`: `read-only`, `workspace-write`, or `danger-full-access`
- `approval_mode`: `auto_review` or `deny_all`
- `developer_instructions`
- `personality`, `reasoning_effort`, and `service_tier`
- `output_schema` for SDK-native structured output
- `config_overrides` as dotted Codex configuration keys applied when the SDK
runtime starts, such as Codex-only MCP timeout or required-server options
Only Codex-specific controls belong in `harness.settings`:

| Setting | Type | Required | Static Default |
| --- | --- | --- | --- |
| `sandbox` | One of `read-only`, `workspace-write`, or `danger-full-access` | No | `read-only` |
| `approval_mode` | One of `auto_review` or `deny_all` | No | `auto_review` |
| `developer_instructions` | Nonempty string | No | No default |
| `personality` | One of `none`, `friendly`, or `pragmatic` | No | No default |
| `reasoning_effort` | One of `none`, `minimal`, `low`, `medium`, `high`, or `xhigh` | No | No default |
| `service_tier` | Nonempty string | No | No default |
| `output_schema` | JSON Schema object for the final assistant message | No | No default |
| `config_overrides` | Object that maps nonempty dotted Codex configuration keys to JSON-compatible values | No | `{}` |

Planning validates these settings against the schema in the resolved Codex
descriptor. Unknown keys, empty dotted-key segments, invalid types, and invalid
enum values fail before the adapter starts. Schema defaults are documentation
only; planning preserves the supplied settings without adding defaults.
`config_overrides` is the intentional adapter-specific escape hatch for Codex
configuration that has no normalized NeMo Fabric field.

Set model selection and endpoints through `models`, system instructions through
`instructions.system`, the invocation deadline through
`runtime.timeout_seconds`, and the working directory and explicit environment
through `environment`.
through `environment`. In particular, the SDK's `base_instructions` value comes
from `instructions.system`, not `harness.settings`.

For `Fabric.start_runtime(...)`, the model provider, MCP configuration, skill
roots, and `config_overrides` are fixed when the runtime starts and cannot vary
between `Runtime.invoke(...)` calls. Start a new runtime to change them.
`Fabric.run(...)` starts the same runtime, invokes it once, and stops it, so the
same settings are scoped to that single invocation.
`Fabric.run(...)` starts the same runtime, invokes it once, and stops it.

The adapter filters the inherited environment. It retains portable OS and
Codex state variables, the selected model's `api_key_env`, and explicit
Expand Down
53 changes: 53 additions & 0 deletions adapters/codex/fabric-adapter.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,59 @@
"runner": {
"module": "nemo_fabric_adapters.codex.adapter"
},
"settings_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"sandbox": {
"type": "string",
"enum": ["read-only", "workspace-write", "danger-full-access"],
"default": "read-only",
"description": "Codex filesystem access level."
},
"approval_mode": {
"type": "string",
"enum": ["auto_review", "deny_all"],
"default": "auto_review",
"description": "Codex approval handling mode for escalated permission requests."
},
"developer_instructions": {
"type": "string",
"minLength": 1,
"description": "Additional developer instructions for the Codex thread."
},
"personality": {
"type": "string",
"enum": ["none", "friendly", "pragmatic"],
"description": "Codex response personality."
},
"reasoning_effort": {
"type": "string",
"enum": ["none", "minimal", "low", "medium", "high", "xhigh"],
"description": "Codex reasoning effort for each turn."
},
"service_tier": {
"type": "string",
"minLength": 1,
"description": "Codex service tier for the thread."
},
"output_schema": {
"type": "object",
"description": "JSON Schema that constrains the final assistant message for each turn."
},
"config_overrides": {
"type": "object",
"propertyNames": {
"pattern": "^[^.]+(?:\\.[^.]+)*$"
},
"additionalProperties": {},
"default": {},
"description": "Dotted Codex configuration keys mapped to JSON-compatible values."
}
},
"required": [],
"additionalProperties": false
},
"config": {
"accepts": [
"models",
Expand Down
Loading
Loading