Skip to main content

Constructor

import { Sinker } from 'sinker-sdk';

const sinker = new Sinker(config: SinkerConfig);
config.model
ModelConfig
required
AI model configuration. The agent cannot run without a provider and API key.
{
  provider: 'anthropic',          // 'openai' | 'anthropic' | 'groq' | 'xai'
  apiKey: process.env.ANTHROPIC_API_KEY!,
  model: 'claude-opus-4-5',       // optional — falls back to provider default
}
config.sidecarUrl
string
default:"http://localhost:7777"
URL of the running Rust sidecar. Change this if you’ve configured a non-default port or are connecting to a remote sidecar over a tunnel.
config.timeoutMs
number
default:"10000"
HTTP request timeout in milliseconds. Does not apply to SSE streams (those reconnect automatically).
config.policy
PolicyConfig
In-memory guardrails for the AI agent — tip caps, retry limits, escalation controls. All keys are optional; omitted keys fall back to built-in defaults. The constructor throws SinkerPolicyError if the policy has hard contradictions.
{
  maxTipLamports: 200_000,      // hard ceiling (lamports) — default 500_000
  maxTipRatioPct: 3,            // % of tx value — default 5.0
  tipPercentile: 'p75',         // target percentile — default 'p75'
  staleAfterMs: 2000,           // oracle max age (ms) — default 2000
  maxRetries: 4,                // re-submission attempts — default 3
  expirySlots: 150,             // drop after N slots — default 150
  escalationStepLamports: 5000, // max tip raise per retry — default 5000
  priority: 'auto',             // tier or 'auto' — default 'auto'
}
See the Policy reference for full documentation, validation rules, and the relationship to sinker.policy.json.
The constructor immediately opens a persistent SSE connection to /internal/events. All sinker.on() handlers receive events from this single connection.

Methods

sinker.enqueue()

Enqueue a pre-signed transaction and return a Transaction handle.
const handle = await sinker.enqueue(rawTxBase64, priority?);
rawTxBase64
string
required
Base64-encoded wire transaction bytes. The transaction must be fully signed before enqueueing — Sinker never handles keypairs.
priority
'normal' | 'high' | 'critical'
default:"'normal'"
Scheduling hint used by the agent when selecting which transactions to bundle first.
Returns: Promise<Transaction> Example:
const tx = buildAndSign(); // your code
const raw = Buffer.from(tx.serialize()).toString('base64');

const handle = await sinker.enqueue(raw, 'high');
console.log(handle.txId);        // "346e5b0e-..."
console.log(handle.enqueuedSlot); // 423602204

sinker.enqueueBatch()

Enqueue multiple transactions in parallel. Useful for batching up to 4 txs into a single Jito bundle.
const handles = await sinker.enqueueBatch([
  { txBytes: rawA },
  { txBytes: rawB, priority: 'high' },
]);
txs
Array<{ txBytes: string; priority?: TxPriority }>
required
Array of transactions to enqueue. Each is assigned an independent tx_id.
Returns: Promise<Transaction[]>

sinker.submit()

Manually trigger a bundle submission. Normally the AI agent calls this automatically — use this only when you need direct control over tip and timing (e.g., AUTO_SUBMIT=false mode).
const result = await sinker.submit({
  tip_lamports: 75000,
  reason: 'manual submission at p75',
  tx_ids: ['346e5b0e-...'],
});
tip_lamports
number
required
Tip amount in lamports. Use sinker.getTip() to get live percentile values.
tx_ids
string[]
required
One or more tx_id values from pending transactions. Get them via sinker.getPending().
reason
string
Human-readable rationale string. Written to the lifecycle entry’s ai_decision_trace.
Returns: Promise<SubmitResult>
bundle_id
string
Bundle identifier assigned by Jito (or a synthetic ID on error/fault).
slot
number
Slot at which the bundle was submitted.
fault_injected
boolean
Present and true when the submission was intercepted by the fault injector.

sinker.getTip()

Latest tip floor percentiles from the Jito oracle (refreshed every 10 seconds).
const tip = await sinker.getTip();

console.log(tip.p50_lamports);      // 9,500
console.log(tip.p75_lamports);      // 11,400
console.log(tip.p99_lamports);      // 1,000,000
console.log(tip.stale);             // true if >30s old
Returns: Promise<TipFloor>

sinker.getSlot()

Current slot and the validator identity scheduled to lead it.
const { slot, leader } = await sinker.getSlot();
Returns: Promise<ChainState>

sinker.getLifecycle()

Full lifecycle log for all bundle submissions in this sidecar session.
const { count, entries } = await sinker.getLifecycle();

const finalized = entries.filter(e => e.stage === 'finalized');
const failed    = entries.filter(e => e.stage === 'failed');
Returns: Promise<LifecycleLog>

sinker.getPending()

Current pending transaction queue snapshot.
const queue = await sinker.getPending();

console.log(queue.count);           // 2
console.log(queue.evicted_total);   // 0
for (const item of queue.items) {
  console.log(item.tx_id, item.age_slots);
}
Returns: Promise<PendingTxQueue>

sinker.on()

Subscribe to a specific SSE event type. See Events for the full list.
sinker.on('tx_status_changed', (e) => {
  console.log(e.tx_id, '→', e.state);
});

sinker.on('bundle_settled', (e) => {
  console.log('bundle', e.bundle_id, ':', e.stage);
});
Returns: this (chainable)

sinker.off()

Remove a previously registered handler.
const handler = (e: TxStatusChangedEvent) => console.log(e.state);

sinker.on('tx_status_changed', handler);
// ...
sinker.off('tx_status_changed', handler);

sinker.validatePolicy()

Check the current policy for contradictions and dangerous values without throwing. Returns { errors, warnings, valid }.
const result = sinker.validatePolicy();

if (!result.valid) {
  console.error(result.errors);
}
for (const w of result.warnings) {
  console.warn(w);
}
Returns: PolicyValidationResult See the Policy reference for the full list of checks.

sinker.destroy()

Disconnect the SSE stream and free resources. Call this when you’re done with the instance.
sinker.destroy();

Properties

sinker.model

The ModelConfig passed at construction. Read-only.
console.log(sinker.model.provider);  // 'anthropic'

sinker.policy

The fully resolved policy — all defaults filled in. Read-only.
console.log(sinker.policy.maxTipLamports);         // 200000  (your value)
console.log(sinker.policy.tipPercentile);           // 'p75'   (default)
console.log(sinker.policy.escalationStepLamports);  // 5000    (default)
Type: ResolvedPolicy — all 8 fields present, no optionals.

sinker.debug

Access to the DebugClient for fault injection.
await sinker.debug.injectFault('fee_too_low');