Agent integration: typed tool routing
Use System One Choice for next-step tool routing in TypeScript agents while you keep permissions and execution local.
Use the System One TypeScript SDK and hosted AI decision API to route an agent request to a named tool or review step. To teach Cursor, Claude Code, or Codex how to write that integration, install the skill on Use System One with coding agents. This guide builds a support-request router: the model selects an option, while your application controls whether and how it runs. The standalone SDK can also connect directly to other services with their own credentials and billing; see SDK installation and provider connections.
Answer-ready
Q: How do I integrate System One with an AI agent for tool routing?
A: Call Choice with the agent state and an allowlisted set of tool names; use the typed selection in your own code to invoke the tool. System One decides; your app keeps permissions and execution.
Q: Does System One execute tools for me?
A: No. The System One API returns a structured decision. Execution and permission checks remain in your agent runtime.
Choose a focused judgment
Use the decision interface when your application can state the question and possible outcomes: route a message, choose among allowed next steps, rate an answer against a rubric, or estimate whether a condition holds. Several questions can independently inspect one shared context.
| Task | Model judgment | Application responsibility |
|---|---|---|
| Triage messages | Choose a reviewing team | Check who can access the message and assign work. |
| Review an answer | Score it against an ordered rubric | Verify facts and approve the final response. |
| Detect a refund request | Estimate whether a refund is requested | Check the order, apply policy, and authorize any refund. |
| Suggest a next step | Answer, gather information, or seek review | Execute only allowed actions with the required permissions. |
System One does not gather missing information, issue refunds, send replies, or invoke tools for you. Open-ended planning belongs in another reasoning step or with a person. Include explicit think or review outcomes when appropriate.
Choose an exact ID from the current model directory. Evaluate task quality, latency, price, and limits on representative cases, including ambiguous wording, missing information, and consequential mistakes. Do not treat a model name or one fast result as a guarantee. Re-evaluate when the model, instructions, options, or rubric changes; SDK adapter support is not the hosted model catalog.
Define the tool routing boundary
Start with a small allowlist, not every tool your agent can access. For a support assistant, search_docs retrieves product guidance, ask_user requests missing information, and human_review hands a consequential or disputed request to a person. Tool descriptions should say when to select each option, not contain credentials or instructions that grant permissions.
The code below returns one of these names; it does not implement document search, send a message, or create a review ticket. Connect the result to your existing tool dispatcher only after applying the permission checks below. Never let a model-generated name, URL, or shell command bypass the allowlist.
Choice fits this mutually exclusive next-step decision. If you need to rank several retrieved documents or evaluate independent conditions, use Score or Noul instead. If the model needs a search result before making another judgment, execute the permitted search first and send a separate evaluation with that result as context.
Authenticate outside model context
Create a platform API key in the signed-in console. Store it in a server environment variable or trusted private credential setting. Calls use Authorization: Bearer against https://system-one.dev/v1; the docs host does not serve inference. Keep keys out of prompts, tool descriptions, browser bundles, URLs, and logs. Revoke a key when its connection is no longer needed.
GET /v1/models is public and does not require the platform key. Playground and browser account-management endpoints instead use your signed-in session; a Bearer key does not replace that session. Keys on an account share its balance, rate limit, and idempotency namespace. Do not give separate agents keys expecting isolated balances or independent duplicate protection.
Add one typed decision step
Install the server-side packages:
npm install @system-one-ai/core@0.8.0 @system-one-ai/adapter-system-one@0.7.0 @system-one-ai/transport-fetch@0.8.0This is the current hosted client stack. For image-based agent decisions, select an image-capable Clef/Clef Flash/Clef Omni model and use the hosted Fetch example; systemOneAdapter does not encode evaluate() images. Persist the exact image representation and array order with the operation, include base64 in the credit estimate, and review image content for sensitive information. For typed extraction from text or images, use Clairic extract().
Use the environment setup from the hosted quickstart: API key, base URL, and model only. Replace YOUR_MODEL_ID with the selected directory ID. The example creates a fresh operation key in code, once per new decision—not in shared configuration or a retry function.
This TypeScript example selects a proposed tool; it does not execute it:
import { createSystemOne, choice } from '@system-one-ai/core';
import { createFetchTransport } from '@system-one-ai/transport-fetch';
import { systemOneAdapter } from '@system-one-ai/adapter-system-one';
const apiKey = process.env.SYSTEM_ONE_API_KEY;
const baseURL = process.env.SYSTEM_ONE_BASE_URL;
const model = process.env.SYSTEM_ONE_MODEL;
if (!apiKey || !baseURL || !model || model === 'YOUR_MODEL_ID') {
throw new Error('Set the API key, base URL, and catalog model.');
}
const client = createSystemOne({
apiKey,
baseURL,
model,
adapter: systemOneAdapter,
transport: createFetchTransport(),
timeoutMs: 30_000,
maxRetries: 0,
});
const operation = {
idempotencyKey: crypto.randomUUID(),
request: {
model,
state: { message: 'Can you explain how credits are calculated?' },
questions: {
tool: choice('Choose the next support tool. Do not execute it.', {
search_docs: 'A product or usage question can be answered from documentation',
ask_user: 'Essential details are missing; ask the user before proceeding',
human_review: 'A payment dispute, sensitive action, or policy exception needs review',
}),
},
},
};
const result = await client.evaluate(operation.request, {
headers: { 'Idempotency-Key': operation.idempotencyKey },
signal: AbortSignal.timeout(30_000),
});
const proposedTool: 'search_docs' | 'ask_user' | 'human_review' = result.answers.tool.choice;
console.log({ proposedTool, requestId: result.response.requestId });To run, save as agent-decision.mts and use node --env-file=.env agent-decision.mts with Node.js 22.18+. Every script run starts a new billable operation, even with the same input. This in-memory example is not restart-safe recovery.
Creation and retry are separate steps: an in-process retry calls evaluate() with this same operation.request and operation.idempotencyKey, without rerunning the operation-creation block. In an actual job system, durably save the whole operation in the application's job record before calling evaluate(), including its key, selected model, and complete request. After a crash or restart, load that record rather than generating a UUID or rebuilding the request from a newer prompt or current model selection. The quickstart does not provide this durable storage. Each genuinely new decision gets a new key, including an intentional reevaluation of identical input. Set your own total deadline, attempt limit, and spending policy; timeout or cancellation is not proof the service failed to finish.
State should contain only needed information, never secrets or unrelated private history. A state array is a single shared input, not a batch of separate evaluations. Questions do not consume one another's answers; if a next judgment depends on a previous one, review it and send a separate request. See primitives for the SDK/native boolean and noul distinction.
Separate suggestions from permissions
A model result is not authorization. Check the acting user's identity and permissions, action allowlists, and required approvals outside the model. Detecting a refund request does not establish eligibility. Payments, access changes, and external messages must retain the confirmations your policy requires.
Start with suggestions rather than automatic execution. Define a review path for ambiguous, conflicting, or incomplete evidence. Choose thresholds from your own evaluation cases and consequences, not a universal percentage. Missing answers or failed requests are unresolved, not an automatic yes, no, or lowest score. Keep every connection's permissions limited to what it needs; see data and privacy.
Evaluate the router before enabling tools
Build a labeled set from your own support cases. The following are review targets for this example, not measured model outputs:
| Input | Intended handling | What to check |
|---|---|---|
| “How are credits calculated?” | Search the billing documentation | Retrieved text answers the question; no account mutation is needed. |
| “It does not work.” | Ask for the failing step and error | No guessed account details or tool arguments. |
| “You charged me twice; refund the second payment.” | Human review | A claim is not proof of a duplicate charge or permission to refund. |
Include ambiguous requests and instructions that try to force another tool. Measure wrong-tool selections and missed review cases separately; neither a successful HTTP response nor a high model probability proves safe routing. Start in suggestion-only mode, then decide which read-only actions can run under your existing policy. Track model, option descriptions, request IDs, and outcome labels without logging keys or unnecessary personal data.
Follow the hosted SDK quickstart for connection and response metadata, and API pricing for credit packs. Every new evaluation is a separate decision operation; review billing before running a large evaluation set.
Recover before starting a new operation
Start with maxRetries: 0. Core APIError provides statusCode, requestId, and retryAfterMs, but not all platform classification headers. A recovery controller can use raw Fetch, or the official TypeSafe client's APIError.headers, to inspect X-System-One-Error-Code, X-System-One-Error-Source, X-Request-Id, Retry-After, and X-System-One-Credits. Do not classify failures by arbitrary body text. The official client quickstart covers success headers.
| Observed result | Application response |
|---|---|
| Connection lost, timeout, or unknown outcome | Recover with the saved key and identical effective request within the retention window. Do not infer a refund. |
409 request_in_progress | Respect Retry-After, then use the same key and request. |
| Successful replay | Use the answer. X-Idempotency-Replayed: true and a zero charge header identify the replay; no new evaluation was charged. |
409 idempotency_failed | The prior attempt failed and was refunded. A deliberate new attempt needs a new key. |
409 idempotency_conflict | Restore the original request, or use a new key only for a genuinely different operation. |
429 rate_limit_exceeded | Wait for the account limit to reset, then retry the same operation. provider_busy is a distinct classification; follow its documented recovery guidance. |
503 request_reconciliation_pending, 410 idempotency_expired, or another unresolved failure | Preserve identifiers and inspect account usage before trying another operation. Missing credit headers do not mean zero charge. |
| Authentication, input, or insufficient-credit error | Correct the cause before trying again. Inspect classification headers; do not assume any 401 means the browser session expired. |
Retry-After can be seconds or an HTTP date. Bound retries by an application deadline and attempt count, and use backoff with jitter when no delay is given. Preserve the effective model, rubric order, extensions, and numeric values. Do not silently fall back to another model during recovery. A new key can create another charge; a changed body with the old key conflicts.
Completed keys and answers are retained for 24 hours from request creation. After expiry, the same key can begin a new operation; this is not permanent deduplication. Check idempotency and retries, errors, and versioning for the full contract.
Decision recovery does not prevent a duplicate tool action. Your application needs separate durable execution state and idempotency for sending a reply, making a payment, or assigning work. If the result or charge stays uncertain, pause and inspect usage; contact support@system-one.dev with the request ID, never the API key.
Decision primitives
Pick Choice, Score, or yes-no probability for agent routing, ranking, and binary classification — with a shared example.
System One for coding agents (Claude Code, Cursor, Codex)
Install the System One agent skill and paste a prompt for classification, tool routing, or completion checks in Claude Code, Cursor, or Codex.