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

# Agents

> Register agent versions, record what they do, and read the registry.

An agent has a name and immutable **versions**. Each version records the
agent's instructions, model, and tool definitions. Chronicle also records
**runs**: what happened each time the agent was invoked.

Use a [configured SDK client](/api-reference/languages/overview#use-the-guide-examples).
The examples share that client and the IDs returned by earlier steps.

## Methods

| Method                                     | HTTP                                   | Does                                           |
| ------------------------------------------ | -------------------------------------- | ---------------------------------------------- |
| `agents.register(...)`                     | `POST /v1/agents/register`             | Register a version.                            |
| `agents.runs.record(runs)`                 | `POST /v1/agents/runs/batch`           | Record observed runs.                          |
| `agents.list()`                            | `GET /v1/agents`                       | List agents.                                   |
| `agents.retrieve(name)`                    | `GET /v1/agents/{name}`                | Read an agent, its versions, and run stats.    |
| `agents.update(name, ...)`                 | `PATCH /v1/agents/{name}`              | Update description, owner, and other metadata. |
| `agents.chat.start(name)`                  | `POST /v1/agents/{name}/chat/sessions` | Start a chat session with the current version. |
| `agents.chat.send(name, session_id, text)` | `POST .../messages`                    | Send a message.                                |

Registering and recording runs need a key with `agents:write`. Reads use a
`platform` key.

## Before you register

Have your agent code and service tools ready. Registration records a version's
configuration; to execute it in Chronicle, [configure its run command](#run-in-an-evaluation)
and package its dependencies. Use `billing-agent@1.0.0` consistently in the
examples below and the evaluation guide.

[Inspect the task it will run](/product-explorer#agent-task): moving ENG-2 and
posting the handoff requires actual Linear and Slack tool calls.

## Register from your agent code

The simplest way is an adapter that captures the configuration straight from
your agent object and records its runs automatically.

<Tabs>
  <Tab title="Vercel AI SDK">
    ```typescript register-agent.ts theme={null}
    import { createChronicle } from "@chroniclelabs/ai-sdk";
    import { billingAgent } from "./billing-agent.js";

    const chronicle = createChronicle({
      apiKey: process.env.CHRONICLE_API_KEY!,
      baseURL: process.env.CHRONICLE_API_URL!,
    });

    const observed = await chronicle.agents.instrument(billingAgent, {
      name: "billing-agent",
      version: "1.0.0",
      description: "Triages billing incidents across Linear, Salesforce, and Slack.",
    });

    // Use `observed` from here on so runs are recorded.
    await observed.generate({ prompt: "Triage the duplicate invoice on order 7001." });
    await chronicle.flush();
    ```
  </Tab>

  <Tab title="Vercel eve">
    ```typescript agent/hooks/chronicle.ts theme={null}
    import { defineHook } from "eve/hooks";
    import { chronicleEveHook } from "@chroniclelabs/eve";

    export default defineHook(
      chronicleEveHook({
        apiKey: process.env.CHRONICLE_API_KEY!,
        baseURL: process.env.CHRONICLE_API_URL!,
        agent: { version: "1.0.0" },
      }),
    );
    ```

    Run `eve build` or `eve dev`, then use the agent normally.
  </Tab>
</Tabs>

`@chroniclelabs/agentforce` observes Salesforce Agentforce agents the same way;
those can't run in evaluations.

## Register any agent

For frameworks without an adapter, or from a build pipeline, describe the
version yourself. This is what the adapters send under the hood.

<CodeGroup dropdown>
  ```python Python theme={null}
  version = client.agents.register(
      name="billing-agent",
      version="1.0.0",
      framework="langchain",
      model={"provider": "openai", "id": "gpt-4.1"},
      instructions="Investigate billing incidents, make the requested tool changes, and report the evidence without claiming unverified fixes.",
      tools=[
          {"name": "linear_update_issue", "description": "Update a Linear issue"},
          {"name": "slack_post_message", "description": "Post to a Slack channel"},
      ],
      status="current",
  )
  ```

  ```typescript TypeScript theme={null}
  const version = await client.agents.register({
    name: "billing-agent",
    version: "1.0.0",
    framework: "langchain",
    model: { provider: "openai", id: "gpt-4.1" },
    instructions: "Investigate billing incidents, make the requested tool changes, and report the evidence without claiming unverified fixes.",
    tools: [
      { name: "linear_update_issue", description: "Update a Linear issue" },
      { name: "slack_post_message", description: "Post to a Slack channel" },
    ],
    status: "current",
  });
  ```

  ```javascript JavaScript theme={null}
  const version = await client.agents.register({
    name: "billing-agent",
    version: "1.0.0",
    framework: "langchain",
    model: { provider: "openai", id: "gpt-4.1" },
    instructions: "Investigate billing incidents, make the requested tool changes, and report the evidence without claiming unverified fixes.",
    tools: [
      { name: "linear_update_issue", description: "Update a Linear issue" },
      { name: "slack_post_message", description: "Post to a Slack channel" },
    ],
    status: "current",
  });
  ```

  ```go Go theme={null}
  _, err := client.Agents.Register(ctx, chronicle.AgentRegisterParams{
      Name:         "billing-agent",
      Version:      "1.0.0",
      Framework:    "langchain",
      Model:        chronicle.AgentModel{Provider: "openai", ID: "gpt-4.1"},
      Instructions: "Investigate billing incidents, make the requested tool changes, and report the evidence without claiming unverified fixes.",
      Tools: []chronicle.AgentTool{
          {Name: "linear_update_issue", Description: "Update a Linear issue"},
          {Name: "slack_post_message", Description: "Post to a Slack channel"},
      },
      Status: "current",
  })
  if err != nil {
      return err
  }
  ```

  ```rust Rust theme={null}
  let version = client.agents().register(AgentRegisterParams {
      name: "billing-agent".into(),
      version: "1.0.0".into(),
      framework: "langchain".into(),
      model: AgentModel { provider: "openai".into(), id: "gpt-4.1".into() },
      instructions: "Investigate billing incidents, make the requested tool changes, and report the evidence without claiming unverified fixes.".into(),
      tools: vec![
          AgentTool { name: "linear_update_issue".into(), description: "Update a Linear issue".into() },
          AgentTool { name: "slack_post_message".into(), description: "Post to a Slack channel".into() },
      ],
      status: Some("current".into()),
      ..Default::default()
  }).await?;
  ```
</CodeGroup>

| Parameter                        | Meaning                                                                                      |
| -------------------------------- | -------------------------------------------------------------------------------------------- |
| `name`, `version`                | The identity. Immutable once registered.                                                     |
| `framework`                      | A label: `ai-sdk`, `eve`, `langchain`, `openai-agents`, `crewai`, `pydantic-ai`, and others. |
| `model`, `instructions`, `tools` | What you're testing. Shown in the dashboard's **Manifest** tab and diffed in **Changes**.    |
| `status`                         | `current` (default), `stable`, `draft`, or `deprecated`.                                     |
| `metadata`                       | Optional: `owner`, `environment`, `purpose`. Editable later with `agents.update`.            |

Registering the same name and version with identical content is a no-op; with
different content it's a `409`. Bump the version when the agent changes.
Registering a new `current` version moves the old one to `stable`.

## Run in an evaluation

Registration does not upload code or configure execution. Package the selected
agent version and its dependencies, supply its model credentials, and configure
a command that starts its entry point inside the evaluation sandbox.

Chronicle sets these variables for each trial:

| Variable                  | Purpose                                             |
| ------------------------- | --------------------------------------------------- |
| `INSTRUCTION_PATH`        | Read the task instruction from this file.           |
| `WORK_DIR`                | Write the final answer to `$WORK_DIR/output.txt`.   |
| `CHRONICLE_AGENT_VERSION` | The selected `name@version`; run the matching code. |
| `CHRONICLE_TRIAL_ID`      | The trial ID for your logs.                         |

For tasks in a world, use the service credentials injected into the trial
instead of production credentials. Your HTTP clients must respect `HTTP_PROXY`,
`HTTPS_PROXY`, and the supplied CA settings (`NODE_EXTRA_CA_CERTS`,
`SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`).

Standard output and error are captured as diagnostics. A clean exit means the
agent finished; attached scorers determine whether it completed the task.
See [Run an evaluation](/platform/evaluations) to configure the test.

## Record runs

Adapters do this for you. From your own code, record each invocation after it
finishes:

<CodeGroup dropdown>
  ```python Python theme={null}
  client.agents.runs.record([{
      "run_id": "inv-2026-09-10-001",
      "agent": "billing-agent@1.0.0",
      "started_at": "2026-09-10T14:00:00Z",
      "finished_at": "2026-09-10T14:00:42Z",
      "status": "success",
      "tool_calls": [{"name": "linear_update_issue", "status": "ok", "duration_ms": 310}],
      "usage": {"input_tokens": 4200, "output_tokens": 380},
  }])
  ```

  ```typescript TypeScript theme={null}
  await client.agents.runs.record([{
    runId: "inv-2026-09-10-001",
    agent: "billing-agent@1.0.0",
    startedAt: "2026-09-10T14:00:00Z",
    finishedAt: "2026-09-10T14:00:42Z",
    status: "success",
    toolCalls: [{ name: "linear_update_issue", status: "ok", durationMs: 310 }],
    usage: { inputTokens: 4200, outputTokens: 380 },
  }]);
  ```

  ```javascript JavaScript theme={null}
  await client.agents.runs.record([{
    runId: "inv-2026-09-10-001",
    agent: "billing-agent@1.0.0",
    startedAt: "2026-09-10T14:00:00Z",
    finishedAt: "2026-09-10T14:00:42Z",
    status: "success",
    toolCalls: [{ name: "linear_update_issue", status: "ok", durationMs: 310 }],
    usage: { inputTokens: 4200, outputTokens: 380 },
  }]);
  ```

  ```go Go theme={null}
  _, err = client.Agents.Runs.Record(ctx, []chronicle.AgentRun{{
      RunID:      "inv-2026-09-10-001",
      Agent:      "billing-agent@1.0.0",
      StartedAt:  "2026-09-10T14:00:00Z",
      FinishedAt: "2026-09-10T14:00:42Z",
      Status:     "success",
      ToolCalls:  []chronicle.ToolCall{{Name: "linear_update_issue", Status: "ok", DurationMs: 310}},
      Usage:      &chronicle.Usage{InputTokens: 4200, OutputTokens: 380},
  }})
  if err != nil { return err }
  ```

  ```rust Rust theme={null}
  client.agents().runs().record(vec![AgentRun {
      run_id: "inv-2026-09-10-001".into(),
      agent: "billing-agent@1.0.0".into(),
      started_at: "2026-09-10T14:00:00Z".into(),
      finished_at: Some("2026-09-10T14:00:42Z".into()),
      status: "success".into(),
      tool_calls: vec![ToolCall { name: "linear_update_issue".into(), status: "ok".into(), duration_ms: Some(310) }],
      usage: Some(Usage { input_tokens: 4200, output_tokens: 380 }),
      ..Default::default()
  }]).await?;
  ```
</CodeGroup>

Up to 100 runs per call. A `run_id` is write-once: send the same body again to
retry, use a new ID for a new run.

## Read the registry

<CodeGroup dropdown>
  ```python Python theme={null}
  for agent in client.agents.list().items:
      print(agent.name, agent.current_version)

  agent = client.agents.retrieve("billing-agent")
  for v in agent.versions:
      print(v.version, v.status, v.model, len(v.tools))
  print(agent.runs.count, agent.runs.success_rate)
  ```

  ```typescript TypeScript theme={null}
  for (const agent of (await client.agents.list()).items) console.log(agent.name, agent.currentVersion);

  const agent = await client.agents.retrieve("billing-agent");
  for (const v of agent.versions) console.log(v.version, v.status, v.model, v.tools.length);
  console.log(agent.runs.count, agent.runs.successRate);
  ```

  ```javascript JavaScript theme={null}
  for (const agent of (await client.agents.list()).items) console.log(agent.name, agent.currentVersion);

  const agent = await client.agents.retrieve("billing-agent");
  for (const v of agent.versions) console.log(v.version, v.status, v.model, v.tools.length);
  console.log(agent.runs.count, agent.runs.successRate);
  ```

  ```go Go theme={null}
  agents, err := client.Agents.List(ctx)
  if err != nil {
      return err
  }
  for _, a := range agents.Items {
      fmt.Println(a.Name, a.CurrentVersion)
  }

  agent, err := client.Agents.Retrieve(ctx, "billing-agent")
  if err != nil {
      return err
  }
  for _, v := range agent.Versions {
      fmt.Println(v.Version, v.Status, v.Model, len(v.Tools))
  }
  fmt.Println(agent.Runs.Count, agent.Runs.SuccessRate)
  ```

  ```rust Rust theme={null}
  for a in client.agents().list().await?.items {
      println!("{} {:?}", a.name, a.current_version);
  }

  let agent = client.agents().retrieve("billing-agent").await?;
  for v in &agent.versions {
      println!("{} {:?} {:?} {}", v.version, v.status, v.model, v.tools.len());
  }
  println!("{} {}", agent.runs.count, agent.runs.success_rate);
  ```
</CodeGroup>

Run stats describe observed invocations. Task pass rates come from
[evaluations](/api-reference/evaluations).

## Chat with an agent

A quick way to confirm Chronicle can run the agent at all. Requires a run
command on the agent; see [Connect your agent](/platform/agents).

<CodeGroup dropdown>
  ```python Python theme={null}
  session = client.agents.chat.start("billing-agent")
  reply = client.agents.chat.send(
      "billing-agent", session.id,
      text="How would you triage a duplicate invoice?",
  )
  for message in reply.messages:
      print(message.role, message.text)
  print(reply.trace_id)
  ```

  ```typescript TypeScript theme={null}
  const session = await client.agents.chat.start("billing-agent");
  const reply = await client.agents.chat.send("billing-agent", session.id, {
    text: "How would you triage a duplicate invoice?",
  });
  console.log(reply.messages.at(-1)?.text, reply.traceId);
  ```

  ```javascript JavaScript theme={null}
  const session = await client.agents.chat.start("billing-agent");
  const reply = await client.agents.chat.send("billing-agent", session.id, {
    text: "How would you triage a duplicate invoice?",
  });
  console.log(reply.messages.at(-1)?.text, reply.traceId);
  ```

  ```go Go theme={null}
  session, err := client.Agents.Chat.Start(ctx, "billing-agent")
  if err != nil {
      return err
  }
  reply, err := client.Agents.Chat.Send(ctx, "billing-agent", session.ID, "How would you triage a duplicate invoice?")
  if err != nil {
      return err
  }
  for _, message := range reply.Messages { fmt.Println(message.Role, message.Text) }
  fmt.Println(reply.TraceID)
  ```

  ```rust Rust theme={null}
  let session = client.agents().chat().start("billing-agent").await?;
  let reply = client.agents().chat()
      .send("billing-agent", &session.id, "How would you triage a duplicate invoice?")
      .await?;
  println!("{:?} {}", reply.messages.last().map(|m| &m.text), reply.trace_id);
  ```
</CodeGroup>

The reply includes the messages so far, tool `steps`, and a `trace_id` you can
open in Timeline. Chat runs the agent without a world; to test it against a
task, run an evaluation.
