Catalog PyPI
PACKAGE EXPLORERView in registry

agent-capability-negotiation

PyPI

Canonical capability manifest schema + 3-step pre-delegation negotiation handshake with HMAC-signed manifests, capability diff, and TTL. Zero runtime dependencies.

agentaiai-agentsai-codingcapabilitydelegationhmacmanifestnegotiationsubagent
Versions collected1
Direct dependencies0From release 0.1.0
Last observed20 Oct, 21:36 UTC

README

Version 0.1.0 documentation

agent-capability-negotiation

Zero-dependency Python plugin for canonical capability manifests and 3-step pre-delegation negotiation handshake.

Before a parent AI agent delegates a task, it has no standardized way to know what a subagent can actually do. This plugin solves pre-delegation capability blindness with a signed manifest schema and a CAPABILITY_QUERY → CAPABILITY_ADVERTISE → CAPABILITY_BIND handshake.

pipeline-status python license


Quick Start

pip install agent-capability-negotiation

Create a signed capability manifest:

from agent_capability_negotiation import create_manifest

manifest = create_manifest(
    agent_id="subagent-abc123",
    tools=["read_file", "write_file", "bash"],
    scope={"read": ["/project/**"], "write": ["/project/src/**"], "forbid": []},
    model="claude-sonnet-4-20250514",
    context_window_tokens=200000,
    skills=["python", "git"],
    ttl_seconds=3600,
    secret_key="shared-secret",
)
print(manifest.to_dict())

Negotiate a capability binding (parent side):

# 1. Query a subagent
python -m agent_capability_negotiation negotiate query \
  --parent-id parent-xyz --subagent-id subagent-abc123

# 2. (Subagent responds with advertise + signed manifest)
# 3. Bind agreed scope
python -m agent_capability_negotiation negotiate bind \
  --negotiation neg.json --agreed-scope-read /project/** \
  --agreed-scope-write /project/src/**

# 4. Check compatibility before binding
python -m agent_capability_negotiation negotiate diff-report \
  --manifest manifest.json --requires-tools read_file,write_file,bash \
  --requires-scope-read /project/**

⚡ Performance & Benchmarks

This is a zero-dependency, stdlib-only library. It performs pure in-memory JSON/HMAC operations with no I/O, network, or external process overhead. No comparative benchmark against alternatives applies — there are no comparable alternatives on PyPI.

OperationLatency
create_manifest (sign)< 0.1 ms
verify_manifest (HMAC check)< 0.1 ms
tokenize_manifest (base64)< 0.1 ms
diff_report (5 tools, 3 scopes)< 0.5 ms

Local replication: python3 benchmarks/run_benchmark.py


Why agent-capability-negotiation?

The problem: Before delegating to a subagent (via delegate_task, kanban_create, Codex subagent, etc.), a parent agent cannot discover the subagent's actual capabilities. This leads to:

  • Blind delegation — task fails at runtime because the subagent lacks a required tool
  • Capability drift — subagent tool bindings differ from what the parent assumed
  • No pre-flight signal — existing solutions bind scope only after the subagent is already running

Existing tools that don't solve this:

  • subagent-delegation-contract (cycle_45): post-delegation scope binding, not pre-delegation discovery
  • Cursor/Copilot workspace permissions: static allowlists, not negotiated per-delegation
  • multi-agent-protocol (npm): message routing schema, not capability negotiation

Trade-off decisions:

  • Zero runtime dependencies (stdlib only) — no install friction, no supply-chain risk
  • HMAC-SHA256 signatures using stdlib hmac + hashlib — no cryptography package needed
  • TTL on manifests — prevents stale capability info from causing runtime failures
  • Structured diff report — human + machine-readable incompatibility signal before binding

Key Features

  • Canonical capability manifest — signed JSON declaring tools, scoped permissions, model version, context window, skill tags
  • 3-step negotiation handshake — CAPABILITY_QUERY → CAPABILITY_ADVERTISE → CAPABILITY_BIND (or NEGOTIATE_REJECT)
  • HMAC-SHA256 signed manifests — stdlib only, prevents spoofing
  • Capability diff report — compare task requirements against manifest, surface [OK] / [WARN] / [ERROR] per requirement
  • Manifest TTL — re-advertise after long-running tasks or config changes
  • Base64 token round-trip — serialize manifests for transport over text-only channels
  • Zero runtime dependencies — pure Python stdlib

API Reference

Manifest API (agent_capability_negotiation.manifest)

from agent_capability_negotiation import (
    create_manifest,
    verify_manifest,
    manifest_from_dict,
    manifest_to_dict,
    diff_manifests,
    tokenize_manifest,
    untokenize_manifest,
    is_valid_tool_token,
    is_valid_path_glob,
    scope_covers,
    CapabilityManifest,
    ManifestError,
    SignatureError,
    ExpiredError,
)

# Create and sign a manifest
manifest = create_manifest(
    agent_id="subagent-abc123",
    tools=["read_file", "write_file", "bash"],
    scope={"read": ["/project/**"], "write": ["/project/src/**"], "forbid": ["/project/secrets/**"]},
    model="claude-sonnet-4-20250514",
    context_window_tokens=200000,
    skills=["python", "git"],
    ttl_seconds=3600,
    secret_key="shared-secret",
)

# Verify a manifest
ok, reason = verify_manifest(manifest, secret_key="shared-secret")
# -> (True, "signature ok, TTL ok, schema version compatible")

# Diff two manifests
diff = diff_manifests(manifest_a, manifest_b)
# -> DiffReport with per-tool and per-scope findings

# Token round-trip (base64-encoded signed blob)
token = tokenize_manifest(manifest, secret_key="shared-secret")
restored = untokenize_manifest(token, secret_key="shared-secret")
assert restored.agent_id == manifest.agent_id

# Validation helpers
is_valid_tool_token("read_file")      # True
is_valid_tool_token("bash:git")        # True (scoped tool)
is_valid_path_glob("/project/**/*.py")  # True
scope_covers(["/project/**"], "/project/src/app.py")  # True

