OSSA Canonical Project Structure
The canonical agents/ folder structure that scales from a single agent to thousands of agents
OSSA Canonical Project Structure
OSSA defines a canonical folder structure that scales from a single agent to thousands of agents. This layout is the foundation for agent portability, discovery, and governance.
Canonical Folder Structure
agents/
├── registry.yaml # Optional: local index (generated)
├── _shared/ # Optional: shared prompt/tool/policy assets
│ ├── policies/ # Org/team policy bundles (OPA, etc.)
│ ├── prompts/ # Reusable prompt fragments
│ └── tools/ # Shared tool schemas (OpenAPI/JSON Schema)
├── <agent-name>/
│ ├── agent.ossa.yaml # REQUIRED: single source of truth
│ ├── system-prompt.md # Optional: long prompt (referenced by manifest)
│ ├── tools/ # Optional: agent-specific tool schemas
│ ├── knowledge/ # Optional: RAG sources (docs) or pointers
│ └── tests/ # Optional: evals + conformance tests
agents-workspace/ # Derived outputs (gitignored)
├── exports/ # Generated target formats (LangGraph/CrewAI/etc.)
├── state/ # Runtime checkpoints
└── logs/ # Traces
[object Object] - Agent Definitions
The agents/ directory is your version-controlled source of truth for all agent definitions. Every agent lives in its own subdirectory with a single required file: agent.ossa.yaml.
Purpose
- Single source of truth: Each agent is fully defined by its
agent.ossa.yamlmanifest - Scales to thousands: The flat structure with optional registry supports large agent fleets
- Shareable assets: The
_shared/directory prevents duplication across agents - Platform-agnostic: Works with any OSSA-compliant runtime
Agent Directory
Each agent gets its own subdirectory within agents/:
agents/
└── customer-support/
├── agent.ossa.yaml # REQUIRED: single source of truth
├── system-prompt.md # Optional: long system prompt (referenced by manifest)
├── tools/
│ ├── search.yaml # Agent-specific tool schema (OpenAPI/JSON Schema)
│ └── create-ticket.yaml # Agent-specific tool schema
├── knowledge/
│ ├── faq.md # RAG source documents
│ └── product-catalog.json # Structured knowledge
└── tests/
├── eval-suite.yaml # Evaluation test cases
└── conformance.yaml # OSSA conformance checks
[object Object] - Shared Assets
The optional _shared/ directory holds assets reused across multiple agents:
agents/_shared/
├── policies/
│ ├── data-access.rego # OPA policy: data boundary enforcement
│ ├── pii-handling.rego # OPA policy: PII rules
│ └── cost-limits.yaml # Budget and rate limit policies
├── prompts/
│ ├── safety-preamble.md # Common safety instructions
│ ├── output-format.md # Shared output formatting rules
│ └── brand-voice.md # Consistent brand tone
└── tools/
├── slack-api.yaml # OpenAPI schema for Slack integration
├── database-query.yaml # Shared database tool schema
└── email-sender.yaml # Shared email tool schema
Agents reference shared assets via relative paths in their manifest:
spec: prompts: system: - $ref: "../_shared/prompts/safety-preamble.md" - ./system-prompt.md policies: - $ref: "../_shared/policies/pii-handling.rego"
[object Object] - Local Index
The optional registry.yaml file provides a generated local index of all agents. This enables fast discovery without scanning the filesystem:
# agents/registry.yaml (auto-generated by `ossa registry build`) agents: - name: customer-support version: "1.2.0" role: worker domain: customer-service capabilities: [qa, ticket-creation, escalation] - name: data-analyst version: "0.8.0" role: specialist domain: data-analysis capabilities: [sql-queries, visualization]
[object Object] - The Manifest
The manifest is the single source of truth for each agent. It answers four essential questions:
- Who published this? (identity + provenance)
- What can it do? (capabilities/skills + interfaces)
- What does it need? (runtime + resources + dependencies)
- What is it allowed to access? (policy + secrets + data boundaries)
apiVersion: ossa/v0.4.9 kind: Agent metadata: name: customer-support version: "1.2.0" description: Handles customer support inquiries with escalation labels: team: support-engineering tier: production annotations: ossa.io/owner: "support-team@example.com" ossa.io/sla: "99.9%" spec: # What can it do? role: worker domain: customer-service capabilities: [qa, ticket-creation, escalation, sentiment-analysis] # What does it need? llm: provider: ${LLM_PROVIDER:-anthropic} model: ${LLM_MODEL:-claude-sonnet-4-5-20250929} profile: balanced temperature: 0.3 maxTokens: 8192 fallback_models: - provider: openai model: gpt-4o condition: on_error runtime: execution: sandbox: enabled: true type: container resource_limits: memory_mb: 512 cpu_millicores: 1000 # What is it allowed to access? policies: - $ref: "../_shared/policies/pii-handling.rego" - $ref: "../_shared/policies/cost-limits.yaml" secrets: - name: SUPPORT_API_KEY source: vault path: "secret/support/api-key" data_boundaries: allowed_namespaces: [customer-data, knowledge-base] denied_namespaces: [financial-records, hr-data] # Context window strategy context: strategy: sliding_window max_tokens: 32768 reserved_tokens: system_prompt: 4096 tools: 2048 output: 4096 long_term_memory: provider: qdrant embedding_model: text-embedding-3-small prompts: system: - $ref: "../_shared/prompts/safety-preamble.md" - ./system-prompt.md tools: - name: search_knowledge_base source: ./tools/search.yaml - name: create_support_ticket source: ./tools/create-ticket.yaml - name: send_slack_notification source: ../_shared/tools/slack-api.yaml
[object Object] - Derived Outputs
The agents-workspace/ directory holds derived outputs and runtime state. This folder should NOT be version controlled (add to .gitignore).
Purpose
- Export targets: Generated agent definitions for specific frameworks (LangGraph, CrewAI, AutoGen, etc.)
- Runtime state: Checkpoints, session data, and memory snapshots
- Traces and logs: Execution traces for debugging and observability
Structure
agents-workspace/
├── exports/
│ ├── langgraph/
│ │ └── customer-support.py # Generated LangGraph agent
│ ├── crewai/
│ │ └── customer-support.yaml # Generated CrewAI config
│ └── mcp/
│ └── customer-support.json # Generated MCP server definition
├── state/
│ ├── customer-support/
│ │ ├── checkpoint-2026-02-12.json # Runtime checkpoint
│ │ └── session-abc123.json # Active session state
│ └── data-analyst/
│ └── checkpoint-latest.json
└── logs/
├── customer-support/
│ ├── 2026-02-12.jsonl # Structured trace logs
│ └── metrics.json # Performance metrics
└── data-analyst/
└── 2026-02-12.jsonl
Why Separate from [object Object]?
- Clean version control: Agent definitions are committed; runtime artifacts are not
- Security: Sensitive runtime data (session state, user data) stays out of git
- Reproducibility:
agents/is portable and reproducible;agents-workspace/is environment-specific - Export flexibility: Generate target formats on demand without polluting source
[object Object] Recommendation
# OSSA derived outputs (never commit) agents-workspace/ # Keep agent definitions (always commit) # agents/ # <-- NOT ignored, this is your source of truth
What a Perfect Agent Must Declare
At minimum, the agent.ossa.yaml manifest must answer four questions:
1. Who published this? (Identity + Provenance)
metadata: name: customer-support version: "1.2.0" description: Handles customer support inquiries labels: team: support-engineering tier: production annotations: ossa.io/owner: "support-team@example.com" ossa.io/repo: "https://gitlab.com/org/agents"
2. What can it do? (Capabilities/Skills + Interfaces)
spec: role: worker domain: customer-service capabilities: [qa, ticket-creation, escalation, sentiment-analysis] tools: - name: search_knowledge_base source: ./tools/search.yaml - name: create_support_ticket source: ./tools/create-ticket.yaml
3. What does it need? (Runtime + Resources + Dependencies)
spec: llm: provider: ${LLM_PROVIDER:-anthropic} model: ${LLM_MODEL:-claude-sonnet-4-5-20250929} profile: balanced runtime: execution: sandbox: enabled: true type: container resource_limits: memory_mb: 512 cpu_millicores: 1000 dependencies: - name: knowledge-base-service version: ">=2.0.0"
4. What is it allowed to access? (Policy + Secrets + Data Boundaries)
spec: policies: - $ref: "../_shared/policies/pii-handling.rego" - $ref: "../_shared/policies/cost-limits.yaml" secrets: - name: SUPPORT_API_KEY source: vault path: "secret/support/api-key" data_boundaries: allowed_namespaces: [customer-data, knowledge-base] denied_namespaces: [financial-records, hr-data]
Context Windows: Sliding Window + Long-Term Memory
Modern agents operate under hard model context limits. A well-defined agent declares a context strategy that is deterministic enough to audit but flexible enough to evolve.
Declaring Context Strategy
spec: context: strategy: sliding_window # How to manage the context window max_tokens: 32768 # Hard limit for the context window reserved_tokens: system_prompt: 4096 # Reserved for system instructions tools: 2048 # Reserved for tool definitions output: 4096 # Reserved for generation overflow: summarize # What to do when window is full long_term_memory: provider: qdrant # Vector store for persistent recall embedding_model: text-embedding-3-small dimensions: 1536 retrieval: top_k: 10 # Number of memories to retrieve score_threshold: 0.7 # Minimum relevance score
Context Strategies
| Strategy | Behavior | Best For |
|---|---|---|
sliding_window | Remove oldest messages first (FIFO) | General-purpose agents |
summarize | Compress old messages into summaries | Long conversations |
importance_based | Keep high-importance messages longer | Complex multi-step tasks |
hybrid | Sliding window + summarize on overflow | Production workloads |
Token Budget Breakdown
The context window is divided into reserved zones:
┌──────────────────────────────────────────┐
│ System Prompt (reserved: 4096) │
├──────────────────────────────────────────┤
│ Tool Definitions (reserved: 2048) │
├──────────────────────────────────────────┤
│ Retrieved Memories (dynamic) │
├──────────────────────────────────────────┤
│ Conversation History (sliding window) │
├──────────────────────────────────────────┤
│ Output Generation (reserved: 4096) │
└──────────────────────────────────────────┘
The remaining tokens after reservations are split between retrieved long-term memories and the sliding conversation window. This ensures the agent never exceeds its context limit while maintaining access to both recent context and relevant historical knowledge.
Agent Taxonomy
OSSA uses a taxonomy system to classify and organize agents within the manifest:
spec: role: worker | supervisor | coordinator | specialist domain: customer-service | data-analysis | content-creation | ... capabilities: [capability-1, capability-2, ...] labels: tags: [optional, custom, tags]
Roles
worker: Executes specific tasks (e.g., customer support agent, data fetcher)supervisor: Manages and coordinates workers (e.g., team lead, orchestrator)coordinator: Routes tasks between agents (e.g., dispatcher, load balancer)specialist: Domain expert for complex tasks (e.g., legal advisor, data scientist)
Using Taxonomy for Routing
Taxonomy enables intelligent routing in multi-agent systems:
// Find all customer service workers const supportAgents = await ossa.findAgents({ role: 'worker', domain: 'customer-service' }); // Find agents with specific capability const escalationHandlers = await ossa.findAgents({ capabilities: ['escalation-handling'] }); // Route task to appropriate agent const agent = await ossa.routeTask(task, { requiredCapabilities: ['sql-queries', 'data-visualization'] });
Project Initialization
When you run ossa init, OSSA creates the canonical structure:
$ ossa init my-project ✅ Created agents/ directory (version controlled) ✅ Created agents-workspace/ directory (gitignored) ✅ Created agents/_shared/ with starter policies and prompts ✅ Created .gitignore with agents-workspace/
To add a new agent:
$ ossa agent create customer-support --role worker --domain customer-service ✅ Created agents/customer-support/agent.ossa.yaml ✅ Created agents/customer-support/system-prompt.md ✅ Created agents/customer-support/tools/ ✅ Created agents/customer-support/tests/
Best Practices
✅ DO
- Commit
agents/to version control (git) - Ignore
agents-workspace/in.gitignore - Use
_shared/for policies, prompts, and tools reused across agents - Answer all four questions in every
agent.ossa.yaml(identity, capabilities, runtime, access) - Declare a context strategy so token usage is auditable and predictable
- Include tests/ with evaluation suites and conformance checks
- Version your manifests (semver recommended)
❌ DON'T
- Don't commit
agents-workspace/(contains runtime state, exports, logs) - Don't hardcode secrets in agent manifests (use
${ENV_VAR}substitution or vault references) - Don't mix runtime state with agent definitions
- Don't duplicate shared assets across agents (use
_shared/with$ref) - Don't skip context strategy (undefined context behavior leads to unpredictable token usage)
Migration from Other Frameworks
When migrating from other frameworks (LangChain, CrewAI, etc.), use ossa migrate:
# Migrate LangChain agents to OSSA canonical structure $ ossa migrate --from langchain --input ./langchain-agents --output ./agents ✅ Created agents/ with migrated agent definitions (agent.ossa.yaml) ✅ Created agents-workspace/ for runtime exports ✅ Generated agents/registry.yaml index
See Migration Guides for framework-specific instructions.
Summary
| Folder | Purpose | Version Control | Contains |
|---|---|---|---|
agents/ | Agent definitions | ✅ YES | agent.ossa.yaml, prompts, tool schemas, knowledge, tests |
agents/_shared/ | Shared assets | ✅ YES | Policies, reusable prompts, shared tool schemas |
agents-workspace/ | Derived outputs | ❌ NO | Exports, runtime state, traces/logs |
Key Takeaway: agents/ is the single source of truth (commit to git). agents-workspace/ holds generated outputs and runtime state (never commit).
Next Steps
- Create Your First Agent
- Agent Manifest Reference
- Runtime Specification - Execution model, memory, and context management
- Migration Guides - Framework-specific migration instructions