import type { Beta } from "@anthropic-ai/sdk/resources/beta/beta" import type { VersionListParams } from "@anthropic-ai/sdk/resources/beta/agents/versions" import type { Events as SessionEvents } from "@anthropic-ai/sdk/resources/beta/sessions/events" import type { Resources as SessionResources } from "@anthropic-ai/sdk/resources/beta/sessions/resources" import type { Threads as SessionThreads } from "@anthropic-ai/sdk/resources/beta/sessions/threads/threads" import type { Events as ThreadEvents } from "@anthropic-ai/sdk/resources/beta/sessions/threads/events" import * as z from "zod" import { defineAction } from "../../automation/actions" import { anthropicInputSchema, anthropicOutputSchema, fromAnthropic, fromAnthropicPage, getAnthropicApi, toAnthropic, } from "./lib" const ID_SCHEMA = z.string().min(1) const PAGE_SCHEMA = z.looseObject({ limit: z.number().int().min(1).max(100).optional(), page: ID_SCHEMA.nullable().optional(), }) /** Cloud-only create parameters intentionally exclude self-hosted scope. */ type CloudEnvironmentCreateParams = Omit< Beta.EnvironmentCreateParams, "betas" | "config" | "scope" > & { config?: Beta.BetaCloudConfigParams | null } /** Cloud-only update parameters intentionally exclude self-hosted scope. */ type CloudEnvironmentUpdateParams = Omit< Beta.EnvironmentUpdateParams, "betas" | "config" | "scope" > & { config?: Beta.BetaCloudConfigParams | null } /** Session update fields currently accepted by the managed cloud API. */ type CloudSessionUpdateParams = Omit< Beta.SessionUpdateParams, "betas" | "vault_ids" > /** Hosted tool confirmation with optional subagent routing. */ type CloudToolConfirmationEvent = SessionEvents.BetaManagedAgentsUserToolConfirmationEventParams & { session_thread_id?: string | null } /** Hosted custom-tool result with optional subagent routing. */ type CloudCustomToolResultEvent = SessionEvents.BetaManagedAgentsUserCustomToolResultEventParams & { session_thread_id?: string | null } /** Event variants accepted by Anthropic-hosted sessions. */ type CloudSessionEvent = | CloudCustomToolResultEvent | CloudToolConfirmationEvent | SessionEvents.BetaManagedAgentsSystemMessageEventParams | SessionEvents.BetaManagedAgentsUserDefineOutcomeEventParams | SessionEvents.BetaManagedAgentsUserInterruptEventParams | SessionEvents.BetaManagedAgentsUserMessageEventParams /** Bulk event input restricted to hosted-session variants. */ interface CloudEventSendParams { events: CloudSessionEvent[] } /** Creates a versioned Claude agent definition. */ export const createClaudeAgent = defineAction("Create Claude agent") .describe("Creates a reusable Claude Managed Agents definition.") .account("anthropic") .input( anthropicInputSchema( z.looseObject({ model: z.union([z.string(), z.object({}).loose()]), name: z.string().min(1), }), [ "description", "mcpServers", "metadata", "model", "multiagent", "name", "skills", "system", "tools", ], ), ) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.agents.create( toAnthropic(input), ), ), ) /** Gets a Claude agent, optionally at an exact version. */ export const getClaudeAgent = defineAction("Get Claude agent") .describe("Gets the latest or an exact version of a Claude agent.") .account("anthropic") .input( z.object({ agentId: ID_SCHEMA, version: z.number().int().positive().optional(), }), ) .output(anthropicOutputSchema()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.agents.retrieve( input.agentId, { version: input.version, }, ), ), ) /** Lists Claude agents one provider page at a time. */ export const listClaudeAgents = defineAction("List Claude agents") .describe("Lists Claude agents with cursor and archive filters.") .account("anthropic") .input( anthropicInputSchema( PAGE_SCHEMA.extend({ includeArchived: z.boolean().optional() }), ["createdAtGte", "createdAtLte", "includeArchived", "limit", "page"], ), ) .output( anthropicOutputSchema<{ data: Beta.BetaManagedAgentsAgent[] next_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropicPage( await getAnthropicApi(account.secret).beta.agents.list( toAnthropic(input), ), ), ) /** Updates a Claude agent and creates its next immutable version. */ export const updateClaudeAgent = defineAction("Update Claude agent") .describe("Updates a Claude agent, optionally enforcing its current version.") .account("anthropic") .input( anthropicInputSchema( z.looseObject({ agentId: ID_SCHEMA }), [ "agentId", "description", "mcpServers", "metadata", "model", "multiagent", "name", "skills", "system", "tools", "version", ], ), ) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { agentId, ...parameters } = input return fromAnthropic( await getAnthropicApi(account.secret).beta.agents.update( agentId, toAnthropic(parameters), ), ) }) /** Archives a Claude agent. Archived agents cannot start new sessions. */ export const archiveClaudeAgent = defineAction("Archive Claude agent") .describe("Permanently archives a Claude agent definition.") .account("anthropic") .input(z.object({ agentId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.agents.archive(input.agentId), ), ) /** Lists immutable versions of a Claude agent. */ export const listClaudeAgentVersions = defineAction( "List Claude agent versions", ) .describe("Lists the saved versions of one Claude agent.") .account("anthropic") .input( anthropicInputSchema( PAGE_SCHEMA.extend({ agentId: ID_SCHEMA }), ["agentId", "limit", "page"], ), ) .output( anthropicOutputSchema<{ data: Beta.BetaManagedAgentsAgent[] next_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { agentId, ...parameters } = input return fromAnthropicPage( await getAnthropicApi(account.secret).beta.agents.versions.list( agentId, toAnthropic(parameters), ), ) }) /** Creates an Anthropic-hosted cloud environment. */ export const createClaudeEnvironment = defineAction("Create Claude environment") .describe("Creates a hosted cloud environment for Claude agent sessions.") .account("anthropic") .input( anthropicInputSchema( z.looseObject({ config: z .looseObject({ type: z.literal("cloud") }) .nullable() .optional(), name: z.string().min(1), }), ["config", "description", "metadata", "name"], ), ) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.environments.create( toAnthropic(input), ), ), ) /** Gets a Claude environment. */ export const getClaudeEnvironment = defineAction("Get Claude environment") .describe("Gets one Claude Managed Agents environment.") .account("anthropic") .input(z.object({ environmentId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.environments.retrieve( input.environmentId, ), ), ) /** Lists Claude environments one provider page at a time. */ export const listClaudeEnvironments = defineAction("List Claude environments") .describe("Lists hosted Claude environments with cursor and archive filters.") .account("anthropic") .input( anthropicInputSchema( PAGE_SCHEMA.extend({ includeArchived: z.boolean().optional() }), ["includeArchived", "limit", "page"], ), ) .output( anthropicOutputSchema<{ data: Beta.BetaEnvironment[] next_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropicPage( await getAnthropicApi(account.secret).beta.environments.list( toAnthropic(input), ), ), ) /** Updates a hosted Claude environment. */ export const updateClaudeEnvironment = defineAction("Update Claude environment") .describe("Updates a hosted Claude environment and its network policy.") .account("anthropic") .input( anthropicInputSchema< CloudEnvironmentUpdateParams & { environmentId: string } >( z.looseObject({ config: z .looseObject({ type: z.literal("cloud") }) .nullable() .optional(), environmentId: ID_SCHEMA, }), ["config", "description", "environmentId", "metadata", "name"], ), ) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { environmentId, ...parameters } = input return fromAnthropic( await getAnthropicApi(account.secret).beta.environments.update( environmentId, toAnthropic(parameters), ), ) }) /** Archives a Claude environment while retaining its history. */ export const archiveClaudeEnvironment = defineAction( "Archive Claude environment", ) .describe("Archives a Claude environment so it cannot start sessions.") .account("anthropic") .input(z.object({ environmentId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.environments.archive( input.environmentId, ), ), ) /** Deletes a Claude environment. */ export const deleteClaudeEnvironment = defineAction("Delete Claude environment") .describe("Deletes an unused Claude environment.") .account("anthropic") .input(z.object({ environmentId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.environments.delete( input.environmentId, ), ), ) /** Starts a Claude agent session and returns it immediately. */ export const startClaudeAgentSession = defineAction( "Start Claude agent session", ) .describe( "Starts a Claude agent session with optional initial work and resources.", ) .account("anthropic") .input( anthropicInputSchema( z.looseObject({ agent: z.union([ID_SCHEMA, z.object({}).loose()]), environmentId: ID_SCHEMA, initialEvents: z .array( z.looseObject({ type: z.enum(["user.define_outcome", "user.message"]), }), ) .max(50) .optional(), }), [ "agent", "budget", "environmentId", "initialEvents", "metadata", "resources", "title", "vaultIds", ], ), ) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.create( toAnthropic(input), ), ), ) /** Gets the current state of a Claude agent session. */ export const getClaudeAgentSession = defineAction("Get Claude agent session") .describe( "Gets a Claude agent session, including status, usage, and resources.", ) .account("anthropic") .input(z.object({ sessionId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.retrieve( input.sessionId, ), ), ) /** Lists Claude agent sessions one provider page at a time. */ export const listClaudeAgentSessions = defineAction( "List Claude agent sessions", ) .describe( "Lists Claude agent sessions with status, agent, time, and cursor filters.", ) .account("anthropic") .input( anthropicInputSchema(PAGE_SCHEMA, [ "agentId", "agentVersion", "createdAtGt", "createdAtGte", "createdAtLt", "createdAtLte", "deploymentId", "includeArchived", "limit", "memoryStoreId", "order", "page", "statuses", ]), ) .output( anthropicOutputSchema<{ data: Beta.BetaManagedAgentsSession[] next_page: string | null prev_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropicPage( await getAnthropicApi(account.secret).beta.sessions.list( toAnthropic(input), ), ), ) /** Updates mutable configuration on a Claude agent session. */ export const updateClaudeAgentSession = defineAction( "Update Claude agent session", ) .describe("Updates a session title, metadata, budget, tools, or MCP servers.") .account("anthropic") .input( anthropicInputSchema( z.looseObject({ sessionId: ID_SCHEMA }), ["agent", "budget", "metadata", "sessionId", "title"], ), ) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { sessionId, ...parameters } = input return fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.update( sessionId, toAnthropic(parameters), ), ) }) /** Archives a Claude agent session. */ export const archiveClaudeAgentSession = defineAction( "Archive Claude agent session", ) .describe("Archives a Claude agent session while retaining its history.") .account("anthropic") .input(z.object({ sessionId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.archive( input.sessionId, ), ), ) /** Deletes a Claude agent session. */ export const deleteClaudeAgentSession = defineAction( "Delete Claude agent session", ) .describe("Deletes a Claude agent session and its retained history.") .account("anthropic") .input(z.object({ sessionId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.delete( input.sessionId, ), ), ) /** Lists the durable event history for a Claude session. */ export const listClaudeSessionEvents = defineAction( "List Claude session events", ) .describe( "Lists authoritative Claude session events in chronological or reverse order.", ) .account("anthropic") .input( anthropicInputSchema( PAGE_SCHEMA.extend({ sessionId: ID_SCHEMA }), [ "createdAtGt", "createdAtGte", "createdAtLt", "createdAtLte", "limit", "order", "page", "sessionId", "types", ], ), ) .output( anthropicOutputSchema<{ data: SessionEvents.BetaManagedAgentsSessionEvent[] next_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { sessionId, ...parameters } = input return fromAnthropicPage( await getAnthropicApi(account.secret).beta.sessions.events.list( sessionId, toAnthropic(parameters), ), ) }) /** Sends one or more provider-native events to a Claude session. */ export const sendClaudeSessionEvents = defineAction( "Send Claude session events", ) .describe( "Sends messages, outcomes, confirmations, results, or interrupts in order.", ) .account("anthropic") .input( anthropicInputSchema( z.looseObject({ events: z .array( z.looseObject({ type: z.enum([ "system.message", "user.custom_tool_result", "user.define_outcome", "user.interrupt", "user.message", "user.tool_confirmation", ]), }), ) .min(1) .max(50) .superRefine(validateSystemEventPlacement), sessionId: ID_SCHEMA, }), ["events", "sessionId"], ), ) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { sessionId, ...parameters } = input return fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.events.send( sessionId, toAnthropic(parameters), ), ) }) /** Sends a user message to a Claude session. */ export const sendClaudeAgentMessage = createEventAction( "Send Claude agent message", "Sends user content and starts the next agent turn.", "user.message", ) /** Sends a user event followed by privileged system context in one request. */ export const sendClaudeSystemContext = defineAction( "Send Claude system context", ) .describe( "Sends a user message or custom tool result followed by system context.", ) .account("anthropic") .input( anthropicInputSchema<{ content: SessionEvents.BetaManagedAgentsSystemMessageEventParams["content"] precedingEvent: | CloudCustomToolResultEvent | SessionEvents.BetaManagedAgentsUserMessageEventParams sessionId: string }>( z.looseObject({ content: z .array(z.object({ text: z.string(), type: z.literal("text") })) .min(1), precedingEvent: z.looseObject({ type: z.enum(["user.message", "user.custom_tool_result"]), }), sessionId: ID_SCHEMA, }), ["content", "precedingEvent", "sessionId"], ), ) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { content, precedingEvent, sessionId } = input return fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.events.send( sessionId, { events: [ toAnthropic(precedingEvent), toAnthropic( { content, type: "system.message", }, ), ], }, ), ) }) /** Defines an evaluated outcome for a Claude session. */ export const defineClaudeAgentOutcome = createEventAction( "Define Claude agent outcome", "Defines work and a rubric for iterative outcome evaluation.", "user.define_outcome", ) /** Approves or denies a pending Claude tool execution. */ export const confirmClaudeAgentTool = createEventAction( "Confirm Claude agent tool", "Approves or denies one pending tool execution.", "user.tool_confirmation", ) /** Returns the result of an application-defined custom tool. */ export const submitClaudeCustomToolResult = createEventAction( "Submit Claude custom tool result", "Resolves one pending custom tool call.", "user.custom_tool_result", ) /** Interrupts the primary thread, one child thread, or every active thread. */ export const interruptClaudeAgentSession = createEventAction( "Interrupt Claude agent session", "Interrupts active Claude agent execution.", "user.interrupt", ) /** Lists threads spawned within a Claude multi-agent session. */ export const listClaudeSessionThreads = defineAction( "List Claude session threads", ) .describe("Lists primary and child execution threads for a Claude session.") .account("anthropic") .input( anthropicInputSchema< SessionThreads.ThreadListParams & { sessionId: string } >(PAGE_SCHEMA.extend({ sessionId: ID_SCHEMA }), [ "limit", "page", "sessionId", ]), ) .output( anthropicOutputSchema<{ data: SessionThreads.BetaManagedAgentsSessionThread[] next_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { sessionId, ...parameters } = input return fromAnthropicPage( await getAnthropicApi(account.secret).beta.sessions.threads.list( sessionId, toAnthropic(parameters), ), ) }) /** Gets one thread in a Claude session. */ export const getClaudeSessionThread = defineAction("Get Claude session thread") .describe("Gets one Claude session thread and its current status and usage.") .account("anthropic") .input(z.object({ sessionId: ID_SCHEMA, threadId: ID_SCHEMA })) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.threads.retrieve( input.threadId, { session_id: input.sessionId }, ), ), ) /** Archives a child thread in a Claude session. */ export const archiveClaudeSessionThread = defineAction( "Archive Claude session thread", ) .describe("Archives one Claude session child thread.") .account("anthropic") .input(z.object({ sessionId: ID_SCHEMA, threadId: ID_SCHEMA })) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.threads.archive( input.threadId, { session_id: input.sessionId }, ), ), ) /** Lists the event history for one Claude session thread. */ export const listClaudeSessionThreadEvents = defineAction( "List Claude session thread events", ) .describe("Lists authoritative events scoped to one Claude session thread.") .account("anthropic") .input( anthropicInputSchema( PAGE_SCHEMA.extend({ sessionId: ID_SCHEMA, threadId: ID_SCHEMA }), ["limit", "page", "sessionId", "threadId"], ), ) .output( anthropicOutputSchema<{ data: SessionEvents.BetaManagedAgentsSessionEvent[] next_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { sessionId, threadId, ...parameters } = input return fromAnthropicPage( await getAnthropicApi(account.secret).beta.sessions.threads.events.list( threadId, toAnthropic({ ...parameters, sessionId, }), ), ) }) /** Adds a previously uploaded file to a Claude session. */ export const addClaudeSessionFile = defineAction("Add Claude session file") .describe( "Mounts a previously uploaded Anthropic file into a Claude session.", ) .account("anthropic") .input( anthropicInputSchema< SessionResources.ResourceAddParams & { sessionId: string } >( z.looseObject({ fileId: ID_SCHEMA, sessionId: ID_SCHEMA, type: z.literal("file"), }), ["fileId", "mountPath", "sessionId", "type"], ), ) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { sessionId, ...parameters } = input return fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.resources.add( sessionId, toAnthropic(parameters), ), ) }) /** Gets one resource attached to a Claude session. */ export const getClaudeSessionResource = defineAction( "Get Claude session resource", ) .describe( "Gets a repository, file, or memory resource attached to a session.", ) .account("anthropic") .input(z.object({ resourceId: ID_SCHEMA, sessionId: ID_SCHEMA })) .output(anthropicOutputSchema()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.resources.retrieve( input.resourceId, { session_id: input.sessionId }, ), ), ) /** Lists resources attached to a Claude session. */ export const listClaudeSessionResources = defineAction( "List Claude session resources", ) .describe( "Lists repositories, files, and memory stores attached to a session.", ) .account("anthropic") .input( anthropicInputSchema< SessionResources.ResourceListParams & { sessionId: string } >(PAGE_SCHEMA.extend({ sessionId: ID_SCHEMA }), [ "limit", "page", "sessionId", ]), ) .output( anthropicOutputSchema<{ data: SessionResources.BetaManagedAgentsSessionResource[] next_page: string | null }>(), ) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { sessionId, ...parameters } = input return fromAnthropicPage( await getAnthropicApi(account.secret).beta.sessions.resources.list( sessionId, toAnthropic(parameters), ), ) }) /** Rotates the authorization token for a session repository. */ export const updateClaudeRepositoryToken = defineAction( "Update Claude repository token", ) .describe( "Rotates the authorization token for an attached GitHub repository.", ) .account("anthropic") .input( z.object({ authorizationToken: z.string().min(1), resourceId: ID_SCHEMA, sessionId: ID_SCHEMA, }), ) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => anthropicOutputSchema().parse( fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.resources.update( input.resourceId, { authorization_token: input.authorizationToken, session_id: input.sessionId, }, ), ), ), ) /** Deletes a detachable resource from a Claude session. */ export const deleteClaudeSessionResource = defineAction( "Delete Claude session resource", ) .describe( "Deletes an attached session resource when the provider permits it.", ) .account("anthropic") .input(z.object({ resourceId: ID_SCHEMA, sessionId: ID_SCHEMA })) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.resources.delete( input.resourceId, { session_id: input.sessionId }, ), ), ) /** * Creates a single-event session action. * * @param name - Public action name. * @param description - Public action description. * @param type - Provider event discriminator. */ function createEventAction( name: string, description: string, type: TEvent["type"], ) { return defineAction(name) .describe(description) .account("anthropic") .input( anthropicInputSchema & { sessionId: string }>( z.looseObject({ sessionId: ID_SCHEMA }), eventInputKeys(type), ), ) .output( anthropicOutputSchema(), ) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { sessionId, ...parameters } = z .looseObject({ sessionId: ID_SCHEMA }) .parse(input) return fromAnthropic( await getAnthropicApi(account.secret).beta.sessions.events.send( sessionId, { events: z .custom() .parse([ { ...toAnthropic>(parameters), type }, ]), }, ), ) }) } /** * Returns the exact public keys accepted by a single-event action. * * @param type - Provider event discriminator. */ function eventInputKeys(type: CloudSessionEvent["type"]): readonly string[] { const common = ["sessionId"] switch (type) { case "user.message": return [...common, "content"] case "user.define_outcome": return [...common, "description", "maxIterations", "rubric"] case "user.tool_confirmation": return [ ...common, "denyMessage", "result", "sessionThreadId", "toolUseId", ] case "user.custom_tool_result": return [ ...common, "content", "customToolUseId", "isError", "sessionThreadId", ] case "user.interrupt": return [...common, "sessionThreadId"] default: return common } } /** * Enforces Anthropic's placement rule for privileged system context. * * @param events - Bulk hosted-session event sequence. * @param context - Zod refinement context. */ function validateSystemEventPlacement( events: { type: CloudSessionEvent["type"] }[], context: z.RefinementCtx, ) { const systemIndexes = events.flatMap((event, index) => event.type === "system.message" ? [index] : [], ) const systemIndex = systemIndexes[0] const precedingType = systemIndex === undefined ? undefined : events[systemIndex - 1]?.type if ( systemIndexes.length > 1 || (systemIndex !== undefined && (systemIndex !== events.length - 1 || !["user.custom_tool_result", "user.message"].includes( precedingType ?? "", ))) ) { context.addIssue({ code: "custom", message: "A system message must be the final event and immediately follow a user message or custom-tool result.", }) } }