import { CloudManagedSandboxExpiredError, LettaAgentClient, type LettaCodeClientSessionOptions, type LettaCodeSession, type LettaConversation, type ListMessagesResult, type MessageContentItem, type SDKMessage, type SDKResultMessage, type SendMessage, } from "@letta-ai/letta-agent-sdk"; import { createHash } from "node:crypto"; import { stableKey } from "../core/ids.js"; import type { JsonObject } from "../core/json.js"; import { resolveImageArtifact } from "../connectors/telegram-images.js"; import type { InferenceUsage } from "../store/types.js"; import type { LettaConversationBinding } from "../store/types.js"; import { createOutputContractRegistry, outputContractForDeclaration, outputContractIdentityJson, OutputContractValidationError, PUBLIC_KNOWLEDGE_PROPOSED_DIFF_OUTPUT_CONTRACT_ID, PUBLIC_KNOWLEDGE_RECOMMENDATION_OUTPUT_CONTRACT_ID, type PublicKnowledgeProposedDiffOutput, type PublicKnowledgeRecommendationOutput, type ObservationOutput, type OutputContractRegistry, } from "./output-contracts.js"; import { AgentRunFailure, type AgentOutput, type AgentRunInput, type AgentRunner, type RunnerTrace, type ThoughtAgentDeclaration, } from "./types.js"; import { validatePublicKnowledgeProposedDiffAgainstContext, validatePublicKnowledgeRecommendationAgainstContext, } from "../public-knowledge/context.js"; import { PUBLIC_KNOWLEDGE_PROPOSAL_TOOL_NAME, PublicKnowledgeProposalToolCapture, type PublicKnowledgeProposalToolSnapshot, } from "../public-knowledge/proposal-tool.js"; export const LETTA_AGENT_SDK_ADAPTER_REVISION = "letta-agent-sdk-conversations-v5"; export const LETTA_AGENT_SDK_PACKAGE_VERSION = "0.5.7"; const MAX_FINAL_JSON_CHARS = 64_000; const MAX_CONVERSATION_TEXT_CHARS = 2_000; const HISTORY_PAGE_SIZE = 100; const MAX_HISTORY_PAGES = 10; const DEFAULT_RECONCILIATION_DELAY_MS = 1_500; const LETTA_CLOUD_API_BASE_URL = "https://api.letta.com"; const RUN_USAGE_TIMEOUT_MS = 3_000; const MAX_USAGE_RUN_IDS = 20; const RUN_ID_PATTERN = /^run-[A-Za-z0-9-]{1,200}$/; export interface LettaAgentSdkClient { createSession?(id: string, options?: LettaCodeClientSessionOptions): LettaCodeSession; resumeSession(id: string, options?: LettaCodeClientSessionOptions): LettaCodeSession; conversations?: { list(options?: { agentId?: string; limit?: number; summarySearch?: string }): Promise; create(options: { agentId: string; summary?: string; description?: string; model?: string; hidden?: boolean; }): Promise; retrieve(conversationId: string): Promise; }; } export interface LettaConversationBindingStore { getLettaConversationBinding(id: string): Promise; upsertLettaConversationBinding(binding: LettaConversationBinding): Promise; } export interface LettaRunUsageClient { retrieve(runId: string): Promise; } export interface LettaAgentSdkRunnerOptions { client?: LettaAgentSdkClient; apiKey?: string | undefined; environment?: NodeJS.ProcessEnv; historyPages?: number; reconciliationDelayMs?: number; sleep?: (milliseconds: number) => Promise; outputContracts?: OutputContractRegistry; runUsageClient?: LettaRunUsageClient; fetch?: typeof fetch; conversationStore?: LettaConversationBindingStore | undefined; artifactRoot?: string | undefined; } interface HistoryToolCall { id: string; name: string; input: unknown; } interface HistoryToolResult { id: string; isError: boolean; } interface HistoryToolActivity { calls: HistoryToolCall[]; results: HistoryToolResult[]; } type HistoryReconciliation = | { status: "missing" } | { status: "pending" } | { status: "inconclusive" } | { status: "completed"; text: string; tools: HistoryToolActivity }; class LettaAgentSdkTimeoutError extends Error { constructor() { super("Letta Agent SDK turn timed out"); this.name = "LettaAgentSdkTimeoutError"; } } export class LettaAgentSdkRunner implements AgentRunner { readonly mode = "letta-agent-sdk" as const; private readonly outputContracts: OutputContractRegistry; private readonly historyPages: number; private readonly reconciliationDelayMs: number; private readonly sleep: (milliseconds: number) => Promise; private readonly clients = new Map<"cloud" | "local", LettaAgentSdkClient>(); private runUsageClient: LettaRunUsageClient | undefined; private runUsageClientResolved = false; constructor(private readonly options: LettaAgentSdkRunnerOptions = {}) { this.outputContracts = options.outputContracts ?? createOutputContractRegistry(); this.historyPages = options.historyPages ?? MAX_HISTORY_PAGES; this.reconciliationDelayMs = options.reconciliationDelayMs ?? DEFAULT_RECONCILIATION_DELAY_MS; this.sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))); if (!Number.isSafeInteger(this.historyPages) || this.historyPages < 1 || this.historyPages > MAX_HISTORY_PAGES) { throw new Error(`historyPages must be an integer from 1 through ${MAX_HISTORY_PAGES}`); } if (!Number.isSafeInteger(this.reconciliationDelayMs) || this.reconciliationDelayMs < 0 || this.reconciliationDelayMs > 30_000) { throw new Error("reconciliationDelayMs must be an integer from 0 through 30000"); } if (options.client) { this.clients.set("cloud", options.client); this.clients.set("local", options.client); } this.runUsageClient = options.runUsageClient; } async run(input: AgentRunInput, onTrace: (trace: RunnerTrace) => Promise): Promise { const declaration = input.declaration; const config = declaration.lettaAgent; if (!config || !["cloud", "local"].includes(config.backend) || !config.agentId) { throw new AgentRunFailure("Letta Agent SDK declaration is not runnable", { advanceProgress: false, diagnostic: { code: "letta-sdk-config-invalid", stage: "sdk-setup", backend: config?.backend ?? "missing", agentConfigured: Boolean(config?.agentId), }, }); } const client = this.resolveClient(declaration); const turnKey = lettaTurnKey(declaration, input.event.id); const proposalCapture = config.proposalTool === "public-knowledge-diff" ? new PublicKnowledgeProposalToolCapture(input.context) : undefined; const options = sessionOptions( declaration, this.options.environment ?? process.env, proposalCapture, ); const conversation = await this.resolveConversation(client, input); const session = client.resumeSession(conversation.sessionId, options); let sent = false; let acceptTraces = true; const emitTrace = async (trace: RunnerTrace): Promise => { if (acceptTraces) await onTrace(trace); }; try { return await this.withTimeout(session, declaration.timeoutMs, async () => { await emitTrace({ kind: "letta.session.opened", data: { backend: config.backend, executionAdapterRevision: LETTA_AGENT_SDK_ADAPTER_REVISION, sdkVersion: LETTA_AGENT_SDK_PACKAGE_VERSION, agentId: config.agentId!, conversation: config.conversation, outputOnly: config.outputOnly, proposalTool: config.proposalTool ?? "none", ...(conversation.bindingId ? { bindingId: conversation.bindingId } : {}), }, }); let reconciliation = await reconcileHistory(session, turnKey, this.historyPages); if (reconciliation.status === "inconclusive") throw inconclusiveHistoryFailure(session, turnKey); if (reconciliation.status === "completed") { await emitTrace({ kind: "letta.turn.recovered", data: { turnKey, conversationId: session.conversationId ?? "", text: textMetadata(reconciliation.text), }, }); return this.outputFromReconciliation(input, reconciliation, proposalCapture); } if (reconciliation.status === "pending") { reconciliation = await this.delayedReconciliation(session, turnKey); if (reconciliation.status === "completed") { await emitTrace({ kind: "letta.turn.recovered", data: { turnKey, conversationId: session.conversationId ?? "", text: textMetadata(reconciliation.text), }, }); return this.outputFromReconciliation(input, reconciliation, proposalCapture); } if (reconciliation.status === "inconclusive") throw inconclusiveHistoryFailure(session, turnKey); throw pendingTurnFailure(session, turnKey); } if ((input.attempt ?? 1) > 1) { reconciliation = await this.delayedReconciliation(session, turnKey); if (reconciliation.status === "completed") { await emitTrace({ kind: "letta.turn.recovered", data: { turnKey, conversationId: session.conversationId ?? "", text: textMetadata(reconciliation.text), }, }); return this.outputFromReconciliation(input, reconciliation, proposalCapture); } if (reconciliation.status === "inconclusive") throw inconclusiveHistoryFailure(session, turnKey); if (reconciliation.status === "pending") throw pendingTurnFailure(session, turnKey); } const messageText = buildLettaTurnMessage(input, turnKey, this.outputContracts); const message = await buildLettaSendMessage(input, messageText, this.options.artifactRoot, emitTrace); await emitTrace({ kind: "letta.prompt", data: { ...textMetadata(messageText), imageCount: input.context.imageArtifacts?.length ?? 0, }, }); await session.send(message); sent = true; await emitTrace({ kind: "letta.turn.sent", data: { turnKey, agentId: session.agentId ?? config.agentId!, conversationId: session.conversationId ?? "", }, }); let result: SDKResultMessage | undefined; const traces = new SdkTraceAccumulator(); try { for await (const messageEvent of session.stream()) { traces.add(messageEvent); if (messageEvent.type === "result") { result = messageEvent; break; } } } finally { await traces.flush(emitTrace); } if (!result) { throw new AgentRunFailure("Letta Agent SDK stream ended without a terminal result", { advanceProgress: false, diagnostic: { code: "letta-sdk-stream-closed", stage: "sdk-stream", turnKey, sent: true, }, }); } const usage = await this.reportedUsage(result, emitTrace, declaration); if (config.outputOnly && !proposalCapture && traces.hasToolActivity()) { throw invalidSdkOutput("unexpected-tool-activity", result.result ?? "", declaration, undefined, usage); } if (!result.success) throw terminalSdkFailure(result, turnKey, usage); let output: AgentOutput; try { if (proposalCapture) { output = this.outputFromProposalCapture(input, proposalCapture, traces.proposalToolActivity()); } else { if (typeof result.result !== "string" || result.result.trim().length === 0) { throw invalidSdkOutput("empty-final-text", result.result ?? "", declaration); } output = this.parseOutput(input, result.result); } } catch (error) { if (error instanceof AgentRunFailure && usage) { throw new AgentRunFailure(error.message, { advanceProgress: error.advanceProgress, ...(error.diagnostic ? { diagnostic: error.diagnostic } : {}), usage, }); } throw error; } return { ...output, ...(usage ? { usage } : {}), }; }, () => { acceptTraces = false; }); } catch (error) { if (error instanceof AgentRunFailure) throw error; if (error instanceof LettaAgentSdkTimeoutError) { throw new AgentRunFailure("Letta Agent SDK turn timed out", { advanceProgress: false, diagnostic: { code: "letta-sdk-timeout", stage: sent ? "sdk-stream" : "sdk-setup", turnKey, sent, }, }); } if (error instanceof CloudManagedSandboxExpiredError) { throw new AgentRunFailure("Letta Cloud managed sandbox expired before the turn", { advanceProgress: false, diagnostic: { code: "letta-cloud-sandbox-expired", stage: "sdk-setup", turnKey, sent: false, }, }); } throw new AgentRunFailure(sent ? "Letta Agent SDK turn entered ambiguous recovery" : "Letta Agent SDK session could not start", { advanceProgress: false, diagnostic: { code: sent ? "letta-sdk-turn-ambiguous" : "letta-sdk-session-unavailable", stage: sent ? "sdk-stream" : "sdk-setup", turnKey, sent, errorClass: safeErrorClass(error), }, }); } finally { acceptTraces = false; session.close(); } } private async resolveConversation( client: LettaAgentSdkClient, input: AgentRunInput, ): Promise<{ sessionId: string; bindingId?: string }> { const config = input.declaration.lettaAgent!; if (config.conversation === "main") return { sessionId: config.agentId! }; const store = this.options.conversationStore; if (!store || !client.conversations) { throw new AgentRunFailure("Per-document Letta conversation storage is unavailable", { advanceProgress: false, diagnostic: { code: "letta-conversation-store-missing", stage: "sdk-setup" }, }); } const documentId = requiredEventString(input.event, "documentId"); const currentPath = requiredEventString(input.event, "path"); const bindingId = lettaDocumentConversationBindingId(input.declaration, input.event); const remoteMarker = lettaDocumentConversationRemoteMarker(bindingId); const existing = await store.getLettaConversationBinding(bindingId); if (existing) { const remote = await client.conversations.retrieve(existing.conversationId); if (remote.id !== existing.conversationId || remote.agent_id !== existing.agentId || remote.summary !== existing.remoteMarker || existing.remoteMarker !== remoteMarker) { throw new AgentRunFailure("Per-document Letta conversation binding no longer matches remote state", { advanceProgress: false, diagnostic: { code: "letta-conversation-binding-drift", stage: "sdk-setup", bindingId }, }); } await store.upsertLettaConversationBinding({ ...existing, declarationVersion: input.declaration.version, currentPath, updatedAt: new Date().toISOString(), }); return { sessionId: existing.conversationId, bindingId }; } const matches = (await client.conversations.list({ agentId: config.agentId!, summarySearch: remoteMarker, limit: 10, })).filter((conversation) => ( conversation.summary === remoteMarker && conversation.agent_id === config.agentId )); if (matches.length > 1) { throw new AgentRunFailure("Per-document Letta conversation marker is ambiguous", { advanceProgress: false, diagnostic: { code: "letta-conversation-marker-ambiguous", stage: "sdk-setup", bindingId, matchCount: matches.length, }, }); } const conversation = matches[0] ?? await client.conversations.create({ agentId: config.agentId!, summary: remoteMarker, description: "thought stream private file-scoped Public Knowledge recommendation conversation.", ...(input.declaration.model ? { model: input.declaration.model } : {}), hidden: true, }); if (!conversation.id || conversation.agent_id !== config.agentId) { throw new AgentRunFailure("Per-document Letta conversation identity is invalid", { advanceProgress: false, diagnostic: { code: "letta-conversation-identity-invalid", stage: "sdk-setup", bindingId }, }); } const now = new Date().toISOString(); await store.upsertLettaConversationBinding({ id: bindingId, declarationId: input.declaration.id, declarationVersion: input.declaration.version, agentId: config.agentId!, source: input.event.source, scopeType: "document", scopeKey: documentId, conversationId: conversation.id, remoteMarker, currentPath, createdAt: now, updatedAt: now, }); return { sessionId: conversation.id, bindingId }; } private resolveClient(declaration: ThoughtAgentDeclaration): LettaAgentSdkClient { const backend = declaration.lettaAgent!.backend; const cached = this.clients.get(backend); if (cached) return cached; const environment = this.options.environment ?? process.env; if (backend === "local") { const client = new LettaAgentClient({ backend: "local", appServer: { harnessBackend: "api" } }); this.clients.set("local", client); return client; } const apiKey = this.options.apiKey ?? environment.LETTA_API_KEY; if (!apiKey) { throw new AgentRunFailure("Letta Cloud API credential is unavailable", { advanceProgress: false, diagnostic: { code: "letta-cloud-credential-missing", stage: "sdk-setup" }, }); } const client = new LettaAgentClient({ backend: "cloud", apiKey }); this.clients.set("cloud", client); return client; } private async reportedUsage( result: SDKResultMessage, emitTrace: (trace: RunnerTrace) => Promise, declaration: ThoughtAgentDeclaration, ): Promise { const usage: InferenceUsage = {}; const costMicrousd = usdToMicrousd(result.totalCostUsd); if (costMicrousd !== undefined) usage.costMicrousd = costMicrousd; const sdkRunIds = [...new Set(result.runIds ?? [])]; const validRunIds = sdkRunIds.filter((runId) => RUN_ID_PATTERN.test(runId)); const runSetComplete = validRunIds.length === sdkRunIds.length && validRunIds.length <= MAX_USAGE_RUN_IDS; const client = declaration.lettaAgent?.backend === "cloud" ? this.resolveRunUsageClient() : undefined; let retrievedRunCount = 0; if (client && runSetComplete && validRunIds.length > 0) { const results = await Promise.all(validRunIds.map(async (runId) => { try { return await client.retrieve(runId); } catch { return undefined; } })); retrievedRunCount = results.filter((value): value is InferenceUsage => value !== undefined).length; if (retrievedRunCount === validRunIds.length) { const inputTokens = sumReportedDimension(results, "inputTokens"); const outputTokens = sumReportedDimension(results, "outputTokens"); if (inputTokens !== undefined) usage.inputTokens = inputTokens; if (outputTokens !== undefined) usage.outputTokens = outputTokens; } } const tokenUsageReported = usage.inputTokens !== undefined || usage.outputTokens !== undefined; await emitTrace({ kind: "letta.usage", data: { sdkRunCount: sdkRunIds.length, validRunCount: validRunIds.length, runSetComplete, retrievedRunCount, inputTokensReported: usage.inputTokens !== undefined, outputTokensReported: usage.outputTokens !== undefined, costReported: usage.costMicrousd !== undefined, source: tokenUsageReported ? "run-usage-api" : retrievedRunCount > 0 ? "run-usage-api-partial" : costMicrousd !== undefined ? "sdk-result" : "unavailable", }, }); return Object.keys(usage).length > 0 ? usage : undefined; } private resolveRunUsageClient(): LettaRunUsageClient | undefined { if (this.runUsageClientResolved) return this.runUsageClient; this.runUsageClientResolved = true; if (this.runUsageClient) return this.runUsageClient; const environment = this.options.environment ?? process.env; const apiKey = this.options.apiKey ?? environment.LETTA_API_KEY; if (!apiKey) return undefined; this.runUsageClient = new LettaCloudRunUsageClient(apiKey, this.options.fetch ?? fetch); return this.runUsageClient; } private outputFromReconciliation( input: AgentRunInput, reconciliation: Extract, proposalCapture: PublicKnowledgeProposalToolCapture | undefined, ): AgentOutput { if (!proposalCapture) return this.parseOutput(input, reconciliation.text); const call = reconciliation.tools.calls[0]; if (reconciliation.tools.calls.length === 1 && call?.name === PUBLIC_KNOWLEDGE_PROPOSAL_TOOL_NAME) { proposalCapture.recover(call.input); } return this.outputFromProposalCapture(input, proposalCapture, reconciliation.tools); } private outputFromProposalCapture( input: AgentRunInput, proposalCapture: PublicKnowledgeProposalToolCapture, activity: HistoryToolActivity, ): AgentOutput { const snapshot = proposalCapture.snapshot(); const call = activity.calls[0]; const result = activity.results[0]; if (snapshot.failure) { throw invalidProposalToolOutput(snapshot.failure, input.declaration, snapshot, activity); } if (activity.calls.length !== 1 || call?.name !== PUBLIC_KNOWLEDGE_PROPOSAL_TOOL_NAME) { throw invalidProposalToolOutput( activity.calls.length === 0 ? "missing-tool-call" : "unexpected-tool-call", input.declaration, snapshot, activity, ); } if (activity.results.length !== 1 || result?.id !== call.id || result.isError) { throw invalidProposalToolOutput( activity.results.length === 0 ? "missing-tool-result" : "invalid-tool-result", input.declaration, snapshot, activity, ); } if (snapshot.calls !== 1 || snapshot.failure || !snapshot.accepted) { throw invalidProposalToolOutput( snapshot.failure ?? (snapshot.calls === 0 ? "callback-not-invoked" : "proposal-not-captured"), input.declaration, snapshot, activity, ); } return this.withModelIdentity(input.declaration, snapshot.accepted); } private withModelIdentity(declaration: ThoughtAgentDeclaration, output: AgentOutput): AgentOutput { const config = declaration.lettaAgent!; return { ...output, model: { provider: declaration.provider ?? `letta-${config.backend}`, id: declaration.model ?? "agent-default", revision: `${LETTA_AGENT_SDK_PACKAGE_VERSION}/${LETTA_AGENT_SDK_ADAPTER_REVISION}`, }, }; } private async delayedReconciliation(session: LettaCodeSession, turnKey: string): Promise { if (this.reconciliationDelayMs > 0) await this.sleep(this.reconciliationDelayMs); return reconcileHistory(session, turnKey, this.historyPages); } private parseOutput(input: AgentRunInput, text: string): AgentOutput { const declaration = input.declaration; const identity = outputContractForDeclaration(declaration); const config = declaration.lettaAgent!; let parsed: AgentOutput; if (responseModeForInput(input) === "conversation-text") { const summary = text.trim(); if (!summary) throw invalidSdkOutput("empty-final-text", text, declaration); if (summary.length > MAX_CONVERSATION_TEXT_CHARS) { throw invalidSdkOutput("final-text-too-large", text, declaration); } try { parsed = this.outputContracts.validate(identity, { summary, tags: ["conversation"], importance: "normal", confidence: 0.5, }); } catch (error) { if (!(error instanceof OutputContractValidationError)) throw error; throw invalidSdkOutput("output-contract-invalid", text, declaration, error); } } else { if (text.length > MAX_FINAL_JSON_CHARS) throw invalidSdkOutput("final-text-too-large", text, declaration); let value: unknown; try { value = JSON.parse(text); } catch { throw invalidSdkOutput("invalid-json", text, declaration); } try { parsed = this.outputContracts.validate(identity, value); } catch (error) { if (!(error instanceof OutputContractValidationError)) throw error; throw invalidSdkOutput("output-contract-invalid", text, declaration, error); } if (identity.id === PUBLIC_KNOWLEDGE_RECOMMENDATION_OUTPUT_CONTRACT_ID) { try { validatePublicKnowledgeRecommendationAgainstContext( parsed as PublicKnowledgeRecommendationOutput, input.context, ); } catch { throw invalidSdkOutput("context-bound-output-invalid", text, declaration); } } if (identity.id === PUBLIC_KNOWLEDGE_PROPOSED_DIFF_OUTPUT_CONTRACT_ID) { try { validatePublicKnowledgeProposedDiffAgainstContext( parsed as PublicKnowledgeProposedDiffOutput, input.context, ); } catch { throw invalidSdkOutput("context-bound-output-invalid", text, declaration); } } } return this.withModelIdentity(declaration, parsed); } private async withTimeout( session: LettaCodeSession, timeoutMs: number, operation: () => Promise, onTimeout: () => void, ): Promise { let timer: NodeJS.Timeout | undefined; const timeout = new Promise((_resolve, reject) => { timer = setTimeout(() => { onTimeout(); void session.abort().catch(() => {}); session.close(); reject(new LettaAgentSdkTimeoutError()); }, timeoutMs); }); try { return await Promise.race([operation(), timeout]); } finally { if (timer) clearTimeout(timer); } } } export function lettaTurnKey(declaration: ThoughtAgentDeclaration, eventId: string): string { return stableKey("letta-agent-sdk-turn", declaration.id, String(declaration.version), eventId); } export function lettaDocumentConversationBindingId( declaration: ThoughtAgentDeclaration, event: AgentRunInput["event"], ): string { const agentId = declaration.lettaAgent?.agentId; if (!agentId) throw new Error("Document conversation binding requires a resolved agent id"); return stableKey( "letta-conversation", declaration.id, agentId, event.source, requiredEventString(event, "documentId"), ); } export function lettaDocumentConversationRemoteMarker(bindingId: string): string { return `thoughtstream:coil:${createHash("sha256").update(bindingId).digest("hex")}`; } export function buildLettaTurnMessage( input: AgentRunInput, turnKey: string, contracts: OutputContractRegistry = createOutputContractRegistry(), ): string { const config = input.declaration.lettaAgent; if (!config) throw new Error("Letta Agent SDK configuration is missing"); const finalInstruction = config.proposalTool === "public-knowledge-diff" ? `Call ${PUBLIC_KNOWLEDGE_PROPOSAL_TOOL_NAME} exactly once. Set raw to one JSON-encoded object satisfying the complete proposed-diff contract. Do not call any other tool. After the tool accepts the proposal, return only PROPOSAL_CAPTURED.` : responseModeForInput(input) === "conversation-text" ? input.event.type === "stream.thought.source.telegram.message" ? "Return only your reply to Cameron." : input.event.type === "stream.thought.derived.event.batch" ? "Return only one concise private internal ATProto batch observation. This is not a reply to Cameron." : "Return only a concise private internal observation." : contracts.resolve(outputContractForDeclaration(input.declaration)).prompt; return [ ``, "", input.declaration.systemPrompt, "", ...(input.context.systemText ? [ "", input.context.systemText, "", ] : []), input.context.text, "", finalInstruction, "", ].join("\n"); } async function buildLettaSendMessage( input: AgentRunInput, text: string, artifactRoot: string | undefined, onTrace: (trace: RunnerTrace) => Promise, ): Promise { const artifacts = input.context.imageArtifacts ?? []; if (artifacts.length === 0) return text; if (input.event.type !== "stream.thought.source.telegram.message" || artifacts.length !== 1) { throw unauthorizedImageArtifactFailure(); } if (!artifactRoot) throw invalidImageArtifactFailure(); const content: MessageContentItem[] = [{ type: "text", text }]; for (const artifact of artifacts) { try { const image = await resolveImageArtifact(artifact, artifactRoot); content.push({ type: "image", source: { type: "base64", media_type: image.mimeType, data: image.data, }, }); await onTrace({ kind: "letta.image_resolved", data: { sha256: artifact.sha256, mimeType: artifact.mimeType, sizeBytes: artifact.sizeBytes, }, }); } catch (error) { await onTrace({ kind: "letta.image_resolution_failed", data: { sha256: artifact.sha256, code: "image-artifact-invalid" }, }); throw invalidImageArtifactFailure(error); } } return content; } function invalidImageArtifactFailure(cause?: unknown): AgentRunFailure { return new AgentRunFailure("Current-turn image artifact could not be validated", { ...(cause ? { cause } : {}), advanceProgress: false, diagnostic: { code: "letta-sdk-image-invalid", stage: "trusted-evidence-resolution", }, }); } function unauthorizedImageArtifactFailure(): AgentRunFailure { return new AgentRunFailure("Image content is outside this SDK turn's authority", { advanceProgress: false, diagnostic: { code: "letta-sdk-image-not-authorized", stage: "effect-boundary", }, }); } function responseModeForInput(input: AgentRunInput): "strict-json" | "conversation-text" { const config = input.declaration.lettaAgent; if (!config) throw new Error("Letta Agent SDK configuration is missing"); return input.event.type === "stream.thought.derived.event.batch" && config.batchResponseMode ? config.batchResponseMode : config.responseMode; } export async function reconcileHistory( session: LettaCodeSession, turnKey: string, maxPages = MAX_HISTORY_PAGES, ): Promise { const history = await loadBoundedHistory(session, maxPages); const reconciliation = findTurnInHistory(history.messages, turnKey); return reconciliation.status === "missing" && !history.complete ? { status: "inconclusive" } : reconciliation; } export function findTurnInHistory(messagesNewestFirst: unknown[], turnKey: string): HistoryReconciliation { const chronological = [...messagesNewestFirst].reverse(); let markerIndex = -1; for (let index = chronological.length - 1; index >= 0; index -= 1) { const message = chronological[index]; if (historyRole(message) === "user" && historyText(message).includes(turnKey)) { markerIndex = index; break; } } if (markerIndex < 0) return { status: "missing" }; const assistant: string[] = []; const tools: HistoryToolActivity = { calls: [], results: [] }; for (let index = markerIndex + 1; index < chronological.length; index += 1) { const message = chronological[index]; const role = historyRole(message); if (role === "user") break; if (role === "assistant") { const text = historyText(message); if (text) assistant.push(text); } tools.calls.push(...historyToolCalls(message)); tools.results.push(...historyToolResults(message)); } const text = assistant.join("").trim(); return text ? { status: "completed", text, tools } : { status: "pending" }; } async function loadBoundedHistory( session: LettaCodeSession, maxPages: number, ): Promise<{ messages: unknown[]; complete: boolean }> { const messages: unknown[] = []; const seen = new Set(); let before: string | undefined; let complete = false; for (let page = 0; page < maxPages; page += 1) { const result: ListMessagesResult = await session.listMessages({ order: "desc", limit: HISTORY_PAGE_SIZE, ...(before ? { before } : {}), }); for (const message of result.messages) { const id = historyMessageId(message); if (id && seen.has(id)) continue; if (id) seen.add(id); messages.push(message); } if (result.hasMore !== true) { complete = true; break; } if (!result.nextBefore || result.messages.length === 0) { throw new AgentRunFailure("Letta Cloud history pagination was internally inconsistent", { advanceProgress: false, diagnostic: { code: "letta-sdk-history-pagination-invalid", stage: "history-reconciliation", hasMore: true, cursorPresent: Boolean(result.nextBefore), pageMessageCount: result.messages.length, }, }); } before = result.nextBefore; } return { messages, complete }; } class RedactedTextAccumulator { private readonly hash = createHash("sha256"); private chars = 0; private parts = 0; add(value: string): void { this.hash.update(value); this.chars += value.length; this.parts += 1; } metadata(): JsonObject { return { chars: this.chars, parts: this.parts, sha256: this.hash.digest("hex"), redacted: true }; } get empty(): boolean { return this.parts === 0; } } class SdkTraceAccumulator { private readonly assistant = new RedactedTextAccumulator(); private readonly reasoning = new RedactedTextAccumulator(); private readonly toolCalls = new Map(); private readonly toolResults = new Map(); private readonly streamEventTypes = new Map(); private readonly errors: JsonObject[] = []; private retries = 0; private maximumQueueLength = 0; private latestLoopStatus = ""; private init: JsonObject | undefined; private result: SDKResultMessage | undefined; hasToolActivity(): boolean { return this.toolCalls.size > 0 || this.toolResults.size > 0; } proposalToolActivity(): HistoryToolActivity { return { calls: [...this.toolCalls.values()].map((call) => ({ id: String(call.toolCallId ?? ""), name: String(call.toolName ?? ""), input: undefined, })), results: [...this.toolResults].map(([id, result]) => ({ id, isError: result.isError })), }; } add(message: SDKMessage): void { switch (message.type) { case "assistant": this.assistant.add(message.content); return; case "reasoning": this.reasoning.add(message.content); return; case "tool_call": { const key = message.toolCallId || message.uuid; const prior = this.toolCalls.get(key); const keys = new Set(Array.isArray(prior?.inputKeys) ? prior.inputKeys.map(String) : []); for (const inputKey of Object.keys(message.toolInput)) keys.add(inputKey); this.toolCalls.set(key, { toolCallId: message.toolCallId, toolName: message.toolName, inputKeys: [...keys].sort().slice(0, 100), argumentsRedacted: true, runId: message.runId ?? "", }); return; } case "tool_result": { const current = this.toolResults.get(message.toolCallId) ?? { metadata: new RedactedTextAccumulator(), isError: false, runId: message.runId ?? "", }; current.metadata.add(message.content); current.isError ||= message.isError; this.toolResults.set(message.toolCallId, current); return; } case "result": this.result = message; return; case "error": if (this.errors.length < 20) { this.errors.push({ errorCode: safeToken(message.errorCode), stopReason: safeToken(message.stopReason), approvalConflict: message.approvalConflict ?? false, recoverable: message.recoverable ?? false, runId: message.runId ?? "", detailRedacted: true, }); } return; case "retry": this.retries += 1; return; case "queue_update": this.maximumQueueLength = Math.max(this.maximumQueueLength, message.queue.length); return; case "loop_status": this.latestLoopStatus = safeToken(message.status); return; case "init": this.init = { agentId: message.agentId, sessionId: message.sessionId, conversationId: message.conversationId, model: message.model, toolCount: message.tools?.length ?? 0, memfsEnabled: message.memfsEnabled ?? false, }; return; case "stream_event": { const event = asRecord(message.event); const type = safeToken(event?.type ?? event?.message_type) || "unknown"; this.streamEventTypes.set(type, (this.streamEventTypes.get(type) ?? 0) + 1); return; } } } async flush(onTrace: (trace: RunnerTrace) => Promise): Promise { if (this.init) await onTrace({ kind: "letta.init", data: this.init }); if (!this.reasoning.empty) await onTrace({ kind: "letta.reasoning", data: this.reasoning.metadata() }); if (!this.assistant.empty) await onTrace({ kind: "letta.assistant", data: this.assistant.metadata() }); for (const call of this.toolCalls.values()) await onTrace({ kind: "letta.tool_call", data: call }); for (const [toolCallId, result] of this.toolResults) { await onTrace({ kind: "letta.tool_result", data: { toolCallId, isError: result.isError, runId: result.runId, content: result.metadata.metadata() }, }); } if (this.streamEventTypes.size > 0) { await onTrace({ kind: "letta.stream_events", data: { counts: Object.fromEntries([...this.streamEventTypes].sort(([left], [right]) => left.localeCompare(right))) }, }); } if (this.errors.length > 0) await onTrace({ kind: "letta.errors", data: { count: this.errors.length, items: this.errors } }); if (this.retries > 0 || this.maximumQueueLength > 0 || this.latestLoopStatus) { await onTrace({ kind: "letta.runtime", data: { retries: this.retries, maximumQueueLength: this.maximumQueueLength, latestLoopStatus: this.latestLoopStatus, }, }); } if (this.result) await onTrace({ kind: "letta.result", data: resultMetadata(this.result) }); } } function sessionOptions( declaration: ThoughtAgentDeclaration, environment: NodeJS.ProcessEnv, proposalCapture?: PublicKnowledgeProposalToolCapture, ): LettaCodeClientSessionOptions { const config = declaration.lettaAgent!; if (Boolean(config.proposalTool) !== Boolean(proposalCapture)) { throw new AgentRunFailure("Letta Agent SDK proposal tool profile is inconsistent", { advanceProgress: false, diagnostic: { code: "letta-proposal-tool-config-invalid", stage: "sdk-setup" }, }); } if (config.backend === "local") { if (!config.outputOnly) { throw new AgentRunFailure("Local Agent SDK session is not explicitly output-only", { advanceProgress: false, diagnostic: { code: "letta-local-output-only-required", stage: "sdk-setup" }, }); } const memoryDir = config.memoryDirEnv ? environment[config.memoryDirEnv]?.trim() : undefined; if (!memoryDir || !memoryDir.startsWith("/")) { throw new AgentRunFailure("Local Agent SDK memory root is unavailable", { advanceProgress: false, diagnostic: { code: "letta-local-memory-root-missing", stage: "sdk-setup" }, }); } return { permissionMode: "strict", skillSources: [], allowedTools: proposalCapture ? [PUBLIC_KNOWLEDGE_PROPOSAL_TOOL_NAME] : [], ...(proposalCapture ? { tools: [proposalCapture.tool] } : {}), ...(proposalCapture ? { canUseTool: async (toolName) => toolName === PUBLIC_KNOWLEDGE_PROPOSAL_TOOL_NAME ? { behavior: "allow" as const } : { behavior: "deny" as const, message: "Tool is outside the proposal-only session", interrupt: false }, } : {}), dreaming: { trigger: "off" }, cwd: memoryDir, env: { MEMORY_DIR: memoryDir }, filesystemConfinement: "memory", ...(declaration.model ? { model: declaration.model } : {}), ...(config.reasoningEffort ? { reasoningEffort: config.reasoningEffort } : {}), }; } return { permissionMode: config.permissionMode, ...(config.skillSources ? { skillSources: config.skillSources } : {}), dreaming: { trigger: config.dreaming.trigger, ...(config.dreaming.stepCount ? { stepCount: config.dreaming.stepCount } : {}), }, sandbox: config.sandbox, ...(declaration.model ? { model: declaration.model } : {}), ...(config.reasoningEffort ? { reasoningEffort: config.reasoningEffort } : {}), }; } function requiredEventString(event: AgentRunInput["event"], field: string): string { const value = event.payload[field]; if (typeof value !== "string" || !value) { throw new AgentRunFailure(`Per-document Letta event ${field} is invalid`, { advanceProgress: true, diagnostic: { code: "letta-document-scope-invalid", stage: "sdk-setup", field }, }); } return value; } function pendingTurnFailure(session: LettaCodeSession, turnKey: string): AgentRunFailure { return new AgentRunFailure("Letta Cloud turn exists without a terminal assistant result", { advanceProgress: false, diagnostic: { code: "letta-sdk-turn-pending", stage: "history-reconciliation", turnKey, conversationId: session.conversationId ?? "", }, }); } function inconclusiveHistoryFailure(session: LettaCodeSession, turnKey: string): AgentRunFailure { return new AgentRunFailure("Letta Cloud turn marker was outside the bounded history window", { advanceProgress: false, diagnostic: { code: "letta-sdk-history-inconclusive", stage: "history-reconciliation", turnKey, conversationId: session.conversationId ?? "", }, }); } function terminalSdkFailure( result: SDKResultMessage, turnKey: string, usage?: InferenceUsage, ): AgentRunFailure { const failureClass = classifySdkFailure(result); return new AgentRunFailure("Letta Agent SDK turn failed", { advanceProgress: false, diagnostic: { code: failureClass.code, stage: failureClass.stage, turnKey, errorCode: safeToken(result.errorCode), stopReason: safeToken(result.stopReason), approvalConflict: result.approvalConflict ?? false, recoverable: result.recoverable ?? false, recoveryAttempts: result.recoveryAttempts ?? 0, runIds: result.runIds?.slice(0, 20) ?? [], durationMs: nonnegativeInteger(result.durationMs) ?? 0, detailRedacted: true, }, ...(usage ? { usage } : {}), }); } function classifySdkFailure(result: SDKResultMessage): { code: string; stage: string } { const detail = [result.error, result.errorDetail, result.errorCode, result.stopReason] .filter((value): value is string => typeof value === "string") .join(" ") .toLowerCase(); if (detail.includes("insufficient credits") || detail.includes("minimum $1 in credits")) { return { code: "letta-cloud-insufficient-credits", stage: "provider-billing" }; } if (detail.includes("rate limit") || detail.includes("too many requests")) { return { code: "letta-cloud-rate-limited", stage: "provider-request" }; } if (detail.includes("unauthorized") || detail.includes("forbidden") || detail.includes("authentication")) { return { code: "letta-cloud-authorization-failed", stage: "provider-authorization" }; } return { code: "letta-sdk-turn-failed", stage: "sdk-result" }; } function invalidProposalToolOutput( reason: string, declaration: ThoughtAgentDeclaration, snapshot: PublicKnowledgeProposalToolSnapshot, activity: HistoryToolActivity, ): AgentRunFailure { return new AgentRunFailure("Letta Agent SDK proposal tool output rejected", { diagnostic: { code: "invalid-proposal-tool-output", stage: "proposal-tool-validation", reason, callbackCalls: snapshot.calls, captured: Boolean(snapshot.accepted), toolCallCount: activity.calls.length, toolResultCount: activity.results.length, toolNames: activity.calls.map((call) => safeToken(call.name)).filter(Boolean).slice(0, 20), errorResultCount: activity.results.filter((result) => result.isError).length, outputContract: outputContractIdentityJson(outputContractForDeclaration(declaration)), ...(snapshot.validationIssues.length > 0 ? { validationIssues: snapshot.validationIssues.map((issue) => ({ code: issue.code, path: issue.path })), } : {}), }, }); } function invalidSdkOutput( reason: string, text: string, declaration: ThoughtAgentDeclaration, contractError?: OutputContractValidationError, usage?: InferenceUsage, ): AgentRunFailure { return new AgentRunFailure("Letta Agent SDK final output rejected", { diagnostic: { code: "invalid-final-output", stage: "final-output-validation", reason, text: textMetadata(text), outputContract: outputContractIdentityJson(outputContractForDeclaration(declaration)), ...(contractError ? { validationIssues: contractError.issues.map((issue) => ({ code: issue.code, path: issue.path })), } : {}), }, ...(usage ? { usage } : {}), }); } class LettaCloudRunUsageClient implements LettaRunUsageClient { constructor( private readonly apiKey: string, private readonly fetchImpl: typeof fetch, ) {} async retrieve(runId: string): Promise { if (!RUN_ID_PATTERN.test(runId)) return undefined; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), RUN_USAGE_TIMEOUT_MS); try { const response = await this.fetchImpl(`${LETTA_CLOUD_API_BASE_URL}/v1/runs/${encodeURIComponent(runId)}/usage`, { method: "GET", headers: { Accept: "application/json", Authorization: `Bearer ${this.apiKey}` }, signal: controller.signal, }); if (!response.ok) return undefined; const body = asRecord(await response.json()); if (!body) return undefined; const inputTokens = nonnegativeInteger(body.prompt_tokens); const outputTokens = nonnegativeInteger(body.completion_tokens); return inputTokens === undefined && outputTokens === undefined ? undefined : { ...(inputTokens !== undefined ? { inputTokens } : {}), ...(outputTokens !== undefined ? { outputTokens } : {}), }; } catch { return undefined; } finally { clearTimeout(timer); } } } function sumReportedDimension( values: Array, key: "inputTokens" | "outputTokens", ): number | undefined { let total = 0; for (const value of values) { const amount = value?.[key]; if (amount === undefined) return undefined; total += amount; if (!Number.isSafeInteger(total)) return undefined; } return total; } function resultMetadata(result: SDKResultMessage): JsonObject { const costMicrousd = usdToMicrousd(result.totalCostUsd); const failureClass = result.success ? undefined : classifySdkFailure(result); return { success: result.success, ...(result.result ? { result: textMetadata(result.result) } : {}), errorCode: safeToken(result.errorCode), stopReason: safeToken(result.stopReason), approvalConflict: result.approvalConflict ?? false, recoverable: result.recoverable ?? false, recoveryAttempts: result.recoveryAttempts ?? 0, durationMs: nonnegativeInteger(result.durationMs) ?? 0, ...(costMicrousd !== undefined ? { costMicrousd } : {}), conversationId: result.conversationId ?? "", runIds: result.runIds?.slice(0, 20) ?? [], ...(failureClass ? { failureCode: failureClass.code, failureStage: failureClass.stage } : {}), detailRedacted: true, }; } function historyRole(value: unknown): "user" | "assistant" | "other" { const record = asRecord(value); const role = String(record?.role ?? "").toLowerCase(); const type = String(record?.message_type ?? record?.messageType ?? record?.type ?? "").toLowerCase(); if (role === "user" || type === "user_message" || type === "user") return "user"; if (role === "assistant" || type === "assistant_message" || type === "assistant") return "assistant"; return "other"; } function historyText(value: unknown): string { const record = asRecord(value); if (!record) return ""; for (const key of ["content", "message", "text", "assistant_message", "user_message"]) { const text = extractText(record[key]); if (text) return text; } return ""; } function extractText(value: unknown, depth = 0): string { if (depth > 5) return ""; if (typeof value === "string") return value; if (Array.isArray(value)) return value.map((item) => extractText(item, depth + 1)).join(""); const record = asRecord(value); if (!record) return ""; for (const key of ["text", "content", "message", "value"]) { const text = extractText(record[key], depth + 1); if (text) return text; } return ""; } function historyToolCalls(value: unknown): HistoryToolCall[] { const record = asRecord(value); if (!record) return []; const type = String(record.message_type ?? record.messageType ?? record.type ?? "").toLowerCase(); if (type !== "tool_call_message" && !record.tool_call && !record.tool_calls) return []; const candidates = Array.isArray(record.tool_calls) ? record.tool_calls : record.tool_call ? [record.tool_call] : record.tool_calls ? [record.tool_calls] : []; return candidates.flatMap((candidate) => { const call = asRecord(candidate); const id = call?.tool_call_id ?? call?.toolCallId ?? call?.id; const name = call?.name ?? call?.toolName; if (typeof id !== "string" || !id || typeof name !== "string" || !name) return []; let input: unknown = call?.input ?? call?.toolInput; if (input === undefined && typeof call?.arguments === "string") { try { input = JSON.parse(call.arguments); } catch { input = undefined; } } return [{ id, name, input }]; }); } function historyToolResults(value: unknown): HistoryToolResult[] { const record = asRecord(value); if (!record) return []; const type = String(record.message_type ?? record.messageType ?? record.type ?? "").toLowerCase(); if (type !== "tool_return_message" && !record.tool_return && !record.tool_returns) return []; const candidates = Array.isArray(record.tool_returns) && record.tool_returns.length > 0 ? record.tool_returns : [record]; return candidates.flatMap((candidate) => { const result = asRecord(candidate); const id = result?.tool_call_id ?? result?.toolCallId; if (typeof id !== "string" || !id) return []; const status = String(result?.status ?? "").toLowerCase(); return [{ id, isError: status === "error" || result?.is_err === true || record.is_err === true }]; }); } function historyMessageId(value: unknown): string | undefined { const record = asRecord(value); const id = record?.id ?? record?.uuid ?? record?.message_id; return typeof id === "string" && id ? id : undefined; } function textMetadata(value: string): JsonObject { return { chars: value.length, sha256: createHash("sha256").update(value).digest("hex"), redacted: true, }; } function safeErrorClass(error: unknown): string { if (!(error instanceof Error)) return "non-error"; return /^[A-Za-z][A-Za-z0-9_.-]{0,99}$/.test(error.name) ? error.name : "Error"; } function safeToken(value: unknown): string { return typeof value === "string" && /^[A-Za-z0-9_.:-]{1,100}$/.test(value) ? value : ""; } function usdToMicrousd(value: number | undefined): number | undefined { if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return undefined; const converted = Math.round(value * 1_000_000); return Number.isSafeInteger(converted) ? converted : undefined; } function nonnegativeInteger(value: unknown): number | undefined { return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : undefined; } function asRecord(value: unknown): Record | undefined { return value && typeof value === "object" && !Array.isArray(value) ? value as Record : undefined; }