@saw-protocol/runtime-local
v1.0.8
Published
Rule-based local agent runtime for the SAW Protocol (Solana Agentic Wallet Protocol)
Readme
@saw-protocol/runtime-local
A rule-based, local IAgentRuntime implementation for the SAW Protocol.
This module provides a mock reasoning engine suitable for testing, tightly coupled automation, or strict rule-based agent logic. It allows developers to register custom Javascript/TypeScript callbacks (decideFn, incomingMessageFn, negotiateFn) which execute instantly without relying on heavy external LLM APIs like OpenAI or Anthropic.
Installation
npm install @saw-protocol/runtime-localExample Usage
1. Basic Instantiation with Fallbacks
If no callbacks are provided, LocalRuntime falls back to its default secure logic: creating acknowledgement intents and safely rejecting negotiation proposals.
import { LocalRuntime } from "@saw-protocol/runtime-local";
const myAgentId = "did:sol:mySuperAgentX";
// A runtime that automatically defaults correctly to all incoming messages.
const defaultRuntime = new LocalRuntime({
agentId: myAgentId,
});
// Decides to fallback to a basic 'Hello World' signMessage intent
const intent = await defaultRuntime.decide(
{
walletState: { balance: 10 },
memory: [],
},
[],
);2. Custom Rule-Based Callback Logic
You can pass in completely custom Typescript functions to execute strict logic when deciding intents or handling agent-to-wallet messages.
import {
LocalRuntime,
DecisionLogic,
IncomingMessageLogic,
} from "@saw-protocol/runtime-local";
import { PublicKey } from "@solana/web3.js";
import { v4 as uuidv4 } from "uuid";
// Strictly define how this specific runtime decides things
const customDecisionLogic: DecisionLogic = async (
context,
availableActions,
) => {
console.log("Evaluating context:", context);
if (context.walletState.balance > 0.5) {
// Generate a strictly typed TransactionIntent
return {
id: uuidv4(),
agentId: "did:sol:myAgent",
action: {
type: "transfer",
params: { amount: 0.1, to: "DestinationPubKey..." },
estimatedValue: 0.1,
},
walletAddress: new PublicKey("..."),
reasoning: "Balance > 0.5, proceeding with automated transfer.",
signature: "unsigned",
timestamp: Date.now(),
};
}
throw new Error("Conditions not met for decision");
};
// Strongly define how the runtime responds to messages from external agents
const customMessageLogic: IncomingMessageLogic = async (message, context) => {
if (message.payload.prompt === "Swap 0.05 SOL to USDC") {
// Acknowledge the swap intent immediately
return {
id: uuidv4(),
from: "did:sol:walletRuntime",
to: message.from,
replyTo: message.id,
type: "Response",
payload: { ack: message.id, status: "swap_intent_approved" },
signature: "unsigned",
timestamp: Date.now(),
ttl: 60,
};
}
// Fallback to simple generic Response message for unhandled payload types
return {
id: uuidv4(),
from: "did:sol:walletRuntime",
to: message.from,
type: "Response",
payload: { status: "unknown" },
signature: "unsigned",
timestamp: Date.now(),
ttl: 60,
};
};
const strictRuntime = new LocalRuntime({
agentId: "did:sol:myAgent",
decideFn: customDecisionLogic,
incomingMessageFn: customMessageLogic,
});
// Using the exact implementation matching the SAWP SDK
const receivedMsg = await strictRuntime.handleIncomingMessage(
{
id: "random-id",
from: "did:sol:externalAgent",
to: "did:sol:walletRuntime",
type: "Request",
payload: { prompt: "Swap 0.05 SOL to USDC" },
signature: "...",
timestamp: Date.now(),
ttl: 60,
},
{ walletState: {}, memory: [] },
);
console.log("Response payload:", receivedMsg.payload); // { ack: "random-id", status: "swap_intent_approved" }