> ## 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 SDK v1 runtime RFC

> Historical design boundary for the Agent SDK v1 runtime.

Status: historical RFC. The v1 surface is now published in `@spfunctions/agent/v1`.

This RFC records the boundary that preceded the `@spfunctions/agent/v1` pre-1.0 runtime. The package now exposes a model-loop surface, sessions, hooks, watch primitives, Cursor-style compatibility, and policy-gated live execution through the strict SDK tools. The remaining constraints are no endpoint expansion from broad `/api/tools`, no CLI shell-out from the SDK package, and no hosted trace/session backend.

## Current boundary

`@spfunctions/agent` v0 is the governed direct tool runner.

It provides:

* strict manifest loading from `GET /api/contracts/tools`
* canonical direct tool calls through `call()`
* event streaming through `stream()`
* policy gates for permissions, `sideEffect`, and `costEffect`
* trace record/replay
* API-key-first live execution

v1 is different. It is an model-backed workflow runtime that plans and executes governed tool calls over several turns. It must not blur into the CLI, MCP, or `/api/tools`; live execution is only through strict policy-gated tools such as `execution.place`.

## Non-goals

These remain non-goals for the v1:

* model provider SDK dependency
* hosted run endpoint
* background worker
* MCP runtime
* browser runtime
* unguarded live trading
* write/default Agent tools
* `events.*`
* `market.related`
* `auth.status`
* `investigations.create`
* `intents.propose`
* `webhooks.create`

The v1 runtime must not shell out to `sf agent`. The CLI may later reuse Agent SDK internals, but the Agent SDK must remain an embeddable library.

## Proposed package boundary

`@spfunctions/sdk` remains the typed data and contract client.

`@spfunctions/agent` v0 remains the governed direct runner:

```ts theme={null}
await agent.call("world.read")
for await (const event of agent.stream("markets.search", { query: "Fed CPI" })) {
  console.log(event)
}
```

`@spfunctions/agent` v1 adds objective-oriented runtime APIs:

```ts theme={null}
const runtime = new SimpleFunctionsRuntime({
  client: sf,
  model,
  policy,
  trace,
  tools: {
    mode: "manifest-search",
    preload: ["world.read", "markets.search"],
  },
})

const result = await runtime.run({
  objective: "Research Fed CPI repricing using read-only market tools.",
})

for await (const event of runtime.runStream({
  objective: "Monitor Fed cut markets and report read-only changes.",
})) {
  console.log(event)
}
```

`run()` and `runStream()` must be layered on top of the v0 direct runner. They must not bypass v0 policy, identity, trace, or canonical tool resolution.

## Interface sketches

```ts theme={null}
export interface SimpleFunctionsRuntimeOptions {
  client: SimpleFunctions
  model: ModelAdapter
  policy?: AgentPolicy
  trace?: TraceStore
  tools?: RuntimeToolSelectionPolicy
  sessionStore?: SessionStore
}

export interface RuntimeRunInput {
  objective: string
  tools?: string[]
  context?: Record<string, unknown>
  sessionId?: string
  maxSteps?: number
}

export interface RuntimeRunResult {
  runId: string
  sessionId?: string
  status: "completed" | "failed" | "blocked" | "requires_approval"
  output?: unknown
  steps: RuntimeStep[]
  usage?: RuntimeUsage
}

export interface ModelAdapter {
  name: string
  complete(input: ModelCompleteInput): Promise<ModelCompleteResult>
  stream?(input: ModelCompleteInput): AsyncIterable<ModelEvent>
}
```

The model adapter is an interface only. This RFC does not add OpenAI, Anthropic, Cursor, or other provider packages.

## Tool selection

v1 should not load every broad hosted or MCP tool into context.

The tool source remains `GET /api/contracts/tools`, not `/api/tools`.

```ts theme={null}
export type RuntimeToolSelectionMode =
  | "explicit"
  | "manifest-search"
  | "preload-only"

export interface RuntimeToolSelectionPolicy {
  mode: RuntimeToolSelectionMode
  preload?: string[]
  maxCandidateTools?: number
}
```

Rules:

* `explicit`: only tools supplied in `RuntimeRunInput.tools`
* `preload-only`: only configured preloaded canonical tools
* `manifest-search`: search strict contract metadata, then select a small set of canonical candidates

