@spfunctions/agent
v1.0.2
Published
SimpleFunctions Agent SDK.
Readme
@spfunctions/agent
Agent SDK for SimpleFunctions market-intelligence agents.
Install the stable packages:
npm install @spfunctions/[email protected] @spfunctions/[email protected]This package is not a browser runtime, CLI shell wrapper, or MCP runtime. It is a TypeScript agent runtime for prediction-market applications that need model streaming, strict SimpleFunctions tools, semi-realtime watch inputs, policy hooks, trace, replay, and explicitly policy-gated Kalshi or Polymarket execution.
Five-minute path
The primary surface is Cursor-style:
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 prior = await Agent.getRun(run.id, { agentId: run.agentId })
await prior?.wait()Agent.create({ apiKey }) mounts read-only SimpleFunctions strict tools by default. Write tools are not mounted unless you explicitly opt into them with builtinTools: "all" or a named allowlist. If no provider is passed, the runtime uses openRouterApiKey, options.env.OPENROUTER_API_KEY, or process.env.OPENROUTER_API_KEY when available.
The run handle supports:
run.stream()for ordered SDK messagesrun.wait()for a cached event arrayrun.cancel()for interruptionrun.statusforpending,running,completed,cancelled, orfailedAgent.getRun(run.id, { agentId })for same-process reconnectagent.send(prompt, { onDelta, onStep })for callback-style harnesses
Market-native primitives
Generic coding-agent SDKs give an agent files, shell, and editor control. SimpleFunctions gives an agent market context:
- strict canonical tools from
/api/contracts/tools world.read,markets.search,market.inspect,market.candles,markets.screen,regime.scan,calendar.list,index.current,contagion.scan,crossvenue.pairs,query.ask,econ.query,gov.query, and user-scoped reads through an API-keyed SDK clientwatch.ticks()andwatch.world()as cancellable semi-realtime async inputscanUseTool()and hooks for permission, input shrinking, and cost control- file-local sessions and replayable traces
import { Agent, OpenRouterProvider, watch } 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: "anthropic/claude-haiku-4.5",
options: {
watch: [
{ kind: "ticks", tickers: ["KXEXAMPLE"], cadence: "5min" },
],
maxTurns: 4,
maxBudgetUsd: 0.50,
maxOutputTokens: 768,
canUseTool(toolName, input) {
if (toolName === "markets.search" && typeof input === "object" && input) {
return { behavior: "allow", updatedInput: { ...input, limit: 5 } }
}
return { behavior: "allow" }
},
},
})
for await (const tick of watch.ticks({
tickers: ["KXEXAMPLE"],
cadence: "5min",
cycles: 1,
apiKey: process.env.SF_API_KEY,
})) {
console.log(tick.type, tick.ticker, tick.price)
}watch.ticks() reads live SimpleFunctions market inspection prices when you pass an
API key or client. Without one, it emits a deterministic synthetic: true development
tick; production agents should pass apiKey, client, or a custom fetchSnapshot.
Tool loop surface
For Anthropic-style consumers, the package also exports query(), startup(), tool(), createSdkMcpServer(), session helpers, OpenRouterProvider, and createSimpleFunctionsBuiltinTools() from the root package and from @spfunctions/agent/v1.
import { SimpleFunctions } from "@spfunctions/sdk"
import {
OpenRouterProvider,
createSimpleFunctionsBuiltinTools,
query,
} from "@spfunctions/agent/v1"
const sf = new SimpleFunctions({
baseUrl: "https://simplefunctions.dev",
apiKey: process.env.SF_API_KEY,
})
for await (const message of query({
prompt: "Watch macro markets and explain any large move. Do not write or trade.",
options: {
provider: new OpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY }),
model: "anthropic/claude-haiku-4.5",
tools: createSimpleFunctionsBuiltinTools(sf),
allowedTools: ["world.read", "markets.search", "market.inspect", "market.candles"],
maxTurns: 4,
maxBudgetUsd: 0.50,
},
})) {
console.log(message.type)
}query() yields ordered SDKMessage events: system, user, assistant, stream_event, tool_result, status, and final result messages depending on the run. The returned Query supports interrupt(), setModel(), setPermissionMode(), setMcpServers(), streamInput(), close(), and initializationResult().
OpenRouterProvider defaults to max_tokens=512. Set new OpenRouterProvider({ maxTokens }) for a provider-wide default, or options.maxOutputTokens for a single agent run that needs a longer memo.
Low-level direct runner
The package still exports SimpleFunctionsAgent for deterministic direct tool execution, trace recording, replay-only runs, and compatibility with sf agent --tool semantics. It is the low-level primitive underneath some tests and examples, not the main agent product surface.
import { SimpleFunctions } from "@spfunctions/sdk"
import { FileTraceStore, SimpleFunctionsAgent } from "@spfunctions/agent"
const sf = new SimpleFunctions({
baseUrl: "https://simplefunctions.dev",
apiKey: process.env.SF_API_KEY,
})
const direct = new SimpleFunctionsAgent({
client: sf,
policy: { maxSideEffect: "none", maxCostEffect: "api_cost" },
trace: new FileTraceStore("./sf-agent.trace.jsonl"),
})
await direct.tools.world.read({})Live execution is disabled by default. Applications must opt in with both side-effect and cost ceilings plus trade guardrails:
const executionAgent = new SimpleFunctionsAgent({
client: sf,
policy: {
maxSideEffect: "live_trade",
maxCostEffect: "venue_request_cost",
trade: {
allowedVenues: ["kalshi"],
allowedTickers: ["KXFED-27APR-T3.50"],
maxQuantity: 2,
maxOrderCostCents: 100,
requireLimitPrice: true,
allowRuntimeStart: true,
confirmToken: "operator-approved",
},
},
})
await executionAgent.tools.execution.place({
ticker: "KXFED-27APR-T3.50",
action: "buy",
quantity: 1,
limitPrice: 32,
confirm: "operator-approved",
})For Polymarket, the same tool accepts venue: "polymarket" plus a CLOB tokenId or marketId and an explicit limit price. Add venue and jurisdiction guardrails in the policy when your application needs compliance controls:
const polyAgent = new SimpleFunctionsAgent({
client: sf,
policy: {
maxSideEffect: "live_trade",
maxCostEffect: "venue_request_cost",
trade: {
allowedVenues: ["polymarket"],
blockedJurisdictions: ["US", "FR"],
requireJurisdiction: true,
maxQuantity: 2,
maxOrderCostCents: 100,
requireLimitPrice: true,
},
},
})
await polyAgent.tools.execution.place({
venue: "polymarket",
tokenId: "POLYMARKET_CLOB_TOKEN_ID",
action: "buy",
quantity: 1,
limitPrice: 32,
jurisdiction: "CA",
})execution.place uses SDK runtime orchestration before creating the intent. Set trade.allowRuntimeStart: false to require an already-running runtime, or pass runtime: { mode: "none" } for an explicit intent-only workflow.
No-key direct modes are intentionally narrow: inspectOnly can inspect strict contracts and replayOnly can replay existing traces. Replay misses never fall through to live execution.
Maintainer smokes
npm --prefix packages/agent run pack:smoke
npm --prefix packages/agent run smoke:quickstart
npm --prefix packages/agent run smoke:product-grade
npm --prefix packages/agent run smoke:live
npm --prefix packages/agent run smoke:registry-latestWithout SF_API_KEY, live sections print SKIP and exit successfully. With a key, the smokes call production world.read, verify broad legacy names stay non-canonical, record redacted traces, and replay them offline. Scripts do not write API keys to disk.
The Agent package publishes only dist/ and README.md. Build output omits source maps and declaration maps, and package-surface tests reject stale source-map artifacts before release.
Cookbook examples
examples/watch-iran-oil.tswatches semi-realtime market ticks and summarizes sharp moves.examples/market-monitor.tsis the shortest Agent SDK path for world state, market search, market inspection, policy gates, and JSONL trace.examples/trace-replay.tsrecords a redacted trace, replaysworld.readoffline, and proves replay misses fail withReplayMissError.examples/thesis-monitor.tsevaluates a thesis against live world/regime context.examples/calibration-loop.tscollects markets and computes a simple calibration signal.examples/subagent-handoff.tsruns a coordinator with specialist subagents.examples/mcp-bridge.tsexposes the strict Agent-callable read subset through a local stdio MCP bridge.
