> ## 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>
