> ## Documentation Index
> Fetch the complete documentation index at: https://docs.checkfu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from your own agent loop

> Replace a hand-rolled model loop with AgentVersions, Environments, Sessions, and governed capabilities.

Most hand-built agents start as one process: call the model, execute tool calls,
append messages to an array, and repeat. The hard part arrives later. Process
memory is not durable execution truth, credentials leak into runtime
configuration, retries blur external side effects, and each new surface invents
another transcript.

Checkfu separates the authored configuration from execution:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
Agent → immutable AgentVersion ─┐
Environment ────────────────────┼→ Session → Turn → Run → RunAttempt
Resources and Vaults ───────────┘
```

The Session event log is the durable source of truth. The harness is a
replaceable execution lane around it.

<Warning>
  Plain HTTP against the Checkfu API needs no private package.
  The Checkfu CLI and TypeScript SDK are optional private-alpha artifacts; the HTTP requests work without them.
  Part of this flow is behind a rollout fence; a deployment that has not enabled it refuses the request rather than running it.
  Confirm [capability status](/getting-started/status) before making availability part of your application's contract.
</Warning>

## What moves where

| Your loop owns today | Checkfu resource |
| - | - |
| System instructions, model choice, and tools | Agent and immutable AgentVersions |
| Container, packages, networking, or self-hosted placement | Environment |
| Conversation and task history | Session event log |
| One unit of execution | Run inside a Session Turn |
| Child agents and delegated work | Automation composition between Sessions |
| Tool credentials in environment variables | Vault and Credential custody |
| Third-party tool access | Connection |
| Safety checks before effects | PermissionAssignment → ActionPolicy → ActionApproval |
| Files and repository inputs | File and Session resources |
| Long-lived learned state | MemoryStore and MemoryVersion |
| Reusable procedures | Skill and SkillVersion |
| Cron jobs and inbound triggers | Automation and AutomationRun |
| Cost counters inferred from traces | Authoritative Usage |

A Vault owns secret-bearing Credential custody. A Connection is the nonsecret
governed link to an external account or provider capability. Credential values
never enter Agent or AgentVersion configuration, model context, harness
configuration, Session events, Files, Memory, tool arguments, or the sandbox.
A connected effect follows PermissionAssignment → ActionPolicy → ActionApproval
when required, then a live authority and revocation recheck before provider I/O.

An uncertain external effect never becomes safe merely because the platform can
retry execution. Reconcile it with the provider or use an operation-specific
idempotency key before trying again. See [Capabilities](/concepts/capabilities)
and [Tenancy and governance](/concepts/tenancy-and-governance) for the current
resource and trust surfaces.

## Before

A typical loop mixes configuration, execution, credentials, and history in one
process:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const history: Message[] = [{ role: "user", content: task }]

while (true) {
  const reply = await model.chat(history, tools)
  history.push(reply)

  if (!reply.toolCalls?.length) break

  for (const call of reply.toolCalls) {
    const result = await runTool(call)
    history.push({ role: "tool", content: result })
  }
}
```

## After

