From 3369c9b85e3d203695790159ea97b4b4a8142c39 Mon Sep 17 00:00:00 2001 From: Sapir Malka Date: Mon, 5 Jan 2026 16:25:16 +0200 Subject: [PATCH 1/7] Restructure Agent definitions to new folder with separated system instructions --- .../content_graph/objects/agentix_agent.py | 38 ++ .../content_graph/parsers/agentix_agent.py | 29 +- .../content_graph/parsers/related_files.py | 18 + .../prepare_content/agentix_agent_unifier.py | 175 ++++++ .../validate/sdk_validation_config.toml | 2 + ...G104_is_system_instructions_file_exists.py | 49 ++ plans/agentix-agent-restructuring-plan.md | 501 ++++++++++++++++++ 7 files changed, 811 insertions(+), 1 deletion(-) create mode 100644 demisto_sdk/commands/prepare_content/agentix_agent_unifier.py create mode 100644 demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py create mode 100644 plans/agentix-agent-restructuring-plan.md diff --git a/demisto_sdk/commands/content_graph/objects/agentix_agent.py b/demisto_sdk/commands/content_graph/objects/agentix_agent.py index 6c1b35d2758..6252cb27421 100644 --- a/demisto_sdk/commands/content_graph/objects/agentix_agent.py +++ b/demisto_sdk/commands/content_graph/objects/agentix_agent.py @@ -1,7 +1,15 @@ +from functools import cached_property from pathlib import Path +from demisto_sdk.commands.common.constants import MarketplaceVersions from demisto_sdk.commands.content_graph.common import ContentType from demisto_sdk.commands.content_graph.objects.agentix_base import AgentixBase +from demisto_sdk.commands.content_graph.parsers.related_files import ( + SystemInstructionsRelatedFile, +) +from demisto_sdk.commands.prepare_content.agentix_agent_unifier import ( + AgentixAgentUnifier, +) class AgentixAgent(AgentixBase, content_type=ContentType.AGENTIX_AGENT): @@ -20,3 +28,33 @@ def match(_dict: dict, path: Path) -> bool: if "color" in _dict and path.suffix == ".yml": return True return False + + @cached_property + def system_instructions_file(self) -> SystemInstructionsRelatedFile: + """Get the system instructions related file.""" + return SystemInstructionsRelatedFile(self.path, git_sha=self.git_sha) + + def prepare_for_upload( + self, + current_marketplace: MarketplaceVersions = MarketplaceVersions.PLATFORM, + **kwargs, + ) -> dict: + """ + Prepare the AgentixAgent for upload by unifying system instructions. + + This method merges the system instructions from the separate file + into the YAML data during content creation. + + Args: + current_marketplace: Target marketplace (default: PLATFORM) + **kwargs: Additional arguments + + Returns: + Unified YAML dict with systeminstructions field populated from file + """ + if not kwargs.get("unify_only"): + data = super().prepare_for_upload(current_marketplace) + else: + data = self.data + data = AgentixAgentUnifier.unify(self.path, data, current_marketplace) + return data diff --git a/demisto_sdk/commands/content_graph/parsers/agentix_agent.py b/demisto_sdk/commands/content_graph/parsers/agentix_agent.py index baf49719d52..b216d2f556d 100644 --- a/demisto_sdk/commands/content_graph/parsers/agentix_agent.py +++ b/demisto_sdk/commands/content_graph/parsers/agentix_agent.py @@ -5,7 +5,13 @@ from demisto_sdk.commands.common.constants import MarketplaceVersions from demisto_sdk.commands.content_graph.common import ContentType from demisto_sdk.commands.content_graph.parsers.agentix_base import AgentixBaseParser +from demisto_sdk.commands.content_graph.parsers.related_files import ( + SystemInstructionsRelatedFile, +) from demisto_sdk.commands.content_graph.strict_objects.agentix_agent import AgentixAgent +from demisto_sdk.commands.prepare_content.agentix_agent_unifier import ( + AgentixAgentUnifier, +) class AgentixAgentParser(AgentixBaseParser, content_type=ContentType.AGENTIX_AGENT): @@ -22,7 +28,6 @@ def __init__( self.color: str = self.yml_data.get("color") # type: ignore self.visibility: str = self.yml_data.get("visibility") # type: ignore self.actionids: list[str] = self.yml_data.get("actionids", []) - self.systeminstructions: str = self.yml_data.get("systeminstructions", "") self.conversationstarters: list[str] = self.yml_data.get( "conversationstarters", [] ) @@ -34,6 +39,23 @@ def __init__( self.sharedwithroles: list[str] = self.yml_data.get("sharedwithroles", []) self.add_action_dependencies() + @property + def systeminstructions(self) -> str: + """Gets the agent system instructions. + + The system instructions are read from a separate file named + _systeminstructions.md in the agent's directory. + + Returns: + str: The agent system instructions. + """ + if not self.git_sha: + return AgentixAgentUnifier.get_system_instructions(self.path.parent) + else: + return AgentixAgentUnifier.get_system_instructions_with_sha( + self.path, self.git_sha + ) + def add_action_dependencies(self) -> None: """Collects the actions used in the agent as optional dependencies.""" if actions_ids := self.yml_data.get("actionids"): @@ -47,6 +69,11 @@ def field_mapping(self): super().field_mapping.update({"display": "name"}) return super().field_mapping + @cached_property + def system_instructions_file(self) -> SystemInstructionsRelatedFile: + """Get the system instructions related file.""" + return SystemInstructionsRelatedFile(self.path, git_sha=self.git_sha) + @property def strict_object(self): return AgentixAgent diff --git a/demisto_sdk/commands/content_graph/parsers/related_files.py b/demisto_sdk/commands/content_graph/parsers/related_files.py index 98151c5c330..04d9d9b6968 100644 --- a/demisto_sdk/commands/content_graph/parsers/related_files.py +++ b/demisto_sdk/commands/content_graph/parsers/related_files.py @@ -40,6 +40,7 @@ class RelatedFileType(Enum): AUTHOR_IMAGE = "author_image_file" RELEASE_NOTE = "release_note" VERSION_CONFIG = "version_config" + SYSTEM_INSTRUCTIONS = "system_instructions" class RelatedFile(ABC): @@ -273,6 +274,23 @@ def get_optional_paths(self) -> List[Path]: ] +class SystemInstructionsRelatedFile(TextFiles): + """Related file for AgentixAgent system instructions.""" + + file_type = RelatedFileType.SYSTEM_INSTRUCTIONS + + def get_optional_paths(self) -> List[Path]: + """ + Get the path to the system instructions file. + + The file should be named: _systeminstructions.md + """ + return [ + self.main_file_path.parent + / f"{self.main_file_path.parts[-2]}_systeminstructions.md" + ] + + class ImageFiles(RelatedFile): def get_file_size(self): raise NotImplementedError diff --git a/demisto_sdk/commands/prepare_content/agentix_agent_unifier.py b/demisto_sdk/commands/prepare_content/agentix_agent_unifier.py new file mode 100644 index 00000000000..6d4f9902cd1 --- /dev/null +++ b/demisto_sdk/commands/prepare_content/agentix_agent_unifier.py @@ -0,0 +1,175 @@ +import copy +from pathlib import Path +from typing import Optional + +from demisto_sdk.commands.common.constants import MarketplaceVersions +from demisto_sdk.commands.common.files import TextFile +from demisto_sdk.commands.common.logger import logger +from demisto_sdk.commands.prepare_content.unifier import Unifier + + +class AgentixAgentUnifier(Unifier): + """ + Unifier for AgentixAgent content items. + + This class handles merging system instructions from a separate file into the + agent's YAML during the content creation process. + + The system instructions file follows the naming convention: + `_systeminstructions.md` + + Directory structure: + AgentixAgents/AgentName/ + ├── AgentName.yml + └── AgentName_systeminstructions.md + """ + + # File suffix for system instructions + SYSTEM_INSTRUCTIONS_SUFFIX = "_systeminstructions.md" + + @staticmethod + def unify( + path: Path, + data: dict, + marketplace: Optional[MarketplaceVersions] = None, + **kwargs, + ) -> dict: + """ + Merges system instructions from a separate file into the YAML. + + Args: + path: Path to the agent YAML file + data: Parsed YAML data + marketplace: Target marketplace (unused for agents, kept for interface compatibility) + **kwargs: Additional arguments (unused) + + Returns: + Unified YAML dict with systeminstructions field populated from file + """ + logger.debug(f"Unifying AgentixAgent: {path}") + + package_path = path.parent + yml_unified = copy.deepcopy(data) + + # Find and insert system instructions + yml_unified = AgentixAgentUnifier.insert_system_instructions_to_yml( + package_path, yml_unified + ) + + logger.debug(f"Created unified AgentixAgent yml: {path.name}") + return yml_unified + + @staticmethod + def get_system_instructions_file(package_path: Path) -> Optional[Path]: + """ + Find the system instructions file in the package directory. + + The file should be named: _systeminstructions.md + + Args: + package_path: Path to the agent package directory + + Returns: + Path to the system instructions file if found, None otherwise + """ + # Get the folder name (which should match the agent name pattern) + folder_name = package_path.name + + # Build the expected system instructions file path + instructions_file = ( + package_path / f"{folder_name}{AgentixAgentUnifier.SYSTEM_INSTRUCTIONS_SUFFIX}" + ) + + if instructions_file.exists(): + return instructions_file + + return None + + @staticmethod + def insert_system_instructions_to_yml( + package_path: Path, yml_unified: dict + ) -> dict: + """ + Read system instructions from file and add to YAML. + + Args: + package_path: Path to the agent package directory + yml_unified: The YAML dict to update + + Returns: + Updated YAML dict with systeminstructions field + """ + instructions_file = AgentixAgentUnifier.get_system_instructions_file(package_path) + + if instructions_file: + try: + instructions_content = instructions_file.read_text(encoding="utf-8") + yml_unified["systeminstructions"] = instructions_content.strip() + logger.debug( + f"Inserted system instructions from '{instructions_file.name}'" + ) + except Exception as e: + logger.warning( + f"Failed to read system instructions file '{instructions_file}': {e}" + ) + else: + logger.debug( + f"No system instructions file found in '{package_path}'" + ) + + return yml_unified + + @staticmethod + def get_system_instructions(package_path: Path) -> str: + """ + Get system instructions content from the package directory. + + This method is used by the parser to read system instructions from the + separate file during content graph parsing. + + Args: + package_path: Path to the agent package directory + + Returns: + The system instructions content, or empty string if not found + """ + instructions_file = AgentixAgentUnifier.get_system_instructions_file(package_path) + + if instructions_file: + try: + return instructions_file.read_text(encoding="utf-8").strip() + except Exception as e: + logger.warning( + f"Failed to read system instructions file '{instructions_file}': {e}" + ) + return "" + + @staticmethod + def get_system_instructions_with_sha(yml_path: Path, git_sha: str) -> str: + """ + Get system instructions content from a specific git commit. + + This method is used when comparing content between different versions + (e.g., for backward compatibility checks). + + Args: + yml_path: Path to the agent YAML file + git_sha: The git commit SHA to read from + + Returns: + The system instructions content, or empty string if not found + """ + # Build the expected system instructions file path + folder_name = yml_path.parent.name + instructions_file_path = str( + yml_path.parent / f"{folder_name}{AgentixAgentUnifier.SYSTEM_INSTRUCTIONS_SUFFIX}" + ) + + try: + content = TextFile.read_from_git_path(instructions_file_path, tag=git_sha) + return content.strip() if content else "" + except Exception as e: + logger.debug( + f"Could not read system instructions from git sha {git_sha}: {e}" + ) + return "" \ No newline at end of file diff --git a/demisto_sdk/commands/validate/sdk_validation_config.toml b/demisto_sdk/commands/validate/sdk_validation_config.toml index d6e40c24737..5984da369cb 100644 --- a/demisto_sdk/commands/validate/sdk_validation_config.toml +++ b/demisto_sdk/commands/validate/sdk_validation_config.toml @@ -162,6 +162,7 @@ select = [ "AG101", "AG102", "AG103", + "AG104", "AG105", "AG106", "AG107", @@ -328,6 +329,7 @@ select = [ "AG101", "AG102", "AG103", + "AG104", "AG105", "AG106", "AG107", diff --git a/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py b/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py new file mode 100644 index 00000000000..8950d439035 --- /dev/null +++ b/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from typing import Iterable, List + +from demisto_sdk.commands.common.constants import GitStatuses +from demisto_sdk.commands.content_graph.objects.agentix_agent import AgentixAgent +from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType +from demisto_sdk.commands.validate.validators.base_validator import ( + BaseValidator, + ValidationResult, +) + +ContentTypes = AgentixAgent + + +class IsSystemInstructionsFileExistsValidator(BaseValidator[ContentTypes]): + error_code = "AG104" + description = "Checks if the AgentixAgent has a system instructions file." + error_message = ( + "The AgentixAgent '{0}' is missing a system instructions file. " + "Please create a file named '{1}_systeminstructions.md' in the agent's directory." + ) + related_field = "systeminstructions" + rationale = ( + "AgentixAgent system instructions should be stored in a separate file " + "for better maintainability and readability." + ) + related_file_type = [RelatedFileType.SYSTEM_INSTRUCTIONS] + expected_git_statuses = [ + GitStatuses.ADDED, + GitStatuses.MODIFIED, + GitStatuses.RENAMED, + ] + + def obtain_invalid_content_items( + self, content_items: Iterable[ContentTypes] + ) -> List[ValidationResult]: + return [ + ValidationResult( + validator=self, + message=self.error_message.format( + content_item.display_name, + content_item.path.parent.name, + ), + content_object=content_item, + ) + for content_item in content_items + if not content_item.system_instructions_file.exist + ] \ No newline at end of file diff --git a/plans/agentix-agent-restructuring-plan.md b/plans/agentix-agent-restructuring-plan.md new file mode 100644 index 00000000000..7538315d8a6 --- /dev/null +++ b/plans/agentix-agent-restructuring-plan.md @@ -0,0 +1,501 @@ +# AgentixAgent Restructuring Implementation Plan + +## Overview + +Restructure AgentixAgent content items to use a directory-based structure (similar to Integrations/Scripts) with system instructions in a separate file that gets merged during the content creation process. + +## Requirements Summary + +### Current Structure + +``` +Packs/MyPack/AgentixAgents/my-agent.yml +``` + +### New Structure + +``` +Packs/MyPack/AgentixAgents/MyAgent/ +├── MyAgent.yml (without systeminstructions field inline) +└── MyAgent_systeminstructions.md +``` + +### Key Constraints + +1. **Output must remain unchanged**: The final unified YAML must contain the `systeminstructions` field exactly as before +2. **No backward compatibility required**: The content repo will be updated to the new structure at release time +3. **Markdown format only**: System instructions use `.md` format (no `.txt` support) +4. **Consistent naming**: File naming follows the pattern `_systeminstructions.md` (similar to `_description.md` for integrations) +5. **Platform marketplace only**: AgentixAgents are only for the Platform marketplace, so no `replace_marketplace_references` is needed +6. **Extensible**: Design should allow adding more files in the future (README, images, etc.) + +## Detailed Implementation Plan + +### 1. Design Decisions + +#### 1.1 File Naming Convention + +**Decision**: Use `_systeminstructions.md` (markdown format) + +- **Rationale**: + - Consistent with existing patterns (e.g., `_description.md` for integrations) + - Industry standard for AI agent instructions (OpenAI, Anthropic use markdown) + - Allows rich formatting (code blocks, lists, emphasis) + - Better developer experience with syntax highlighting +- **Format**: Markdown only (`.md`), no `.txt` support + +**Implementation**: + +- Constant `SYSTEM_INSTRUCTIONS_SUFFIX = "_systeminstructions.md"` in [`agentix_agent_unifier.py`](demisto_sdk/commands/prepare_content/agentix_agent_unifier.py) +- Related file type `RelatedFileType.SYSTEM_INSTRUCTIONS` in [`related_files.py`](demisto_sdk/commands/content_graph/parsers/related_files.py) + +#### 1.2 Directory Structure Pattern + +``` +AgentixAgents/ +├── AgentName1/ # Directory per agent +│ ├── AgentName1.yml # Main YAML (matches folder name) +│ └── AgentName1_systeminstructions.md +├── AgentName2/ +│ ├── AgentName2.yml +│ └── AgentName2_systeminstructions.md +``` + +**Naming Rules**: + +- Directory name: PascalCase (e.g., `MyAgent`, `SecurityAnalyst`) +- YAML filename: Matches folder name (e.g., `MyAgent.yml`) +- System instructions: `_systeminstructions.md` (e.g., `MyAgent_systeminstructions.md`) + +### 2. Core Components to Implement + +#### 2.1 AgentixAgent Unifier (NEW) + +**File**: [`demisto_sdk/commands/prepare_content/agentix_agent_unifier.py`](demisto_sdk/commands/prepare_content/agentix_agent_unifier.py) + +**Purpose**: Merge system instructions file into YAML during content creation + +**Key Methods**: + +```python +class AgentixAgentUnifier(Unifier): + """Unifier for AgentixAgent content items. + + Merges system instructions from a separate markdown file into the YAML + during content creation/upload. + """ + + SYSTEM_INSTRUCTIONS_SUFFIX = "_systeminstructions.md" + + @staticmethod + def unify( + path: Path, + data: dict, + marketplace: MarketplaceVersions = None, + **kwargs, + ) -> dict: + """Merges system instructions from separate file into YAML.""" + + @staticmethod + def get_system_instructions_file(package_path: Path) -> Optional[Path]: + """Find _systeminstructions.md in package directory.""" + + @staticmethod + def insert_system_instructions_to_yml( + package_path: Path, yml_unified: dict + ) -> dict: + """Read system instructions file and add to YAML.""" +``` + +**Note**: Unlike `IntegrationScriptUnifier`, `AgentixAgentUnifier` does NOT call `replace_marketplace_references` because AgentixAgents are only for the Platform marketplace. + +**Similar to**: [`IntegrationScriptUnifier`](demisto_sdk/commands/prepare_content/integration_script_unifier.py:69-138) + +#### 2.2 Update AgentixAgent Parser + +**File**: [`demisto_sdk/commands/content_graph/parsers/agentix_agent.py`](demisto_sdk/commands/content_graph/parsers/agentix_agent.py) + +**Changes**: + +1. Add `system_instructions_file` cached property using `SystemInstructionsRelatedFile` +2. Add `systeminstructions` property that reads from the file via the unifier +3. Use the Related Files pattern (consistent with how integrations handle description files) + +**Key Logic**: + +```python +@cached_property +def system_instructions_file(self) -> Optional[SystemInstructionsRelatedFile]: + """Get the system instructions related file.""" + return self.get_related_text_file(RelatedFileType.SYSTEM_INSTRUCTIONS) + +@property +def systeminstructions(self) -> str: + """Get system instructions content from the separate file.""" + return AgentixAgentUnifier.insert_system_instructions_to_yml( + self.path.parent, {} + ).get("systeminstructions", "") +``` + +#### 2.3 Related Files Pattern + +**File**: [`demisto_sdk/commands/content_graph/parsers/related_files.py`](demisto_sdk/commands/content_graph/parsers/related_files.py) + +**New Components**: + +1. Add `RelatedFileType.SYSTEM_INSTRUCTIONS` enum value +2. Add `SystemInstructionsRelatedFile` class extending `TextFiles` + +```python +class RelatedFileType(str, Enum): + # ... existing types ... + SYSTEM_INSTRUCTIONS = "system_instructions" + +class SystemInstructionsRelatedFile(TextFiles): + """Related file for AgentixAgent system instructions.""" + + @property + def file_suffix(self) -> str: + return "_systeminstructions.md" +``` + +**Note**: No new `FileType` constant is needed in `constants.py` - the system instructions file is handled as a related file, not a separate content type. + +#### 2.4 TestSuite Helper Class + +**Status**: ON HOLD - There is an active PR for the agent test suite. This will be addressed after that PR is merged. + +**File**: [`TestSuite/agentix_agent.py`](TestSuite/agentix_agent.py) + +**Purpose**: Provide test utilities for creating AgentixAgent test fixtures + +**Structure** (similar to [`Integration`](TestSuite/integration.py:18-185)): + +```python +class AgentixAgent(TestSuiteBase): + def __init__(self, tmpdir: Path, name: str, repo): + self.name = name + self._repo = repo + self.path = tmpdir / name # Directory path + self.path.mkdir() + + self.yml = YAML(self.path / f"{name}.yml", repo.path) + self.system_instructions = File( + self.path / f"{name}_systeminstructions.md", + repo.path + ) + + def build(self, yml: Optional[dict] = None, + system_instructions: Optional[str] = None): + """Write files to disk""" + + def create_default_agentix_agent(self, name: str = "SampleAgent"): + """Create agent with default template""" +``` + +#### 2.5 Update Default Templates + +**Status**: ON HOLD - Part of the TestSuite work that will be addressed after the active PR is merged. + +**Files**: + +- Create: [`TestSuite/assets/default_agentix_agent/`](TestSuite/assets/default_agentix_agent/) directory + - `agentix_agent-sample.yml` (without systeminstructions field) + - `agentix_agent-sample_systeminstructions.md` (sample instructions) + +**Template Structure**: + +```yaml +# agentix_agent-sample.yml +commonfields: + id: sample_agent + version: -1 +name: Sample Agent +description: A sample AI agent +color: "#00CD33" +visibility: internal +actionids: [] +conversationstarters: [] +builtinactions: [] +autoenablenewactions: false +roles: [] +sharedwithroles: [] +``` + +```markdown +# agentix_agent-sample_systeminstructions.md +You are a helpful AI assistant designed to help users with their tasks. + +## Your Capabilities +- Analyze and respond to user queries +- Execute actions when needed +- Provide clear and concise information + +## Guidelines +- Always be helpful and professional +- Ask clarifying questions when needed +- Explain your reasoning when appropriate +``` + +### 3. Integration Points + +#### 3.1 Format Command + +**File**: [`demisto_sdk/commands/format/format_module.py`](demisto_sdk/commands/format/format_module.py) + +**Changes**: + +1. Add AgentixAgent to format handlers +2. Create formatter class similar to [`update_generic_agentix.py`](demisto_sdk/commands/format/update_generic_agentix.py) +3. Handle both old and new structures + +#### 3.2 Validation + +**Files to update**: + +- Existing AG validators in [`demisto_sdk/commands/validate/validators/AG_validators/`](demisto_sdk/commands/validate/validators/AG_validators/) +- Add new validator: [`AG104_is_system_instructions_file_exists.py`](demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py) + +**New Validation Rules**: + +1. System instructions file (`_systeminstructions.md`) must exist +2. System instructions file must not be empty + +**Validator Implementation**: + +```python +class IsSystemInstructionsFileExistsValidator(BaseValidator[ContentTypes]): + error_code = "AG104" + description = "Validates that the system instructions file exists for AgentixAgent." + rationale = "AgentixAgents require a system instructions file." + error_message = "The system instructions file '{0}' does not exist." + related_field = "systeminstructions" + is_auto_fixable = False + expected_git_statuses = [ + GitStatuses.ADDED, + GitStatuses.MODIFIED, + GitStatuses.RENAMED, + ] +``` + +#### 3.3 Content Graph + +**Files**: + +- [`demisto_sdk/commands/content_graph/objects/agentix_agent.py`](demisto_sdk/commands/content_graph/objects/agentix_agent.py) +- [`demisto_sdk/commands/content_graph/strict_objects/agentix_agent.py`](demisto_sdk/commands/content_graph/strict_objects/agentix_agent.py) + +**Changes**: + +1. Add `system_instructions_file` property to `AgentixAgent` object +2. Add `prepare_for_upload` method that calls the unifier to merge system instructions + +```python +@property +def system_instructions_file(self) -> Optional[Path]: + """Get the path to the system instructions file.""" + return self._system_instructions_file + +@system_instructions_file.setter +def system_instructions_file(self, value: Optional[Path]): + self._system_instructions_file = value + +def prepare_for_upload( + self, + current_marketplace: MarketplaceVersions = MarketplaceVersions.MarketplaceV2, + **kwargs, +) -> dict: + """Prepare the content item for upload by unifying system instructions.""" + data = self.data + return AgentixAgentUnifier.unify( + path=self.path, + data=data, + marketplace=current_marketplace, + **kwargs, + ) +``` + +### 4. Backward Compatibility Strategy + +**Decision**: No backward compatibility required. + +The content repository will be updated to the new directory-based structure at release time. All AgentixAgents will be migrated to the new structure before this implementation is released. + +**Implications**: + +1. No need to detect old vs new structure +2. No fallback to inline `systeminstructions` field in YAML +3. Simpler implementation with only the new structure supported +4. Validation will require the system instructions file to exist + +### 5. Testing Strategy + +**Status**: ON HOLD - The agentix agent test suite and related tests are on hold pending an active PR for the agent test suite. + +#### 5.1 Unit Tests + +**Files to create/update**: + +- `demisto_sdk/commands/prepare_content/tests/agentix_agent_unifier_test.py` (new) +- `demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py` (update) +- `demisto_sdk/commands/validate/tests/AG_validators_test.py` (update) + +**Test Cases**: + +1. Unifier reads system instructions from file +2. Unifier handles missing system instructions file gracefully +3. Unifier preserves other YAML fields +4. Parser reads system instructions via the unifier +5. Validation passes when system instructions file exists +6. Validation fails when system instructions file is missing +7. `prepare_for_upload` correctly merges system instructions + +#### 5.2 Integration Tests + +**Scenarios**: + +1. Create new agent with directory structure +2. Build/pack agent with system instructions file +3. Upload agent to platform (verify output YAML has `systeminstructions` field) +4. Validate agent with system instructions file + +### 6. Implementation Order + +#### Phase 1: Core Infrastructure ✅ + +1. ✅ Design and planning (this document) +2. ✅ Create AgentixAgentUnifier class with `SYSTEM_INSTRUCTIONS_SUFFIX` +3. ✅ Add `SystemInstructionsRelatedFile` and `RelatedFileType.SYSTEM_INSTRUCTIONS` +4. ✅ Update AgentixAgentParser with `system_instructions_file` and `systeminstructions` properties +5. ✅ Update AgentixAgent object with `system_instructions_file` property and `prepare_for_upload` method + +#### Phase 2: Validation ✅ + +1. ✅ Create AG104 validator for system instructions file existence +2. ✅ Add `expected_git_statuses` to AG104 for consistency with other AG validators +3. ✅ Update validation config (`sdk_validation_config.toml`) + +#### Phase 3: Testing Infrastructure (ON HOLD) + +Pending active PR for the agent test suite. + +#### Phase 4: Documentation & Polish + +1. Update this plan document +2. Code review and refinement + +### 7. Files to Create + +**New Files**: + +1. `demisto_sdk/commands/prepare_content/agentix_agent_unifier.py` +2. `demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py` +3. `TestSuite/agentix_agent.py` (ON HOLD) + +### 8. Files to Modify + +**Core Logic**: + +1. [`demisto_sdk/commands/content_graph/parsers/related_files.py`](demisto_sdk/commands/content_graph/parsers/related_files.py) - Add `SystemInstructionsRelatedFile` +2. [`demisto_sdk/commands/content_graph/parsers/agentix_agent.py`](demisto_sdk/commands/content_graph/parsers/agentix_agent.py) - Add `system_instructions_file` property +3. [`demisto_sdk/commands/content_graph/objects/agentix_agent.py`](demisto_sdk/commands/content_graph/objects/agentix_agent.py) - Add `prepare_for_upload` method +4. [`demisto_sdk/commands/validate/sdk_validation_config.toml`](demisto_sdk/commands/validate/sdk_validation_config.toml) - Add AG104 + +### 9. Key Design Patterns + +#### 9.1 Unifier Pattern + +Follow the same pattern as [`IntegrationScriptUnifier`](demisto_sdk/commands/prepare_content/integration_script_unifier.py): + +- Static methods for reusability +- Separate concerns (get file, insert to yml, unify) +- Return modified dict (immutable approach) +- Handle errors gracefully + +#### 9.2 Parser Pattern + +Follow the same pattern as existing parsers: + +- Use Related Files pattern for external files +- Read from file via the unifier +- Cache properties where appropriate + +#### 9.3 TestSuite Pattern + +Follow the same pattern as [`Integration`](TestSuite/integration.py): + +- Directory-based structure +- Separate File objects for each component +- `build()` method for writing +- `create_default_*()` for templates + +### 10. Configuration Points + +**Centralized Configuration**: + +- `SYSTEM_INSTRUCTIONS_SUFFIX = "_systeminstructions.md"` in `AgentixAgentUnifier` + +### 11. Success Criteria + +**Functional Requirements**: + +- [x] New directory structure is recognized and parsed correctly +- [x] System instructions are merged into YAML during upload +- [x] Output YAML contains `systeminstructions` field +- [ ] All existing tests pass +- [ ] New tests added (ON HOLD) + +**Non-Functional Requirements**: + +- [x] Clear error messages for missing system instructions file +- [x] Code follows existing patterns and style + +### 12. Future Enhancements + +**Potential additions** (not in scope for this task): + +1. Support for additional files (README.md, images) +2. Validation of system instructions content +3. Templates for different agent types + +## Mermaid Diagrams + +### Content Creation Flow + +```mermaid +graph TD + A[Agent Directory] --> C[Read agent.yml] + C --> E[Read _systeminstructions.md] + E --> F[AgentixAgentUnifier.unify] + F --> G[Merge instructions into YAML] + G --> H[Output unified YAML] + H --> I[Upload to Platform] +``` + +### Parser Logic + +```mermaid +graph TD + A[AgentixAgentParser] --> B[Get system_instructions_file via RelatedFiles] + B --> C[AgentixAgentUnifier.insert_system_instructions_to_yml] + C --> D[Return systeminstructions content] +``` + +## Summary + +This plan provides a roadmap for restructuring AgentixAgent content items to use a directory-based structure with system instructions in a separate file. The implementation follows existing patterns in the codebase (similar to Integration descriptions). + +**Key Decisions**: + +1. **No backward compatibility** - Content repo will be migrated at release +2. **File naming**: `_systeminstructions.md` (consistent with `_description.md`) +3. **Platform only** - No `replace_marketplace_references` needed +4. **Related Files pattern** - Uses `SystemInstructionsRelatedFile` like `DescriptionRelatedFile` + +**Key Principles**: + +1. **No breaking changes** - Support both old and new structures +2. **Follow existing patterns** - Use Integration/Script as reference +3. **Maintainability** - Centralized configuration for easy changes +4. **Testability** - Comprehensive test coverage +5. **Documentation** - Clear guides for developers and users From 26b250e89ade3a2b5bfec77b9fa72d14ace7c41b Mon Sep 17 00:00:00 2001 From: Sapir Malka <44067957+itssapir@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:18:29 +0200 Subject: [PATCH 2/7] Delete plans/agentix-agent-restructuring-plan.md --- plans/agentix-agent-restructuring-plan.md | 501 ---------------------- 1 file changed, 501 deletions(-) delete mode 100644 plans/agentix-agent-restructuring-plan.md diff --git a/plans/agentix-agent-restructuring-plan.md b/plans/agentix-agent-restructuring-plan.md deleted file mode 100644 index 7538315d8a6..00000000000 --- a/plans/agentix-agent-restructuring-plan.md +++ /dev/null @@ -1,501 +0,0 @@ -# AgentixAgent Restructuring Implementation Plan - -## Overview - -Restructure AgentixAgent content items to use a directory-based structure (similar to Integrations/Scripts) with system instructions in a separate file that gets merged during the content creation process. - -## Requirements Summary - -### Current Structure - -``` -Packs/MyPack/AgentixAgents/my-agent.yml -``` - -### New Structure - -``` -Packs/MyPack/AgentixAgents/MyAgent/ -├── MyAgent.yml (without systeminstructions field inline) -└── MyAgent_systeminstructions.md -``` - -### Key Constraints - -1. **Output must remain unchanged**: The final unified YAML must contain the `systeminstructions` field exactly as before -2. **No backward compatibility required**: The content repo will be updated to the new structure at release time -3. **Markdown format only**: System instructions use `.md` format (no `.txt` support) -4. **Consistent naming**: File naming follows the pattern `_systeminstructions.md` (similar to `_description.md` for integrations) -5. **Platform marketplace only**: AgentixAgents are only for the Platform marketplace, so no `replace_marketplace_references` is needed -6. **Extensible**: Design should allow adding more files in the future (README, images, etc.) - -## Detailed Implementation Plan - -### 1. Design Decisions - -#### 1.1 File Naming Convention - -**Decision**: Use `_systeminstructions.md` (markdown format) - -- **Rationale**: - - Consistent with existing patterns (e.g., `_description.md` for integrations) - - Industry standard for AI agent instructions (OpenAI, Anthropic use markdown) - - Allows rich formatting (code blocks, lists, emphasis) - - Better developer experience with syntax highlighting -- **Format**: Markdown only (`.md`), no `.txt` support - -**Implementation**: - -- Constant `SYSTEM_INSTRUCTIONS_SUFFIX = "_systeminstructions.md"` in [`agentix_agent_unifier.py`](demisto_sdk/commands/prepare_content/agentix_agent_unifier.py) -- Related file type `RelatedFileType.SYSTEM_INSTRUCTIONS` in [`related_files.py`](demisto_sdk/commands/content_graph/parsers/related_files.py) - -#### 1.2 Directory Structure Pattern - -``` -AgentixAgents/ -├── AgentName1/ # Directory per agent -│ ├── AgentName1.yml # Main YAML (matches folder name) -│ └── AgentName1_systeminstructions.md -├── AgentName2/ -│ ├── AgentName2.yml -│ └── AgentName2_systeminstructions.md -``` - -**Naming Rules**: - -- Directory name: PascalCase (e.g., `MyAgent`, `SecurityAnalyst`) -- YAML filename: Matches folder name (e.g., `MyAgent.yml`) -- System instructions: `_systeminstructions.md` (e.g., `MyAgent_systeminstructions.md`) - -### 2. Core Components to Implement - -#### 2.1 AgentixAgent Unifier (NEW) - -**File**: [`demisto_sdk/commands/prepare_content/agentix_agent_unifier.py`](demisto_sdk/commands/prepare_content/agentix_agent_unifier.py) - -**Purpose**: Merge system instructions file into YAML during content creation - -**Key Methods**: - -```python -class AgentixAgentUnifier(Unifier): - """Unifier for AgentixAgent content items. - - Merges system instructions from a separate markdown file into the YAML - during content creation/upload. - """ - - SYSTEM_INSTRUCTIONS_SUFFIX = "_systeminstructions.md" - - @staticmethod - def unify( - path: Path, - data: dict, - marketplace: MarketplaceVersions = None, - **kwargs, - ) -> dict: - """Merges system instructions from separate file into YAML.""" - - @staticmethod - def get_system_instructions_file(package_path: Path) -> Optional[Path]: - """Find _systeminstructions.md in package directory.""" - - @staticmethod - def insert_system_instructions_to_yml( - package_path: Path, yml_unified: dict - ) -> dict: - """Read system instructions file and add to YAML.""" -``` - -**Note**: Unlike `IntegrationScriptUnifier`, `AgentixAgentUnifier` does NOT call `replace_marketplace_references` because AgentixAgents are only for the Platform marketplace. - -**Similar to**: [`IntegrationScriptUnifier`](demisto_sdk/commands/prepare_content/integration_script_unifier.py:69-138) - -#### 2.2 Update AgentixAgent Parser - -**File**: [`demisto_sdk/commands/content_graph/parsers/agentix_agent.py`](demisto_sdk/commands/content_graph/parsers/agentix_agent.py) - -**Changes**: - -1. Add `system_instructions_file` cached property using `SystemInstructionsRelatedFile` -2. Add `systeminstructions` property that reads from the file via the unifier -3. Use the Related Files pattern (consistent with how integrations handle description files) - -**Key Logic**: - -```python -@cached_property -def system_instructions_file(self) -> Optional[SystemInstructionsRelatedFile]: - """Get the system instructions related file.""" - return self.get_related_text_file(RelatedFileType.SYSTEM_INSTRUCTIONS) - -@property -def systeminstructions(self) -> str: - """Get system instructions content from the separate file.""" - return AgentixAgentUnifier.insert_system_instructions_to_yml( - self.path.parent, {} - ).get("systeminstructions", "") -``` - -#### 2.3 Related Files Pattern - -**File**: [`demisto_sdk/commands/content_graph/parsers/related_files.py`](demisto_sdk/commands/content_graph/parsers/related_files.py) - -**New Components**: - -1. Add `RelatedFileType.SYSTEM_INSTRUCTIONS` enum value -2. Add `SystemInstructionsRelatedFile` class extending `TextFiles` - -```python -class RelatedFileType(str, Enum): - # ... existing types ... - SYSTEM_INSTRUCTIONS = "system_instructions" - -class SystemInstructionsRelatedFile(TextFiles): - """Related file for AgentixAgent system instructions.""" - - @property - def file_suffix(self) -> str: - return "_systeminstructions.md" -``` - -**Note**: No new `FileType` constant is needed in `constants.py` - the system instructions file is handled as a related file, not a separate content type. - -#### 2.4 TestSuite Helper Class - -**Status**: ON HOLD - There is an active PR for the agent test suite. This will be addressed after that PR is merged. - -**File**: [`TestSuite/agentix_agent.py`](TestSuite/agentix_agent.py) - -**Purpose**: Provide test utilities for creating AgentixAgent test fixtures - -**Structure** (similar to [`Integration`](TestSuite/integration.py:18-185)): - -```python -class AgentixAgent(TestSuiteBase): - def __init__(self, tmpdir: Path, name: str, repo): - self.name = name - self._repo = repo - self.path = tmpdir / name # Directory path - self.path.mkdir() - - self.yml = YAML(self.path / f"{name}.yml", repo.path) - self.system_instructions = File( - self.path / f"{name}_systeminstructions.md", - repo.path - ) - - def build(self, yml: Optional[dict] = None, - system_instructions: Optional[str] = None): - """Write files to disk""" - - def create_default_agentix_agent(self, name: str = "SampleAgent"): - """Create agent with default template""" -``` - -#### 2.5 Update Default Templates - -**Status**: ON HOLD - Part of the TestSuite work that will be addressed after the active PR is merged. - -**Files**: - -- Create: [`TestSuite/assets/default_agentix_agent/`](TestSuite/assets/default_agentix_agent/) directory - - `agentix_agent-sample.yml` (without systeminstructions field) - - `agentix_agent-sample_systeminstructions.md` (sample instructions) - -**Template Structure**: - -```yaml -# agentix_agent-sample.yml -commonfields: - id: sample_agent - version: -1 -name: Sample Agent -description: A sample AI agent -color: "#00CD33" -visibility: internal -actionids: [] -conversationstarters: [] -builtinactions: [] -autoenablenewactions: false -roles: [] -sharedwithroles: [] -``` - -```markdown -# agentix_agent-sample_systeminstructions.md -You are a helpful AI assistant designed to help users with their tasks. - -## Your Capabilities -- Analyze and respond to user queries -- Execute actions when needed -- Provide clear and concise information - -## Guidelines -- Always be helpful and professional -- Ask clarifying questions when needed -- Explain your reasoning when appropriate -``` - -### 3. Integration Points - -#### 3.1 Format Command - -**File**: [`demisto_sdk/commands/format/format_module.py`](demisto_sdk/commands/format/format_module.py) - -**Changes**: - -1. Add AgentixAgent to format handlers -2. Create formatter class similar to [`update_generic_agentix.py`](demisto_sdk/commands/format/update_generic_agentix.py) -3. Handle both old and new structures - -#### 3.2 Validation - -**Files to update**: - -- Existing AG validators in [`demisto_sdk/commands/validate/validators/AG_validators/`](demisto_sdk/commands/validate/validators/AG_validators/) -- Add new validator: [`AG104_is_system_instructions_file_exists.py`](demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py) - -**New Validation Rules**: - -1. System instructions file (`_systeminstructions.md`) must exist -2. System instructions file must not be empty - -**Validator Implementation**: - -```python -class IsSystemInstructionsFileExistsValidator(BaseValidator[ContentTypes]): - error_code = "AG104" - description = "Validates that the system instructions file exists for AgentixAgent." - rationale = "AgentixAgents require a system instructions file." - error_message = "The system instructions file '{0}' does not exist." - related_field = "systeminstructions" - is_auto_fixable = False - expected_git_statuses = [ - GitStatuses.ADDED, - GitStatuses.MODIFIED, - GitStatuses.RENAMED, - ] -``` - -#### 3.3 Content Graph - -**Files**: - -- [`demisto_sdk/commands/content_graph/objects/agentix_agent.py`](demisto_sdk/commands/content_graph/objects/agentix_agent.py) -- [`demisto_sdk/commands/content_graph/strict_objects/agentix_agent.py`](demisto_sdk/commands/content_graph/strict_objects/agentix_agent.py) - -**Changes**: - -1. Add `system_instructions_file` property to `AgentixAgent` object -2. Add `prepare_for_upload` method that calls the unifier to merge system instructions - -```python -@property -def system_instructions_file(self) -> Optional[Path]: - """Get the path to the system instructions file.""" - return self._system_instructions_file - -@system_instructions_file.setter -def system_instructions_file(self, value: Optional[Path]): - self._system_instructions_file = value - -def prepare_for_upload( - self, - current_marketplace: MarketplaceVersions = MarketplaceVersions.MarketplaceV2, - **kwargs, -) -> dict: - """Prepare the content item for upload by unifying system instructions.""" - data = self.data - return AgentixAgentUnifier.unify( - path=self.path, - data=data, - marketplace=current_marketplace, - **kwargs, - ) -``` - -### 4. Backward Compatibility Strategy - -**Decision**: No backward compatibility required. - -The content repository will be updated to the new directory-based structure at release time. All AgentixAgents will be migrated to the new structure before this implementation is released. - -**Implications**: - -1. No need to detect old vs new structure -2. No fallback to inline `systeminstructions` field in YAML -3. Simpler implementation with only the new structure supported -4. Validation will require the system instructions file to exist - -### 5. Testing Strategy - -**Status**: ON HOLD - The agentix agent test suite and related tests are on hold pending an active PR for the agent test suite. - -#### 5.1 Unit Tests - -**Files to create/update**: - -- `demisto_sdk/commands/prepare_content/tests/agentix_agent_unifier_test.py` (new) -- `demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py` (update) -- `demisto_sdk/commands/validate/tests/AG_validators_test.py` (update) - -**Test Cases**: - -1. Unifier reads system instructions from file -2. Unifier handles missing system instructions file gracefully -3. Unifier preserves other YAML fields -4. Parser reads system instructions via the unifier -5. Validation passes when system instructions file exists -6. Validation fails when system instructions file is missing -7. `prepare_for_upload` correctly merges system instructions - -#### 5.2 Integration Tests - -**Scenarios**: - -1. Create new agent with directory structure -2. Build/pack agent with system instructions file -3. Upload agent to platform (verify output YAML has `systeminstructions` field) -4. Validate agent with system instructions file - -### 6. Implementation Order - -#### Phase 1: Core Infrastructure ✅ - -1. ✅ Design and planning (this document) -2. ✅ Create AgentixAgentUnifier class with `SYSTEM_INSTRUCTIONS_SUFFIX` -3. ✅ Add `SystemInstructionsRelatedFile` and `RelatedFileType.SYSTEM_INSTRUCTIONS` -4. ✅ Update AgentixAgentParser with `system_instructions_file` and `systeminstructions` properties -5. ✅ Update AgentixAgent object with `system_instructions_file` property and `prepare_for_upload` method - -#### Phase 2: Validation ✅ - -1. ✅ Create AG104 validator for system instructions file existence -2. ✅ Add `expected_git_statuses` to AG104 for consistency with other AG validators -3. ✅ Update validation config (`sdk_validation_config.toml`) - -#### Phase 3: Testing Infrastructure (ON HOLD) - -Pending active PR for the agent test suite. - -#### Phase 4: Documentation & Polish - -1. Update this plan document -2. Code review and refinement - -### 7. Files to Create - -**New Files**: - -1. `demisto_sdk/commands/prepare_content/agentix_agent_unifier.py` -2. `demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py` -3. `TestSuite/agentix_agent.py` (ON HOLD) - -### 8. Files to Modify - -**Core Logic**: - -1. [`demisto_sdk/commands/content_graph/parsers/related_files.py`](demisto_sdk/commands/content_graph/parsers/related_files.py) - Add `SystemInstructionsRelatedFile` -2. [`demisto_sdk/commands/content_graph/parsers/agentix_agent.py`](demisto_sdk/commands/content_graph/parsers/agentix_agent.py) - Add `system_instructions_file` property -3. [`demisto_sdk/commands/content_graph/objects/agentix_agent.py`](demisto_sdk/commands/content_graph/objects/agentix_agent.py) - Add `prepare_for_upload` method -4. [`demisto_sdk/commands/validate/sdk_validation_config.toml`](demisto_sdk/commands/validate/sdk_validation_config.toml) - Add AG104 - -### 9. Key Design Patterns - -#### 9.1 Unifier Pattern - -Follow the same pattern as [`IntegrationScriptUnifier`](demisto_sdk/commands/prepare_content/integration_script_unifier.py): - -- Static methods for reusability -- Separate concerns (get file, insert to yml, unify) -- Return modified dict (immutable approach) -- Handle errors gracefully - -#### 9.2 Parser Pattern - -Follow the same pattern as existing parsers: - -- Use Related Files pattern for external files -- Read from file via the unifier -- Cache properties where appropriate - -#### 9.3 TestSuite Pattern - -Follow the same pattern as [`Integration`](TestSuite/integration.py): - -- Directory-based structure -- Separate File objects for each component -- `build()` method for writing -- `create_default_*()` for templates - -### 10. Configuration Points - -**Centralized Configuration**: - -- `SYSTEM_INSTRUCTIONS_SUFFIX = "_systeminstructions.md"` in `AgentixAgentUnifier` - -### 11. Success Criteria - -**Functional Requirements**: - -- [x] New directory structure is recognized and parsed correctly -- [x] System instructions are merged into YAML during upload -- [x] Output YAML contains `systeminstructions` field -- [ ] All existing tests pass -- [ ] New tests added (ON HOLD) - -**Non-Functional Requirements**: - -- [x] Clear error messages for missing system instructions file -- [x] Code follows existing patterns and style - -### 12. Future Enhancements - -**Potential additions** (not in scope for this task): - -1. Support for additional files (README.md, images) -2. Validation of system instructions content -3. Templates for different agent types - -## Mermaid Diagrams - -### Content Creation Flow - -```mermaid -graph TD - A[Agent Directory] --> C[Read agent.yml] - C --> E[Read _systeminstructions.md] - E --> F[AgentixAgentUnifier.unify] - F --> G[Merge instructions into YAML] - G --> H[Output unified YAML] - H --> I[Upload to Platform] -``` - -### Parser Logic - -```mermaid -graph TD - A[AgentixAgentParser] --> B[Get system_instructions_file via RelatedFiles] - B --> C[AgentixAgentUnifier.insert_system_instructions_to_yml] - C --> D[Return systeminstructions content] -``` - -## Summary - -This plan provides a roadmap for restructuring AgentixAgent content items to use a directory-based structure with system instructions in a separate file. The implementation follows existing patterns in the codebase (similar to Integration descriptions). - -**Key Decisions**: - -1. **No backward compatibility** - Content repo will be migrated at release -2. **File naming**: `_systeminstructions.md` (consistent with `_description.md`) -3. **Platform only** - No `replace_marketplace_references` needed -4. **Related Files pattern** - Uses `SystemInstructionsRelatedFile` like `DescriptionRelatedFile` - -**Key Principles**: - -1. **No breaking changes** - Support both old and new structures -2. **Follow existing patterns** - Use Integration/Script as reference -3. **Maintainability** - Centralized configuration for easy changes -4. **Testability** - Comprehensive test coverage -5. **Documentation** - Clear guides for developers and users From 2273e0937bdf9292abf11dc10a13182b75438a5b Mon Sep 17 00:00:00 2001 From: Sapir Malka Date: Tue, 6 Jan 2026 11:51:01 +0200 Subject: [PATCH 3/7] ruff --- .../prepare_content/agentix_agent_unifier.py | 20 +++++++++++-------- ...G104_is_system_instructions_file_exists.py | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/demisto_sdk/commands/prepare_content/agentix_agent_unifier.py b/demisto_sdk/commands/prepare_content/agentix_agent_unifier.py index 6d4f9902cd1..d207bc28094 100644 --- a/demisto_sdk/commands/prepare_content/agentix_agent_unifier.py +++ b/demisto_sdk/commands/prepare_content/agentix_agent_unifier.py @@ -77,7 +77,8 @@ def get_system_instructions_file(package_path: Path) -> Optional[Path]: # Build the expected system instructions file path instructions_file = ( - package_path / f"{folder_name}{AgentixAgentUnifier.SYSTEM_INSTRUCTIONS_SUFFIX}" + package_path + / f"{folder_name}{AgentixAgentUnifier.SYSTEM_INSTRUCTIONS_SUFFIX}" ) if instructions_file.exists(): @@ -99,7 +100,9 @@ def insert_system_instructions_to_yml( Returns: Updated YAML dict with systeminstructions field """ - instructions_file = AgentixAgentUnifier.get_system_instructions_file(package_path) + instructions_file = AgentixAgentUnifier.get_system_instructions_file( + package_path + ) if instructions_file: try: @@ -113,9 +116,7 @@ def insert_system_instructions_to_yml( f"Failed to read system instructions file '{instructions_file}': {e}" ) else: - logger.debug( - f"No system instructions file found in '{package_path}'" - ) + logger.debug(f"No system instructions file found in '{package_path}'") return yml_unified @@ -133,7 +134,9 @@ def get_system_instructions(package_path: Path) -> str: Returns: The system instructions content, or empty string if not found """ - instructions_file = AgentixAgentUnifier.get_system_instructions_file(package_path) + instructions_file = AgentixAgentUnifier.get_system_instructions_file( + package_path + ) if instructions_file: try: @@ -162,7 +165,8 @@ def get_system_instructions_with_sha(yml_path: Path, git_sha: str) -> str: # Build the expected system instructions file path folder_name = yml_path.parent.name instructions_file_path = str( - yml_path.parent / f"{folder_name}{AgentixAgentUnifier.SYSTEM_INSTRUCTIONS_SUFFIX}" + yml_path.parent + / f"{folder_name}{AgentixAgentUnifier.SYSTEM_INSTRUCTIONS_SUFFIX}" ) try: @@ -172,4 +176,4 @@ def get_system_instructions_with_sha(yml_path: Path, git_sha: str) -> str: logger.debug( f"Could not read system instructions from git sha {git_sha}: {e}" ) - return "" \ No newline at end of file + return "" diff --git a/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py b/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py index 8950d439035..48f006626b1 100644 --- a/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py +++ b/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py @@ -46,4 +46,4 @@ def obtain_invalid_content_items( ) for content_item in content_items if not content_item.system_instructions_file.exist - ] \ No newline at end of file + ] From 569235e15644ed3ade5451ce48d539d0fe83bbbd Mon Sep 17 00:00:00 2001 From: Sapir Malka Date: Thu, 8 Jan 2026 11:24:10 +0200 Subject: [PATCH 4/7] remove git statuses from ag104 --- .../AG104_is_system_instructions_file_exists.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py b/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py index 48f006626b1..4929c6d664b 100644 --- a/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py +++ b/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py @@ -2,7 +2,6 @@ from typing import Iterable, List -from demisto_sdk.commands.common.constants import GitStatuses from demisto_sdk.commands.content_graph.objects.agentix_agent import AgentixAgent from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType from demisto_sdk.commands.validate.validators.base_validator import ( @@ -26,11 +25,6 @@ class IsSystemInstructionsFileExistsValidator(BaseValidator[ContentTypes]): "for better maintainability and readability." ) related_file_type = [RelatedFileType.SYSTEM_INSTRUCTIONS] - expected_git_statuses = [ - GitStatuses.ADDED, - GitStatuses.MODIFIED, - GitStatuses.RENAMED, - ] def obtain_invalid_content_items( self, content_items: Iterable[ContentTypes] From 5c50ea986eae39615becdd590e6f344544f3c98d Mon Sep 17 00:00:00 2001 From: Sapir Malka Date: Mon, 19 Jan 2026 17:27:02 +0200 Subject: [PATCH 5/7] Rename new validation to ag109 --- demisto_sdk/commands/validate/sdk_validation_config.toml | 2 ++ ...le_exists.py => AG109_is_system_instructions_file_exists.py} | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) rename demisto_sdk/commands/validate/validators/AG_validators/{AG104_is_system_instructions_file_exists.py => AG109_is_system_instructions_file_exists.py} (98%) diff --git a/demisto_sdk/commands/validate/sdk_validation_config.toml b/demisto_sdk/commands/validate/sdk_validation_config.toml index 14940b4c71d..fbe1bb40d4c 100644 --- a/demisto_sdk/commands/validate/sdk_validation_config.toml +++ b/demisto_sdk/commands/validate/sdk_validation_config.toml @@ -168,6 +168,7 @@ select = [ "AG106", "AG107", "AG108", + "AG109", "DS100", "DS101", "DS105", @@ -337,6 +338,7 @@ select = [ "AG106", "AG107", "AG108", + "AG109", "PA100", "PA101", "PA102", diff --git a/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py b/demisto_sdk/commands/validate/validators/AG_validators/AG109_is_system_instructions_file_exists.py similarity index 98% rename from demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py rename to demisto_sdk/commands/validate/validators/AG_validators/AG109_is_system_instructions_file_exists.py index 4929c6d664b..355fd999fca 100644 --- a/demisto_sdk/commands/validate/validators/AG_validators/AG104_is_system_instructions_file_exists.py +++ b/demisto_sdk/commands/validate/validators/AG_validators/AG109_is_system_instructions_file_exists.py @@ -13,7 +13,7 @@ class IsSystemInstructionsFileExistsValidator(BaseValidator[ContentTypes]): - error_code = "AG104" + error_code = "AG109" description = "Checks if the AgentixAgent has a system instructions file." error_message = ( "The AgentixAgent '{0}' is missing a system instructions file. " From 6205f8f2c622cd7d8ac6eb4e0f92b457e7634b7c Mon Sep 17 00:00:00 2001 From: Sapir Malka Date: Wed, 21 Jan 2026 11:29:47 +0200 Subject: [PATCH 6/7] remove redundant field from parser --- .../commands/content_graph/parsers/agentix_agent.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/demisto_sdk/commands/content_graph/parsers/agentix_agent.py b/demisto_sdk/commands/content_graph/parsers/agentix_agent.py index b216d2f556d..e7ed9ddbffa 100644 --- a/demisto_sdk/commands/content_graph/parsers/agentix_agent.py +++ b/demisto_sdk/commands/content_graph/parsers/agentix_agent.py @@ -5,9 +5,6 @@ from demisto_sdk.commands.common.constants import MarketplaceVersions from demisto_sdk.commands.content_graph.common import ContentType from demisto_sdk.commands.content_graph.parsers.agentix_base import AgentixBaseParser -from demisto_sdk.commands.content_graph.parsers.related_files import ( - SystemInstructionsRelatedFile, -) from demisto_sdk.commands.content_graph.strict_objects.agentix_agent import AgentixAgent from demisto_sdk.commands.prepare_content.agentix_agent_unifier import ( AgentixAgentUnifier, @@ -69,11 +66,6 @@ def field_mapping(self): super().field_mapping.update({"display": "name"}) return super().field_mapping - @cached_property - def system_instructions_file(self) -> SystemInstructionsRelatedFile: - """Get the system instructions related file.""" - return SystemInstructionsRelatedFile(self.path, git_sha=self.git_sha) - @property def strict_object(self): return AgentixAgent From 7e0289e0e1464e063472baece425e3fdd6807dad Mon Sep 17 00:00:00 2001 From: Sapir Malka Date: Sun, 25 Jan 2026 15:39:50 +0200 Subject: [PATCH 7/7] Rename new validation to ag110 and add support for new structure in testsuite --- TestSuite/agentix_agent.py | 38 +++++++++++++++++-- ...110_is_system_instructions_file_exists.py} | 2 +- 2 files changed, 36 insertions(+), 4 deletions(-) rename demisto_sdk/commands/validate/validators/AG_validators/{AG109_is_system_instructions_file_exists.py => AG110_is_system_instructions_file_exists.py} (98%) diff --git a/TestSuite/agentix_agent.py b/TestSuite/agentix_agent.py index cc78e408065..504149a1345 100644 --- a/TestSuite/agentix_agent.py +++ b/TestSuite/agentix_agent.py @@ -1,35 +1,55 @@ from pathlib import Path from typing import Optional +from TestSuite.file import File from TestSuite.yml import YAML, yaml class AgentixAgent(YAML): def __init__(self, tmpdir: Path, name: str, repo): + # Create directory for the agent + self._tmpdir_agent_path = tmpdir / name + self._tmpdir_agent_path.mkdir(exist_ok=True) + # Save entities self.name = name self._repo = repo self.repo_path = repo.path - self.path = str(tmpdir) - super().__init__(tmp_path=tmpdir / f"{self.name}.yml", repo_path=str(repo.path)) + self.path = str(self._tmpdir_agent_path) + + super().__init__( + tmp_path=self._tmpdir_agent_path / f"{self.name}.yml", + repo_path=str(repo.path), + ) + + # Add system instructions file + self.system_instructions = File( + self._tmpdir_agent_path / f"{self.name}_systeminstructions.md", + self._repo.path, + ) def build( self, yml: Optional[dict] = None, + system_instructions: Optional[str] = None, ): """Writes not None objects to files.""" if yml is not None: self.write_dict(yml) + if system_instructions is not None: + self.system_instructions.write(system_instructions) def create_default_agentix_agent( self, name: str = "sample_agentix_agent", agent_id: str = "sample_agentix_agent_id", + system_instructions: str = "", ): """Creates a new agentix agent with basic data. Args: name: The name of the new agentix agent, default is "sample_agentix_agent". agent_id: The ID of the new agentix agent, default is "sample_agentix_agent_id". + system_instructions: The system instructions content, default is empty string. """ default_agentix_agent_dir = ( Path(__file__).parent / "assets" / "default_agentix_agent" @@ -38,4 +58,16 @@ def create_default_agentix_agent( yml = yaml.load(yml_file) yml["id"] = agent_id yml["name"] = name - self.build(yml=yml) + self.build(yml=yml, system_instructions=system_instructions) + + def update(self, update_obj: dict, key_dict_to_update: Optional[str] = None): + """Update the YAML content, handling systeminstructions specially.""" + # Extract systeminstructions if present and write to file + if "systeminstructions" in update_obj: + system_instructions = update_obj.pop("systeminstructions") + if system_instructions: + self.system_instructions.write(system_instructions) + + # Call parent update for remaining fields + if update_obj: + super().update(update_obj, key_dict_to_update) diff --git a/demisto_sdk/commands/validate/validators/AG_validators/AG109_is_system_instructions_file_exists.py b/demisto_sdk/commands/validate/validators/AG_validators/AG110_is_system_instructions_file_exists.py similarity index 98% rename from demisto_sdk/commands/validate/validators/AG_validators/AG109_is_system_instructions_file_exists.py rename to demisto_sdk/commands/validate/validators/AG_validators/AG110_is_system_instructions_file_exists.py index 355fd999fca..77aad7af0e7 100644 --- a/demisto_sdk/commands/validate/validators/AG_validators/AG109_is_system_instructions_file_exists.py +++ b/demisto_sdk/commands/validate/validators/AG_validators/AG110_is_system_instructions_file_exists.py @@ -13,7 +13,7 @@ class IsSystemInstructionsFileExistsValidator(BaseValidator[ContentTypes]): - error_code = "AG109" + error_code = "AG110" description = "Checks if the AgentixAgent has a system instructions file." error_message = ( "The AgentixAgent '{0}' is missing a system instructions file. "