Installation
npm install @rankigi/sdkRequires Node.js 18+ and TypeScript 5+ (recommended).
Configuration
Run npx @rankigi/cli init to issue a credential. The CLI writes a single RANKIGI_CREDENTIAL token plus RANKIGI_CHAIN_ID to your .env.
RANKIGI_CREDENTIAL=rnk_live_cred_v1...
RANKIGI_CHAIN_ID=chn_your_chain_idimport "dotenv/config";
import { Rankigi } from "@rankigi/sdk";
// Reads RANKIGI_CREDENTIAL (preferred single-token form)
// and RANKIGI_CHAIN_ID from your environment.
const rankigi = Rankigi.fromEnv();
// Or from a single credential token explicitly:
// const rankigi = Rankigi.fromCredential(process.env.RANKIGI_CREDENTIAL!);
// Advanced / legacy: explicit four-variable form.
// const rankigi = new Rankigi({
// apiKey: process.env.RANKIGI_API_KEY!,
// agentId: process.env.RANKIGI_AGENT_ID!,
// passportId: process.env.RANKIGI_PASSPORT_ID!,
// signingPrivateKey: process.env.RANKIGI_SIGNING_KEY!,
// });Scoping events to a chain
Every event the SDK emits is scoped to a chain. Rankigi.fromEnv() reads RANKIGI_CHAIN_ID automatically. You can also pass chainId to the constructor.
// .env
// RANKIGI_CREDENTIAL=rnk_live_cred_v1...
// RANKIGI_CHAIN_ID=chn_your_chain_id
import { Rankigi } from "@rankigi/sdk";
// fromEnv() picks up RANKIGI_CHAIN_ID automatically.
const rankigi = Rankigi.fromEnv();
// Or pass chainId explicitly:
// const rankigi = new Rankigi({
// apiKey, agentId, passportId, signingPrivateKey,
// chainId: "chn_your_chain_id",
// });Methods
wrapAnthropic(label, args, fn)
Wrap an Anthropic Messages call. The label, args, and result are hashed and chained.
const response = await rankigi.wrapAnthropic(
"contract-review",
{ prompt, model: "claude-sonnet-4-20250514" },
() => client.messages.create({ model, max_tokens: 1024, messages }),
);wrapOpenAI(label, args, fn)
Wrap an OpenAI Chat Completions call.
const completion = await rankigi.wrapOpenAI(
"answer-generation",
{ model: "gpt-4o", messages },
() => openai.chat.completions.create({ model, messages }),
);wrap(label, args, fn)
Wrap any async function with the same governed-call pattern.
const result = await rankigi.wrap(
"custom-step",
{ input },
async () => doWork(input),
);trackToolCall(tool, input, output)
Record a tool invocation. Input and output are SHA-256 hashed before transmission.
await rankigi.trackToolCall(
"stripe_refund",
{ charge_id: "ch_abc", amount: 100 },
{ success: true, refund_id: "re_abc123" },
);trackAgentOutput(output)
Record an agent output as a chain event.
await rankigi.trackAgentOutput({
report_id: "rpt_456",
pages: 12,
});trackError(error)
Record an error as a chain event.
try {
await runStep();
} catch (err) {
await rankigi.trackError(err);
throw err;
}withSession(id, fn) / withRun(id, fn)
Scope a block of work to a session or run identifier.
await rankigi.withSession("sess_abc123", async () => {
await rankigi.withRun("run_xyz789", async () => {
await rankigi.trackToolCall("web_search", input, output);
});
});close()
Flush pending events and shut down the background sender.
// Flush pending events and shut down the background sender.
await rankigi.close();Failure behavior
The SDK is non-blocking. Each track() call posts one event to the ingest endpoint. Failures retry 3 times with exponential backoff, then are logged to stderr and dropped, your agent never sees an exception from RANKIGI. Enable debug: true (or RANKIGI_DEBUG=true) to see verbose retry logs.
TypeScript Types
interface RankigiConfig {
apiKey: string;
agentId: string;
passportId: string;
signingPrivateKey: string;
chainId?: string; // also read from RANKIGI_CHAIN_ID
baseUrl?: string; // default: "https://rankigi.com"
debug?: boolean; // also read from RANKIGI_DEBUG
}
// Factory shortcuts:
// Rankigi.fromEnv()
// Rankigi.fromCredential("rnk_live_cred_v1.<token>")
// Method signatures:
type WrapFn<T> = () => Promise<T>;
rankigi.wrapAnthropic<T>(label: string, args: object, fn: WrapFn<T>): Promise<T>;
rankigi.wrapOpenAI<T>(label: string, args: object, fn: WrapFn<T>): Promise<T>;
rankigi.wrap<T>(label: string, args: object, fn: WrapFn<T>): Promise<T>;
rankigi.trackToolCall(tool: string, input: unknown, output: unknown): Promise<void>;
rankigi.trackAgentOutput(output: Record<string, unknown>): Promise<void>;
rankigi.trackError(error: Error): Promise<void>;
rankigi.withSession(sessionId: string, fn: () => Promise<void>): Promise<void>;
rankigi.withRun(runId: string, fn: () => Promise<void>): Promise<void>;
rankigi.close(): Promise<void>;Full Example
import "dotenv/config";
import { Rankigi } from "@rankigi/sdk";
import Anthropic from "@anthropic-ai/sdk";
const rankigi = Rankigi.fromEnv();
const anthropic = new Anthropic();
async function reviewContract(text: string) {
return rankigi.wrapAnthropic(
"contract-review",
{ text, model: "claude-sonnet-4-20250514" },
() =>
anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: text }],
}),
);
}
async function refundCharge(chargeId: string, amount: number) {
try {
const result = await stripe.refunds.create({ charge: chargeId, amount });
await rankigi.trackToolCall(
"stripe_refund",
{ charge_id: chargeId, amount },
{ success: true, refund_id: result.id },
);
return result;
} catch (err) {
await rankigi.trackError(err);
throw err;
}
}
process.on("SIGTERM", async () => {
await rankigi.close();
process.exit(0);
});Error Handling
import "dotenv/config";
import { Rankigi } from "@rankigi/sdk";
const rankigi = Rankigi.fromEnv();
// The SDK is non-blocking. If ingest fails, your agent
// continues. Failures are logged to stderr but never thrown
// to your handler. Each track() call posts one event with
// 3 retries on transient failure (exponential backoff).
//
// For verbose retry logs, enable debug mode:
//
// const rankigi = new Rankigi({
// apiKey, agentId, passportId, signingPrivateKey,
// debug: true, // or set RANKIGI_DEBUG=true
// });