AgentPM™ Python SDK
A lean, typed Python SDK for AgentPM tools and installed agent, Knowledge, Memory, Profile, and Loop packages. It discovers tools installed by agentpm install, executes their entrypoints in a subprocess, and can also inspect installed agent manifests plus their resolved dependency refs.
- 🔎 Discovers tools in
.agentpm/tools(project) and~/.agentpm/tools(user), withAGENTPM_TOOL_DIRoverride. - 📦 Loads installed agents from
.agentpm/agentsand exposes their resolved tool and skill refs fromagent.lock. - 📚 Loads installed skills from
.agentpm/skillsand exposes their manual content plus resolved tool refs. - 🧠 Loads installed Knowledge packages from
.agentpm/knowledgeand exposes mode-specific metadata and canonical paths. - ♾️ Loads installed Memory packages from
.agentpm/memoryand exposes authored blueprint metadata, build metadata, contract indexes, and resolved contract paths. - 🎭 Loads installed Profile packages from
.agentpm/profilesand exposes authored role, objective, and communication metadata. - 🔁 Loads installed Loop packages from
.agentpm/loopsand exposes authored phase, transition, and error-policy metadata. - 🚀 Runs entrypoints via
nodeorpython(whitelisted) and exchanges JSON over stdin/stdout. - 🧩 Metadata-aware:
with_meta=Truereturnsfunc + meta(name, version, description, inputs, outputs). - 🧪 Framework adapters (optional): e.g., a LangChain adapter you can use if installed.
Requires Python 3.10+.
Installation
From PyPI (recommended)
Using uv:
uv pip install agentpm
Or with standard pip:
python -m pip install agentpm
If you'll use the optional LangChain adapter:
uv pip install 'agentpm[langchain]'
# or
python -m pip install 'agentpm[langchain]'
Quick Start (with uv)
# create and activate a venv
uv venv
source .venv/bin/activate
# install SDK in editable dev mode (ruff/black/mypy/pytest, etc.)
uv pip install -e ".[dev]"
# sanity checks
uv run ruff check .
uv run black --check .
uv run mypy
uv run pytest -q
If you're not using
uv, standardpython -m venv+pip install -e ".[dev]"works too.
Using the SDK
from agentpm import load
# Spec format: "@scope/name@version"
summarize = load("@zack/summarize@0.1.0")
result = summarize({"text": "Long document content..."})
print(result["summary"])
With metadata (build richer tool descriptions)
from agentpm import load
tool = load("@zack/summarize@0.1.0", with_meta=True)
summarize, meta = tool["func"], tool["meta"]
rich_description = (
f"{meta.get('description','')} "
f"Inputs: {meta.get('inputs')}. "
f"Outputs: {meta.get('outputs')}."
)
print(rich_description)
print(summarize({"text": "hello"})["summary"])
Load an installed agent package
from agentpm import (
load,
load_agent,
load_knowledge,
load_loop,
load_memory,
load_profile,
load_skill,
)
agent = load_agent("@zack/support-agent@0.1.0")
docs = load_knowledge("@zack/python-docs@0.1.0")
loop = load_loop("@zack/incident-response-loop@0.3.0")
memory = load_memory("@zack/profile-memory@0.1.0")
profile = load_profile("@zack/support-style@0.1.0")
first_skill = agent["resolvedSkills"][0]
skill = load_skill(f'{first_skill["name"]}@{first_skill["version"]}')
first_tool = skill["resolvedTools"][0]
tool = load(f'{first_tool["name"]}@{first_tool["version"]}')
print(agent["resolvedKnowledge"])
print(agent["resolvedLoop"])
print(agent["resolvedMemory"])
print(agent["resolvedProfiles"])
print(loop["loop"]["transitions"])
print(docs["knowledge"]["mode"])
print(memory["contracts"])
print(profile["profile"]["communication"])
load_agent() returns:
- the installed agent manifest
- the installed agent root path
resolvedKnowledgefromagent.lockresolvedLoopfromagent.lockresolvedMemoryfromagent.lockresolvedProfilesfromagent.lock- reserved refs (
knowledge,memory,profiles) as metadata resolvedToolsfromagent.lockresolvedSkillsfromagent.lock
It does not execute the agent package or orchestrate the tools for you.
Compatibility note:
resolvedKnowledgeis populated from the modern first-classroot.knowledgeentries inagent.lock.reserved.knowledgeis legacy pass-through metadata from older lockfile shapes. For current installs, treatresolvedKnowledgeas the authoritative Knowledge dependency list and expectreserved.knowledgeto usually be empty.- If your workspace still has an older pre-Knowledge lockfile shape where Knowledge refs only exist under
reserved.knowledge, rerunagentpm installto rewrite the lockfile before expectingresolvedKnowledgeto be populated. resolvedLoopis populated from the modern first-classroot.loopentry inagent.lock.resolvedProfilesis populated from the modern first-classroot.profilesentries inagent.lock.reserved.profilesis legacy pass-through metadata from older lockfile shapes. For current installs, treatresolvedProfilesas the authoritative Profile dependency list and expectreserved.profilesto usually be empty.manifest["loop"]andmanifest["bindings"]preserve the authored declarative metadata from the installedagent.json.
This is the Python mirror of the Node SDK’s loadAgent() flow:
- load the installed agent package
- read its resolved skill and tool refs
- optionally load a resolved skill package
- choose which tool packages to
load()
Run Harness over the machine protocol
from agentpm import BeforeToolCallDecision, BeforeToolCallInput, HarnessClient
harness = HarnessClient(agent="@zack/support-agent@0.1.0")
def model_provider(request):
return {
"id": "turn-1",
"assistant_content": "Handled by the host model.",
"actions": [],
"usage": {},
"finish_reason": "stop",
"provider_metadata": {"model": request["selection"]["model"]},
}
def before_tool_call(input: BeforeToolCallInput) -> BeforeToolCallDecision:
return {"decision": "continue", "patch": {"arguments": input["arguments"]}}
harness.register_model_provider("company-model", model_provider)
harness.on_before_tool_call(before_tool_call)
harness.on_approval(lambda checkpoint: "approve")
result = harness.run("Use the configured agent.")
harness.shutdown()
print(result)
agent is optional. When set, it is passed to agentpm harness as either an installed Agent package ref such as @zack/support-agent@0.1.0 or a local manifest path such as ./agent.json; when omitted, Harness uses its normal workspace discovery/default Agent selection. Register host services before starting a run. The SDK launches agentpm harness --machine, correlates request/response frames, streams Harness events, and routes host-service callbacks for model providers, embedding providers, Knowledge runtimes, hooks, and approvals.
The convenience Hook helpers use contract-specific type hints. on_before_model_request may return context_sections and provider_options, on_before_tool_selection may return candidate_ids, on_before_tool_call may return replacement arguments, on_before_knowledge_request may shape the same package request options, and on_after_knowledge_retrieval may filter/reorder existing results or transform model-visible text while retaining result identity. Memory hook helpers are also exposed for the version-1 contract, but memory execution becomes live with the later Harness memory milestones. A returned object must include decision: "continue" or decision: "reject"; returning None is treated as an explicit continue with no patch. There is no embedding Hook ID; use register_embedding_provider to host query embedding.
Host capability advertisement is role-specific. register_model_provider automatically advertises the registry ID as provider plus semantic-action, structured-output, multimodal-input, and usage-reporting flags; pass model-specific overrides such as model or context_window_tokens when known. register_embedding_provider registers the Milestone 12 embed contract and advertises supported embedding spaces. register_knowledge_runtime registers the Milestone 12 retrieve contract and advertises supported modes/features/package attestations. register_host_provider remains available for lower-level role-specific capability shapes. on_approval advertises approval support and optional cancellation support. After initialization, host_service_registration(role, registry_id) exposes the Harness registration result, including active: false and a reason when Harness rejects a registration.
For CLI-launched custom Knowledge runtimes, use serve_knowledge_runtime_process(registry_id, handler, capabilities) to serve the public agentpm-service JSONL process protocol from a Python provider process. The Pinecone and pgvector reference providers live in https://github.com/agentpm-dev/agentpm-examples/tree/main/knowledge-packages/m13-reference-providers; they are examples to copy from, not provider-specific SDK exports.
Load an installed skill package
from agentpm import load_skill
skill = load_skill("@zack/triage-playbook@0.1.0")
print(skill["entrypointPath"])
print(skill["entrypointContent"])
print(skill["references"])
print(skill["scripts"])
print(skill["resolvedTools"])
load_skill() returns an inspectable Skill object. Skills are not runnable SDK objects.
Load an installed Knowledge package
from agentpm import load_knowledge
knowledge = load_knowledge("@zack/python-docs@0.1.0")
print(knowledge["knowledge"]["mode"])
print(knowledge["documentPaths"])
print(knowledge["chunksPath"])
print(knowledge["sourcesPath"])
print(knowledge["vectorsPath"])
print(knowledge["indexPaths"])
load_knowledge() returns an inspectable Knowledge object with:
- the installed knowledge manifest
- the installed package root path
- parsed
knowledgemetadata - absolute paths for declared context documents, chunks, sources, vectors, indexes, and provenance when present
Load an installed Memory package
from agentpm import load_memory, load_memory_contract
memory = load_memory("@zack/profile-memory@0.1.0")
profile_contract = load_memory_contract(
memory,
space="profile",
record_type="user_preference",
)
print(memory["memory"]["spaces"])
print(memory["build"])
print(memory["contractIndex"])
print(memory["contracts"])
print(profile_contract)
load_memory() returns an inspectable Memory Blueprint object with:
- the installed memory manifest
- the installed package root path
- parsed
memorymetadata - parsed
memory/build.json - parsed
memory/contracts/index.json - absolute paths for declared source schemas and indexed resolved contracts
It is a metadata and contract loader only. It does not provide live record CRUD, retention enforcement, trigger execution, or a hosted memory runtime.
load_memory_contract() loads one indexed resolved contract on demand by space + record_type.
Load an installed Profile package
from agentpm import load_profile
profile = load_profile("@zack/support-style@0.1.0")
print(profile["profile"]["identity"]["role"])
print(profile["profile"]["objectives"])
print(profile["profile"]["communication"])
load_profile() returns an inspectable Instruction Profile object with:
- the installed profile manifest
- the installed package root path
- parsed authored
profilemetadata
Load an installed Loop package
from agentpm import load_loop
loop = load_loop("@zack/incident-response-loop@0.3.0")
print(loop["loop"]["entry_phase"])
print(loop["loop"]["phases"])
print(loop["loop"]["transitions"])
print(loop["loop"]["error_policy"])
load_loop() returns an inspectable Loop object with:
- the installed loop manifest
- the installed package root path
- parsed authored
loopmetadata
load() stays tool-only
from agentpm import load
load("@zack/triage-playbook@0.1.0")
# raises: use load_skill("@zack/triage-playbook@0.1.0") instead
load("@zack/python-docs@0.1.0")
# raises: use load_knowledge("@zack/python-docs@0.1.0") instead
load("@zack/profile-memory@0.1.0")
# raises: use load_memory("@zack/profile-memory@0.1.0") instead
load("@zack/support-style@0.1.0")
# raises: use load_profile("@zack/support-style@0.1.0") instead
load("@zack/incident-response-loop@0.3.0")
# raises: use load_loop("@zack/incident-response-loop@0.3.0") instead
Optional: LangChain adapter
The adapter is lazy-imported and only needed if you call it.
from agentpm import load, to_langchain_tool # to_langchain_tool is loaded on first access
loaded = load("@zack/summarize@0.1.0", with_meta=True)
tool = to_langchain_tool(loaded) # requires `langchain-core` installed
If you use the adapter, install LangChain core:
uv pip install langchain-core
Where tools are discovered
Resolution order:
AGENTPM_TOOL_DIR(environment variable)./.agentpm/tools(project-local)~/.agentpm/tools(user-local)
Each tool lives in a directory like:
.agentpm/
tools/
@zack/summarize/
0.1.0/
agent.json
(tool files…)
Installed registry agent packages live separately:
.agentpm/
agents/
@zack/support-agent/
0.1.0/
agent.json
README.md
Installed registry skill packages live separately:
.agentpm/
skills/
@zack/triage-playbook/
0.1.0/
agent.json
SKILL.md
Installed registry Knowledge packages live separately:
.agentpm/
knowledge/
@zack/python-docs/
0.1.0/
agent.json
knowledge/
Installed registry Memory packages live separately:
.agentpm/
memory/
@zack/profile-memory/
0.1.0/
agent.json
schemas/
memory/
Where installed agents are discovered
Resolution order for load_agent():
AGENTPM_AGENT_DIR(environment variable)./.agentpm/agents(project-local)~/.agentpm/agents(user-local)
You can also override per call:
load_agent("@zack/support-agent@0.1.0", agent_dir_override="/path/to/agents")
Where installed skills are discovered
Resolution order for load_skill():
AGENTPM_SKILL_DIR(environment variable)./.agentpm/skills(project-local)~/.agentpm/skills(user-local)
You can also override per call:
load_skill("@zack/triage-playbook@0.1.0", skill_dir_override="/path/to/skills")
Where installed Knowledge packages are discovered
Resolution order for load_knowledge():
AGENTPM_KNOWLEDGE_DIR(environment variable)./.agentpm/knowledge(project-local)~/.agentpm/knowledge(user-local)
You can also override per call:
load_knowledge("@zack/python-docs@0.1.0", knowledge_dir_override="/path/to/knowledge")
Where installed Memory packages are discovered
Resolution order for load_memory():
AGENTPM_MEMORY_DIR(environment variable)./.agentpm/memory(project-local)~/.agentpm/memory(user-local)
You can also override per call:
load_memory("@zack/profile-memory@0.1.0", memory_dir_override="/path/to/memory")
Manifest & Runtime Contract
agent.json (minimal fields used by the SDK):
{
"name": "@zack/summarize",
"version": "0.1.0",
"description": "Summarize long text.",
"inputs": {
"type": "object",
"properties": { "text": { "type": "string", "description": "Text to summarize" } },
"required": ["text"]
},
"outputs": {
"type": "object",
"properties": { "summary": { "type": "string", "description": "Summarized text" } },
"required": ["summary"]
},
"entrypoint": {
"command": "python",
"args": ["main.py"],
"cwd": ".",
"timeout_ms": 60000,
"env": {}
}
}
Execution contract:
- SDK writes inputs JSON to the process stdin.
- Tool writes a single outputs JSON object to stdout.
- Non-JSON logs should go to stderr.
- Process must exit with code 0 on success.
Interpreter whitelist: node, nodejs, python, python3.
The SDK validates the interpreter and checks it’s present on PATH.
Development
Project layout
src/
agentpm/
__init__.py # re-exports: load, load_agent, load_knowledge, load_memory, load_skill, to_langchain_tool (lazy)
core.py # resolver/spawn/JSON plumbing
types.py # JsonValue, TypedDicts
adapters/
__init__.py
langchain.py # optional adapter
py.typed # marks package as typed
tests/
test_basic.py
test_load_agent.py
test_load_memory.py
test_load_skill.py
Common tasks (via uv)
uv run ruff check .
uv run black --check .
uv run mypy
uv run pytest -q
# run hooks locally on all files
uv run pre-commit run --all-files
Building & Publishing
# build wheel & sdist
uv run python -m build
# verify metadata
uv run twine check dist/*
# upload (PyPI)
uv run twine upload dist/*
# or TestPyPI first
uv run twine upload -r testpypi dist/*
Running mixed-runtime Agent apps with Docker
Some AgentPM tools run on Node, some on Python—and your agent may need to spawn both. Using Docker gives you a single, reproducible environment where both interpreters are installed and on PATH, which avoids the common “interpreter not found” issues that pop up on PaaS/CI or IDEs.
Why Docker?
✅ Hermetic: Python + Node versions are pinned inside the image.
✅ No PATH drama: node/python are present and discoverable.
✅ Prod/CI parity: the same image runs on your laptop, CI, and servers.
✅ Easy secrets: pass API keys via env at docker run/Compose time.
✅ Fewer surprises: consistent OS libs for LLM clients, SSL, etc.
When to use it
- You deploy to platforms that don’t let you apt-get both runtimes.
- Your agent uses tools with different interpreters (Node + Python).
- Your local dev/IDE PATH differs from production and causes failures.
- You want reproducible builds and easy rollback.
How to use it
- Copy the provided Dockerfile into your repo.
- (Optional) Pre-install tools locally with agentpm install ... and commit or copy .agentpm/tools/ into the image, or run agentpm install at build time if your CLI is available in the image.
- Build & run:
docker build -t agent-app .
docker run --rm -e OPENAI_API_KEY=$OPENAI_API_KEY agent-app
- For development, use the docker-compose.yml snippet to mount your source and pass env vars conveniently.
Troubleshooting
- Set
AGENTPM_DEBUG=1to print the SDK’s project root, search paths, merged PATH, and resolved interpreters. - You can force interpreters via:
AGENTPM_NODE=/usr/bin/node
AGENTPM_PYTHON=/usr/local/bin/python3.11
- Prefer absolute interpreters in agent.json.entrypoint.command for production (e.g., /usr/bin/node). The SDKs still enforce the Node/Python family.
Troubleshooting
-
No JSON object found on stdout.Ensure your tool prints a single JSON object as the last thing on stdout, and writes logs to stderr. -
Unsupported agent.json.entrypoint.commandOnlynode/pythonare allowed (includingnodejs/python3). Updateentrypoint.command. -
Interpreter "... " not found on PATHInstall the interpreter or adjustentrypoint.command. The SDK runs<command> --versionto verify availability. -
PEP 668 / “externally managed” Use a venv (we recommend
uv venv) and install withuv pip install -e ".[dev]". -
IDE can’t import
agentpmEnsure your interpreter is the project’s.venv/bin/python, and that you ran the editable install.
License
MIT — see LICENSE.
Metadata
Release files for agentpm 0.1.15
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agentpm-0.1.15.tar.gz | 36.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentpm-0.1.15-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 74.9 kB
Release files / agentpm-0.1.15.tar.gz
| Download URL | agentpm-0.1.15.tar.gz |
|---|---|
| Size | 36.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6cfe604b4e348cdf5d8da1049eb27eaca722d7a33e93c38963457411ca6c1de7
|
|
BLAKE2b-256 checksum How to use checksums |
cf670ae5b6207e55dbcb3bcdcc9ed967ed5c26875462188ba100bac5d54993ff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 3, 2026.
Transparency logRelease files / agentpm-0.1.15-py3-none-any.whl
| Download URL | agentpm-0.1.15-py3-none-any.whl |
|---|---|
| Size | 38.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e7d0615864bd251a1d568abe2c68ed9d859681b56a354e5704c7c9638d2e845c
|
|
BLAKE2b-256 checksum How to use checksums |
08c72f342c1abe86873e4f40e8f1910eb678baac244c394ba9c418e31fb9ab15
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 3, 2026.
Transparency log