Agent SDK
The Syntic Agent SDK is a library for building your own agents in Python or TypeScript. It gives you the parts every agent needs (the model loop, tools, permission checks, context management, sub-agents and session storage) so you write only what is specific to your agent: its instructions and its tools.
It works in two modes with the same code:
| Mode | Where the loop runs | Where your tools run | Use it when |
|---|---|---|---|
| Local | In your process | In your process | Tools must touch your files, network or databases directly; you want full control. |
| Managed | On Syntic, as a session | Syntic-run tools on Syntic; your function tools in your process, called back over the session stream | You want Syntic to keep history, run built-in tools and environments, and show runs in the Console. |
Switching modes is one constructor argument.
Where it fits
| You want | Use |
|---|---|
| A single model call, or a simple tool loop | The Python or TypeScript SDK’s tool runner |
| An agent defined and run entirely on Syntic, with no code of your own in the loop | Agent definitions and sessions through the API |
| A custom agent with your own tools, permission rules, hooks and sub-agents | The Agent SDK |
| A ready-made coding agent | Syntic Code |
Install
pip install syntic-agents # Python 3.10+
npm install @syntic/agents # Node.js 18+Both depend on the matching Syntic SDK and read SYNTIC_API_KEY.
A first agent
import asyncio
from syntic_agents import Agent, tool, Permissions
@tool
async def read_invoice(invoice_id: str) -> dict:
"""Return an invoice's lines and totals."""
return await billing_db.fetch_invoice(invoice_id)
@tool(requires_approval=True)
async def issue_credit(invoice_id: str, amount: float, reason: str) -> str:
"""Issue a credit note against an invoice."""
return await billing_db.credit(invoice_id, amount, reason)
agent = Agent(
name="billing-helper",
model="Amara S1.2",
instructions="You answer billing questions. Check the invoice before replying.",
tools=[read_invoice, issue_credit],
permissions=Permissions(ask=["issue_credit"]), # pause for a person before crediting
max_turns=20,
)
async def main():
async for event in agent.run("Customer says invoice INV-2201 double-charged shipping."):
if event.type == "approval.required":
await event.approve() if input(f"Allow {event.name}{event.arguments}? [y/N] ") == "y" else await event.reject("Declined")
elif event.type == "message.completed":
print(event.content)
asyncio.run(main())To run the same agent in managed mode, add mode="managed" (Python) or mode: 'managed' (TypeScript). The SDK creates or updates the agent definition by name, starts a session, and runs your function tools locally when the session asks for them.
Building blocks
| Piece | What it does |
|---|---|
Agent | Model, instructions, tools, limits. run() yields typed events; run_sync() / runToCompletion() returns the final result. |
tool | Turns a function into a tool; schema from type hints (Python) or Zod / JSON Schema (TypeScript). Arguments are validated before your function runs. |
| Built-in tools | web_search, web_fetch, code_execution (Syntic-run), plus local read_file, write_file, edit_file, shell and glob limited to a working directory you choose. |
Permissions | Rules per tool: allow, ask, deny, with argument patterns (for example allow shell only for git status and npm test). Rules are checked before every tool call. |
| Hooks | Functions called before and after each tool call and model call, to log, change arguments, or block. |
| Sub-agents | An agent can be given other agents as tools; each runs with its own instructions, tools and context, and returns a summary. |
| Context management | Long runs are kept inside the context window by summarising old turns and trimming large tool results, at a threshold you set. |
| Sessions | agent.run(..., session=store) saves history to a store (in memory, a local file, or Syntic in managed mode) so a later run resumes the conversation. |
| MCP | Attach remote MCP servers by URL; their tools appear alongside yours. |
| Tracing | Each run produces a trace with every model call, tool call, approval and token count. In managed mode it is also an execution in the Console. |
Events
run() yields the same event types as the session stream, in both modes: status.changed, message.delta, message.completed, tool_call.started, tool_call.completed, approval.required, usage.updated and error. Code written against local mode keeps working in managed mode.
Errors and limits
- API errors surface as the SDK’s typed exceptions (see Python SDK).
- A run stops with a final
status.changedwhose outcome ismax_turns,budget_exhaustedorerrorwhen a limit is hit; it never stops silently. max_turns,max_output_tokens_per_turnand a token or cost budget are set on the agent and can be lowered per run.- Billing is the same as any API traffic: the model tokens each run uses, plus built-in tool fees.
Build notes for the platform team
- Depends on managed agents, sessions and the native SDKs; build after those are stable.
- The event types are the session stream’s types, so one set of SDK types covers both modes.
- A later phase can expose the Syntic Code engine itself as a library behind the same interface; design the
Agentsurface so that swap is invisible to users.