Shipping the TypeScript SDK · typed Conflict
@agentdraftio/sdk is live on npm. What the install gives a TypeScript agent, why losing a race is a typed exception, and one honest detour.
TL;DR. npm install @agentdraftio/sdk works. The SDK exposes a four-method surface — availability, bookings, agents, mailbox — and surfaces a lost race as a typed Conflict exception with the winning agent's id and priority on it. That last part is on purpose. A coordination layer whose SDK papers over conflicts has no point; the losing agent needs to know it lost so it can do something intelligent instead of retrying blindly.
This post: what the install gives you, why the error hierarchy is shaped the way it is, the npm-scope detour worth being honest about, and what's next.
What the install gives you
npm install @agentdraftio/sdk
ESM-only, Node 18+, ~8.7kB tarball. The package exports one client class, five typed exceptions, and the TypeScript declarations for every request and response on the AgentDraft protocol.
import { AgentDraft, Conflict } from "@agentdraftio/sdk";
const client = new AgentDraft({ apiKey: process.env.AGENTDRAFT_API_KEY! });
try {
const booking = await client.bookings.commit({
start, end,
idempotencyKey: "ik_call_42",
});
} catch (e) {
if (e instanceof Conflict) {
console.log(`outranked by ${e.winningAgentId} (rank ${e.winningAgentPriority})`);
} else {
throw e;
}
}
Four call surfaces on client:
client.availability— read the merged availability of every agent and external source on a calendar.client.bookings—hold,release,commit,cancel. The four state transitions the engine arbitrates.client.agents.me()— confirm the API key, current priority rank, and granted scopes. The first call you'll write while wiring the SDK up.client.mailbox— the inbound/outbound mail surface for agents that book via email instead of HTTP.
idempotencyKey is a first-class argument on bookings.commit({...}), not a header you have to remember to set. The server caches the result by (agentId, key) for 24 hours, so a network blip that triggers a retry returns the original booking instead of creating a duplicate.
Why Conflict is typed on purpose
The interesting decision in an SDK for a coordination layer is what to do when the engine returns a 409. The lazy thing is to fold it into a generic ApiError and let the caller catch (e) { retry() }. The correct thing is the opposite: surface the loss as a structured, typed exception, with the winner's identity attached, so the losing agent can react intelligently.
Conflict carries four fields:
winningAgentId— who beat you to the slot.winningAgentPriority— the rank that won. Compare to your own.winningBookingId— the canonical booking on the calendar now.auditEventId— the audit-log row your agent can quote when asked "why did you fail?"
That handful of fields is what unlocks the design space. A losing agent that knows it was outranked by a higher-priority sales bot can propose an alternate slot to the human, or escalate, or defer to tomorrow. A losing agent that catches an opaque error retries — and the retry hits the same conditional write and fails the same way, forever. That's bot ping-pong, and it's the failure mode every multi-agent calendar product ends up shipping when the SDK hides the shape of the failure.
The full error hierarchy is the same in TypeScript and Python by design:
Conflict— you lost a race. Handle deliberately. (See above.)AuthError— bad, missing, or expired API key. Surface to the operator, not the LLM.RateLimited— token bucket exhausted. CarriesretryAfterSeconds. Respect it.RuleViolation— the booking was syntactically valid but violated a user-defined rule (outside business hours, blocked slot). Not retryable; needs a different slot.ApiError— base class. Only catch this at the top of your agent's loop, as a last resort.
If your code catches ApiError and retries unconditionally, the SDK is doing less work for you than fetch would. The types exist so each case gets the handler it deserves.
A detour worth being honest about
The npm scope @agentdraft was held by an inactive account when we went to publish — zero packages, zero downloads, no evidence of active product use. We filed a dispute with npm support under their inactive holder policy and shipped in the meantime as @agentdraftio/sdk, which matches the domain agentdraft.io exactly.
The trade-off: a slightly longer install command, for as long as the dispute takes to resolve (npm gives the current holder a 30-day contact window before transferring, so realistic timeline is 4–8 weeks). The alternative was waiting and missing the build-in-public moment. If the dispute succeeds we'll publish under both names and deprecate one to the other.
A few hours of friction; honest detour.
What's next on the SDK roadmap
- Provenance restoration. v0.1.0 shipped without
npm --provenancebecause Sigstore attestation only supports github-hosted runners and our publish workflow currently runs on self-hosted. Moving the workflow restoresnpm audit signaturesfor v0.1.1+. - Microsoft 365 sync. Graph delta + subscription channels behind the same calendar abstraction, so the SDK's "any calendar" story matches the spec.
- Google Calendar production cutover. The OAuth client config is the last owner-action gating the cohort recruit. Engineering is done; awaiting credentials.
The SDK surface won't change for any of these. Adding a calendar provider is a server-side change; the four methods on client are deliberately stable. The public changelog tracks each release as it ships.
Try it
npm install @agentdraftio/sdk
- TypeScript SDK quickstart — install command, upgrade command, and a live commit example
- Quickstart — five-minute integration against a live AgentDraft
- Protocol spec — what the SDK actually implements
- Benchmark — what "correct" looks like under 500 concurrent agent writes
If you've been building an AI agent that touches calendars and you've hit the multi-agent collision problem firsthand, we'd like to hear which of the four patterns from the earlier post is the one ruining your week.
— agentdraft.io · v0.2
Liked this? One short note every other Tuesday.
Conflict-engine post-mortems, new endpoints, the rare opinion. No tracking pixels.
Double opt-in — you'll get a confirmation link. Unsubscribe in one click.