Broad compatibility names such as `get_world_state` and `get_regime_history` remain invalid for v1 runtime planning.

## Session and run state

v1 needs stable IDs and resumable state before stable hosted runtime expansion.

```ts theme={null}
export interface RuntimeSession {
  sessionId: string
  createdAt: string
  updatedAt: string
  objective?: string
  policySummary: Record<string, unknown>
}

export interface RuntimeStep {
  stepId: string
  runId: string
  type: "model" | "tool" | "approval" | "handoff" | "system"
  status: "started" | "completed" | "failed" | "blocked"
  tool?: string
  traceId?: string
  startedAt: string
  completedAt?: string
}

export interface SessionStore {
  get(sessionId: string): Promise<RuntimeSession | null>
  put(session: RuntimeSession): Promise<void>
}
```

Initial v1 can use an in-memory session store for package-local dogfood. Hosted or database-backed sessions require a later design.

## Policy, budgets, and approvals

v1 must enforce the same policy gates as v0 before any tool call:

* identity
* canonical tool existence
* tool status
* `agent.callable`
* deny list
* allow list
* `maxSideEffect`
* `maxCostEffect`
* user-data auth invariants
* live-trade policy gate

Budgeting should start as counters and hard limits over known local events, not estimated billing unless the platform exposes reliable per-call cost metadata.

```ts theme={null}
export interface RuntimeBudgetPolicy {
  maxSteps?: number
  maxToolCalls?: number
  maxCostEffect?: CostEffect
  maxSideEffect?: SideEffect
  budgetUsd?: number
}

export interface ApprovalPolicy {
  requireForSideEffectAtOrAbove?: SideEffect
  requireForCostEffectAtOrAbove?: CostEffect
}
```

Approvals must block execution and emit events. They must not auto-approve writes, runtime actions, paper trades, or live trades.

## Human escalation

Human escalation is a runtime event and state transition, not an endpoint in this RFC.

```ts theme={null}
export type RuntimeEvent =
  | AgentEvent
  | { type: "runtime.started"; runId: string; sessionId?: string }
  | { type: "model.started"; runId: string; stepId: string }
  | { type: "model.completed"; runId: string; stepId: string }
  | { type: "approval.required"; runId: string; stepId: string; reason: string }
  | { type: "runtime.completed"; runId: string }
  | { type: "runtime.failed"; runId: string; error: { code: string; message: string } }
```

No hosted approval workflow is defined here.

## Trace and replay

v1 must preserve v0 replay rules:

* strict `tool + inputHash` matching
* replay miss never calls live
* input normalization is deterministic
* traces redact secret-shaped fields
* model prompts must not include raw API keys
* trace entries must distinguish model steps from direct tool calls

Model replay is a separate problem and should not be implied by v0 tool replay.

## Implementation entry criteria

Do not implement v1 until these are true:

| Entry criterion | Required evidence |
| - | - |
| v0 direct runner is package-stable | tests, pack smoke, live smoke |
| SDK preflight is stable | no-key/auth/cost/side-effect tests |
| strict manifest drift guards pass | `/api/contracts/tools` and package tests |
| trace redaction and replay tests pass | Agent trace suite |
| CLI/direct parity tests pass | CLI/manifest parity suite |
| release checklist exists | SDK and Agent release checklist RFC |
| model adapter interface is approved | RFC review |
| tool selection plan is approved | RFC review |
| session and approval plan is approved | RFC review |

If any criterion is missing, continue hardening v0 instead.

## Test plan for future implementation

When implementation is approved, add tests before runtime expansion:

* v1 refuses to construct without API-keyed client for live mode
* v1 resolves only canonical contract tools
* broad names are rejected
* model adapter receives only policy-approved tool candidates
* `maxSideEffect` and `maxCostEffect` are enforced before tool execution
* approval-required runs stop before tool execution
* replay-only mode never calls live
* trace redaction covers model context and tool inputs
* no unguarded live trading tool can be enabled

## Stop line

This RFC is complete when it documents boundaries and entry criteria. It is not complete if it adds provider dependencies, endpoint code, model calls, or runtime execution behavior.


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