Skip to main content

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.

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

npm init -y
npm install @spfunctions/sdk@1.0.0 @spfunctions/agent@1.0.0
Use Node 18 or newer.

2. Set Keys

For SDK data calls and Agent live execution:
export SF_API_KEY="sf_..."
For model-backed Agent runs:
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.
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:
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

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

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

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,
    maxTokens: 1024,
  }),
  model: { id: "anthropic/claude-haiku-4.5" },
  builtinTools: ["world.read", "markets.search", "market.inspect", "market.candles"],
  options: {
    watch: [
      { kind: "ticks", tickers: ["KXEXAMPLE"], cadence: "5min" },
    ],
    maxTurns: 4,
    maxBudgetUsd: 0.50,
    maxOutputTokens: 768,
    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

import { watch } from "@spfunctions/agent/v1"

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

8. Use Query Directly

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,
    maxOutputTokens: 768,
  },
})) {
  console.log(message.type)
}

9. Trace And Replay A Low-level Tool Run

The low-level direct runner is still useful for deterministic harnesses:
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.
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

SurfaceScope
@spfunctions/sdkTyped data and contract client
@spfunctions/agentCursor-style market-intelligence Agent SDK
Low-level direct runnerDeterministic canonical tool calls, trace, replay
sf agent --toolCLI wrapper for direct tool semantics
/api/contracts/toolsStrict canonical SDK/Agent truth
/api/toolsBroad hosted compatibility inventory
MCPBroad 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.