@agentkeeper-ai/runtime-sdk
v0.1.0-beta.2
Published
AgentKeeper Agent Runtime SDK for app-embedded TypeScript agents.
Maintainers
Readme
AgentKeeper Agent Runtime SDK
Package for sending promptless TypeScript AI agent runtime events to AgentKeeper.
npm install @agentkeeper-ai/runtime-sdkThis pre-release package is published under the
@agentkeeper-ainpm org. Use the normal install command so npm resolves the currentlatestbuild. It supports TypeScript integrations for Vercel AI SDK, LangChain, LangGraph, AWS Bedrock Runtime, OpenAI Agents SDK, Azure OpenAI, Claude Managed Agents, Anthropic SDK, and custom agents. Python support lives in the siblingagentkeeper-runtime-sdkpackage for custom agents, LangChain, LangGraph, boto3 Bedrock Runtime clients, OpenAI Agents SDK, Azure OpenAI, Claude Managed Agents, and Anthropic SDK. Vercel AI SDK remains TypeScript-only.
Usage
import { createAgentKeeperRuntimeClient } from "@agentkeeper-ai/runtime-sdk";
const ak = createAgentKeeperRuntimeClient({
endpoint: "https://agentkeeper.dev",
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeEnvironment: process.env.VERCEL_ENV ?? "local",
runtimeIntegration: "vercel_ai_sdk",
});
ak.track({
event_kind: "runtime_heartbeat",
capability: "observe",
evidence_summary: "AI agent runtime connected",
});
await ak.flush();Vercel AI SDK
wrapVercelAISDK injects AgentKeeper telemetry metadata, wraps tool execute functions, and keeps raw prompt, message, tool argument, and model output fields out of AgentKeeper events.
import * as ai from "ai";
import { z } from "zod";
import {
createAgentKeeperRuntimeClient,
wrapVercelAISDK,
} from "@agentkeeper-ai/runtime-sdk/vercel-ai";
const ak = createAgentKeeperRuntimeClient({
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeIntegration: "vercel_ai_sdk",
});
const guardedAi = wrapVercelAISDK(ai, ak, {
evaluateTool: async () => ({ verdict: "passed" }),
});
const lookupCustomer = ai.tool({
description: "Look up a customer by id",
inputSchema: z.object({ customerId: z.string() }),
execute: async ({ customerId }) => ({ customerId, tier: "enterprise" }),
});
await guardedAi.generateText({
model: process.env.AI_MODEL ?? "openai/gpt-5.5",
tools: { lookupCustomer },
prompt: "Check customer risk.",
});
await ak.flush();You can also pass createVercelAITelemetry(ak) in experimental_telemetry.integrations if you already manage your own AI SDK calls.
OpenAI Agents SDK
wrapOpenAIAgentsRun observes run lifecycle without storing the input or final output. wrapOpenAIAgentsTool guards function tools before their execute/invoke boundary.
import { Agent, run, tool } from "@openai/agents";
import { z } from "zod";
import {
createAgentKeeperRuntimeClient,
wrapOpenAIAgentsRun,
wrapOpenAIAgentsTool,
} from "@agentkeeper-ai/runtime-sdk/openai-agents";
const ak = createAgentKeeperRuntimeClient({
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeIntegration: "openai_agents",
});
const guardedRun = wrapOpenAIAgentsRun(run, ak);
const lookupCustomer = tool(wrapOpenAIAgentsTool({
name: "lookup_customer",
description: "Look up a customer before support actions.",
parameters: z.object({ customerId: z.string() }),
async execute({ customerId }) {
return { customerId, tier: "enterprise" };
},
}, ak, {
evaluateTool: async () => ({ verdict: "passed" }),
}));
await guardedRun(new Agent({
name: "Support triage",
tools: [lookupCustomer],
}), "Check customer risk.");
await ak.flush();Azure OpenAI
Azure OpenAI is model-only observation. The wrapper records safe metadata for chat.completions.create(), responses.create(), and embeddings.create(), including deployment/model name, operation, token counts, message/tool counts, and stream event counts. It does not store prompts, message arrays, inputs, outputs, or provider request/response bodies.
import { AzureOpenAI } from "openai";
import {
createAgentKeeperRuntimeClient,
wrapAzureOpenAIClient,
} from "@agentkeeper-ai/runtime-sdk/azure-openai";
const ak = createAgentKeeperRuntimeClient({
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeIntegration: "azure_openai",
});
const deployment = process.env.AZURE_OPENAI_DEPLOYMENT ?? "gpt-4o-mini";
const azureOpenAI = wrapAzureOpenAIClient(
new AzureOpenAI({
apiKey: process.env.AZURE_OPENAI_API_KEY,
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
apiVersion: process.env.AZURE_OPENAI_API_VERSION ?? "2024-10-21",
deployment,
}),
ak,
);
await azureOpenAI.chat.completions.create({
model: deployment,
messages: [{ role: "user", content: "Check customer risk." }],
});
await ak.flush();Claude Managed Agents
wrapClaudeManagedAgentsClient observes hosted Managed Agents lifecycle calls through Anthropic's SDK: beta.agents.*, beta.environments.*, beta.sessions.*, and beta.sessions.events.*. It records safe agent/session/event metadata, token usage when returned, stream event counts, tool names, and stop reasons. Hosted tool execution remains observe/post-run only; use a local guarded tool wrapper when your application executes side effects outside Anthropic's managed sandbox.
import Anthropic from "@anthropic-ai/sdk";
import {
createAgentKeeperRuntimeClient,
wrapClaudeManagedAgentsClient,
} from "@agentkeeper-ai/runtime-sdk/claude-managed-agents";
const ak = createAgentKeeperRuntimeClient({
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeIntegration: "claude_managed_agents",
});
const client = wrapClaudeManagedAgentsClient(new Anthropic(), ak);
const agent = await client.beta.agents.create({
name: "Support triage",
model: process.env.ANTHROPIC_MANAGED_AGENT_MODEL ?? "claude-sonnet-4-6",
system: "Help support engineers without exposing raw payloads.",
tools: [{ type: "agent_toolset_20260401" }],
});
const session = await client.beta.sessions.create({
agent: agent.id,
environment_id: process.env.ANTHROPIC_ENVIRONMENT_ID,
title: "Support triage",
});
const stream = await client.beta.sessions.events.stream(session.id);
await client.beta.sessions.events.send(session.id, {
events: [{
type: "user.message",
content: [{ type: "text", text: "Check customer risk." }],
}],
});
for await (const event of stream) {
if (event.type === "session.status_idle") break;
}
await ak.flush();Anthropic SDK
wrapAnthropicSDK records model-only evidence for messages.create() and messages.stream(). It also wraps beta client.beta.messages.toolRunner() runnable tools so local run(input) functions can be guarded before side effects. Use wrapAnthropicTool for lower-level manual loops where your application executes a function after Claude emits a tool_use block.
import Anthropic from "@anthropic-ai/sdk";
import {
createAgentKeeperRuntimeClient,
wrapAnthropicRunnableTool,
wrapAnthropicSDK,
wrapAnthropicTool,
} from "@agentkeeper-ai/runtime-sdk/anthropic";
const ak = createAgentKeeperRuntimeClient({
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeIntegration: "anthropic_sdk",
});
const anthropic = wrapAnthropicSDK(new Anthropic(), ak);
const lookupCustomer = wrapAnthropicRunnableTool({
name: "lookup_customer",
description: "Look up a customer before support actions.",
input_schema: {
type: "object",
properties: { customerId: { type: "string" } },
required: ["customerId"],
},
parse: (content) => content as { customerId: string },
async run({ customerId }: { customerId: string }) {
return JSON.stringify({ customerId, tier: "enterprise" });
},
}, ak, {
evaluateTool: async () => ({ verdict: "passed" }),
});
const finalMessage = await anthropic.beta.messages.toolRunner({
model: process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-6",
max_tokens: 1024,
tools: [lookupCustomer],
messages: [{ role: "user", content: "Check customer risk." }],
});
// Manual Messages API loop alternative:
const manualLookupCustomer = wrapAnthropicTool("lookup_customer", async ({ customerId }) => {
return { customerId, tier: "enterprise" };
}, ak);
const message = await anthropic.messages.create({
model: process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-6",
max_tokens: 1024,
tools: [{
name: "lookup_customer",
description: "Look up a customer before support actions.",
input_schema: {
type: "object",
properties: { customerId: { type: "string" } },
required: ["customerId"],
},
}],
messages: [{ role: "user", content: "Check customer risk." }],
});
for (const block of message.content) {
if (block.type === "tool_use" && block.name === "lookup_customer") {
await manualLookupCustomer(block.input as { customerId: string });
}
}
await ak.flush();Guarded Tools
wrapTool evaluates before the wrapped function runs. If the evaluator returns blocked, the original function is not called.
const getCustomer = ak.wrapTool("get_customer", async ({ customerId }) => {
return { customerId, tier: "enterprise" };
}, {
evaluate: async () => ({ verdict: "passed" }),
});LangChain and LangGraph
Callbacks observe chain/model/tool lifecycle events. Tool blocking requires wrapping the actual tool before it is passed to the agent or graph.
import {
createAgentKeeperRuntimeClient,
createLangChainCallbackHandler,
createLangGraphCallbackHandler,
wrapLangChainTool,
wrapLangGraphTool,
} from "@agentkeeper-ai/runtime-sdk/langchain";
const ak = createAgentKeeperRuntimeClient({
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeIntegration: "langchain",
});
const callbacks = [createLangChainCallbackHandler(ak)];
const guardedTool = wrapLangChainTool(customerLookupTool, ak);
await chain.invoke({ input: "Check customer risk." }, { callbacks });
await ak.flush();AWS Bedrock Runtime
Bedrock is model-only observation. The wrapper records model metadata and command names for ConverseCommand and InvokeModelCommand, not raw prompts or response bodies.
import { BedrockRuntimeClient, ConverseCommand, InvokeModelCommand } from "@aws-sdk/client-bedrock-runtime";
import {
createAgentKeeperRuntimeClient,
wrapBedrockRuntimeClient,
} from "@agentkeeper-ai/runtime-sdk/bedrock";
const ak = createAgentKeeperRuntimeClient({
apiKey: process.env.AGENTKEEPER_RUNTIME_SDK_KEY,
runtimeService: "support-agent",
runtimeIntegration: "bedrock",
});
const bedrock = wrapBedrockRuntimeClient(
new BedrockRuntimeClient({ region: "us-east-1" }),
ak,
);
await bedrock.send(new ConverseCommand({
modelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
messages: [{ role: "user", content: [{ text: "Hello" }] }],
}));
await bedrock.send(new InvokeModelCommand({
modelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
contentType: "application/json",
body: JSON.stringify({
anthropic_version: "bedrock-2023-05-31",
max_tokens: 64,
messages: [{ role: "user", content: "Hello" }],
}),
}));
await ak.flush();Privacy
Raw prompts, assistant output, tool arguments, provider request bodies, provider response bodies, and file contents are rejected by default. Send redacted summaries, hashes, model names, tool names, token counts, domains, and structured detector evidence instead.
Verification
From the Agentkeeper repo:
npm --prefix packages/runtime-sdk test
npm --prefix web run verify:runtime-sdk-integrations
npm --prefix web run verify:runtime-sdk-integrations:registryThe local verifier packs this package, installs it into a clean temporary app, installs real ai, LangChain, LangGraph, AWS Bedrock, OpenAI Agents SDK, OpenAI, and Anthropic SDK packages, and runs promptless integration contracts. The registry verifier must pass after publishing the current package.
