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

# SDK: Agents as Code

> Define action-first agents in TypeScript and synchronize them with Simple

Install the SDK and CLI in a pnpm project:

```bash theme={null}
pnpm add @simple-ai-lab/sdk zod
pnpm add -D typescript
pnpm add -g @simple-ai-lab/cli
```

For a new Agent, run `simple init`. To synchronize an existing production branch, run:

```bash theme={null}
simple login
simple pull <agent-uuid> --branch main
```

`agent.ts` is the desired state. `simple.lock.json` records the remote Agent, selected branch and head commit, graph identities, and immutable Action heads. Commit both files.

Pull refuses to overwrite local changes. Reconcile them first, or pass `--force` only when you intentionally want production to replace the local source. If `--branch` is omitted, the CLI follows the product's most-recently-updated branch selection.

## Action-first authoring

Define integrations with `action.http`, `action.code`, `action.customDataQuery`, `action.customDataSearch`, or `action.knowledgeBaseSearch`. Attach an Action to a prompt with `action.callable`, or execute it deterministically with an Action node or `action.run`.

```typescript theme={null}
import { action, defineAgent, node, prompt } from '@simple-ai-lab/sdk';
import { z } from 'zod';

const lookup = action.http({
  key: 'lookup-account',
  name: 'Lookup account',
  description: 'Fetch an account',
  input: z.object({
    account_id: z.string().describe('The account identifier from the caller'),
  }),
  output: z.object({ account_name: z.string() }),
  outputMappings: { account_name: '{{ httpResponse.body.name }}' },
  request: {
    method: 'GET',
    url: 'https://example.com/accounts/{{ inputs.account_id }}',
  },
});

export default defineAgent({
  key: 'support',
  name: 'Support',
  branch: 'main',
  nodes: [
    node.prompt({
      key: 'root',
      name: 'Root',
      root: true,
      prompt: prompt.inline('Help the caller.'),
      actions: [
        action.callable({
          key: 'lookup-action',
          action: lookup,
          name: 'lookup_account',
          description: 'Look up an account',
          inputs: {
            account_id: action.fromAgent('The account identifier'),
          },
          // Action output name -> Agent parameter name
          outputParameters: { account_name: 'customer_account_name' },
        }),
      ],
    }),
  ],
});
```

New authoring never emits legacy tool assignments or HTTP graph nodes. A pull uses `defineAgentSnapshot(...)` when necessary to preserve every current Agent v2 node and legacy field losslessly; you can refactor that snapshot to the ergonomic API deliberately.

## Validate and publish

```bash theme={null}
simple typecheck
simple diff
simple publish --label "Improve account lookup"
```

Publish validates the complete project on the server before mutation, checks Agent and branch metadata plus the
relevant Action heads, creates or revises changed Actions with retry-safe identities, and finally creates one Agent
commit pinned to those exact versions. If remote state changed, publish stops and asks you to pull or upgrade instead
of overwriting it.

`simple actions upgrade` rewrites a pulled `defineAgentSnapshot(...)` with the accepted remote Action values and version pins. For ergonomic `defineAgent(...)` source, update the Action definition to match the remote head first; the CLI refuses to make the lockfile disagree with source.

## CLI authentication

`simple login` opens PropelAuth in your browser and uses Authorization Code + PKCE. When your account belongs to more
than one organization, the CLI asks which organization to use. It stores the rotating refresh token with user-only
permissions and refreshes short-lived access tokens automatically.

The CLI is OAuth-only and uses the signed-in user's existing Agent and Action permissions. The published CLI includes
Simple's public OAuth client configuration and registered localhost callback. No client secret is used by the CLI.
