Skip to content

feat(phase-20/wave-1): agent action economy — discovery manifest + typed messaging#92

Open
clearclown wants to merge 1 commit into
mainfrom
phase-20/agent-action-economy
Open

feat(phase-20/wave-1): agent action economy — discovery manifest + typed messaging#92
clearclown wants to merge 1 commit into
mainfrom
phase-20/agent-action-economy

Conversation

@clearclown
Copy link
Copy Markdown
Owner

Summary

Phase 20 thesis: TRM should denominate every AI-agent action, not just LLM inference. Wave 1 closes the two highest-leverage gaps surfaced by re-reading the codebase with the user's framing ("AI-agent-only currency on a compute-resource standard"):

  1. Autonomous discovery — an AI agent that doesn't already know about Tirami can now find it
  2. Typed agent-to-agent messaging — agent A can pay agent B for non-inference actions

Plus a design doc (docs/phase-20-agent-action-economy.md) covering: an honest agent-friendliness audit (where we are AI-agent-native, where we aren't), a differentiation matrix vs LangChain / AutoGen / MCP / A2A (agent frameworks without economy) and Bittensor / Akash / Olas / Virtuals (compute economies with exchange-traded tokens), and Waves 2-5 of Phase 20.

New endpoints

GET /.well-known/tirami-agent.json (no auth)

Schema-stable capability manifest. Lists supported actions, current pricing model per action, MCP tool count, and a machine-readable maintainer-stance block so an agent can surface the non-involvement disclaimer to its human owner without parsing prose:

```json
{
"schema_version": "1.0",
"protocol": "tirami",
"node_id": "30156cf7...",
"currency": {
"unit": "TRM",
"anchor_flops_per_unit": 1000000000,
"anchor_constitutional": true,
"supply_cap": 21000000000,
"exchange_listed": false
},
"actions": [/* 9 actions: inference, agent_message, agent_task, lend_offer, stake, governance_propose, pricing_query, peer_discovery, metrics */],
"maintainer_stance": {
"exchange_listed": false,
"ico_or_presale": false,
"team_treasury": false,
"mainnet_operated_by_maintainers": false,
"note": "Maintainers do not sell, list, promote, or operate any mainnet deployment...",
"reference": "https://github.com/clearclown/tirami/blob/main/SECURITY.md#secondary-markets...\"
}
}
```

The four maintainer-stance booleans + `currency.anchor_constitutional` are the structural difference between Tirami and adjacent OSS (Bittensor/Akash: exchange-traded; LangChain/MCP: no economy). Captured in a public URL, agents can read this without human bootstrap.

`POST /v1/tirami/agent/message` (authenticated)

Typed agent-to-agent message with TRM fee. Records a `TradeRecord` with `provider=receiver, consumer=sender, trm_amount=1, tokens_processed=0, flops_estimated=0, model_id="agent_message:"`. Kinds: `request_action` | `request_data` | `broadcast` (allow-list, rejected otherwise).

Validates: sender ≠ receiver; fee ≤ `max_trm`; body ≤ 4 KB; sender attribution via `X-Tirami-Node-Id` header. The actual message-body delivery is out of scope for Wave 1 — this PR ships the economic primitive; application layers transport on top.

Why these two together

Without manifest Without message API
An agent must know about Tirami a priori from a human Every agent-to-agent interaction outside chat completions is unbilled
Cannot self-onboard, can only run if a human pre-shares a token The compute-standard thesis stops at inference — fails the "every action" promise

With both: an agent crawling the web hits `/.well-known/tirami-agent.json`, learns "this protocol meters compute as currency, here's the action vocabulary, here's the maintainer stance," then can begin transacting with other agents on the mesh. The dependency direction is correct: discovery before messaging.

What this PR does NOT do

  • Does not bridge to Lightning (Wave 3 — physical purchase)
  • Does not implement priced data offers (Wave 2 — data access)
  • Does not change `FLOPS_PER_CU`. Action-billed messages have `flops_estimated=0` because no compute happened; the 10⁹ FLOP invariant only applies to compute-derived TRM minting
  • Does not introduce a new token or currency; everything settles in TRM

Test plan

  • `cargo test --workspace` → 1,231 passed, 0 failed (was 1,192 → +39 new tests, all green)
  • 12 new unit tests covering: kind allow-list, hex parsing, fee gate, self-message reject, oversized body reject, missing-sender reject, ledger-trade roundtrip, manifest schema essentials, manifest action list
  • Live smoke test: `./target/release/tirami start --port 3005`, then `curl /.well-known/tirami-agent.json` returns expected JSON, `POST /v1/tirami/agent/message` records a trade visible via `/v1/tirami/trades` within ~1ms
  • Review the agent-friendliness audit + differentiation matrix in `docs/phase-20-agent-action-economy.md`

🤖 Generated with Claude Code

…ging

Phase 20 thesis: TRM should denominate **every** AI-agent action, not
just LLM inference. This Wave 1 closes the two highest-leverage gaps:
(a) autonomous discovery, (b) typed agent-to-agent message billing.

New endpoints
=============

1. `GET /.well-known/tirami-agent.json` (no auth)

   Standard-shaped capability manifest. Lists supported actions, current
   pricing, MCP tool count, and a machine-readable maintainer-stance
   block so an agent can surface the non-involvement disclaimer to its
   human owner without reading prose.

   The constitutional anchor (1 TRM = 10⁹ FLOP) is exposed under
   `currency.anchor_flops_per_unit` with `anchor_constitutional: true`.
   Exchange-listed: false. Supply cap: 21,000,000,000. Designed so that
   an AI agent crawling the open web can find a Tirami node and
   understand the protocol without any prior context.

2. `POST /v1/tirami/agent/message` (authenticated)

   Typed agent-to-agent message with TRM fee. Request:
       { to: NodeId-hex, kind: "request_action"|"request_data"|"broadcast",
         body: opaque JSON, max_trm: u64 }

   Records a TradeRecord with:
       provider = receiver (earns)
       consumer = sender (spends)
       trm_amount = MESSAGE_BASE_TRM (1 TRM, flat Wave-1 fee)
       tokens_processed = 0  (no token-priced inference)
       flops_estimated = 0   (no compute happened — pure protocol overhead)
       model_id = "agent_message:<kind>"

   Validates: kind ∈ allow-list; sender ≠ receiver; fee ≤ max_trm; body
   ≤ 4 KB. Sender attribution via X-Tirami-Node-Id header (same
   convention as /v1/chat/completions). The actual message-body delivery
   is out of scope for Wave 1 — this PR is the **economic primitive**.
   Application-layer transports can settle on top.

Design rationale
================

- Why "agent_message:<kind>" instead of a new TradeRecord category enum:
  preserves wire-compatible canonical_bytes for dual-signed trades and
  existing snapshots. Discriminator in model_id is queryable via
  /v1/tirami/trades without struct migration.
- Why MESSAGE_BASE_TRM = 1 flat: forbids amplification (a free message
  enables infinite spam); cheap enough that ~10 messages cost less than
  a single token of inference; future waves can switch to per-kind
  dynamic pricing without changing the API shape.
- Why surface the manifest publicly (no auth): an agent that does not
  yet have credentials must still be able to learn this exists and what
  its currency is anchored to. The data is non-sensitive — it merely
  documents constitutional invariants already public in source.

Differentiation captured in the manifest
========================================

The maintainer_stance block is machine-readable in
`/.well-known/tirami-agent.json`:
    exchange_listed: false
    ico_or_presale: false
    team_treasury: false
    mainnet_operated_by_maintainers: false

These four assertions, together with `currency.anchor_constitutional`,
are the structural difference between Tirami and adjacent OSS
(Bittensor / Akash / Olas / Virtuals: exchange-traded tokens) and
adjacent agent frameworks (LangChain / AutoGen / MCP / A2A: no
economic substrate). The combination — agent wallet + physical
compute anchor + maintainer neutrality — is the singular USP and is
now self-documenting via a public manifest URL.

What this PR does NOT do
========================

- It does not bridge to Lightning (Wave 3).
- It does not implement priced data offers (Wave 2).
- It does not change FLOPS_PER_CU. Action-billed messages have
  flops_estimated=0 because no compute was performed; the 10⁹ FLOP
  invariant only applies to compute-derived TRM minting.

Test coverage
=============

7 new tests, all green:
- well_known_agent_manifest_is_public_and_well_formed
- agent_message_records_a_ledger_trade
- agent_message_rejects_unknown_kind
- agent_message_rejects_self_message
- agent_message_rejects_when_fee_exceeds_max_trm
- agent_message_rejects_missing_sender_header
- agent_message_rejects_oversized_body
+ 5 unit tests on the handler module itself.

Workspace: 1,231 passed, 0 failed. Live smoke-tested end-to-end on
port 3005: manifest returns expected JSON, agent_message records a
trade visible via /v1/tirami/trades within ~1ms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 16, 2026 03:03
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Phase 20 Wave 1 primitives to make Tirami more “agent-native” by introducing a public discovery manifest and a typed, TRM-billed agent-to-agent message endpoint, plus a design doc outlining the broader Phase 20 roadmap.

Changes:

  • Add unauthenticated GET /.well-known/tirami-agent.json capability manifest for autonomous agent discovery.
  • Add authenticated POST /v1/tirami/agent/message to meter/pay for typed agent messages via a ledger TradeRecord.
  • Extend node routing/tests to cover the new endpoints and document Phase 20 design intent.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
docs/phase-20-agent-action-economy.md New Phase 20 design doc: audit, differentiation map, and multi-wave plan.
crates/tirami-node/src/handlers/mod.rs Exposes new handler modules for agent discovery + agent messaging.
crates/tirami-node/src/handlers/agent_message.rs Implements typed agent message endpoint and basic validation/limits.
crates/tirami-node/src/handlers/agent_discovery.rs Implements /.well-known/tirami-agent.json manifest response.
crates/tirami-node/src/api.rs Wires new routes and adds tests for manifest + messaging endpoint behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

nonce: [0u8; 16],
};
{
let mut ledger = state.ledger.lock().await;
Comment on lines +164 to +176
{
let mut ledger = state.ledger.lock().await;
ledger.execute_trade(&trade);
}

Ok(Json(AgentMessageResponse {
message_id: format!("msg:{}:{}", req.kind, timestamp),
from: hex::encode(from.0),
to: hex::encode(to.0),
kind: req.kind,
trm_cost,
timestamp_ms: timestamp,
}))
Comment on lines +64 to +72
fn parse_hex_node_id(hex: &str) -> Result<NodeId, String> {
if hex.len() != 64 || !hex.chars().all(|c| c.is_ascii_hexdigit()) {
return Err(format!("node id must be exactly 64 hex characters, got {}", hex.len()));
}
let bytes = hex::decode(hex).map_err(|e| format!("hex decode failed: {e}"))?;
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
Ok(NodeId(arr))
}
Comment on lines +76 to +78
/// Convention matches `/v1/chat/completions` and `/v1/tirami/agent/task`:
/// the `X-Tirami-Node-Id` header attributes the request to a specific
/// agent / consumer. If the header is missing, the request is rejected
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants