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
5 changes: 5 additions & 0 deletions .agents/skills/contribute-adapter/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ Keep descriptor claims, implementation, focused tests, public documentation,
catalog entries, and packaged metadata synchronized. Start with the narrowest
truthful capability set.

For `instructions.system`, keep `config.system_instruction_modes`, planning
behavior, direct adapter validation, and target-native composition synchronized.
New descriptors must declare their exact `replace` and `append` support rather
than relying on the legacy omitted-value behavior.

## Repository Evidence

In addition to the evidence required by the public skill, include:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class AgentInstructionConfig(AgentContractBlock):
"""One normalized instruction value."""

content: str
mode: Literal["replace"] = "replace"
mode: Literal["replace", "append"] = "replace"

def _validate(self) -> None:
_nonblank(self.content, "content")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@
"type": "string"
},
"type": "array"
},
"system_instruction_modes": {
"description": "Exact system-instruction modes supported by this adapter.\n\nAn omitted value preserves compatibility with descriptors that predate\nmode discovery and means `replace` when `instructions.system` is accepted.",
"items": {
"$ref": "#/$defs/InstructionMode"
},
"minItems": 1,
"type": [
"array",
"null"
],
"uniqueItems": true
}
},
"type": "object"
Expand Down Expand Up @@ -209,6 +221,21 @@
},
"type": "object"
},
"InstructionMode": {
"description": "How an instruction value is applied to the selected harness.",
"oneOf": [
{
"const": "replace",
"description": "Replace the harness default instruction value.",
"type": "string"
},
{
"const": "append",
"description": "Preserve the harness default and append this instruction after it.",
"type": "string"
}
]
},
"RuntimeCapabilities": {
"description": "Lifecycle behavior implemented by a resolved runtime path.",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,11 @@
"const": "replace",
"description": "Replace the harness default instruction value.",
"type": "string"
},
{
"const": "append",
"description": "Preserve the harness default and append this instruction after it.",
"type": "string"
}
]
},
Expand Down
13 changes: 13 additions & 0 deletions adapter-contract/typescript/src/generated/adapter-descriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export type AdapterConfigField =
| "mcp.auth.service_account"
| "mcp.tool_filters"
| "skills";
/**
* How an instruction value is applied to the selected harness.
*/
export type InstructionMode = "replace" | "append";
/**
* Adapter target categories understood by this Adapter Contract version.
*/
Expand Down Expand Up @@ -136,6 +140,15 @@ export type AdapterConfigSupport = {
* Harness-native files generated by this adapter.
*/
generates?: string[];
/**
* Exact system-instruction modes supported by this adapter.
*
* An omitted value preserves compatibility with descriptors that predate
* mode discovery and means `replace` when `instructions.system` is accepted.
*
* @minItems 1
*/
system_instruction_modes?: [InstructionMode, ...InstructionMode[]] | null;
} & JsonObject;
/**
* Runtime requirements.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export interface AgentInstructionConfig {
/**
* How the instruction is applied.
*/
mode?: "replace";
mode?: "replace" | "append";
}
/**
* Named MCP servers routed to an adapter target.
Expand Down
2 changes: 1 addition & 1 deletion adapters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ and additive extension maps because their support does not vary by adapter:
| `models.<role>.base_url` | Yes | Yes | Yes | Yes | Yes | Yes; known catalog models only |
| `models.<role>.temperature` | No | No | Yes | Yes | Yes | No |
| `models.<role>.settings.<key>` | No keys declared | No keys declared | No keys declared | No keys declared | No keys declared | No keys declared |
| `instructions.system` | Yes | Yes; base instructions | Yes | Yes | Yes | Yes; replaces Pi base instructions |
| `instructions.system` | `replace`, `append` | `replace`; base instructions | `replace` | `replace` | `replace` | `replace`; Pi base instructions |
| `runtime.input_schema`, `.output_schema` | Core | Core | Core | Core | Core | Core |
| `runtime.artifacts`, `.timeout_seconds` | Core | Core | Core | Core | Core | Core |
| `runtime.max_turns` | Yes | No | No | Yes; iteration limit | Yes | No |
Expand Down
4 changes: 3 additions & 1 deletion adapters/claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ Configure portable capabilities through the normalized `FabricConfig` fields:
- `models` selects the Claude model. The native `anthropic` provider retains
Claude authentication and endpoint discovery. Any other provider name must
configure an Anthropic Messages-compatible `base_url` and `api_key_env`.
- `instructions.system` supplies the Claude system instructions.
- `instructions.system` supports `replace` and `append`. `replace` supplies the
complete Claude system prompt. `append` preserves the `claude_code` preset and
adds the configured content after it.
- `runtime.max_turns` sets the Claude turn limit.
- `runtime.timeout_seconds` sets the NeMo Fabric invocation deadline.
- `environment.workspace` sets the Claude working directory, and
Expand Down
3 changes: 2 additions & 1 deletion adapters/claude/claude.fabric-adapter.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@
"tools.blocked",
"mcp",
"skills"
]
],
"system_instruction_modes": ["replace", "append"]
},
"telemetry": {
"providers": {
Expand Down
19 changes: 16 additions & 3 deletions adapters/claude/src/nemo_fabric_adapters/claude/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from nemo_fabric_adapter_contract.models import AgentRunStatus
from nemo_fabric_adapter_contract.models import AgentUsage
from nemo_fabric_adapter_contract.models import RuntimeContext
from nemo_fabric_adapters.common import instructions as common_instructions
from nemo_fabric_adapters.common import lifecycle
from nemo_fabric_adapters.common import relay_artifacts
from nemo_fabric_adapters.common import relay_gateway
Expand Down Expand Up @@ -644,10 +645,22 @@ def build_options(
)
cli_path = os.environ.get("FABRIC_TEST_CLAUDE_CLI_PATH")

instructions = config.instructions
system_prompt = (
instructions.system.content if instructions and instructions.system else None
instruction = common_instructions.system_instruction(
config,
adapter="Claude",
supported_modes={"replace", "append"},
)
system_prompt: Any = None
if instruction is not None:
system_prompt = (
instruction.content
if instruction.mode == "replace"
else {
"type": "preset",
"preset": "claude_code",
"append": instruction.content,
}
)
enabled_tools = config.tools.enabled if config.tools is not None else None
allowed_tools = (
enabled_tools
Expand Down
3 changes: 2 additions & 1 deletion adapters/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ Use normalized `FabricConfig` fields for portable configuration:
- `models` selects the Codex model. The native `openai` provider retains Codex
authentication and endpoint discovery. Any other provider name must configure
a Responses-compatible `base_url` and `api_key_env`.
- `instructions.system` maps to Codex base instructions.
- `instructions.system` supports `replace` and maps to Codex base instructions.
Codex rejects `append` during planning and direct adapter startup.
- `runtime.timeout_seconds` sets the NeMo Fabric invocation deadline.
- `environment.workspace` sets the working directory, and `environment.env`
supplies explicit harness-visible variables.
Expand Down
3 changes: 2 additions & 1 deletion adapters/codex/codex.fabric-adapter.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
"mcp",
"mcp.auth.oauth2",
"skills"
]
],
"system_instruction_modes": ["replace"]
},
"telemetry": {
"providers": {
Expand Down
12 changes: 7 additions & 5 deletions adapters/codex/src/nemo_fabric_adapters/codex/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from nemo_fabric_adapter_contract.models import McpOAuth2Config
from nemo_fabric_adapter_contract.models import McpServiceAccountConfig
from nemo_fabric_adapter_contract.models import RuntimeContext
from nemo_fabric_adapters.common import instructions as common_instructions
import nemo_fabric_adapters.common.relay_gateway as relay_gateway
import nemo_fabric_adapters.common.relay_hooks as relay_hooks
import nemo_fabric_adapters.common.relay_artifacts as relay_artifacts
Expand Down Expand Up @@ -1119,13 +1120,14 @@ def _thread_options(
relay: CodexRelaySettings | None,
) -> dict[str, Any]:
settings = _settings(config)
instruction = common_instructions.system_instruction(
config,
adapter="Codex",
supported_modes={"replace"},
)
return {
"approval_mode": approval_mode(config),
"base_instructions": (
config.instructions.system.content
if config.instructions and config.instructions.system
else None
),
"base_instructions": instruction.content if instruction else None,
"config": thread_config(config, context, relay) or None,
"cwd": str(resolve_cwd(context, base_dir)),
"developer_instructions": _optional_string(settings, "developer_instructions"),
Expand Down
40 changes: 40 additions & 0 deletions adapters/common/src/nemo_fabric_adapters/common/instructions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Shared system-instruction mode validation for adapter hosts."""

from __future__ import annotations

from collections.abc import Collection
from typing import Literal

from nemo_fabric_adapter_contract.models import AgentConfig
from nemo_fabric_adapter_contract.models import AgentInstructionConfig
from nemo_fabric_adapters.common import lifecycle


def system_instruction(
config: AgentConfig,
*,
adapter: str,
supported_modes: Collection[Literal["replace", "append"]],
) -> AgentInstructionConfig | None:
"""Return the configured instruction after validating adapter support."""

instruction = config.instructions.system if config.instructions else None
if instruction is None:
return None

supported = sorted(set(supported_modes))
if instruction.mode not in supported:
raise lifecycle.LifecycleError(
"unsupported_system_instruction_mode",
f"{adapter} does not support instructions.system.mode="
f"{instruction.mode!r}; supported modes: {', '.join(supported)}",
metadata={
"field": "instructions.system.mode",
"mode": instruction.mode,
"supported_modes": supported,
},
)
return instruction
3 changes: 2 additions & 1 deletion adapters/deepagents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ NeMo Fabric maps the following into the harness:

- The selected `models` role supplies `model`, `provider`, `api_key_env`,
`base_url`, and `temperature`.
- `instructions.system` becomes the Deep Agents `system_prompt`.
- `instructions.system` supports `replace` and becomes the Deep Agents
`system_prompt`. Deep Agents rejects `append`.
- `runtime.timeout_seconds` sets the NeMo Fabric invocation deadline.
- `environment.workspace` roots the Deep Agents filesystem backend
(`FilesystemBackend(root_dir=..., virtual_mode=True)`). `virtual_mode`
Expand Down
3 changes: 2 additions & 1 deletion adapters/deepagents/deepagents.fabric-adapter.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@
"tools.blocked",
"mcp",
"skills"
]
],
"system_instruction_modes": ["replace"]
},
"telemetry": {
"providers": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from nemo_fabric_adapter_contract.models import AgentRunStatus
from nemo_fabric_adapter_contract.models import AgentUsage
from nemo_fabric_adapter_contract.models import RuntimeContext
from nemo_fabric_adapters.common import instructions as common_instructions
from nemo_fabric_adapters.common import lifecycle
import nemo_fabric_adapters.common.utils as common_utils

Expand Down Expand Up @@ -371,16 +372,16 @@ async def build_agent_kwargs(
model: Any,
settings: dict[str, Any],
) -> dict[str, Any]:
instructions = config.instructions
instruction = common_instructions.system_instruction(
config,
adapter="Deep Agents",
supported_modes={"replace"},
)
kwargs: dict[str, Any] = {
"model": model,
"tools": await resolve_tools(config),
# deepagents 0.5.x/0.6.x take the system prompt as ``system_prompt``.
"system_prompt": (
instructions.system.content
if instructions and instructions.system
else None
),
"system_prompt": instruction.content if instruction else None,
"skills": resolve_skills(config),
"backend": resolve_backend(runtime_context, base_dir),
}
Expand Down
2 changes: 1 addition & 1 deletion adapters/hermes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The adapter receives a normalized payload from NeMo Fabric and materializes a na

- selected model provider, model name, base URL, and temperature through
`models`;
- `instructions.system` and `runtime.max_turns`;
- replacement `instructions.system` and `runtime.max_turns`;
- workspace and explicit environment variables through `environment`;
- invocation timeout through `runtime.timeout_seconds`;
- NeMo Fabric skills as external skill directories for Hermes Agent;
Expand Down
3 changes: 2 additions & 1 deletion adapters/hermes/hermes.fabric-adapter.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@
"mcp",
"mcp.auth.oauth2",
"skills"
]
],
"system_instruction_modes": ["replace"]
},
"telemetry": {
"providers": {
Expand Down
6 changes: 6 additions & 0 deletions adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from nemo_fabric_adapter_contract.models import AgentRunResult
from nemo_fabric_adapter_contract.models import AgentRunStatus
from nemo_fabric_adapter_contract.models import RuntimeContext
from nemo_fabric_adapters.common import instructions as common_instructions
from nemo_fabric_adapters.common import lifecycle
from nemo_fabric_adapters.hermes import configuration
from nemo_fabric_adapters.hermes import telemetry
Expand Down Expand Up @@ -87,6 +88,11 @@ async def start(self, payload: dict[str, Any]) -> None:
"hermes_invalid_config",
"Hermes requires a validated AgentConfig",
)
common_instructions.system_instruction(
agent_config,
adapter="Hermes",
supported_modes={"replace"},
)
runtime_context = RuntimeContext.from_mapping(
payload.get("runtime_context")
)
Expand Down
3 changes: 2 additions & 1 deletion adapters/mini-swe-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ The `harness` and `full` extras install the latest compatible mini-SWE-agent
## Configuration

The adapter supports `models`, `models.base_url`, `models.temperature`,
`instructions.system`, `runtime.max_turns`, and `environment.workspace`.
replacement `instructions.system`, `runtime.max_turns`, and
`environment.workspace`. It rejects `append` system instructions.
`runtime.timeout_seconds` sets the NVIDIA NeMo Fabric invocation deadline. Use
`harness.settings.timeout` to set the maximum duration of one command; the
default is `30` seconds.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"models.temperature",
"instructions.system",
"runtime.max_turns"
]
],
"system_instruction_modes": ["replace"]
},
"capabilities": {
"service": false,
Expand Down
Loading
Loading