Skip to main content

中文

AEP - Agent Environment Protocol

AEP Logo

A file-system-first terminal environment for LLM agents.

Manage capabilities with Profile; run session-bound tools with AEP.


Why AEP?

AEP is designed around one assumption: an agent already knows how to work in a terminal. Instead of stuffing every capability into prompt text or forcing everything through remote wrappers, AEP mounts capabilities into a workspace and gives the host a small runtime API.

It keeps three capability categories:

  • tools/: a shared Python tool environment, invoked through tools run
  • skills/: isolated skill packages, invoked through skills run
  • library/: tree-structured reference documents with generated index.md

The public Python surface is intentionally small:

  • Profile: add tools, skills, library docs, MCP config, then generate indexes
  • AEP: mount one profile into one workspace, build agent context, expose tool schemas for one logical session, route tool calls for that session
  • ToolSchemaBinding: lightweight session_id + schemas pair returned by AEP.tool_schemas(session_id)

Architecture

Current structure is split into two public surfaces:

  1. Profile: resource management and indexing
  2. AEP: runtime mounting, agent context generation, and session-bound tool execution

Internal implementation is organized under:

  • src/aep/runtime/: runtime facade, context rendering, session runtime, tool-call dispatcher
  • src/aep/capability/: tools, skills, library, indexing
  • src/aep/profile.py: public config facade
Profile directory layout
config_dir/
├── tools/
│   ├── .venv/
│   ├── requirements.txt
│   ├── index.md
│   └── *.py
├── skills/
│   ├── index.md
│   └── <skill-name>/
│       ├── .venv/
│       ├── SKILL.md
│       ├── requirements.txt
│       └── scripts/...
├── library/
│   ├── index.md
│   └── ...
└── _mcp/
    └── <server>/config.json

API Summary

from aep import AEP, Profile, ToolSchemaBinding
  • Profile(config_dir, ...)
  • Profile.add_tool(...)
  • Profile.add_skill(...)
  • Profile.add_library(...)
  • Profile.add_mcp_server(...)
  • Profile.index()
  • AEP(config_or_profile, workspace=..., agent_dir=".agents")
  • AEP.build_context()
  • AEP.tool_schemas(session_id) -> ToolSchemaBinding
  • AEP.call_tool(session_id, name, arguments)
  • AEP.detach()

tool_schemas(session_id) returns:

ToolSchemaBinding(
    session_id="agent-1",
    schemas=[...],
)

schemas is what you send to the model. session_id stays on the host side and is used to route later tool calls back into the correct logical session.

Quick Start

Installation

Once published, install via pip:

pip install agent-env-protocol

For local development:

git clone https://github.com/Slipstream-Max/Agent-Environment-Protocol
cd Agent-Environment-Protocol
uv sync --extra dev

CLI

# 1. Create or update a profile
aep index --profile ./agent_config

# 2. Add capabilities
aep tool add ./examples/calc.py --name calc --profile ./agent_config
aep skill add ./examples/greeter --name greeter --profile ./agent_config
aep library add ./docs/guide.md --target-dir intro --profile ./agent_config
aep index --profile ./agent_config

# 3. Start a shell inside one workspace
aep shell --profile ./agent_config --workspace ./workspace

# Inside the shell:
# > tools list
# > tools run "tools.calc.add(1, 2)"
# > tools run PY<<
# > import pandas as pd
# > print(tools.calc.add(1, 2))
# > PY
# > skills run greeter/main.py

Minimal Python API

import asyncio

from aep import AEP, Profile


async def main() -> None:
    profile = Profile("./agent_capabilities")
    profile.index()

    aep = AEP(profile, workspace="./my_project")
    context = aep.build_context()
    binding = aep.tool_schemas("agent-main")

    print(context)
    print(binding.session_id)
    print(binding.schemas)

    result = await aep.call_tool(
        binding.session_id,
        "aep_exec",
        {"command": "tools list"},
    )
    print(result)

    aep.detach()


asyncio.run(main())

Agent Context

AEP.build_context() returns one prompt block for the model. It contains:

  • an introduction explaining that this is an agent terminal environment
  • the exposed tool schemas and what each one does
  • the special command conventions: tools run "...", tools run PY<< ... PY, and skills run ...
  • the generated indexes for the current profile

This keeps prompt construction centralized in one place instead of scattering it between profile and adapters.

OpenAI SDK Integration

import asyncio
import json
from dataclasses import dataclass

from openai import AsyncOpenAI

