Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Pi tool integration [SPEC]

Cross-ref: 00-agents-overview.md (architecture overview for archetypes and tool profiles), 03-delegation.md (tool resolution pipeline and delegation DAG), ../01-golem/13-runtime-extensions.md (Pi extension loading and tool registration)

Reader orientation: This document specifies how the bardo-tools extension bridges 190+ tool implementations in golem-tools with the Pi agent session. It belongs to Section 19 (Agents & Skills) and covers the tool resolution pipeline (category filtering, override application, Pi adapter conversion, two-layer registration), the profile system, capability gating by BehavioralPhase (one of five survival phases: Thriving/Stable/Conservation/Desperate/Terminal), spending limits, and PolicyCage (on-chain smart contract enforcing safety constraints) enforcement. See prd2/shared/glossary.md for full term definitions.


How the Pi runtime discovers tools

The bardo-tools extension is the bridge between golem-tools (190+ tool implementations) and the Pi agent session. It loads at boot, resolves which tools the golem needs based on its archetype and profile, converts ToolDef objects to Pi-native tool definitions, and registers them with the session.

There is no MCP server. No stdio transport. No HTTP transport. Tools are loaded in-process, registered directly with Pi’s tool system, and called through Pi’s tool_call hook infrastructure.

golem-tools (190+ ToolDef objects)
  |
  v
bardo-tools extension (profile-based filtering)
  |  - Reads archetype.toolCategories
  |  - Filters ALL_TOOL_DEFS by category
  |  - Applies toolOverrides (include/exclude)
  |  - Converts ToolDef -> PiToolDefinition
  |
  v
Pi Session (8 Pi-facing tools registered)
  |  - preview_action, commit_action, cancel_action
  |  - query_state, search_context, query_grimoire
  |  - update_directive, emergency_halt
  |
  v
bardo-safety extension (policy enforcement)
  |  - PolicyCage validation on every tool_call
  |  - Spending limits, simulation gates
  |  - Phase-based action blocking
  |
  v
bardo-permits extension (action lifecycle)
     - ActionPermit: preview -> commit
     - Simulation hash verification
     - TTL enforcement

Tool resolution pipeline

Step 1: Category filtering

Each ToolDef in golem-tools has a category field:

CategoryExample tools
dataget_pool_info, get_token_price, search_tokens
tradingget_quote, execute_swap, simulate_transaction
lpadd_liquidity, remove_liquidity, collect_fees
vaultvault_deposit, vault_withdraw, vault_rebalance
safetycheck_safety_status, simulate_transaction
identityregister_agent, query_reputation
memorysearch_memory, store_episode, get_insights
streamingsubscribe_price_feed, subscribe_trades
intelligencecompute_vpin, compute_lvr, optimize_lp_range
feesget_tokenjar_balances, execute_burn
self_improvementrecord_execution, record_outcome, tune_parameters
hookhook_discover, hook_evaluate, get_hook_template
testnetsetup_local_testnet, deploy_mock_pool, time_travel

The archetype declares which categories it needs:

// trade-executor archetype
toolCategories: ["data", "trading", "safety"]

The extension filters:

const tools = ALL_TOOL_DEFS.filter((t) =>
  archetype.toolCategories.includes(t.category),
);

Step 2: Override application

Archetypes can fine-tune their tool set:

toolOverrides: {
  exclude: ["execute_burn"],  // Don't need fee tools
  include: ["compute_vpin"],  // Need this one intelligence tool
}

Step 3: Pi adapter conversion

Each ToolDef is converted to a Pi-native tool definition. The Pi tool wraps the ToolDef.handler with context injection, error handling, and event emission:

function toPiTool(toolDef: ToolDef, ctx: GolemToolContext): PiToolDefinition {
  return {
    name: toolDef.name,
    description: toolDef.description,
    inputSchema: toolDef.inputSchema,
    handler: async (params: Record<string, unknown>) => {
      const result = await toolDef.handler(params, ctx);
      ctx.eventBus.emit({
        kind: "runtime",
        name: "ToolCallAttempt",
        payload: { toolName: toolDef.name, params, result },
      });
      return result;
    },
  };
}

