Progressive Enhancement for Agent Tools
Projecting Buffaly's ProtoScript tools into AIP capability manifests without giving up the ontology-native runtime as the source of truth.
Agent systems are starting to outgrow simple tool lists.
A basic agent tool list says:
Here are some functions the model can call.
That is useful, but it is not enough once the tool surface becomes large, semantic, and operationally important. At that point, we need to know more:
What does this tool do?
Is it read-only or mutating?
Does it touch memory, files, business state, or external systems?
How risky is it?
What input and output shape does it have?
Who should be allowed to call it?
What evidence should be recorded if it runs?
That is the problem space where AIP is interesting.
AIP, as described by Octopus Core Pty Ltd, is a capability-governance pattern for agents. Instead of exposing tools as a flat pile of callable functions, AIP describes tools as capabilities. A capability has an identity, version, schema, risk tier, lifecycle, execution binding, and eventually policy and evidence around invocation.
A simple AIP-shaped capability might look like this:
{
"capability_id": "buffaly.session.plan.get",
"version": "1.0.0",
"description": "Read the current session plan markdown.",
"risk_tier": "LOW",
"input_schema": {
"type": "object",
"properties": {},
"required": []
},
"output_schema": {
"type": "string"
},
"execution": {
"type": "protoscript",
"method": "ToGetSessionPlan.Execute"
},
"lifecycle": {
"status": "active"
}
}
That is the right shape for a governance layer. It lets an external system ask: “What is this action, how risky is it, what does it accept, and what actually runs?”
Buffaly agrees with that direction.
But Buffaly starts from a different place.
Buffaly does not begin with JSON descriptors. It begins with ProtoScript: an ontology-native language where tools are prototypes, prototypes are typed semantic objects, and actions are part of a live runtime. Buffaly’s tools are not just functions. They are semantic programs inside an ontology-backed agent environment.
That changes the integration problem.
Buffaly stays the source of truth and projects into AIP when useful.
Here is what we built.
The core idea: progressive enhancement of ProtoScript tools
The implementation is a removable module:
Buffaly.Agent.AipAdapter
It projects selected Buffaly ProtoScript tools into AIP-shaped capability manifests.
The important part is how it does that.
ProtoScript supports a mechanism we call partial prototypes. An existing prototype can be reopened from another file and extended with additional metadata. That means an adapter can attach AIP metadata to a tool without editing the original tool implementation.
So an existing Buffaly tool can remain exactly what it already is:
ToGetSessionPlan.Execute
Then the AIP adapter can progressively enhance it from another file:
prototype ToGetSessionPlan : AipCapability
{
CapabilityID = "buffaly.session.plan.get";
CapabilityVersion = "1.0.0";
CapabilityDescription = "Read the current session plan markdown.";
RiskTier = "LOW";
EffectType = "READ";
LifecycleStatus = "active";
ExecutionPrototypeName = "ToGetSessionPlan";
ExecutionMethodName = "Execute";
Owner = "Buffaly.Agent.Tools.Sessions";
TagsCsv = "session,plan,read";
}
That is the central mechanism.
We did that without rewriting the tool, wrapping it in a separate JSON registry, changing the dispatcher, or forcing every Buffaly tool to inherit a new global base class. We used ProtoScript itself to attach protocol-specific metadata from outside the original tool.
This is progressive enhancement:
Existing ProtoScript tool
+ optional AIP metadata
-> AIP-shaped capability
The original tool continues to work as before. The AIP projection is additive, removable, and external to the tool’s core behavior.
Why this matters at Buffaly scale
This matters because Buffaly is not dealing with a handful of tools.
The Buffaly ecosystem already has over 2,000 tools and tool-like actions across its growing runtime, skills, projections, and semantic surfaces. Manually writing and maintaining one JSON capability descriptor per tool would be a dead end.
At small scale, a hand-authored registry looks fine.
At Buffaly scale, it becomes drift:
Tool changes.
JSON descriptor does not.
Schema changes.
Registry is stale.
Risk changes.
Policy metadata is wrong.
ProtoScript gives us a better path.
The tool remains the source of truth. The AIP adapter reads live runtime metadata and projects capability manifests on demand. If the adapter is removed, the AIP metadata disappears, but the original tools remain intact.
This means Buffaly can expose a large semantic tool surface incrementally:
start with safe read-only tools
add explicit AIP lifts for important tools
generate candidate metadata for broader surfaces
review risk and effect classifications
publish only approved capabilities
add policy and evidence later
This is the move that lets Buffaly scale.
AIP gets a capability catalog.
Buffaly keeps its ontology-native runtime.
What we implemented
The first implementation exposes a read-only capability catalog through three HTTP routes:
GET /api/buffaly-aip/info
GET /api/buffaly-aip/capabilities
GET /api/buffaly-aip/capabilities/{capabilityId}
It also adds three ProtoScript-facing actions:
ToGetAipAdapterInfo
ToListAipCapabilities
ToGetAipCapability
The /capabilities route supports filters such as:
includeInactive
includeInferred
riskTier
effectType
skillName
The first catalog includes six read-only capabilities:
| Capability ID | ProtoScript Tool | Risk | Effect |
|---|---|---|---|
buffaly.session.plan.get |
ToGetSessionPlan.Execute |
LOW | READ |
buffaly.session.scratch.get |
ToGetSessionScratch.Execute |
LOW | READ |
buffaly.skills.list |
ToListSkills.Execute |
LOW | READ |
buffaly.skills.actions.list |
ToListSkillActions.Execute |
LOW | READ |
buffaly.files.block.get |
ToGetFileBlock.Execute |
MEDIUM | READ |
buffaly.files.text.read_raw |
ToReadTextFileRaw.Execute |
MEDIUM | READ |
Starting with read-only tools was intentional. It lets us prove discovery, projection, filtering, schema generation, and non-intrusive metadata before moving into tool invocation.
The goal of this first step is capability visibility.
An external system can now ask Buffaly:
What capabilities do you expose?
Which ProtoScript tool backs each one?
What is its risk tier?
What is its effect type?
What schema does it accept?
What skill owns it?
Is the execution method resolvable?
It gives an external system a real bridge into what Buffaly can do.
What makes Buffaly’s projection different
A generic capability manifest can say:
This is a tool.
Here is its schema.
Here is its risk tier.
Buffaly can say more:
This is a ProtoScript semantic program.
It belongs to this skill.
It is backed by this prototype.
It executes this method.
It has this semantic effect.
It carries these infinitive phrases.
It is projected from live runtime metadata.
The manifest includes a Buffaly-specific extension block:
{
"x_buffaly": {
"prototype_name": "ToGetSessionPlan",
"method_name": "Execute",
"effect_type": "READ",
"semantic_runtime": "ProtoScript",
"metadata_source": "explicit_partial_prototype",
"skill_name": "Buffaly.Agent.Tools.Sessions",
"input_type_name": "Execute.input",
"output_type_name": "string",
"infinitive_phrases": []
}
}
That x_buffaly block is where the deeper semantics live.
The outside world sees an AIP-shaped capability. Buffaly still knows the capability is grounded in a ProtoScript prototype inside a semantic runtime.
Wrapping a function and projecting a semantic tool are not the same thing.
The adapter reads the live runtime
The projection service does not scrape .pts files from disk. It reads the live ProtoScript runtime.
The flow is:
Find AipCapability prototype
Enumerate descendants
Read AIP metadata through ProtoScript property resolution
Validate required fields
Resolve the execution method
Project input/output schemas
Return AIP-shaped manifests
That matters because the projection reflects loaded runtime state.
If a tool is loaded and its AIP metadata is present, it appears in the catalog.
If the adapter is removed, those AIP partial definitions are gone.
The runtime stays clean.
Schema projection is explicit about uncertainty
The adapter projects runtime input and output types into schema objects.
Simple types project cleanly:
string -> string
bool -> boolean
int -> integer
double -> number
DateTime -> string/date-time
arrays -> array
Complex or prototype types are projected as objects with a marker that says projection is incomplete.
That marker is important. A capability catalog should not pretend to fully understand a type when it does not. If a schema projection is incomplete, the manifest says so.
Clear capability metadata is better than false precision.
Why this is a general interoperability pattern
AIP is the first projection, but the pattern is bigger than AIP.
A Buffaly tool should be able to project into many external surfaces:
AIP capability manifests
MCP tools
OpenAI tool schemas
internal policy catalogs
documentation
audit surfaces
developer consoles
Those should be views.
They should not all become separate sources of truth.
ProtoScript gives us a clean architecture:
ProtoScript tool
-> optional protocol metadata
-> runtime inspection
-> external projection
That is the part I find most exciting. Buffaly can support external protocols without letting those protocols define Buffaly’s internal model.
The next major direction: tools Buffaly creates itself
The next step is not just to annotate existing tools.
The more important direction is: how should Buffaly expose tools that Buffaly itself creates?
Buffaly can generate or author new tools over time. Some will be small read-only helpers. Some will read memory. Some will update ontology state. Some will call external systems. Some may operate on business data. Some may execute code or deployment workflows.
Those tools should not all become public capabilities by default.
The right model is a lifecycle.
1. Internal tool
The default state.
No external capability metadata.
Not externally visible.
Callable only through normal Buffaly mechanisms.
2. Capability candidate
Buffaly creates draft capability metadata, but the tool is not active externally.
LifecycleStatus = "draft";
This is useful for review, documentation, and classification.
3. Active read-only capability
Safe read-only tools can become visible externally.
RiskTier = "LOW";
EffectType = "READ";
LifecycleStatus = "active";
These are good candidates for discovery.
4. Governed mutating capability
Tools that change memory, ontology, files, business state, or external systems need stronger metadata.
RiskTier = "HIGH";
EffectType = "UPDATE_BUSINESS_STATE";
CapabilityGateEnabled = true;
RequiresEvidence = true;
These should be visible only when the system can explain how they are governed.
5. Critical capability
Some capabilities may be described but never externally invocable.
RiskTier = "CRITICAL";
EffectType = "MUTATE_ONTOLOGY";
LifecycleStatus = "active";
For these, discovery may be useful, but invocation should require strict policy or remain internal-only.
The general path is:
internal tool
-> candidate capability
-> active read-only capability
-> governed mutating capability
-> audited invocation
That is how Buffaly should expose the tools it creates.
Not everything should be public or invocable, but everything important should be classifiable.
Toward semantic policy
Once capabilities are visible, the next step is policy.
The important field is not just RiskTier. It is EffectType.
Buffaly should distinguish effects such as:
READ
REASON
WRITE_MEMORY
MUTATE_ONTOLOGY
UPDATE_BUSINESS_STATE
CALL_EXTERNAL
EXECUTE_OS
SEND_MESSAGE
DEPLOY
DELETE
This is where Buffaly can become much stronger than a flat tool protocol.
A generic tool registry might know a function is called UpdateLead.
Buffaly can know:
This tool mutates business state.
It operates on a Lead object.
It belongs to the CRM skill.
It requires evidence.
It should require approval when called by an autonomous agent.
It should be read-only visible but not externally executable by default.
That is semantic governance.
The point is not to make the tool list longer. The point is to make the tool surface legible.
What this means for AIP
AIP is useful because it gives the ecosystem a language for capability governance.
Buffaly is useful because it can supply semantic capabilities to that ecosystem.
The two ideas fit together:
AIP:
capability identity, schema, policy, invocation, evidence
Buffaly:
ontology-bound ProtoScript tools with semantic effect, runtime binding, and typed context
AIP can govern capabilities.
Buffaly can manufacture capabilities that are worth governing.
That is the bridge.
Closing
This implementation is small, but the architectural implication is large.
We can take an existing Buffaly ProtoScript tool, attach AIP metadata from a removable adapter-owned partial prototype, and project it into an AIP-shaped capability manifest from the live runtime.
That is progressive enhancement for agent tools.
Buffaly remains the semantic source of truth. AIP becomes one projection of that truth into an external capability ecosystem.
As Buffaly’s tool ecosystem grows, that distinction matters. A system with thousands of tools cannot rely on hand-written wrappers forever. It needs a way to progressively enhance semantic tools into governed capabilities.
ProtoScript gives us that path.