Agents#

Compose tools, skills, knowledge, memory, profiles, and an optional loop behind a single agent manifest.

What is an “agent”?#

An agent is a composition layer: it declares which tools, skills, knowledge packages, memory blueprints, instruction profiles, and optional Loop package it can use. The agent manifest doesn’t execute code itself. It is a package artifact that enumerates package dependencies, optional orchestration metadata, and prompt examples so the CLI and SDKs can resolve, install, and expose the graph to your agent runtime.

  • Defines capability surface by listing the packages the agent may use.
  • Drives deterministic installs via agent.lock (package identity + integrity + relationships).
  • Keeps host/runtime lightweight: each tool runs in a managed subprocess with its own dependencies.
  • Treats tools, skills, knowledge, memory, profiles, and loops as first-class resolved package dependencies.

Scaffold an agent#

agentpm init --kind agent --name research-assistant --description "Assistant composed of multiple tools"

Generated skeleton (agent.json):

{
  "kind": "agent",
  "name": "research-assistant",
  "version": "0.1.0",
  "description": "Assistant composed of multiple tools",
  "tools": [],
  "skills": [],
  "knowledge": [],
  "memory": [],
  "profiles": [],
  "examples": [
    {
      "title": "Example prompt",
      "prompt": "Describe the user request this agent should handle."
    }
  ]
}
Note

Add tools to tools[], skills to skills[], knowledge packages to knowledge[], memory blueprints to memory[], instruction profiles to profiles[], and an optional loop through loop when needed, then run agentpm install to resolve and download artifacts.

Field reference (overview)#

FieldTypeRequiredNotes
$schemastringnoURI to this schema (optional but recommended)
kindenumyesMust be "agent"
namestringyes^[a-z][a-z0-9-]{0,63}$
versionsemveryesSemVer string (supports pre/metadata)
descriptionstringyesFree text
toolsarrayyesArray of tool refs: string or {name, version}; may be empty ([])
skillsarraynoArray of skill refs: string or {name, version}
knowledgearraynoArray of knowledge refs: string or {name, version}
memoryarraynoArray of memory refs: string or {name, version}
profilesarraynoArray of profile refs: string or {name, version}
loopstring or objectnoSingular loop ref: string or {name, version}
bindingsobjectnoOptional global, phase, MCP, and consumer-context binding metadata
examplesarraynoInline prompt examples { title, prompt }.
readmestringnoPath to README file. Will automatically look for README.md if not specified.
licenseobjectno{ spdx: "license spdx", file: "Path to LICENSE file" }

Declaring tools#

You can reference tools using either a string spec or an object. Both carry the same information; choose one style per team preference.

The tools field is required for kind: "agent" manifests, even when the agent currently has no tool dependencies and the value is just [].

String spec (concise)#

{
  "tools": [
    "@zack/summarize@0.1.2"
  ]
}

Object form (explicit)#

{
  "tools": [
    { "name": "@zack/summarize", "version": "0.1.2" }
  ]
}

Declaring skills#

Agents may also declare first-class skill dependencies:

{
  "skills": [
    "@zack/incident-commander@0.1.0",
    { "name": "@zack/slack-incident-update", "version": "^0.1.0" }
  ]
}

Skill dependencies are resolved and locked during install just like tool dependencies.

Declaring knowledge#

Agents may also declare first-class knowledge dependencies:

{
  "knowledge": [
    "@zack/python-docs@0.1.0",
    { "name": "@zack/support-playbook", "version": "^0.1.0" }
  ]
}

Knowledge dependencies are resolved and locked during install just like tool and skill dependencies.

Declaring memory#

Agents may also declare first-class memory dependencies:

{
  "memory": [
    "@zack/profile-memory@0.1.0",
    { "name": "@zack/history-memory", "version": "^0.1.0" }
  ]
}

Memory dependencies are resolved and locked during install just like tool, skill, and knowledge dependencies.

Declaring profiles#

Agents may also declare first-class profile dependencies:

{
  "profiles": [
    "@zack/support-style@0.1.0",
    { "name": "@zack/incident-voice", "version": "^0.1.0" }
  ]
}

