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

# Agent Runtime

> Long-running execution daemon for SimpleFunctions intents — hard + soft triggers, smart-mode LLM evaluation, BYOK exchange credentials, local + cloud runners.

The runtime is the long-running process that watches active intents and executes authorized workflows. It is the bridge between an analyst writing intents (CLI / API / agent) and orders actually leaving for Kalshi or Polymarket.

For automated execution, prefer **intents + runtime** over raw `sf buy` / `sf sell`. Intents are persisted, replayable, race-free, and reviewable before execution.

## Two ways to run it

| Mode | Where it runs | Credentials | Best for |
| - | - | - | - |
| **Local** | Your machine, foreground or background | Local Kalshi / Polymarket private keys (file paths) | Operators, dev, single-machine setups. |
| **Cloud (BYOK)** | SimpleFunctions cloud | Encrypted exchange credentials, connected by the user and never returned by the API | Always-on automation without keeping a laptop awake. |

## Quick start (local)

```bash theme={null}
sf setup                                # configure SimpleFunctions + Kalshi keys
sf intent buy KXRATECUT-26DEC31 10 \
  --trigger below:45 --rationale "edge below 45c"
sf runtime start --smart --daemon       # start watching, with LLM soft-condition support
sf runtime status --json
```

## What runtime watches

Each tick (every **30 seconds**) the runtime:

1. Reads your **active intents** from `GET /api/intents?status=...`.
2. For each intent, checks the **trigger** — hard price/time triggers locally, soft NL conditions if `--smart` is enabled.
3. Marks newly-firing intents `armed` → `triggered` → `executing`.
4. Places the order on the configured venue using your local exchange keys.
5. Tracks fills against the intent (`filledQuantity`), retries partial fills on the next tick.
6. Writes structured status / errors to `~/.sf/runtime.log`.

Runtime state files live under `~/.sf/`:

| File | Purpose |
| - | - |
| `~/.sf/runtime.pid` | PID of the foreground / daemonized runtime. |
| `~/.sf/runtime.log` | Append-only log of tick activity, fills, and errors. |
| `~/.sf/runtime-executed.json` | De-dupes execution attempts across crashes. |

## CLI

### `sf runtime start`

```bash theme={null}
sf runtime start                     # foreground
sf runtime start --daemon            # background; logs to ~/.sf/runtime.log
sf runtime start --smart             # enable LLM soft-condition evaluation
sf runtime start --smart --daemon
```

| Flag | Default | Notes |
| - | - | - |
| `--daemon` | off | Detach into a background process. PID written to `~/.sf/runtime.pid`. |
| `--smart` | off | Evaluate `softCondition` strings on intents ("only if VIX \< 20") with the configured LLM. Costs LLM tokens. |

Refuses to start if Kalshi credentials aren't configured; the fix is `sf setup --enable-trading`.

### `sf runtime stop`

```bash theme={null}
sf runtime stop
```

Sends SIGTERM to the PID in `~/.sf/runtime.pid`. Falls back to scanning for orphan runtime processes.

### `sf runtime status`

```bash theme={null}
sf runtime status
sf runtime status --json
```

Returns daemon liveness, last tick timestamp, count of intents per status, and a recent error trail.

## Intents

An intent is a persisted instruction to do something on a venue when a trigger fires. Create them with the CLI or the HTTP API.

### Create an intent (CLI)

```bash theme={null}
sf intent buy <ticker> <quantity>  [flags]
sf intent sell <ticker> <quantity> [flags]
```

| Flag | Default | Notes |
| - | - | - |
| `--price <cents>` | none (market) | Max price per contract in cents (1–99). |
| `--side <yes\|no>` | `yes` | Direction. |
| `--trigger <trigger>` | `immediate` | See trigger forms below. |
| `--soft "<condition>"` | none | NL condition the runtime evaluates each tick when `--smart` is on. |
| `--expire <duration>` | `1d` | When the intent auto-cancels: `1h`, `6h`, `1d`, `3d`, `1w`. |
| `--venue <kalshi\|polymarket>` | `kalshi` | Venue. |
| `--rationale "<text>"` | none | Free-form why-string saved on the intent. |
| `--auto` | off | Skip the interactive confirmation. |
| `--style <immediate\|twap>` | `immediate` | Execution style. `twap` slices the order over time. |
| `--json` | off | Return JSON envelope. |

**Trigger forms**

| Form | Meaning |
| - | - |
| `immediate` | Fire on the next tick (or right now if not arming). |
| `below:<cents>` | Fire when price ≤ that many cents. |
| `above:<cents>` | Fire when price ≥ that many cents. |
| `time:<ISO>` | Fire at the given ISO-8601 timestamp. |

**Soft conditions** (`--soft`) are evaluated by the configured LLM on every tick when `--smart` is enabled. Examples:

```bash theme={null}
--soft "only if Polymarket twin is at least 4c richer"
--soft "skip if SimpleFunctions Index moved more than 3 points in the last hour"
```

### List + cancel

```bash theme={null}
sf intent list                   # active only
sf intent list --all --json      # all statuses, JSON
sf intent list --status armed
sf intent cancel <id>
```

### Lifecycle

```text theme={null}
created → armed → triggered → executing → filled
                                  │
                                  └→ canceled | expired | failed
```

| Status | Meaning |
| - | - |
| `created` | Persisted but not yet activated (e.g. waiting for `activateAt`). |
| `armed` | Watching; trigger has not fired. |
| `triggered` | Trigger fired this tick; runtime is about to submit. |
| `executing` | Order submitted, awaiting fill. |
| `filled` | Fully filled (or partially, if the venue closed early). |
| `canceled` | User canceled. |
| `expired` | `expireAt` passed without firing. |
| `failed` | Venue rejected or runtime errored. The error is captured on the intent row. |

## Intents API

```http theme={null}
POST   /api/intents
GET    /api/intents
GET    /api/intents/{id}
PATCH  /api/intents/{id}
DELETE /api/intents/{id}
```

**Auth:** required.

### POST /api/intents

**Required body fields**

| Field | Type | Notes |
| - | - | - |
| `action` | `"buy"` \| `"sell"` | Order direction. |
| `venue` | `"kalshi"` \| `"polymarket"` | Venue. |
| `marketId` | string | Kalshi ticker or Polymarket conditionId. |
| `marketTitle` | string | Display title — saved on the intent for UX. |
| `direction` | `"yes"` \| `"no"` | Contract side. |
| `targetQuantity` | integer ≥ 1 | Number of contracts to fill. |

**Optional body fields**

| Field | Type | Notes |
| - | - | - |
| `maxPrice` | integer 1–99 | Max price per contract in cents. |
| `executionStyle` | `"immediate"` \| `"twap"` | TWAP slices the order. |
| `triggerType` | string | `"immediate"`, `"price_below"`, `"price_above"`, `"time"`. |
| `triggerPrice` | number | Cents threshold for price triggers. |
| `triggerAt` | ISO timestamp | For `triggerType: "time"`. |
| `triggerParams` | object | Trigger-specific extras. |
| `softCondition` | string | NL condition evaluated each tick when `--smart` is on. |
| `expireAt` | ISO timestamp | When the intent auto-cancels. |
| `autoExecute` | boolean | If true, skip confirmation. |
| `source` | string | Free-form attribution (`"cli"`, `"agent"`, ...). |
| `sourceId` | string | Linked thesis id, evaluation id, etc. |
| `rationale` | string | Why-string saved on the intent. |

**Errors**

| Status | Body | Cause |
| - | - | - |
| `400` | `Missing required fields: ...` | Missing required field. |
| `400` | `action must be "buy" or "sell"` | Bad enum. |
| `400` | `venue must be "kalshi" or "polymarket"` | Bad enum. |
| `400` | `direction must be "yes" or "no"` | Bad enum. |
| `400` | `targetQuantity must be a positive integer` | Bad number. |
| `400` | `maxPrice must be 1-99 cents` | Out of range. |
| `401` | unauthorized | No / invalid auth. |

### GET /api/intents

| Query | Notes |
| - | - |
| `status` | Filter (`active`, `armed`, `triggered`, `executing`, `filled`, `canceled`, `expired`, `failed`). |
| `venue` | Filter by venue. |
| `source` | Filter by `source` field. |
| `limit` | Cap on rows. |

### PATCH / DELETE

`PATCH` updates `status`, `softCondition`, `expireAt`, or `triggerParams`. `DELETE` cancels.

## Smart mode

`--smart` enables three behaviors:

1. **Soft-condition evaluation.** Each tick, if any active intent has a `softCondition`, the runtime calls the LLM with current market context and the condition. Only fires the order if the LLM returns a clear positive.
2. **Edge re-check before firing.** Just before submitting the order, the runtime calls `inspect_ticker` on the market and aborts if the suggestion has flipped to `avoid`.
3. **Adaptive delay.** Trades that look like they'd cross the spread are deferred until the spread is reasonable — implementation default is 8 cents.

Smart mode costs LLM tokens; budget with `sf agent --budget-usd ...` and / or per-thesis `monthlyBudgetUsd` on heartbeat.

## Risk gates

The runtime calls the same risk-gate engine as the autopilot tick. Before any order is placed it checks:

* per-trade max notional
* per-market exposure cap
* daily loss circuit breaker
* max open positions
* minimum balance
* per-tick max orders

A rejected order writes a structured `risk_gate_fail` reason to the intent and stops there — the intent stays armed for the next tick. See [Risk gates](/concepts/risk-gates).

## Cloud runtime (BYOK)

The cloud runner gives you "always on" without keeping a laptop awake. Treat this as an advanced operator surface: start locally, verify dry-run behavior, then connect cloud credentials only when you are ready for unattended automation.

### Enable

```bash theme={null}
sf setup --cloud                     # encrypt & upload exchange keys
sf runtime start --remote            # boot cloud container (~3s cold start)
sf runtime stop --remote             # scale to zero
sf --remote agent ...                # route any agent command through the cloud
```

`sf setup --cloud` connects encrypted exchange credentials for the cloud runner. The API never returns plaintext credentials; rotate or revoke them from the CLI when access should change.

The cloud runner uses the same intent + tick + risk-gate code paths as the local runtime. The only difference is the host.

### One-shot remote exec

```http theme={null}
POST /api/runtime/exec
GET  /api/runtime/exec?runId=...
```

Used by `sf --remote <command>`. Sends a single CLI invocation to the cloud runner, returns a `runId`, and lets you stream or poll output. Auth: `Authorization: Bearer sf_live_...`.

This is **not** a long-running daemon — for that, use `sf runtime start --remote`.

## Events

Runtime emits webhook events when configured:

| Event | When |
| - | - |
| `intent.armed` | Trigger watcher attached. |
| `intent.triggered` | Trigger fired. |
| `intent.executing` | Order submitted. |
| `intent.filled` | Order fully filled. |
| `intent.canceled` | User canceled. |
| `intent.expired` | `expireAt` passed without firing. |
| `intent.failed` | Venue rejected or runtime errored. |

Configure receivers via [Webhooks](/build/webhooks).

## Operational tips

* Run `sf doctor` before going live to catch missing keys, time-skew, or a stale CLI.
* Use `sf intent list --all --json` to review what the runtime is watching before you start it in `--smart` mode (smart-mode runs LLM calls).
* Soft conditions are tokens; over-broad conditions on many intents accumulate cost. Prefer hard triggers when the rule is mechanical.
* Cloud runner respects `executionMode` — `dry-run` evaluates everything but skips placing orders. Flip to `live` only when you're satisfied with dry-run output.

## Related

```bash theme={null}
sf agent --plain                     # interactive agent (uses intents under the hood)
sf agent --headless --deny trade,runtime
sf telegram --daemon                 # mobile operator visibility
```

`sf agent` is for reasoning + tool use. `sf telegram` is for human-in-the-loop. The runtime is the worker that closes the loop.

## See also

<CardGroup cols={2}>
  <Card title="Trade intents" href="/build/trade-intents">
    The intent-object model in depth.
  </Card>

  <Card title="Risk gates" href="/concepts/risk-gates">
    Pre-trade safety rails.
  </Card>

  <Card title="Portfolio autopilot" href="/guides/portfolio-autopilot">
    Cloud-run portfolio loop with BYOK credential connection.
  </Card>

  <Card title="Webhooks" href="/build/webhooks">
    Signed delivery for runtime events.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.