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

# Runtime Overview

> How the agent runtime ticks, what it requires, and how to scale to a fleet.

The `@darkpool/agent-service` package is the **hosted-fleet runtime**. One process polls `/v1/agents/fleet` every 30s, maintains an in-memory `FleetAgent` per row, and ticks each on staggered offsets. Agent keys are AES-GCM encrypted at rest in Postgres (`agent_runtime` table) and only decrypted into memory inside this process.

This is a v0.3 pivot. Pre-pivot the package was one-process-per-agent with `AGENT_KEY` in `.env`; that path still exists for self-hosters but the production deployment runs the hosted fleet exclusively.

## Architecture

<Frame caption="One signed message creates the agent; the hosted fleet runtime ticks it on staggered offsets with AES-GCM-encrypted keys.">
  <img src="https://mintcdn.com/dp-550c5f99/z9sBhG7Nbc_h18_Q/images/hosted-fleet-architecture.png?fit=max&auto=format&n=z9sBhG7Nbc_h18_Q&q=85&s=efb61802e4bb22fb1b11956282a8c7ad" alt="Hosted-fleet architecture. Step 1: Wizard (frontend) calls buildCreateAgentChallenge. you sign once with your wallet. Step 2: POST /v1/agents/create verifies the signature, mints an ephemeral keypair, AES-GCM encrypts the runtime secret, signs 2 sponsored PTBs, INSERTs into agent_runtime, and transfers the AgentOwnerCap back to you. Step 3: fleet.ts (this package) keeps in-memory FleetAgent[N], polling GET /v1/agents/fleet (Bearer) every 30s for decrypted rows. Each tick: Pyth candles to summarizeTrend to LLM (Groq or Gemini) to sponsored mint via [sponsor, agent] dual signatures. Step 4: POST tick reports the decision and counters back to the api." width="1536" height="1024" data-path="images/hosted-fleet-architecture.png" />
</Frame>

## Required env (hosted fleet)

```bash theme={"system"}
# Server side. set on the api container
AGENT_KEY_ENCRYPTION_KEY=<32-byte hex>       # AES-256-GCM key for agent secrets
FLEET_REGISTRY_TOKEN=<32-byte hex>           # Bearer for /v1/agents/fleet
SPONSOR_KEY=suiprivkey1…                     # gas sponsor (falls back to RESOLVER_KEY)
SPONSOR_AGENT_GAS_BUDGET_SUI=0.05            # per-tick gas cap
MAX_AGENTS_PER_OWNER=3                       # spam cap
LLM_API_KEY=gsk_…                            # Groq (default)
GEMINI_API_KEY=AIza…                         # optional, for gemini-* models

# Fleet container only
SERVER_HTTP_URL=http://api:8081              # in-network DNS
FLEET_REGISTRY_TOKEN=<same as server>
AGENT_FLEET_TICK_SECONDS=300                 # 5 min default; 900 for Groq free tier
AGENT_FLEET_REFRESH_SECONDS=30               # registry poll cadence
```

## Tick loop

For each `FleetAgent` whose stagger offset is due:

1. **Read on-chain pause.** `sui.getObject(tradeAgentId)`. If `is_active=false`, record one `paused on-chain by owner` action and return.
2. **Read PredictManager balance.** `predict_manager::balance<DUSDC>` via devInspect.
3. **Fetch price context.** Pyth Hermes 1-min BTC candles for the last 60 min:
   ```text theme={"system"}
   https://benchmarks.pyth.network/v1/shims/tradingview/history?symbol=Crypto.BTC%2FUSD&resolution=1&from=…&to=…
   ```