Create the Agent and Environment once. For each task, create a Session with an
initial event. This exact request selects an AgentVersion and Environment at
admission:

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  SESSION_ID="$(
    jq -n \
      --arg agent_id "$AGENT_ID" \
      --arg environment_id "$ENVIRONMENT_ID" \
      --arg task "$TASK" \
      '{
        agent: {
          type: "agent",
          id: $agent_id,
          version: 1
        },
        environment_id: $environment_id,
        initial_events: [{
          type: "user.message",
          content: [{ type: "text", text: $task }]
        }]
      }' | \
      curl --silent --request POST https://api.checkfu.com/v1/sessions \
        --header "Authorization: Bearer $CHECKFU_API_KEY" \
        --header "Checkfu-Version: 2026-08-31" \
        --header "Idempotency-Key: sess-$TASK_ID" \
        --header "Content-Type: application/json" \
        --data @- | \
      jq -r '.id'
  )"

  # Tail GET /v1/sessions/$SESSION_ID/events/stream for the reply.
  ```

  ```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { Checkfu } from "@checkfu/sdk"

  const checkfu = new Checkfu({
    apiKey: process.env.CHECKFU_API_KEY,
  })

  const session = await checkfu.sessions.create(
    {
      agent: {
        type: "agent",
        id: agentId,
        version: 1,
      },
      environment_id: environmentId,
      initial_events: [
        {
          type: "user.message",
          content: [{ type: "text", text: task }],
        },
      ],
    },
    { idempotencyKey: `sess-${taskId}` },
  )

  let reply: string | undefined

  for await (const event of checkfu.sessions.events.stream(session.id)) {
    if (event.type === "agent.message" && "content" in event.payload) {
      reply = event.payload.content
    }

    const terminal =
      event.type === "session.status_completed" ||
      event.type === "session.status_failed" ||
      event.type === "session.status_canceled"

    const idleAfterTurn =
      event.type === "session.status_idle" &&
      event.payload.stop_reason !== "requires_action"

    if (terminal || idleAfterTurn) break
  }
  ```
</CodeGroup>

Creating with `initial_events` combines Session creation and the first user
event. You can also create the Session first and send events later.

A non-`requires_action` `session.status_idle` settles the current Turn. It does
not erase or replace the Session. Because settlement lives in the event log, a
client that disconnects can resume reading durable history rather than infer
the result from a lost HTTP response.

## Admission replaces implicit configuration

At Session creation, Checkfu:

* resolves and freezes the current or explicitly pinned AgentVersion;
* freezes the selected Environment admission authority;
* records the admitted resource, Connection, and Vault references.

Later edits to the Agent or Environment affect later Sessions, not the one
already admitted. PermissionAssignments, ActionPolicies, ActionApprovals,
Connection lifecycle, revocation, and Credential authority remain live at their
documented boundaries; Audit records the resulting decisions and provider
effects without recording Credential values.

## The harness is not the product model

Your loop still exists, but it runs inside the selected harness. Managed and
customer-operated harnesses use the same Agent, Environment, and Session model.
The harness may prepare context and invoke tools; it does not own a separate
transcript or recovery authority.

Your application becomes responsible for a smaller interface:

1. author Agents and supporting resources through one active desired-state
   owner;
2. create and drive Sessions;
3. consume durable events and decisions.

UI, HTTP, SDK, CLI, and MCP are interfaces over the same resource operations,
not separate owners. YAML and Git may supply desired state, but they do not
become another runtime or reconciler. See [Author an Agent
project](/guides/author-an-agent-project) before adding a source-backed authoring
path.

## Migration order

1. **Create the Environment.** Reproduce the minimum packages, networking, and
   execution mode your task needs.
2. **Create the Agent.** Move the system instructions and model choice first.
   Creation produces AgentVersion 1.
3. **Create a pinned Session.** Use an exact AgentVersion until behavior is
   stable and compare the durable event history.
4. **Move credentials and tools.** Create Vaults, Credentials, and Connections.
   Assign PermissionAssignments before adding ActionPolicies and approval
   behavior.
5. **Move files, Skills, and Memory.** Keep durable inputs and learned state out
   of harness-specific history.
6. **Move triggers last.** Replace cron or webhook wrappers with Automations
   after direct Session execution is correct.

<CardGroup cols={2}>
  <Card title="Run your first Session" icon="rocket" href="/getting-started/quickstart">
    Create the core resources and execute one task end to end.
  </Card>

  <Card title="Sessions and Runs" icon="timeline" href="/concepts/sessions-and-runs">
    Understand event history, Turns, Runs, settlement, and continuation.
  </Card>

  <Card title="Capabilities" icon="plug" href="/concepts/capabilities">
    Move Connections and resources behind the shared trust boundary.
  </Card>

  <Card title="Tenancy and governance" icon="shield" href="/concepts/tenancy-and-governance">
    Add PermissionAssignments, ActionPolicies, ActionApprovals, revocation, and Audit.
  </Card>

  <Card title="Author an Agent project" icon="code" href="/guides/author-an-agent-project">
    Keep source-backed desired state behind one active authoring owner.
  </Card>

  <Card title="Resume a stream" icon="arrows-rotate" href="/guides/resume-a-stream">
    Reconnect to durable events without treating the stream as authority.
  </Card>
</CardGroup>


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