Quickstart
Wire askRunic and storeResult into an existing agent in a few minutes.
Install
npm install @runic-labs/sdkpnpm add @runic-labs/sdkyarn add @runic-labs/sdkbun add @runic-labs/sdk@runic-labs/sdk depends on @runic-labs/cache and @runic-labs/ledger — you don’t need to install those separately unless you want to construct custom store instances yourself (see Cache and Ledger).
Wrap the call your agent already makes
Find the place in your agent where it turns a task into a call to an LLM or tool, and wrap it with askRunic / storeResult:
import { askRunic, storeResult } from "@runic-labs/sdk";
async function reviewFile(repo: string, file: string) {
const decision = { intent: "review_code", params: { repo, file } };
const cached = await askRunic(decision);
if (cached) {
console.log(`cache hit — saved ${cached.tokensSpent} tokens`);
return cached.artifact;
}
const response = await callYourLLM(promptFor(repo, file));
await storeResult(decision, response.text, response.tokensUsed);
return response.text;
}
That’s the whole integration. No configuration is required to get started — askRunic/storeResult use a default file-backed store under .runic/ in your working directory.
Choosing intent and params
The signature is exact-match, so decide what actually makes two calls “the same decision” for your use case:
- Good:
{ intent: "summarize_pr", params: { repo: "acme/widgets", pr: 482 } }— same repo, same PR number, same intent, every time this exact PR is asked about. - Bad:
{ intent: "summarize_pr", params: { prompt: fullPromptString } }— including the raw prompt text as a param means any wording change (even whitespace) produces a cache miss, defeating the point.
Put only the parameters that actually identify the decision in params. Leave prompt text, reasoning traces, and anything non-deterministic out of the signature entirely — see How it works for why.
Inspect what’s cached
npm install -g @runic-labs/clipnpm add -g @runic-labs/clinpm install -g @runic-labs/clibun add -g @runic-labs/clirunic cache status
runic ledger status
See CLI for full output examples.
Point at a different store location
By default, Runic writes to .runic/ in process.cwd(). Override it with an environment variable if your agent runs from a different working directory than where you want state to live:
RUNIC_HOME=/var/lib/my-agent/runic node agent.js
Use in-memory storage instead (tests, ephemeral runs)
import { createRunicClient } from "@runic-labs/sdk";
import { createCache, MemoryCacheStore } from "@runic-labs/cache";
import { createLedger, MemoryLedgerStore } from "@runic-labs/ledger";
const runic = createRunicClient({
cache: createCache(new MemoryCacheStore()),
ledger: createLedger(new MemoryLedgerStore()),
});
const cached = await runic.askRunic({ intent: "summarize_pr", params: { repo, pr } });
This is exactly what benchmarks/reuse-sweep and benchmarks/openrouter-savings use, so nothing persists between separate script runs.