from aep import AEP, Profile


@dataclass
class AgentState:
    session_id: str
    tools: list[dict]
    messages: list[dict]


def parse_arguments(raw: str | None) -> dict:
    if not raw:
        return {}
    value = json.loads(raw)
    return value if isinstance(value, dict) else {}


async def run_turn(
    *,
    client: AsyncOpenAI,
    model: str,
    aep: AEP,
    agent: AgentState,
    user_text: str,
) -> None:
    agent.messages.append({"role": "user", "content": user_text})

    while True:
        response = await client.chat.completions.create(
            model=model,
            messages=agent.messages,
            tools=agent.tools,
            tool_choice="auto",
        )
        message = response.choices[0].message

        agent.messages.append(
            {
                "role": "assistant",
                "content": message.content or "",
                "tool_calls": [
                    {
                        "id": call.id,
                        "type": call.type,
                        "function": {
                            "name": call.function.name,
                            "arguments": call.function.arguments,
                        },
                    }
                    for call in (message.tool_calls or [])
                ],
            }
        )

        if not message.tool_calls:
            return

        for call in message.tool_calls:
            result = await aep.call_tool(
                agent.session_id,
                call.function.name,
                parse_arguments(call.function.arguments),
            )
            agent.messages.append(
                {
                    "role": "tool",
                    "tool_call_id": call.id,
                    "content": json.dumps(result, ensure_ascii=False),
                }
            )


async def main() -> None:
    profile = Profile("./config")
    profile.index()
    aep = AEP(profile, workspace="./workspace")

    context = aep.build_context()
    binding1 = aep.tool_schemas("agent-1-session")
    binding2 = aep.tool_schemas("agent-2-session")

    system_prompt = (
        "You are working inside an AEP terminal environment.\n\n"
        f"{context}"
    )

    agent1 = AgentState(
        session_id=binding1.session_id,
        tools=binding1.schemas,
        messages=[{"role": "system", "content": system_prompt}],
    )
    agent2 = AgentState(
        session_id=binding2.session_id,
        tools=binding2.schemas,
        messages=[{"role": "system", "content": system_prompt}],
    )

    client = AsyncOpenAI()

    await run_turn(
        client=client,
        model="gpt-4.1-mini",
        aep=aep,
        agent=agent1,
        user_text="Run `pwd` and then export A=1.",
    )
    await run_turn(
        client=client,
        model="gpt-4.1-mini",
        aep=aep,
        agent=agent2,
        user_text="Run `pwd` and then export B=2.",
    )

    env1 = await aep.call_tool(agent1.session_id, "aep_env", {})
    env2 = await aep.call_tool(agent2.session_id, "aep_env", {})
    print(env1)
    print(env2)

    aep.detach()


if __name__ == "__main__":
    asyncio.run(main())

Each agent gets the same tool names, but a different host-side session_id. Session isolation is explicit on the host and invisible to the model.

Tool Schemas

The default tool surface is:

  • aep_exec: execute one command in the current bound session
  • aep_output: fetch incremental output for one execution
  • aep_kill: stop a queued or running execution
  • aep_history: inspect recent command history
  • aep_env: inspect custom session environment variables

References

  • Detailed API docs: docs/api.md
  • Architecture notes: docs/arch.md

License

MIT License

Release files for agent-env-protocol 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agent-env-protocol 0.2.0
File Size Uploaded
agent_env_protocol-0.2.0.tar.gz 113.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-env-protocol 0.2.0
File Interpreter ABI Platform
agent_env_protocol-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 166.1 kB

Release files / agent_env_protocol-0.2.0.tar.gz

Download URL agent_env_protocol-0.2.0.tar.gz
Size 113.2 kB
Tags Source
SHA-256 checksum
How to use checksums
67334ca23d41d344f6c5bad996c7d99c19a6a63ffcc9f5458f7efd6470031515
BLAKE2b-256 checksum
How to use checksums
cef2b529327ca2c759fb06b2a8764308fe0dc990c68de01decb5e94a6de549c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 8, 2026.

Transparency log

Release files / agent_env_protocol-0.2.0-py3-none-any.whl

Download URL agent_env_protocol-0.2.0-py3-none-any.whl
Size 52.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7aca6fade2a75311f2c0bb52add5d8824b43a92d8500fa39a7ee4d5a36a10b25
BLAKE2b-256 checksum
How to use checksums
9c56ac3e8a1e14a425ec3cc94a4e29e0004ff0b53b2de3f26f63fa3808dae526
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page