Step 4: Two-layer registration

The 190+ concrete tools are not exposed directly to the LLM. Instead, they back 8 Pi-facing tools. The bardo-tools extension (in golem-tools) maps action kinds to concrete tool calls:

const ACTION_KIND_MAP: Record<string, string[]> = {
  swap: ["get_quote", "simulate_transaction", "execute_swap"],
  "add-liquidity": ["get_pool_info", "add_liquidity", "simulate_transaction"],
  "vault-deposit": [
    "vault_simulate_deposit",
    "vault_deposit",
    "simulate_transaction",
  ],
  // ... 40+ action kinds
};

When the LLM calls preview_action({ kind: "swap", params: {...} }), the extension resolves kind: "swap" to the appropriate concrete tools and executes them in sequence.


Profile system

Profiles are named bundles of tool categories. They exist as a convenience – operators and configs reference profiles instead of listing individual categories.

ProfileCategoriesTool count
datadata, identity~46
traderdata, trading, safety~57
lpdata, trading, lp, safety~71
vaultdata, vault, safety~40
feesdata, fees~25
erc8004data, identity~28
intelligencedata, intelligence~38
observatorydata, intelligence, identity, memory~72
learningdata, memory, self_improvement~30
dashboarddata, intelligence~38
fullall categories~186
devall + testnet~190

Profile resolution:

function resolveProfile(profile: ProfileName): ToolCategory[] {
  return PROFILE_CATEGORY_MAP[profile];
}

Capability gating

The bardo-safety extension applies capability gating on every tool_call hook:

Phase-based gating

Tools are classified by action class. The golem’s current behavioral phase (Thriving, Cautious, Defensive, Survival, Terminal) determines which action classes are allowed:

Action classThrivingCautiousDefensiveSurvivalTerminal
new-positionyesyesnonono
increase-positionyesyesnonono
rebalanceyesyesyesnono
decrease-positionyesyesyesyesno
close-positionyesyesyesyesyes
read-onlyyesyesyesyesyes

Spending limits

The bardo-safety extension enforces per-transaction and per-day spending limits:

interface SpendingLimits {
  perTransaction: bigint; // Max USDC per single tx
  perDay: bigint; // Max USDC per 24h rolling window
  hotLane: bigint; // Max USDC without safety-guardian delegation
}

Transactions above the hot lane limit must go through the safety-guardian archetype for independent validation.

PolicyCage enforcement

On-chain PolicyCage constraints (approved assets, max position sizes, max drawdown, rebalance frequency) are checked before every write operation. These constraints cannot be overridden by the LLM, the archetype, or the operator. They are enforced at the smart contract level. The bardo-safety extension validates them pre-flight to avoid wasting gas on operations that would revert on-chain.


Injectable transaction executor

The executeTx() function uses dependency injection:

  • setTxExecutor(fn) / resetTxExecutor() – configure the transaction execution function
  • Default throws WALLET_NOT_CONFIGURED
  • Real wallet integration provides the concrete implementation via bardo-custody extension
  • This allows tools to construct transactions without knowing the wallet implementation

Implementation notes

  • Chain client: Use GolemChain::get_client(chain_id) from the golem-chain crate
  • Permit2 tools return typed data only – not actual signatures. Signing requires wallet integration through bardo-custody.
  • V3/V4 auto-detection: Uses UNISWAP_CONTRACTS[chainId].v4PositionManager presence check
  • No @uniswap/smart-order-router dependency: SDK fallback is injectable but defaults to throwing SDK_FALLBACK_UNAVAILABLE
  • Each tool file exports TOOL_DEF: ToolDef – no registration function. The bardo-tools extension reads ALL_TOOL_DEFS and handles registration.