SDKs
The SDKs wrap the control plane’s REST API in typed methods, so you can start runs, hold sessions and script bulk operations from your own code instead of hand-writing HTTP. There are three: TypeScript (the fullest, and the only one that also writes agent code), Python (async, at management parity with Go) and Go. Pick your language in any tab; every tabbed example on the site follows it. For the raw endpoints see REST API.
| SDK | Package | Import | Notes |
|---|---|---|---|
| TypeScript | @pal-runtime/sdk |
PalClient |
Event streams, retries, agent authoring (agent, step) |
| Python | pal-runtime |
from pal import PalClient |
Async; runtime context (ctx.*) is stubbed |
| Go | github.com/dshakes/pal/packages/sdk-go |
package pal |
Retries; no run event stream yet |
Every sample here was checked against the SDK source on master (packages/sdk-ts, packages/sdk-python, packages/sdk-go).
Install
Section titled “Install”# in your checkout( cd packages/sdk-ts && npm install && npm run build )# in your project: a file path, not the registrynpm install /path/to/checkout/packages/sdk-ts# Python 3.11+, from the root of your checkoutpip install ./packages/sdk-python# optional: LangChain deepagents sandbox backendpip install "./packages/sdk-python[deepagents]"go get github.com/dshakes/pal/packages/sdk-goClient setup
Section titled “Client setup”All three read PAL_API_KEY and PAL_API_URL when you pass nothing. The built-in default URL is not a live service, so always set PAL_API_URL to your own control plane, for example http://localhost:8080.
import { PalClient } from "@pal-runtime/sdk";
const client = new PalClient({ apiKey: process.env.PAL_API_KEY, baseUrl: process.env.PAL_API_URL ?? "http://localhost:8080",});from pal import PalClient
# Every request method is async (httpx.AsyncClient underneath).async with PalClient( api_key="...", # or PAL_API_KEY base_url="http://localhost:8080", # or PAL_API_URL) as client: ...import pal "github.com/dshakes/pal/packages/sdk-go"
c := pal.New( pal.WithBaseURL("http://localhost:8080"), // or PAL_API_URL pal.WithAPIKey(os.Getenv("PAL_API_KEY")),)Create a run and read the result
Section titled “Create a run and read the result”import type { Run } from "@pal-runtime/sdk";
const run = (await client.runs.create({ agent: "research-agent", input: { topic: "quantum computing" },})) as Run;
// Live events until the run endsfor await (const event of client.runs.events(run.id)) { console.log(event.kind, event.stepId, event.data);}
const done = await client.runs.get(run.id);console.log(done.status, done.output);run = await client.runs.create(agent="research-agent", input={"topic": "AI safety"})print(run.id, run.status) # queued
async for event in client.runs.events(run.id): print(event)
done = await client.runs.get(run.id)r, err := c.CreateRun(ctx, pal.RunOptions{ AgentName: "research-agent", Input: map[string]string{"topic": "quantum computing"},})if err != nil { log.Fatal(err)}done, _ := c.GetRun(ctx, r.ID) // poll: the Go SDK has no event stream yetfmt.Println(done.Status, string(done.Output), done.CostUsd)Event kinds you will see on the stream
step_started, step_completed, step_failed, tool_call, tool_result, llm_delta, log, approval, confidence_evaluated and end. The stream closes when the run is succeeded, failed or cancelled. See Token streaming.
Forecast cost before running
Section titled “Forecast cost before running”const f = await client.runs.forecast({ agentName: "triage", input: "invoice is wrong" });if (f.wouldExceedBudget) console.log("blocked:", f.blockReason);f = await client.runs.forecast("triage", "invoice is wrong")print(f)f, err := c.ForecastRun(ctx, "triage", "invoice is wrong")fmt.Printf("$%.4f, %.0f%% confident, block=%v\n", f.EstimatedCostUsd, f.Confidence*100, f.WouldExceedBudget)A hard-fail budget then rejects the real run with HTTP 402.
Interactive sessions
Section titled “Interactive sessions”const session = await client.sessions.create({ agent: "my-agent" });
for await (const chunk of client.sessions.streamMessage(session.id, { content: "What is the capital of France?",})) { if (chunk.type === "delta") process.stdout.write(chunk.delta); if (chunk.type === "completed") console.log("\nusage:", chunk.usage);}
await client.sessions.delete(session.id);session = await client.sessions.create(agent="my-agent")await client.sessions.send_message(session.id, content="What is the capital of France?")async for event in client.sessions.stream_events(session.id): print(event) # the reply arrives hereawait client.sessions.delete(session.id)s, _ := c.CreateSession(ctx, "my-agent", nil)_ = c.SendSessionMessage(ctx, s.ID, "What is the capital of France?")_ = c.DeleteSession(ctx, s.ID)Errors and retries
Section titled “Errors and retries”import { PalApiError } from "@pal-runtime/sdk";
try { await client.runs.create({ agent: "my-agent", input: {} });} catch (err) { if (err instanceof PalApiError && err.status === 402) { // a hard-fail budget blocked the run }}from pal import PalApiError
try: await client.runs.create(agent="my-agent", input={})except PalApiError as e: if e.status == 402: ... # a hard-fail budget blocked the runvar apiErr *pal.APIErrorif errors.As(err, &apiErr) && apiErr.Status == http.StatusPaymentRequired { // a hard-fail budget blocked the run}The TypeScript and Go clients retry HTTP 429, 503 and network errors with full-jitter backoff (TypeScript: 3 attempts, 500 ms base, 4 s cap; Go: WithMaxRetries). Other 4xx and 5xx are never retried.
Every namespace
Section titled “Every namespace”client.agents // create, get, list, deleteclient.runs // create, get, list, cancel, events, signal, forecastclient.sessions // create, get, list, sendMessage, streamMessage, events, stop, delete, closeclient.connectors // list, get, execute, test, uninstallclient.budgets // upsert, get, list, deleteclient.triggers // create, list, get, update, deleteclient.evalSuites // upsert, list, get, deleteclient.evalRuns // record, listclient.evalBaselines // set, getclient.experiments // create, list, get, recordOutcome, concludeclient.marketplace // list, get, publish, fork, star, unstarclient.mcp // listServers, getServer, attach, listAttachments, detachclient.receipts // issue, verifyclient.feedback // submit, listForRun, summaryForAgentclient.rehearsals // pullclient.runtime // schedule, list, get, terminate, exec, execStream, pause, resume, fork, listFiles, readFile, writeFileclient.agents # create, get, list, deleteclient.runs # create, get, list, cancel, events, signal, forecastclient.sessions # create, get, list, send_message, stream_events, stop, delete, closeclient.connectors # list, get, execute, list_actionsclient.budgets # upsert, get, delete, list_allclient.triggers # create, list, get, update, deleteclient.evals # upsert_suite, list_suites, get_suite, delete_suite, record_run, list_runs, set_baseline, get_baselineclient.experiments # create, list, get, record_outcome, concludeclient.marketplace # list, get, publish, fork, star, unstarclient.mcp # list_servers, get_server, attach, detach, list_attachmentsclient.receipts # issue, verifyclient.feedback # submit, list_for_run, summary_for_agentclient.rehearsals # pullclient.runtime # schedule, list, get, terminate, exec, exec_stream, pause, resume, fork, list_files, read_file, write_file// Agents CreateAgent GetAgent ListAgents DeleteAgent// Runs CreateRun GetRun ForecastRun// Sessions CreateSession GetSession ListSessions SendSessionMessage StopSession DeleteSession// Connectors InstallConnector ListConnectors GetConnector TestConnector UninstallConnector ExecuteConnectorAction// Budgets UpsertBudget GetBudget DeleteBudget// Triggers CreateTrigger ListTriggers GetTrigger UpdateTrigger DeleteTrigger// Evals ListEvalSuites UpsertEvalSuite RecordEvalRun SetEvalBaseline// Experiments CreateExperiment RecordExperimentOutcome ConcludeExperiment// Marketplace ListMarketplace PublishAgent ForkAgent// MCP ListMCPServers AttachMCPServer DetachMCPServer// Receipts IssueReceipt VerifyReceipt// Feedback SubmitRunFeedback GetAgentFeedbackSummary// Rehearsals Rehearse// Sandboxes ScheduleVM PauseVM ResumeVM ForkVM ListFiles ReadFile WriteFileSandbox VMs are covered in Sandbox lifecycle.
Writing the agent itself
Section titled “Writing the agent itself”The SDKs above manage agents from outside. Inside an agent bundle, the TypeScript SDK gives you ctx: durable steps, LLM calls, tools, memory and connectors. Every step is journaled, so a crashed run resumes instead of restarting.
import { agent, step } from "@pal-runtime/sdk";
export default agent<{ query: string }>({ name: "my-agent", model: "auto", async run({ input, ctx }) { // Durable step: journaled, idempotent, resumable after a crash const data = await step("fetch-data", () => ctx.tools.web.search(input.query));
// Addressed by capability; PAL maps it to a vendor model const summary = await step("summarize", () => ctx.llm.complete({ messages: [{ role: "user", content: `Summarize: ${data}` }], capability: "reasoning-small", }), ); return { summary }; },});Parallel fan-out and human approval
// Fan out: one journaled step per queryconst results = await step.map("search", queries, (q) => ctx.tools.web.search(q));
// Blocks until a human grants or denies; denial or timeout throwsawait ctx.approval.request({ reason: "Send this email to 500 users?", expiresAt: "2026-12-01T00:00:00Z",});Use capability names (auto, reasoning-large, chat-small, vision-large), never a vendor model. See Models.