01 — Hello, agent
The minimum viable example. Create an agent, open a conversation context, send two messages, and read the replies. Everything else in the SDK builds on these three primitives.
contextId automatically.text, status, contextId.Run it
# From the repo root — build the SDK once
cd packages/js && npm install && npm run build
# Install example deps and configure credentials
cd ../../examples/ts
npm install
cp .env.example .env # fill in CORTI_TENANT_NAME, CORTI_CLIENT_ID, CORTI_CLIENT_SECRET
# Run
npm run hello
lifecycle: "ephemeral". The platform garbage-collects them automatically, so no explicit cleanup call is needed. Pass lifecycle: "persistent" if you want the agent to survive the process.
Walkthrough
1 · Build the client
makeClient() is a shared helper in _client.ts that reads three environment variables and returns a CortiClient:
import { CortiClient } from "@corti/sdk";
export function makeClient(): CortiClient {
return new CortiClient({
tenantName: process.env.CORTI_TENANT_NAME!,
environment: (process.env.CORTI_ENVIRONMENT ?? "eu") as "eu" | "us",
auth: {
clientId: process.env.CORTI_CLIENT_ID!,
clientSecret: process.env.CORTI_CLIENT_SECRET!,
},
});
}
Every example starts with new AgentsClient(makeClient()) — AgentsClient wraps the raw client and exposes the agent-specific operations.
2 · Create an agent
const agent = await agents.create({
name: "hello-agent",
description: "A minimal greeting agent.",
systemPrompt: "You are a friendly assistant. Keep replies to one sentence.",
});
agents.create() registers the agent on the Corti platform and returns an AgentHandle. The name field is a slug and must be unique per tenant. Because no lifecycle is specified, it defaults to "ephemeral".
The returned handle gives you agent.id, agent.name, agent.systemPrompt, and methods like createContext(), run(), update(), and delete().
3 · Open a context (lazy)
const ctx = agent.createContext();
createContext() is synchronous and makes no network call. The context is lazy — it initialises on the first message. At this point ctx.id is undefined.
4 · First message
const reply = await ctx.sendText("Say hello and tell me one fun fact.");
console.log("Agent:", reply.text);
console.log("Status:", reply.status);
console.log("Context ID:", ctx.id);
sendText() is a shorthand for sendMessage([{ kind: "text", text: "..." }]). The response carries:
| Getter | Type | Notes |
|---|---|---|
reply.text | string? | All text parts joined into one string. |
reply.status | TaskState? | "completed", "failed", "input-required", … |
reply.contextId | string? | The thread ID — the context stores this automatically. |
reply.taskId | string? | This specific invocation's ID. |
reply.artifacts | Artifact[] | Structured outputs attached to the response. |
reply.raw | AgentsMessageSendResponse | The full unprocessed API response. |
After the first message completes, ctx.id is populated with the contextId that the context extracted from the response.
5 · Second message (same thread)
const followUp = await ctx.sendText("Tell me another one.");
console.log("Agent:", followUp.text);
The context automatically passes the stored contextId back to the API. The agent sees the full prior conversation — it remembers what it said in the previous turn.
You never manage contextId manually. The context object handles it entirely.
One-shot alternative
For single-turn invocations where you don't need conversation history, skip the context:
const r = await agent.run("What is the ICD-10 code for asthma?");
console.log(r.text); // no context created or tracked
agent.run() internally creates a throwaway context. Use it when you don't need to continue the thread.
Full code
Source: examples/ts/01-hello-agent.ts
/**
* 01 — Hello, agent.
*
* The minimum viable example: create an agent, start a conversation,
* read the reply. Ephemeral agents are GC'd server-side — no explicit
* cleanup is needed. Pass `lifecycle: "persistent"` if you want the
* agent to outlive the process.
*
* Run: `npm run hello`
*/
import { AgentsClient } from "@corti/agent-sdk";
import { makeClient } from "./_client";
async function main() {
const agents = new AgentsClient(makeClient());
const agent = await agents.create({
name: "hello-agent",
description: "A minimal greeting agent.",
systemPrompt: "You are a friendly assistant. Keep replies to one sentence.",
});
const ctx = agent.createContext();
const reply = await ctx.sendText("Say hello and tell me one fun fact.");
console.log("Agent:", reply.text);
console.log("Status:", reply.status);
console.log("Context ID:", ctx.id);
// The context persists across calls — the agent remembers the thread.
const followUp = await ctx.sendText("Tell me another one.");
console.log("Agent:", followUp.text);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
What to expect
Actual text varies by model, but the structure is consistent: