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

# SDK and Agent quickstart

> Install the SimpleFunctions SDK and Agent SDK packages and run the first market-intelligence agent.

This guide starts from a blank TypeScript project and ends with:

* SDK install
* strict manifest inspection
* API-keyed `world.read`
* Cursor-style `Agent.create().send().stream()`
* semi-realtime `watch`
* trace/replay for deterministic harnesses
* optional governed execution with runtime and policy guardrails

## 1. Install

```bash theme={null}
npm init -y
npm install @spfunctions/sdk@1.0.1 @spfunctions/agent@1.0.2
```

Use Node 18 or newer.

## 2. Set Keys

For SDK data calls and Agent live execution:

```bash theme={null}
export SF_API_KEY="sf_..."
```

For model-backed Agent runs:

```bash theme={null}
export OPENROUTER_API_KEY="..."
```

Do not put long-lived keys in browser bundles. Use these packages from server-side TypeScript, background jobs, local agent harnesses, or trusted notebooks.

## 3. Inspect The Strict Contract Without A Key

`/api/contracts/tools` is the SDK and Agent contract truth.

```ts theme={null}
import { SimpleFunctions } from "@spfunctions/sdk"

const sf = new SimpleFunctions({
  baseUrl: "https://simplefunctions.dev",
})

const manifest = await sf.manifest.list()
const world = await sf.manifest.get("world.read")
const legacy = await sf.manifest.get("get_world_state")

console.log(manifest.schemaVersion)
console.log(world?.name)
console.log(legacy)
```

Expected:

```text theme={null}
0.3.0-draft
world.read
null
```

`get_world_state` is a broad compatibility name, not a canonical SDK/Agent tool.

## 4. Make The First SDK Data Call

```ts theme={null}
import { SimpleFunctions } from "@spfunctions/sdk"

const sf = new SimpleFunctions({
  baseUrl: "https://simplefunctions.dev",
  apiKey: process.env.SF_API_KEY,
})

const world = await sf.world.get()

console.log(world.asOf)
console.log(world.regime?.label)
console.log(world.salient?.slice(0, 3).map(item => item.label))
```

If no API key is configured, this call throws `MissingApiKeyError` because `world.read` is cost-bearing and not anonymously allowlisted.

## 5. Create A Cursor-style Agent

```ts theme={null}
import { Agent } from "@spfunctions/agent/v1"

const agent = await Agent.create({
  apiKey: process.env.SF_API_KEY,
  openRouterApiKey: process.env.OPENROUTER_API_KEY,
  model: { id: "anthropic/claude-haiku-4.5" },
})

const run = agent.send("Read world state and summarize the largest market moves.")

for await (const event of run.stream()) {
  console.log(event.type)
}

const sameRun = await Agent.getRun(run.id, { agentId: run.agentId })
await sameRun?.wait()
```

`Agent.create({ apiKey })` mounts read-only SimpleFunctions strict tools by default. Write tools are opt-in only.

## 6. Build A Market Watch Agent

```ts theme={null}
import { Agent, OpenRouterProvider } from "@spfunctions/agent/v1"

const agent = await Agent.create({
  apiKey: process.env.SF_API_KEY,
  provider: new OpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY }),
  model: { id: "anthropic/claude-haiku-4.5" },
  builtinTools: ["world.read", "markets.search", "market.inspect"],
  options: {
    watch: [
      { kind: "ticks", tickers: ["KXEXAMPLE"], cadence: "5min" },
    ],
    maxTurns: 4,
    maxBudgetUsd: 0.50,
    canUseTool(toolName, input) {
      if (toolName === "markets.search" && input && typeof input === "object") {
        return { behavior: "allow", updatedInput: { ...input, limit: 5 } }
      }
      return { behavior: "allow" }
    },
  },
})

const run = agent.send([
  "Watch Iran oil risk.",
  "If market ticks move sharply, inspect the ticker and explain what changed.",
  "Do not use write or trading tools.",
].join(" "))

for await (const event of run.stream()) {
  console.log(event.type)
}
```

## 7. Watch Semi-realtime Inputs Directly

```ts theme={null}
import { watch } from "@spfunctions/agent/v1"

for await (const tick of watch.ticks({
  tickers: ["KXEXAMPLE"],
  cadence: "5min",
  cycles: 1,
  apiKey: process.env.SF_API_KEY,
})) {
  console.log(tick.ticker, tick.price, tick.delta)
}
```