Profile dependencies are resolved and locked during install just like tool, skill, knowledge, and memory dependencies.

Declaring a loop#

Agents may declare at most one direct loop dependency:

{
  "loop": "@zack/incident-response-loop@1.0.0"
}

Object form is also supported:

{
  "loop": {
    "name": "@zack/incident-response-loop",
    "version": "^1.0.0"
  }
}

Loop dependencies are resolved and locked during install like other first-class package dependencies, but they remain metadata only. Installing a loop does not execute the graph.

Bindings#

Agents may optionally declare authored bindings metadata describing how already-declared package dependencies participate globally, within named loop phases, across named MCP surfaces, or through a consumer-owned context file.

Example:

{
  "bindings": {
    "consumer_context": {
      "file": "runtime/customer-context.json"
    },
    "global": {
      "tools": ["@zack/ticket-reader"],
      "knowledge": ["@zack/support-playbook"],
      "profiles": ["@zack/support-style"],
      "memory": [
        {
          "package": "@zack/support-customer-state",
          "spaces": ["customer_state"]
        }
      ]
    },
    "phases": {
      "respond": {
        "skills": ["@zack/support-response-checklist"],
        "memory": [
          {
            "package": "@zack/support-customer-state",
            "operations": ["refresh_customer_state"]
          }
        ]
      }
    },
    "mcp": [
      {
        "id": "support-tools",
        "tools": ["@zack/ticket-reader", "@zack/crm-lookup"]
      }
    ]
  }
}

Binding package identities are versionless by design. Versions remain owned by the top-level dependency fields and agent.lock.

AgentPM validates that bound package identities are declared in the matching top-level dependency collection, but it does not resolve phase names against the installed Loop or validate Memory spaces/operations against Memory Blueprint contents during lint.

Global and phase bindings are additive intent metadata. AgentPM does not calculate or enforce effective runtime availability from them.

Memory bindings#

Memory bindings identify:

  • a versionless memory package identity in package
  • one or more spaces
  • one or more operations
  • or both spaces and operations

Operations retain their Memory Blueprint-defined triggers and contract semantics. Binding an operation does not redefine when it runs or how it behaves.

MCP bindings#

bindings.mcp names inspectable MCP Tool surfaces:

  • id
  • tools

It does not define:

  • host
  • port
  • transport
  • process execution
  • auth

Those runtime concerns belong to the consuming harness or host application.

Consumer context#

bindings.consumer_context.file is:

  • author-chosen
  • workspace-relative
  • optional
  • consumer-owned

The file may or may not exist at runtime. AgentPM validates only the safe relative path shape; it does not package or read the file.

Loop access versus bindings#

Loop access metadata and Agent bindings describe different things:

  • Loop access says what kind of activity a phase is intended to allow
  • Agent bindings say which installed package identities are meant to participate globally or in a named phase

AgentPM does not reject conflicts between them during lint, publish, or install. A runtime may choose how to interpret that authored metadata later.

Installing tools, skills, knowledge, memory, profiles, and loops for an agent#

Once tools[], skills[], knowledge[], memory[], profiles[], and/or loop are declared, run:

agentpm install
  • Resolves tool, skill, knowledge, memory, profile, and loop versions.
  • Downloads tool artifacts and prepares them under: .agentpm/tools/<namespace>/<name>/<version>
  • Downloads skill artifacts and prepares them under: .agentpm/skills/<namespace>/<name>/<version>
  • Downloads knowledge artifacts and prepares them under: .agentpm/knowledge/<namespace>/<name>/<version>
  • Downloads memory artifacts and prepares them under: .agentpm/memory/<namespace>/<name>/<version>
  • Downloads profile artifacts and prepares them under: .agentpm/profiles/<namespace>/<name>/<version>
  • Downloads loop artifacts and prepares them under: .agentpm/loops/<namespace>/<name>/<version>
  • Populates/updates the lockfile agent.lock with package identity, integrity, and dependency relationships.
  • Uses the download cache at .agentpm/cache (use --refresh to bypass).

For a local kind: "agent" manifest, AgentPM does not copy that manifest into .agentpm/agents. The local agent.json remains the source of truth.

You can also add & install in one step:

agentpm install @zack/summarize@0.1.2
# writes or updates tools[] for a direct Tool install, then installs
agentpm install @zack/incident-commander@0.1.0
# writes or updates skills[] for a direct Skill install, then installs
agentpm install @zack/python-docs@0.1.0
# writes or updates knowledge[] for a direct Knowledge install, then installs
agentpm install @zack/profile-memory@0.1.0
# writes or updates memory[] for a direct Memory install, then installs
agentpm install @zack/support-style@0.1.0
# writes or updates profiles[] for a direct Profile install, then installs
agentpm install @zack/incident-response-loop@1.0.0
# writes or updates loop for a direct Loop install, then installs

Direct package install vs manifest-driven install#

AgentPM supports two different install workflows for agents:

Manifest-driven install#

agentpm install
  • reads the local agent.json
  • resolves the local manifest's tools, skills, knowledge, memory, profiles, and loop
  • writes a local:agent root into agent.lock
  • installs tools into .agentpm/tools/...
  • installs skills into .agentpm/skills/...
  • installs knowledge into .agentpm/knowledge/...
  • installs memory into .agentpm/memory/...
  • installs profiles into .agentpm/profiles/...
  • installs loops into .agentpm/loops/...
  • does not write the local manifest under .agentpm/agents

Direct package install#

agentpm install @zack/support-agent@0.1.0
  • resolves the requested package by identity
  • if the package is an agent, installs the agent artifact into .agentpm/agents/...
  • reads the installed agent manifest and resolves its tool, skill, knowledge, memory, profile, and loop dependencies
  • installs those tools into .agentpm/tools/...
  • installs those skills into .agentpm/skills/...
  • installs those knowledge packages into .agentpm/knowledge/...
  • installs those memory packages into .agentpm/memory/...
  • installs those profile packages into .agentpm/profiles/...
  • installs that loop package into .agentpm/loops/...
  • writes an agent:@namespace/name@version root into agent.lock

Lockfile (high level)#

agent.lock records package identity and relationships instead of only a flat tool map.

Skill-containing graphs are written as lockfile_version: 3.

At a high level it contains:

  • packages
    • keys like tool:@zack/summarize@0.1.2
    • keys like skill:@zack/incident-commander@0.1.0
    • keys like knowledge:@zack/python-docs@0.1.0
    • keys like memory:@zack/profile-memory@0.1.0
    • keys like profile:@zack/support-style@0.1.0
    • keys like loop:@zack/incident-response-loop@1.0.0
    • keys like agent:@zack/support-agent@0.1.0
  • roots
    • local:agent for local manifest installs
    • agent:@namespace/name@version for registry-installed agents

That is what lets AgentPM:

  • distinguish manifest-driven local installs from installed registry agents
  • keep multiple versions of the same tool when different agents need different versions
  • expose resolved tool refs from installed agents through the SDKs

Example: two agents, two versions of the same tool#

If:

  • @zack/support-agent@0.1.0 resolves @zack/slack-post-message@0.1.1
  • @zack/escalation-agent@0.1.0 resolves @zack/slack-post-message@0.2.0

then both tool versions can coexist in agent.lock and on disk:

  • .agentpm/tools/zack/slack-post-message/0.1.1/
  • .agentpm/tools/zack/slack-post-message/0.2.0/

Modern lockfiles keep those package identities separate instead of collapsing them into one flat tool entry.

How agents and tools work together#

  • Your app (Node or Python) uses the SDK to load callable tool functions based on the agent’s tools[].
  • Each tool executes in its own subprocess (correct interpreter/runtime), keeping the host app clean and language-agnostic.
  • The agent manifest + lockfile make the toolset portable and reproducible across dev/CI/prod.

Best practices#

  • Pin for prod: Exact versions in tools[], commit agent.lock.
  • Gate in CI:
agentpm lint --strict
agentpm install --frozen --quiet
  • Name & describe well: Clear name/description helps discovery and registry docs.
  • Evolve safely: Bump the agent’s version when you change its tool set or operational contract.
Dependency scope

Agents resolve tools[], skills[], knowledge[], memory[], profiles[], and optional singular loop. Bindings are preserved as authored metadata only.