@sage-protocol/pi-adapter

Sage Protocol MCP integration for pi coding agent

Packages

Package details

extension

Install @sage-protocol/pi-adapter from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@sage-protocol/pi-adapter
Package
@sage-protocol/pi-adapter
Version
0.5.1
Published
Sep 12, 2026
Downloads
727/mo · 218/wk
Author
hack4kites
License
MIT
Types
extension
Size
381.4 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@sage-protocol/pi-adapter

Sage integration for the Pi coding agent runtime.

  • Pi agent: earendil-works/pi — extensible terminal coding harness
  • Compatibility: tested with Pi 0.84.1; package peer range >=0.64.0 <0.85.0
  • Sage Protocol: sage-protocol/sage — decentralized capability/library layer for AI agents

What It Does

  • Exposes Sage MCP tools (hub_list_servers, hub_status, hub_session_list, hub_start_server, hub_stop_server, sage_search, sage_execute, sage_status, and sage_behavior)
  • Lets Pi own skill discovery and loading through native <available_skills>, /skill:<name>, and full SKILL.md reads
  • Injects SessionStart Sage context and shows the loaded library/skill inventory
  • Captures prompts and responses for RLM feedback
  • Observes truthful full native skill loads and writes source- and session-qualified load receipts
  • Exposes sage_mark_guided_use in the packaged Pi extension so an agent can bind actually loaded, actually applied skills to the exact prompt capture through Sage's native-use daemon path
  • Optionally exposes one status-only sage_wallet_status tool when Glove supplies either a strict inherited connected descriptor declaration or the complete legacy endpoint/root/owner tuple
  • Preserves optional security hooks and explicit subagent skill resolution
  • Keeps Sage-owned skills under packages/sage/skills/ and the curated Pi export surface rather than bundling copies in this adapter

The adapter does not call sage suggest hook skill for each prompt, inject sage-suggested-skills, or render a suggestion-quality widget. It has no suggestion auto-skip or auto-reject lifecycle. Manual sage suggest skill "<task>" remains available when a person or agent explicitly wants CLI ranking, but it is not automatic Pi injection.

Install

# Pi CLI extension (no source changes)
pi install npm:@sage-protocol/pi-adapter

# SDK embedding
npm install @sage-protocol/pi-adapter

Quick Start

import {
  createAgentSession,
  createCodingTools,
  type ToolDefinition as PiToolDefinition,
} from '@earendil-works/pi-coding-agent';
import {
  createSageSessionConfig,
  type ToolDefinition as SageToolDefinition,
} from '@sage-protocol/pi-adapter';

function asPiCustomTools(tools: SageToolDefinition[]): PiToolDefinition[] {
  // Type-shape compatibility across supported Pi minors; no runtime conversion.
  return tools as unknown as PiToolDefinition[];
}

const sage = await createSageSessionConfig({ source: 'pi-agent-core' });
const cwd = process.cwd();
const securedTools = sage.wrapToolsWithSecurity(createCodingTools(cwd));
const { session } = await createAgentSession({
  cwd,
  noTools: 'builtin',
  customTools: asPiCustomTools([...securedTools, ...sage.customTools]),
});
const disposeSageHooks = sage.setupHooks(session);

In current Pi, tools is a string allowlist. Tool definitions belong in customTools; noTools: 'builtin' prevents duplicate unwrapped built-ins. The single helper cast is only the TypeScript peer-version boundary between the adapter's cross-minor tool shape and Pi's generic ToolDefinition—it does not convert anything at runtime.

Pi presents its native skill catalog separately. When a skill applies, load it with /skill:<name> or read its complete SKILL.md, follow the procedure, then call sage_mark_guided_use only if the loaded procedure actually guided the current turn. Loading alone is not use.

API

createSageSessionConfig(config) returns:

{
  customTools: ToolDefinition[];           // Sage hub and code-mode bridge tools
  mcpBridge: SageMcpBridge;                 // MCP process lifecycle
  setupHooks: (session: AgentSession) => () => void;  // SessionStart + capture hooks
  wrapToolsWithSecurity: (tools: ToolDefinition[]) => ToolDefinition[];
}

Recommended configuration:

interface SageP2Config {
  sageBin?: string;           // default: "sage"
  sageProfile?: string;       // Sage config profile
  timeoutMs?: number;         // default: 20_000
  enableRlmFeedback?: boolean;
  enableSecurityHooks?: boolean;
  source?: string;            // default: "pi"
  // Inherited mode: both exact Glove values are required, with no legacy fields.
  gloveLocalServiceFdsV1?: string;          // exact JSON alias-to-FD map
  gloveGuestChannelServiceAlias?: string;  // exact wallet service alias
  // Legacy mode: all three are required, with no inherited fields.
  gloveGuestChannelEndpoint?: string; // canonical absolute Unix socket path
  gloveGuestChannelRoot?: string;     // canonical absolute trusted root
  gloveGuestChannelOwnerUid?: number; // isolated socket-owner UID (not Pi's UID)
  env?: Record<string, string>;

  /** @deprecated Low-level legacy suggestion compatibility; ignored by the default Pi path. */
  suggestLimit?: number;
  /** @deprecated Low-level legacy suggestion compatibility; ignored by the default Pi path. */
  suggestDebounceMs?: number;
  /** @deprecated Low-level legacy suggestion compatibility; ignored by the default Pi path. */
  enableProvision?: boolean;
}

Migrating to 0.4

Version 0.4 moves Pi skill discovery and loading to Pi's native skill surface. The legacy sageInjectSuggestion(...) method is retained as a no-op for compatibility, and default setup no longer performs automatic suggestions. Do not call sageInjectSuggestion; use Pi's native skill load flow and call sage_mark_guided_use after an actually loaded skill guides the current prompt.

The guided-use write requires both the Sage CLI command sage suggest feedback native-use and daemon capability native_skill_use_v1. Version 0.4.2 reports which runtime half is stale instead of collapsing either case into a generic write failure. It never falls back to weaker suggestion feedback. Update the CLI when it lacks the command; update and restart saged when the daemon lacks the capability. Then retry the same declaration.

Docs

Validate

npm test
npm run typecheck
npm run build
git diff --check

License

MIT

Session inbox liveness (shared with hook-driven harnesses)

The adapter currently renews the daemon lease from its own 2 s poll in src/intercom.ts. The Sage CLI now ships a harness-agnostic keepalive, sage inbox keepalive --auth-file <capability> --watch-pid <pid>, which is what hook-driven harnesses (Claude Code, Codex) use. A follow-up can have the adapter spawn it detached after saveAuth with process.pid as the watched host, send heartbeat only on idle/busy transitions, and replace the receive poll with sage inbox receive --auth-file <capability> --wait <secs>, so both harness families share one liveness mechanism. Fencing (session_inbox_fenced) stops both the adapter timer and the keepalive.