With an API key or `client`, `watch.ticks()` reads the SimpleFunctions market inspect
surface for current prices. Without one, it emits a `synthetic: true` development tick
so tests and local demos stay deterministic.

## 8. Use Query Directly

```ts theme={null}
import { OpenRouterProvider, query, tool } from "@spfunctions/agent/v1"

const inspect = tool("market.inspect", "Inspect one market", { type: "object" }, async input => input)

for await (const message of query({
  prompt: "Summarize what prediction markets imply about Fed cuts.",
  options: {
    provider: new OpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY }),
    model: "anthropic/claude-haiku-4.5",
    tools: [inspect],
    maxTurns: 3,
    maxBudgetUsd: 0.25,
  },
})) {
  console.log(message.type)
}
```

## 9. Trace And Replay A Low-level Tool Run

The low-level direct runner is still useful for deterministic harnesses:

```ts theme={null}
import { SimpleFunctions } from "@spfunctions/sdk"
import { FileTraceStore, ReplayMissError, SimpleFunctionsAgent } from "@spfunctions/agent"

const sf = new SimpleFunctions({
  baseUrl: "https://simplefunctions.dev",
  apiKey: process.env.SF_API_KEY,
})

const trace = new FileTraceStore("./sf-agent.trace.jsonl")
const direct = new SimpleFunctionsAgent({
  client: sf,
  policy: { maxSideEffect: "none", maxCostEffect: "api_cost" },
  trace,
})

await direct.tools.world.read({})

const replay = new SimpleFunctionsAgent({
  client: new SimpleFunctions({ baseUrl: "https://simplefunctions.dev" }),
  mode: "replayOnly",
  trace: new FileTraceStore("./sf-agent.trace.jsonl"),
})

try {
  await replay.tools.world.delta({ since: "1h" })
} catch (error) {
  if (error instanceof ReplayMissError) {
    console.log("Replay miss. No live call was made.")
  }
}
```

## 10. Optional Governed Execution

Execution tools are not mounted by default in model-backed agents and are denied by default in the low-level direct runner unless policy explicitly allows `live_trade`.

```ts theme={null}
import { SimpleFunctions } from "@spfunctions/sdk"
import { SimpleFunctionsAgent } from "@spfunctions/agent"

const sf = new SimpleFunctions({
  baseUrl: "https://simplefunctions.dev",
  apiKey: process.env.SF_API_KEY,
})

await sf.execution.place({
  ticker: "KXFED-27APR-T3.50",
  action: "buy",
  quantity: 1,
  limitPrice: 32,
  runtime: { startIfNeeded: true },
})

await sf.execution.place({
  venue: "polymarket",
  tokenId: "POLYMARKET_CLOB_TOKEN_ID",
  action: "buy",
  quantity: 1,
  limitPrice: 32,
  runtime: { startIfNeeded: true },
})

const executionAgent = new SimpleFunctionsAgent({
  client: sf,
  policy: {
    maxSideEffect: "live_trade",
    maxCostEffect: "venue_request_cost",
    trade: {
      allowedVenues: ["kalshi"],
      maxQuantity: 1,
      maxOrderCostCents: 50,
      requireLimitPrice: true,
      allowRuntimeStart: true,
    },
  },
})
```

Polymarket support is runtime-backed and requires a CLOB token id, a limit price, and user-controlled runtime credentials. Applications can add `blockedJurisdictions` and `requireJurisdiction` to their Agent policy when they need venue-specific compliance safety valves.

## 11. Remember The Boundaries

| Surface                 | Scope                                             |
| ----------------------- | ------------------------------------------------- |
| `@spfunctions/sdk`      | Typed data and contract client                    |
| `@spfunctions/agent`    | Cursor-style market-intelligence Agent SDK        |
| Low-level direct runner | Deterministic canonical tool calls, trace, replay |
| `sf agent --tool`       | CLI wrapper for direct tool semantics             |
| `/api/contracts/tools`  | Strict canonical SDK/Agent truth                  |
| `/api/tools`            | Broad hosted compatibility inventory              |
| MCP                     | Broad client adapter, not SDK truth               |

The packages do not expose every CLI command, every API route, or every MCP tool. They expose the strict governed subset first.
