TypeScript SDK
The TypeScript package is ctxmesh, bundled in the base-node agent image (Node 22). It is at
parity with the Python SDK — the same public surface, the same env/port contract,
and byte-for-byte identical trace trees — with async where the
Python calls are synchronous. Like Python, it takes no credentials: everything comes from the
launcher-injected environment. For when to use it, see the
SDK overview.
Constructing a client
Section titled “Constructing a client”import { agent } from "ctxmesh";
const client = agent.fromEnv(); // reads MODEL_GATEWAY_URL, MEMORY_PORT, AGENT_NAME, ... from envagent.fromEnv() returns a Client and throws NotInPodError when the launcher environment is
absent. For tests/offline, agent.fromConfig(config) builds one from an explicit PlaneConfig. The
Client class is also exported directly. The client exposes memory, tools, feedback, model,
knowledge, a trace client, and requestScope.
client.model — the gateway
Section titled “client.model — the gateway”Pass the ModelRoute name as model (agents choose the route by name — there is no route
reference on the agent):
const resp = await client.model.chat( "gpt-4o", // a ModelRoute name [{ role: "user", content: "Summarize this ticket." }], { timeout: 30_000 }, // ms);resp.text; // "" on a tool-call turnresp.toolCalls; // ToolCall[]resp.hasToolCalls; // booleanresp.usage; // { promptTokens, completionTokens, totalTokens }chat emits an OpenInference llm span automatically.
client.memory / client.knowledge
Section titled “client.memory / client.knowledge”await client.memory.append({ role: "user", content: "..." }, cid);const history = await client.memory.get(cid);await client.memory.remember("The customer prefers email.", { topic: "prefs" });const facts = await client.memory.searchAgent("contact preference", 5, 0.0);
client.knowledge.available(); // granted KnowledgeBase names (sync)const hits = await client.knowledge.search("refund policy", "docs", 10);// each hit: { content, documentRef, chunkIndex, startOffset, endOffset, mimeType, score }client.tools — MCP, delegate & handoff
Section titled “client.tools — MCP, delegate & handoff”const tools = await client.tools.list(); // live manifest + synthetic toolsconst result = await client.tools.call("search_web", { query: "ctxmesh" });A team supervisor also gets the synthetic delegate_to and handoff_to
tools (client.tools.delegate(...) / client.tools.handoff(...)), normally called by the model inside
the managed loop rather than by hand.
client.feedback
Section titled “client.feedback”await client.feedback.score(traceId, "helpfulness", 1.0, "clear");The managed loop and serve
Section titled “The managed loop and serve”import { agent, runManagedLoop, ManagedConfig } from "ctxmesh";
const client = agent.fromEnv();const config = ManagedConfig.fromEnv();const result = await runManagedLoop(client, config, "Where's my order?");result.output; // final textresult.steps; // iteration countresult.toolsCalled; // tool namesresult.consentRequired; // servers needing on-behalf-of consentresult.approvalRequired; // { key, summary } if pausedThe one-liner agent hands the whole lifecycle to the SDK:
import { serve } from "ctxmesh";
await serve(); // no handler → managed loop; serves /invoke, /healthz, /readyz on $AGENT_PORTserve(handler) — where handler(req: InvokeRequest) => string | Promise<string> | ManagedResult —
runs custom per-request logic, binding the request scope and trace context and handling SSE streaming
(req.emitToken, req.emitStep).
Request scope and approvals
Section titled “Request scope and approvals”A custom loop must bind requestScope so tool calls carry the invoking user’s
on-behalf-of credential (an AsyncLocalStorage-based relay); otherwise
they downgrade to org/public credentials:
await client.requestScope(req.headers, req.approvals, async () => { // tool calls here run on-behalf-of the invoking user});Human-in-the-loop:
import { pauseForApproval } from "ctxmesh";
pauseForApproval("refund", "Refund $250 to order #4821"); // throws ApprovalRequiredError if not grantedawait client.tools.call("issue_refund", { order: "4821" });Driving runs — RunsClient
Section titled “Driving runs — RunsClient”import { RunsClient } from "ctxmesh";
const runs = new RunsClient("https://console.example", { token: myBearer });const run = await runs.create({ agent: "support", input: "hi", namespace: "team-a" });for await (const event of runs.stream(run.id)) { // { seq, kind, data } // kind in state | message | token | step}await runs.resume(run.id, { decision: "approve" });await runs.cancel(run.id);const done = await runs.run({ agent: "support", input: "hi" }); // create + poll to terminalErrors
Section titled “Errors”CtxmeshError base; NotInPodError / ConfigError, EndpointError, GuardrailBlockedError,
ApprovalRequiredError, ConsentRequiredError — mirroring Python.
See also
Section titled “See also”- SDK overview · Python SDK — the parity package
- Custom agent loop — traced steps without a framework
- The launcher contract · Runs & execution
- Launcher endpoints · HTTP API