4. **Summarize trend.** Derive 30-min move, 15-min move, direction, high/low, last 10 closes.
5. **Discover oracles.** REST `/v1/oracles` deduped via `oracles.ts` (freshest per (asset, expiry); 1h `lastUpdateMs` bucket primary, `fillCount` tiebreak).
6. **Decide via LLM.** Single-shot prompt (\~1,500 tokens) with mandate + Pyth context + oracle list. Model returns structured JSON: `{action, side, oracleId, strike, spendDusdc, confidence, reasoning}`.
7. **Signal-only gate.** If PM balance is `null` or `< $1` micro, record `kind=skip` with the full decision (`reasoning`, `action`, `confidence`) and return. The agent still ticks, still reasons, just never mints.
8. **Sponsored mint.** `tx.build({onlyTransactionKind:true})` to `Transaction.fromKind` to `setGasOwner(sponsor)` to both sponsor + agent sign the same canonical bytes to `executeTransactionBlock` with `signature: [sponsorSig, userSig]`.
9. **Report.** `POST /v1/agents/fleet/:id/tick` with `{tickCount: sql\`\${tickCount}+1\`, reasoning, action, strike, confidence, txDigest, didMint}`. Inserts a row into `agent\_actions\` so the UI can replay every decision.

## Per-agent LLM routing

`pickLlm(modelName)` routes based on model prefix:

| Model                     | Provider | Base URL                                          | Notes                                         |
| ------------------------- | -------- | ------------------------------------------------- | --------------------------------------------- |
| `llama-3.1-8b-instant`    | Groq     | `api.groq.com/openai/v1`                          | Default. 500k TPD free tier.                  |
| `llama-3.3-70b-versatile` | Groq     | same                                              | Better reasoning, 100k TPD.                   |
| `gemini-2.5-flash`        | Google   | `generativelanguage.googleapis.com/v1beta/openai` | Auto-falls back to Groq on error/no response. |

These are the **only models surfaced in the Create wizard** because they're the only ones tested working end-to-end against the runtime's JSON schema.

## Auto-disable

`GET /v1/agents/fleet` runs inline before returning rows: if a `predict_manager::balance<DUSDC>` \< \$1 micro **and** `now - max(created_ms, last_funded_ms) > 3 days`, sets `enabled=false`. Prevents idle agents from burning sponsor SUI on `signal-only` ticks forever.

## Self-host single agent (legacy)

The pre-v0.3 path still works:

```bash theme={"system"}
AGENT_KEY=suiprivkey1…
AGENT_PREDICT_MANAGER_ID=0x…
AGENT_STRATEGY=alternate                      # alternate | yes-only | no-only | llm
AGENT_NAME=btc-alpha
AGENT_STATUS_PORT=8083                        # 8083+ (resolver owns 8082)
AGENT_TRADE_DUSDC=2
AGENT_TICK_MS=60000
TRADE_AGENT_ID=0x…                            # for is_active check
```

Then `pnpm --filter @darkpool/agent-service dev`. See [Heuristic Strategies](/agents/heuristic) for the strategy options.

## Status URLs

The legacy `/status` HTTP server still ships in the single-agent path. The hosted fleet does not expose `/status`. agent state is queryable via `/v1/agents`, `/v1/agents/:id/actions`, and `/v1/agents/:id/operator`.

## Logs

```text theme={"system"}
[fleet] tick agent 0xabc… (Mid-ITM BTC Agent)
  PM balance $4.92. live mint enabled
  Pyth 30m: +1.2%, 15m: +0.4%, last close $65,213
  LLM (gemini-2.5-flash) to BUY_YES $65,000 conf=78
  "Recent 10-minute closes climbing; YES @ $65,000 implies 24% odds vs ~38% momentum-adjusted"
  mint tx De2bCkR9… ok ($1.00 to 1.31 YES)
[fleet] tick agent 0xdef… (Hunter)
  PM balance $0.84. signal only
  LLM (llama-3.1-8b-instant) to HOLD
  "No clear momentum in last 30-60 min; spreads wide"
```

## Health hardening

* Consecutive identical actions collapse into one row with `×N` counter.
* Common errors humanized: `out of gas` (sponsor SUI \< 0.02), `manager balance too low`, `api unreachable`.
* Fleet card derives a `degraded` state (amber pulse + warn banner) when `humanizeAgentError` matches.
* 429/5xx from the LLM: one retry after 2s, then fall back to Groq `llama-3.3-70b-versatile` if the failing call was Gemini.
