> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trysinker.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Sinker

> The main SDK class. One instance per application.

## Constructor

```ts theme={null}
import { Sinker } from 'sinker-sdk';

const sinker = new Sinker(config: SinkerConfig);
```

<ParamField path="config.model" type="ModelConfig" required>
  AI model configuration. The agent cannot run without a provider and API key.

  ```ts theme={null}
  {
    provider: 'anthropic',          // 'openai' | 'anthropic' | 'groq' | 'xai'
    apiKey: process.env.ANTHROPIC_API_KEY!,
    model: 'claude-opus-4-5',       // optional — falls back to provider default
  }
  ```
</ParamField>

<ParamField path="config.sidecarUrl" type="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.
</ParamField>

<ParamField path="config.timeoutMs" type="number" default="10000">
  HTTP request timeout in milliseconds. Does not apply to SSE streams (those reconnect automatically).
</ParamField>

<ParamField path="config.policy" type="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.

  ```ts theme={null}
  {
    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](/sdk/policy) for full documentation, validation rules,
  and the relationship to `sinker.policy.json`.
</ParamField>

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](/sdk/transaction) handle.

```ts theme={null}
const handle = await sinker.enqueue(rawTxBase64, priority?);
```

<ParamField path="rawTxBase64" type="string" required>
  Base64-encoded wire transaction bytes. The transaction must be fully signed before
  enqueueing — Sinker never handles keypairs.
</ParamField>

<ParamField path="priority" type="'normal' | 'high' | 'critical'" default="'normal'">
  Scheduling hint used by the agent when selecting which transactions to bundle first.
</ParamField>

**Returns:** `Promise<Transaction>`

**Example:**

```ts theme={null}
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.

```ts theme={null}
const handles = await sinker.enqueueBatch([
  { txBytes: rawA },
  { txBytes: rawB, priority: 'high' },
]);
```

<ParamField path="txs" type="Array<{ txBytes: string; priority?: TxPriority }>" required>
  Array of transactions to enqueue. Each is assigned an independent `tx_id`.
</ParamField>

**Returns:** `Promise<Transaction[]>`

***

### sinker.submit()

Manually trigger a bundle submission. Normally the AI agent calls this automatically — use this only when you're running without the agent (or in bypass mode) and need direct control over tip and timing.

```ts theme={null}
const result = await sinker.submit({
  tip_lamports: 75000,
  reason: 'manual submission at p75',
  tx_ids: ['346e5b0e-...'],
});
```

<ParamField path="tip_lamports" type="number" required>
  Tip amount in lamports. Use `sinker.getTip()` to get live percentile values.
</ParamField>

<ParamField path="tx_ids" type="string[]" required>
  One or more `tx_id` values from pending transactions. Get them via `sinker.getPending()`.
</ParamField>

<ParamField path="reason" type="string">
  Human-readable rationale string. Written to the lifecycle entry's `ai_decision_trace`.
</ParamField>

**Returns:** `Promise<SubmitResult>`

<ResponseField name="bundle_id" type="string">
  Bundle identifier assigned by Jito (or a synthetic ID on error/fault).
</ResponseField>

<ResponseField name="slot" type="number">
  Slot at which the bundle was submitted.
</ResponseField>

<ResponseField name="fault_injected" type="boolean">
  Present and `true` when the submission was intercepted by the fault injector.
</ResponseField>

***

### sinker.getMode() / sinker.setMode()

Read or flip the agent's **decision tier** at runtime — no restart. `cortex` reasons on every
submission (the default); `reflex` runs the deterministic fast-path and escalates to the LLM only on
exceptions. See [AI Agent → Two-tier router](/concepts/agent).

```ts theme={null}
const { decision_mode } = await sinker.getMode(); // 'reflex' | 'cortex'

await sinker.setMode('reflex');   // fast-path, no model on the common case
await sinker.setMode('cortex');   // full reasoning every cycle
```

**Returns:** `Promise<ModeState>` — `{ supervised: boolean, decision_mode: 'reflex' | 'cortex' }`

***

### sinker.getTip()

Latest tip floor percentiles from the Jito oracle (refreshed every 10 seconds).

```ts theme={null}
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.

```ts theme={null}
const { slot, leader } = await sinker.getSlot();
```

**Returns:** `Promise<ChainState>`

***

### sinker.getLifecycle()

Full lifecycle log for all bundle submissions in this sidecar session.

```ts theme={null}
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.

```ts theme={null}
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](/sdk/events) for the full list.

```ts theme={null}
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.

```ts theme={null}
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 }`.

```ts theme={null}
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](/sdk/policy) for the full list of checks.

***

### sinker.destroy()

Disconnect the SSE stream and free resources. Call this when you're done with the instance.

```ts theme={null}
sinker.destroy();
```

***

## Properties

### sinker.model

The `ModelConfig` passed at construction. Read-only.

```ts theme={null}
console.log(sinker.model.provider);  // 'anthropic'
```

***

### sinker.policy

The fully resolved policy — all defaults filled in. Read-only.

```ts theme={null}
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](/sdk/debug) for fault injection.

```ts theme={null}
await sinker.debug.injectFault('fee_too_low');
```
