TypeScript
The TypeScript client: install, authenticate, make the first call, and reach the four verbs from your own code.
Install
The package is not published yet. Through the beta it runs from source, out of sdk/typescript in the repository:
cd sdk/typescript
npm install
npm run generate
npm run generate writes the typed client from the contracts. It needs no network. Run it again after pulling a change to the contracts.
When the package is published, this section becomes one npm install line and nothing else on this page changes.
Authenticate
import { createAutomatonClient } from "@rudrite/automaton";
const atmon = createAutomatonClient({
baseUrl: "https://api.atmon.ai",
apiKey: process.env.ATMON_API_KEY ?? "",
});
The key rides on every request on every call. It resolves to one project, and that project scopes everything you can read or write.
Construction throws on an empty key or on an address that is not an http or https URL, so a missing environment variable is a startup failure rather than a mystery on the first call. A key the server rejects comes back as an error with the code unauthenticated.
If you run atmon on your own machines, baseUrl is your own node's address instead.
The first call
Find the tool by describing the task:
const { resolutionId, matches } = await atmon.searchTools(
"file a bug about the failing build",
{ entityId: "user-42", limit: 5 },
);
entityId names the person you are acting for. What comes back is scoped to what that person has connected.
Read the tool, when the compact schema in the match is not enough:
const tool = await atmon.describeTool(matches[0].toolSlug);
Then act:
const call = await atmon.callTool(
"github.create_issue",
{ owner: "rudrite", repo: "automaton", title: "build is red" },
{ entityId: "user-42", resolutionId, idempotencyKey: "issue-build-red-1" },
);
args is a plain object; the helper encodes it. entityId is required. idempotencyKey makes a retry return the original call rather than acting twice. confirm clears the gate on a destructive tool and means the person was told what would change.
Read the answer
What comes back is the receipt, not a bare result. Check the status before you use it:
if (call.status === ToolCallStatus.SUCCEEDED) {
const result = JSON.parse(call.resultJson);
} else {
// errorCode is one of the stable codes; errorDetail says more
}
A destructive call with no confirmation comes back refused, with the reason in the detail, and nothing reached the app: the gate runs before any credential is unlocked.
Close the loop when you can. Reporting whether the tool you chose was the right one is what improves the next ranking:
await atmon.router.reportOutcome({ resolutionId, /* ... */ });
Each resolution is reportable once.
Hand over a whole job
More than about twenty rows, or work that outlives the request you are serving, belongs in a job rather than a loop:
const { job } = await atmon.jobs.submitJob({ program, idempotencyKey: "quarterly-invites" });
const state = await atmon.jobs.getJob({ id: job.id });
See Jobs for what a plan is made of and how to dry-run one first.
Everything else
Every call in the API reference is on the same object: atmon.catalog, atmon.connections, atmon.router, atmon.execution, atmon.jobs, atmon.traces, atmon.triggers, atmon.usage, and atmon.keys. They are standard typed clients, so methods are camelCase and fields follow the contract.
const { authorizationUrl } = await atmon.connections.initiateConnection({
entityId: "user-42",
toolkitSlug: "github",
});