# Author an Agent

> Build a typed Agent package with model calls, durable state, Tools, Resources, analytics, and explicit limits.

## Before you begin {#before-you-begin}

Choose a stable Agent id. Version labels are optional and may be reused when deploying changed code. Configure a logical Model Resource, list every external capability the Agent needs, and identify the Policy boundaries and budget appropriate for its work. Run `npm install @constal/sdk` in an ESM package.

## Steps {#steps}

1. Export `agent()` from the manifest entrypoint. A script Agent provides `onMessage`; a durable Agent provides `init`, `step`, and `output`.
2. Set `model` to a logical manifest binding name, not an external provider model id.
3. Use `ctx.turn()` for model work. Pass only the context the current turn needs and enable streaming only when the caller can consume it.
4. Use `ctx.resources` plus `ctx.invoke()` or a declared Tool for external operations. Never use arbitrary CRNs from message input and never put Credential material in code, prompts, or manifests.
5. Use `ctx.commit()`, `ctx.await()`, `ctx.spawn()`, `ctx.step()`, and ledger operations for durable workflow behavior. Every external effect belongs behind a governed Resource operation with a truthful effect and recovery contract.
6. Declare custom analytics with `analyticsEvent()` and register every declaration in the Agent definition before emitting it.
7. Add `constal.agent.json` with matching id, entry, mode, labels, fixed or scoped bindings, attached Policies, exposed Tool names, limits, and an optional `ui` block. Version is optional. Add a `channels.constal.ai/*` label only when the Agent should be eligible for public Channel exposure. A `ui` block (`id`, `displayName`, `source`, `channel`, and optional `access`, `execution`, `limits`, `labels`) makes `constal deploy` package the `source` directory and publish a `ui` Resource pinned to the exact Agent revision it built; the deployment receipt lists it under `outputs`, only immediate rollouts accept it, and the Agent and its UI are promoted together or not at all. See [Deploy an Agent with a UI](/docs/agents/deploy.md#ui) and [Publish a UI with the Agent](/docs/resources/dynamic-uis.md#publish-with-agent).

```ts
import { agent } from "@constal/sdk";

export default agent({
  id: "triage", model: "model",
  async onMessage(message, ctx) {
    const turn = await ctx.turn({
      system: "Classify and resolve the request using only offered capabilities.",
      objective: message,
      tools: ["lookup_ticket"],
    });
    return turn.message.content;
  },
});
```

## Declare setup requirements {#setup}

Add `setup.requirements` to the Agent manifest to describe its configuration prerequisites. Setup clients can use this metadata to build a guided flow. For example, this manifest fragment requests a reusable GitHub account and a Channel that delivers GitHub events:

```json
{
  "setup": {
    "requirements": [
      {
        "id": "github",
        "title": "GitHub access",
        "resourceKind": "credential",
        "selector": { "matchLabels": { "integration": "github" } },
        "binding": { "key": "github", "owner": "principal" }
      },
      {
        "id": "github-events",
        "title": "Configure GitHub events",
        "resourceKind": "channel",
        "selector": { "matchLabels": { "integration": "github" } }
      }
    ]
  }
}
```

A setup client should offer matching, available credentials from namespaces the operator can access and persist one explicit choice using the ordinary binding API. `github` is a logical slot key, not a stored Credential name. Creating a Credential through `POST /v1/namespaces/:namespace/credentials` accepts `labels`, so a GitHub credential can carry `{ "integration": "github" }`. Reusing a credential does not require repeating provider authorization or copying secret material; normal platform authorization still applies.

Declare `binding.owner` explicitly: `principal` selects the caller's binding, `tenant` selects shared app/service automation authority, and `customer` selects the authenticated customer. Setup and runtime use the same `resolveBindingOwner` helper. There is no fallback between owners. A consuming Resource uses the matching `{ "kind": "scoped", "key": "github", "owner": "principal", "required": true }` in its Credential slot. Bindings are tenant-isolated and namespace-addressed: multiple Agents can reuse the same owner/key assignment, and assignments in other namespaces can reference the same authorized Credential. Changing a shared assignment affects all its consumers. Previously stored declarations without an owner retain their original tenant semantics; new declarations must specify it.

Requirements use the existing Resource selector syntax, including `matchExpressions` and protected identity labels such as `constal.ai/name`. Setup clients should check that a matching Channel's target selector includes this Agent. Set `optional: true` to allow skipping a requirement. Configuration is saved by the ordinary Resource and binding APIs, so a setup client can derive progress from current state. These are configuration prerequisites; runtime access remains governed by actual bindings and Policies. Service-specific provisioning and verification can use the existing `constal.setup.v1` companion workflow.

Resource setup requirements use `resourceKind` and `selector`. They do not accept contract-name lookups. Credential requirements additionally require one `binding.key` and `binding.owner`; arrays of credentials, embedded Credential names, and duplicate owner/key pairs are rejected.

Setup clients can call `credentialSetupState(requirement, scope, inventory, binding)` from `@constal/sdk`, using their authorized non-secret inventory (`crn`, `labels`, `available`) and the existing binding head. It returns `missing` when no credential matches, `choose` when an explicit selection is needed, `ready` for a saved available selection, and `blocked` when the saved selection cannot be used. It never chooses the first match or switches away from an unavailable saved credential. Token rotation preserves the choice because it follows the Credential identity, not a material-version hash. The helper projects state; the ordinary binding API remains responsible for authorization and saving a choice.

For a custom durable setup workflow, `credential-interaction` panels can opt into
`receipt: "reference"` or `receipt: "evidence"`. Add `selection: { selector, binding:
{ key, owner } }` to offer and save an explicitly scoped Credential choice, and
`configuration` for non-secret provider creation settings. Console uses the normal
Credential lifecycle, evidence, and binding APIs, including cross-namespace reuse.
The resulting `constal.setup.credential` receipt contains a Credential reference,
optional provider evidence, and the confirmed binding revision. Interpret provider
claims in Agent code; the receipt never replaces authorization at the point of use.
Use `setupCredentialReceipt(submission.values, screen.current)` after validating the
submission with `setupSubmission` and handling secondary actions.

## Verify {#verify}

Type-check the project, deploy it, and start a controlled Run. Confirm the detail page shows the intended version, mode, logical bindings, Policies, Tools, and limits. Inspect the Run journal to verify every model and Resource operation is pinned and accounted.

## Next steps {#next-steps}

Choose a proven composition from the [Agent patterns catalog](/docs/agents/patterns.md), then read [Durable Agent patterns](/docs/agents/durable.md), [Deploy an Agent](/docs/agents/deploy.md), and [Resources and Tools](/docs/sdk/resources-and-tools.md).