Negotiation API (agent_capability_negotiation.negotiation)

from agent_capability_negotiation import (
    issue_query,
    advertise,
    bind,
    reject,
    status_of,
    diff_report,
    build_diff_report,
    Negotiation,
    NegotiationState,
    NegotiationMessage,
    Requirement,
    DiffFinding,
    DiffReport,
)

# Step 1: parent issues a CAPABILITY_QUERY
neg = issue_query(parent_id="parent-xyz", subagent_id="subagent-abc123")
# neg.state == NegotiationState.QUERY_SENT

# Step 2: subagent advertises its manifest
advertise(neg, manifest)
# neg.state == NegotiationState.ADVERTISED

# Step 3: parent binds agreed scope
bind(neg,
     agreed_scope_read=["/project/**"],
     agreed_scope_write=["/project/src/**"],
     agreed_forbid=["/project/secrets/**"],
     binding_ttl_seconds=3600,
     secret_key="shared-secret")
# neg.state == NegotiationState.BOUND

# Reject at any step
reject(neg, reason="scope too restrictive", party="subagent")
# neg.state == NegotiationState.REJECTED

# Query current status
status = status_of(neg)
# {"state": "BOUND", "binding_id": "bind-xyz", "expires_at": "..."}

# Diff task requirements against a manifest
report = build_diff_report(manifest, [
    {"kind": "tool", "name": "read_file"},
    {"kind": "tool", "name": "bash:git"},
    {"kind": "scope", "mode": "read", "patterns": ["/project/**"]},
])
# report.summary() ->
# [OK]   read_file         — supported
# [WARN] bash:git          — NOT advertised (available: bash without git scope)
# [OK]   scope read        — /project/** satisfied
# RECOMMENDATION: use git CLI wrapper instead of bash:git

CLI Reference

Module entry point

python -m agent_capability_negotiation [--version] [--help]

manifest subcommands

