mastra-af-letta
Version:
Letta .af (Agent File) format support for Mastra - enables importing and exporting agents in Letta's portable format
415 lines (410 loc) • 14.1 kB
JavaScript
import { z } from 'zod';
/**
* mastra-af-letta
* Letta .af (Agent File) format support for Mastra
* @license MIT
*/
var iso8601Schema = z.string().datetime({ message: "Timestamp must be in ISO 8601 format (e.g., 2024-01-01T00:00:00Z)" });
var messageRoleSchema = z.enum(["user", "assistant", "system", "tool"], {
errorMap: () => ({ message: "Role must be one of: user, assistant, system, tool" })
});
var toolTypeSchema = z.enum(["python", "javascript", "json_schema"], {
errorMap: () => ({ message: "Tool type must be one of: python, javascript, json_schema" })
});
var toolRuleTypeSchema = z.string().min(1, "Rule type is required");
var llmConfigSchema = z.object({
provider: z.string().min(1, "Provider is required"),
model: z.string().min(1, "Model is required"),
temperature: z.number().min(0, "Temperature must be >= 0").max(2, "Temperature must be <= 2").optional(),
max_tokens: z.number().positive("Max tokens must be positive").optional(),
top_p: z.number().min(0, "Top-p must be >= 0").max(1, "Top-p must be <= 1").optional(),
frequency_penalty: z.number().min(-2, "Frequency penalty must be >= -2").max(2, "Frequency penalty must be <= 2").optional(),
presence_penalty: z.number().min(-2, "Presence penalty must be >= -2").max(2, "Presence penalty must be <= 2").optional()
}).passthrough().describe("Language model configuration");
var embeddingConfigSchema = z.object({
provider: z.string().min(1, "Provider is required"),
model: z.string().min(1, "Model is required"),
dimensions: z.number().positive("Dimensions must be positive").optional()
}).passthrough().describe("Embedding model configuration");
var coreMemoryBlockSchema = z.object({
label: z.string().min(1, "Memory block label is required"),
value: z.string(),
character_limit: z.number().positive("Character limit must be positive").optional(),
metadata: z.record(z.unknown()).optional()
}).describe("Core memory block");
var toolCallSchema = z.object({
id: z.string().min(1, "Tool call ID is required"),
name: z.string().min(1, "Tool name is required"),
arguments: z.record(z.unknown()),
metadata: z.record(z.unknown()).optional()
}).describe("Tool invocation request");
var toolResultSchema = z.object({
id: z.string().min(1, "Tool result ID is required"),
name: z.string().min(1, "Tool name is required"),
result: z.unknown().optional(),
error: z.string().optional(),
metadata: z.record(z.unknown()).optional()
}).describe("Tool execution result");
var messageSchema = z.object({
id: z.string().min(1, "Message ID is required"),
role: messageRoleSchema,
text: z.string(),
timestamp: iso8601Schema,
tool_calls: z.array(toolCallSchema).optional(),
tool_results: z.array(toolResultSchema).optional(),
metadata: z.record(z.unknown()).optional()
}).describe("Conversation message");
var parameterPropertySchema = z.lazy(
() => z.object({
type: z.string().min(1, "Parameter type is required"),
description: z.string().optional(),
enum: z.array(z.unknown()).optional(),
items: parameterPropertySchema.optional(),
properties: z.record(parameterPropertySchema).optional()
}).passthrough().describe("Parameter property definition")
);
var toolParametersSchema = z.object({
type: z.literal("object", {
errorMap: () => ({ message: 'Tool parameters must have type "object"' })
}),
properties: z.record(parameterPropertySchema),
required: z.array(z.string()).optional(),
additionalProperties: z.boolean().optional()
}).describe("Tool parameter schema");
var authReferenceSchema = z.object({
provider: z.enum(["env", "vault", "oauth2", "keychain", "dynamic"], {
errorMap: () => ({ message: "Auth provider must be one of: env, vault, oauth2, keychain, dynamic" })
}),
config_id: z.string().min(1, "Configuration ID is required"),
metadata: z.object({
auth_type: z.enum(["bearer", "api_key", "oauth2", "basic", "custom"]).optional(),
required_scopes: z.array(z.string()).optional(),
expires_in: z.number().positive("Expiration must be positive").optional(),
prompt: z.string().optional(),
cache_duration: z.number().nonnegative("Cache duration must be non-negative").optional()
}).optional()
}).describe("Authentication reference");
var mastraToolMetadataSchema = z.object({
type: z.enum(["mcp", "url", "reference"], {
errorMap: () => ({ message: "Mastra tool type must be one of: mcp, url, reference" })
}),
server: z.string().url("Server must be a valid URL").optional(),
tool_name: z.string().optional(),
transport: z.enum(["stdio", "http"]).optional(),
endpoint: z.string().url("Endpoint must be a valid URL").optional(),
method: z.enum(["GET", "POST"]).optional(),
auth_ref: authReferenceSchema.optional()
}).refine(
(data) => {
if (data.type === "mcp") {
return !!data.server && !!data.tool_name;
}
if (data.type === "url") {
return !!data.endpoint;
}
return true;
},
{
message: "MCP tools require server and tool_name, URL tools require endpoint"
}
).describe("Mastra tool metadata");
var toolSchema = z.object({
name: z.string().min(1, "Tool name is required"),
description: z.string().min(1, "Tool description is required"),
type: toolTypeSchema,
parameters: toolParametersSchema,
source_code: z.string().optional(),
metadata: z.record(z.unknown()).optional().refine(
(metadata) => {
if (metadata && "_mastra_tool" in metadata) {
return mastraToolMetadataSchema.safeParse(metadata._mastra_tool).success;
}
return true;
},
{
message: "Invalid _mastra_tool metadata structure"
}
)
}).refine(
(tool) => {
if ((tool.type === "python" || tool.type === "javascript") && !tool.source_code) {
return false;
}
return true;
},
{
message: "Source code is required for python and javascript tool types",
path: ["source_code"]
}
).describe("Tool definition");
var toolRuleSchema = z.object({
tool_name: z.string().min(1, "Tool name is required"),
rule_type: toolRuleTypeSchema,
rule_content: z.string().min(1, "Rule content is required")
}).describe("Tool usage rule");
var afAgentSchema = z.object({
// Core identification
agent_type: z.string().min(1, "Agent type is required"),
name: z.string().min(1, "Agent name is required"),
description: z.string().optional(),
// System configuration
system: z.string().min(1, "System prompt is required"),
// Model configuration
llm_config: llmConfigSchema,
embedding_config: embeddingConfigSchema.optional(),
// Memory components - object with named memory blocks
core_memory: z.record(coreMemoryBlockSchema).refine(
(memory) => memory.persona && memory.human,
"Core memory must contain at least persona and human blocks"
),
messages: z.array(messageSchema),
in_context_message_indices: z.array(z.number().nonnegative("Message index must be non-negative")).optional(),
// Tools
tools: z.array(toolSchema),
tool_rules: z.array(toolRuleSchema).optional(),
tool_exec_environment_variables: z.record(z.string()).optional(),
// Metadata
tags: z.array(z.string()).optional(),
metadata_: z.record(z.unknown()).optional(),
// Versioning
version: z.string().min(1, "Version is required"),
created_at: iso8601Schema,
updated_at: iso8601Schema
}).refine(
(agent) => {
if (agent.in_context_message_indices) {
const maxIndex = agent.messages.length - 1;
return agent.in_context_message_indices.every((idx) => idx <= maxIndex);
}
return true;
},
{
message: "in_context_message_indices contains out-of-range message references",
path: ["in_context_message_indices"]
}
).refine(
(agent) => {
if (agent.tool_rules) {
const toolNames = new Set(agent.tools.map((t) => t.name));
return agent.tool_rules.every((rule) => toolNames.has(rule.tool_name));
}
return true;
},
{
message: "tool_rules references non-existent tools",
path: ["tool_rules"]
}
).describe("Complete agent file schema");
function isValidAfSchema(value) {
return afAgentSchema.safeParse(value).success;
}
function parseAfSchema(data) {
try {
return afAgentSchema.parse(data);
} catch (error) {
if (error instanceof z.ZodError) {
const enhancedError = new Error("Agent file validation failed");
enhancedError.validationErrors = error.errors.map((err) => ({
path: err.path.join("."),
message: err.message,
code: err.code
}));
throw enhancedError;
}
throw error;
}
}
function safeParseAfSchema(data) {
return afAgentSchema.safeParse(data);
}
// src/parser.ts
var AgentFileParseError = class _AgentFileParseError extends Error {
constructor(message, options) {
super(message);
this.name = "AgentFileParseError";
this.cause = options?.cause;
this.validationErrors = options?.validationErrors;
Object.setPrototypeOf(this, _AgentFileParseError.prototype);
}
};
function parseAgentFile(jsonString, options = {}) {
const { maxSize = 52428800, autoFix = true } = options;
const byteSize = new TextEncoder().encode(jsonString).length;
if (byteSize > maxSize) {
throw new AgentFileParseError(
`Agent file too large: ${byteSize} bytes (max: ${maxSize} bytes)`
);
}
let data;
try {
data = JSON.parse(jsonString);
} catch (error) {
throw new AgentFileParseError("Invalid JSON format", {
cause: error
});
}
if (autoFix) {
data = applyAutoFixes(data);
}
try {
return parseAfSchema(data);
} catch (error) {
if (error instanceof Error && "validationErrors" in error) {
throw new AgentFileParseError("Schema validation failed", {
validationErrors: error.validationErrors,
cause: error
});
}
throw new AgentFileParseError("Unexpected validation error", {
cause: error
});
}
}
function safeParseAgentFile(jsonString, options = {}) {
try {
const data = parseAgentFile(jsonString, options);
return { success: true, data };
} catch (error) {
if (error instanceof AgentFileParseError) {
return { success: false, error };
}
return {
success: false,
error: new AgentFileParseError("Unknown parsing error", {
cause: error
})
};
}
}
function parseAgentFileObject(data, options = {}) {
const { autoFix = true } = options;
if (autoFix) {
data = applyAutoFixes(data);
}
try {
return parseAfSchema(data);
} catch (error) {
if (error instanceof Error && "validationErrors" in error) {
throw new AgentFileParseError("Schema validation failed", {
validationErrors: error.validationErrors,
cause: error
});
}
throw new AgentFileParseError("Unexpected validation error", {
cause: error
});
}
}
function isValidAgentFile(jsonString) {
try {
const data = JSON.parse(jsonString);
return safeParseAfSchema(data).success;
} catch {
return false;
}
}
function getValidationErrors(jsonString) {
try {
const data = JSON.parse(jsonString);
const result = safeParseAfSchema(data);
if (result.success) {
return null;
}
return result.error.errors.map((err) => ({
path: err.path.join("."),
message: err.message
}));
} catch (error) {
return [
{
path: "",
message: error instanceof Error ? error.message : "Invalid JSON"
}
];
}
}
function applyAutoFixes(data) {
if (!data || typeof data !== "object") {
return data;
}
const fixed = { ...data };
const now = (/* @__PURE__ */ new Date()).toISOString();
if (!fixed.created_at) {
fixed.created_at = now;
}
if (!fixed.updated_at) {
fixed.updated_at = now;
}
if (!fixed.version) {
fixed.version = "0.1.0";
}
if (!fixed.core_memory || typeof fixed.core_memory !== "object" || Array.isArray(fixed.core_memory)) {
fixed.core_memory = {
persona: {
label: "persona",
value: "I am a helpful AI assistant.",
limit: 2e3
},
human: {
label: "human",
value: "The user needs assistance.",
limit: 2e3
}
};
}
if (!Array.isArray(fixed.messages)) {
fixed.messages = [];
}
if (!Array.isArray(fixed.tools)) {
fixed.tools = [];
}
if (Array.isArray(fixed.messages)) {
fixed.messages = fixed.messages.map((msg, index) => {
if (!msg.timestamp) {
const msgTime = new Date(now);
msgTime.setMinutes(msgTime.getMinutes() - (fixed.messages.length - index));
return { ...msg, timestamp: msgTime.toISOString() };
}
return msg;
});
}
if (Array.isArray(fixed.tools)) {
fixed.tools = fixed.tools.map((tool) => {
if (tool.parameters && typeof tool.parameters === "object") {
if (!tool.parameters.type) {
tool.parameters.type = "object";
}
if (!tool.parameters.properties) {
tool.parameters.properties = {};
}
}
return tool;
});
}
return fixed;
}
function extractAgentMetadata(jsonString) {
try {
const data = JSON.parse(jsonString);
if (!data || typeof data !== "object") {
return null;
}
return {
name: data.name,
description: data.description,
version: data.version,
agent_type: data.agent_type,
created_at: data.created_at,
tags: Array.isArray(data.tags) ? data.tags : void 0
};
} catch {
return null;
}
}
// src/index.ts
var SUPPORTED_AF_VERSION = "0.1.0";
var PACKAGE_NAME = "mastra-af-letta";
var PACKAGE_VERSION = "0.1.0";
export { AgentFileParseError, PACKAGE_NAME, PACKAGE_VERSION, SUPPORTED_AF_VERSION, afAgentSchema, authReferenceSchema, coreMemoryBlockSchema, embeddingConfigSchema, extractAgentMetadata, getValidationErrors, isValidAfSchema, isValidAgentFile, llmConfigSchema, mastraToolMetadataSchema, messageSchema, parseAfSchema, parseAgentFile, parseAgentFileObject, safeParseAfSchema, safeParseAgentFile, toolCallSchema, toolParametersSchema, toolResultSchema, toolRuleSchema, toolSchema };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map