Quickstart
See JamJet block an unsafe tool call in 60 seconds, then scaffold and run your first governed agent.
Quickstart
In 60 seconds, see JamJet block an unsafe tool call before execution.
pip install jamjet
jamjet demo unsafe-tool-callYou'll see the blocked tool, the matched policy rule, and an audit JSON file
written under .jamjet-demo/runs/. No API key. No cloud account. The model is
mocked; the enforcement path is real.
Three more demos:
jamjet demo approval # pause for human approval
jamjet demo budget-cap # $0.05 cost cap
jamjet demo mcp-tool-policy # MCP-shaped policyOnce the demos make sense, scaffold a real agent.
Looking for JamJet Cloud (governance, dashboard, audit trail)? See the Cloud Quickstart instead.
1. Install
pip install 'jamjet[model]'The [model] extra pulls in the model seam so agent.run() can call a provider.
The Java SDK is published on Maven Central as dev.jamjet:jamjet-sdk.
Prerequisites: Java 21+ and Maven 3.9+ or Gradle 8+.
Maven:
<dependency>
<groupId>dev.jamjet</groupId>
<artifactId>jamjet-sdk</artifactId>
<version>0.5.0</version>
</dependency>Gradle (Kotlin DSL):
implementation("dev.jamjet:jamjet-sdk:0.5.0")Make sure your project targets Java 21 or later. In Maven:
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>2. Scaffold an agent
jamjet create my-agent
cd my-agentThis writes a minimal project: agent.py, pyproject.toml, and a README. The
generated agent.py is a governed Agent:
# agent.py
import asyncio
from jamjet import Agent, tool
@tool
async def add(a: float, b: float) -> str:
"""Add two numbers and return the sum."""
return f"{a + b:g}"
agent = Agent(
"my-agent",
model="anthropic/claude-sonnet-4-6",
tools=[add],
instructions="You are a helpful assistant. Use the add tool when asked to sum numbers.",
strategy="react",
)
async def main() -> None:
result = await agent.run("What is 7 plus 35?")
print(result.output)
if __name__ == "__main__":
asyncio.run(main())The @tool decorator exposes a typed Python function to the agent, and the
Agent ties a model, tools, instructions, and a reasoning strategy together.
Governance (PII redaction, signed audit, and receipts) is on by default. See
Build an agent and Governance.
Define a tool, the bridge between your agent and the outside world:
import dev.jamjet.tool.Tool;
import dev.jamjet.tool.ToolCall;
@Tool(description = "Search the web for information about a topic")
record WebSearch(String query) implements ToolCall<String> {
public String execute() {
// In production, call your search API here
return "Results for '" + query + "': JamJet is a safety layer "
+ "for AI agents.";
}
}Build an agent with a reasoning strategy and runtime-enforced limits:
import dev.jamjet.agent.Agent;
var agent = Agent.builder("researcher")
.model("claude-haiku-4-5-20251001")
.tools(WebSearch.class)
.instructions("You are a helpful research assistant. "
+ "Always search first, then provide a thorough summary.")
.strategy("react")
.maxIterations(5)
.build();3. Run it in-process
agent.run() runs the loop in your own process, with no server to start. It
needs only a model provider key:
export ANTHROPIC_API_KEY=sk-ant-...
python agent.pypublic static void main(String[] args) {
var agent = Agent.builder("researcher")
.model("claude-haiku-4-5-20251001")
.tools(WebSearch.class)
.instructions("You are a helpful research assistant. "
+ "Always search first, then provide a thorough summary.")
.strategy("react")
.maxIterations(5)
.build();
var result = agent.run("What is JamJet?");
System.out.println(result.output());
System.out.printf("Tool calls: %d%n", result.toolCalls().size());
}export ANTHROPIC_API_KEY=sk-ant-...
mvn compile exec:java -Dexec.mainClass=com.example.MyAgent4. Run it on the durable engine
The same agent runs on the event-sourced JamJet engine. A durable run dispatches
your @tool functions on a separate worker process, and the worker resolves them
by importing the module they live in. So the tools must be importable: call
run_durable() from a small runner that imports the agent, rather than running
agent.py directly (where the tools would belong to __main__).
# run.py
import asyncio
from agent import agent # imports agent.py, where the @tool functions live
async def main() -> None:
result = await agent.run_durable("What is 7 plus 35?")
print(result.output)
if __name__ == "__main__":
asyncio.run(main())The durable stack runs a model sidecar, so install the sidecar extra first:
pip install 'jamjet[sidecar]'Then start the local dev stack with one command, pointing the worker at your
tool module so it can resolve the @tool functions:
jamjet dev --modules agentjamjet dev brings up the model sidecar (health-gated), the durable engine on
http://127.0.0.1:7700, and the python_tool worker. Press Ctrl+C to stop the
whole stack.
Then run the runner in a second terminal:
export ANTHROPIC_API_KEY=sk-ant-...
python run.pyEvery turn is recorded to SQLite, so you can inspect and replay the run. When the
worker picks up the run, the jamjet dev logs print its execution id on a
Claimed ... exec=exec_... line. Copy that id (it looks like exec_...) and pass
it to jamjet inspect and jamjet events:
jamjet inspect exec_...
jamjet events exec_...The run is governed the same way as in-process, and the durable engine adds the
event log, deterministic replay, idempotency, and content-addressed artifacts.
See Reliability. The
react-agent-durable
example shows the full setup.