# Create a signed capability manifest
python -m agent_capability_negotiation manifest create \
  --agent-id subagent-abc123 \
  --tools read_file,write_file,bash \
  --scope-read /project/** \
  --scope-write /project/src/**,/project/tests/** \
  --forbid /project/secrets/** \
  --model claude-sonnet-4-20250514 \
  --context-window-tokens 200000 \
  --skills python,git \
  --ttl-seconds 3600 \
  --secret-key shared-secret

# Verify a manifest
python -m agent_capability_negotiation manifest verify \
  --manifest manifest.json --secret-key shared-secret

# Diff two manifests
python -m agent_capability_negotiation manifest diff \
  --manifest-a a.json --manifest-b b.json

# Serialize to base64 token
python -m agent_capability_negotiation manifest tokenize \
  --manifest manifest.json --secret-key shared-secret

# Deserialize from base64 token
python -m agent_capability_negotiation manifest untokenize \
  --token-file token.txt --secret-key shared-secret

negotiate subcommands

# Emit CAPABILITY_QUERY (parent -> subagent)
python -m agent_capability_negotiation negotiate query \
  --parent-id parent-xyz --subagent-id subagent-abc123

# Attach manifest (subagent response to CAPABILITY_QUERY)
python -m agent_capability_negotiation negotiate advertise \
  --negotiation query.json --manifest manifest.json --secret-key shared-secret

# Confirm CAPABILITY_BIND (parent)
python -m agent_capability_negotiation negotiate bind \
  --negotiation advertised.json \
  --agreed-scope-read /project/** \
  --agreed-scope-write /project/src/** \
  --agreed-forbid /project/secrets/** \
  --binding-ttl-seconds 3600 \
  --secret-key shared-secret

# Reject negotiation
python -m agent_capability_negotiation negotiate reject \
  --negotiation neg.json --reason "scope too restrictive" --party parent

# Show negotiation status
python -m agent_capability_negotiation negotiate status --negotiation neg.json

# Diff task requirements against manifest
python -m agent_capability_negotiation negotiate diff-report \
  --manifest manifest.json \
  --requires-tools read_file,write_file,bash:git \
  --requires-scope-read /project/** \
  --requires-scope-write /project/src/** \
  --human

Plugin Scripts

For Hermes/Claude Code plugin integration:

plugins/agent-capability-negotiation/
├── plugin.json              # Plugin manifest
├── hooks.json               # Pre-delegation handshake hook
├── rules/negotiation-protocol.md  # Schema + negotiation states
├── skills/agent-capability-negotiation/SKILL.md  # Behavioral runbook
└── scripts/
    ├── capability_manifest.py   # Standalone manifest CLI
    └── negotiate.py             # Standalone negotiation CLI
# Smoke tests for plugin scripts
bash plugins/agent-capability-negotiation/scripts/capability_manifest.py --help
bash plugins/agent-capability-negotiation/scripts/capability_manifest.py --version
bash plugins/agent-capability-negotiation/scripts/negotiate.py --help
bash plugins/agent-capability-negotiation/scripts/negotiate.py --version

Limitations

  • HMAC key distribution is out of scope — both parties must share a secret key via a channel this plugin does not manage
  • Manifest transport is not handled — the base64 token must be transmitted by an external channel (stdout, file, message bus)
  • Non-repudiation is not provided — HMAC only gives integrity, not authorship proof; use asymmetric signing if non-repudiation is required
  • Context window units are self-reported by the subagent and not independently verified
  • Path glob scope uses simple fnmatch-style patterns; complex filesystem ACLs require OS-level enforcement beyond this plugin
  • Clock skew can cause legitimate manifests to appear expired if parties' system clocks differ significantly

Non-Goals

  • Post-delegation scope drift detection (see subagent-delegation-contract, cycle_45)
  • Message routing or multi-agent coordination protocols
  • Asymmetric-key signature schemes
  • Automatic key exchange or negotiation protocol establishment
  • Integration with specific agent frameworks beyond the plugin interface

License

MIT License — see LICENSE.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.011 Aug 20260

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
prasad-a-abhishek/agent-capability-negotiation

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 21:36 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:f80b45c69ea424cd75db70485187a4b356899318307c7f5f280ecf5d17acb3e0