diff --git a/.changeset/20260918094326-regenerate-sdk-from-openapi.md b/.changeset/20260918094326-regenerate-sdk-from-openapi.md new file mode 100644 index 000000000..efd8ff00f --- /dev/null +++ b/.changeset/20260918094326-regenerate-sdk-from-openapi.md @@ -0,0 +1,5 @@ +--- +"@truefoundry/trueforge-sdk": patch +--- + +Regenerate SDK from updated OpenAPI spec. diff --git a/.changeset/user-tool-approval-policy-event.md b/.changeset/user-tool-approval-policy-event.md new file mode 100644 index 000000000..97d861d6b --- /dev/null +++ b/.changeset/user-tool-approval-policy-event.md @@ -0,0 +1,6 @@ +--- +"@truefoundry/trueforge-core": patch +"@truefoundry/trueforge": patch +--- + +Add `user.tool_approval_policy` send-event schema (`allow_session`, optional ISO `expire_at`). Send-only like `user.tool_approval` / `user.tool_response` — not on the durable stream. diff --git a/packages/trueforge-core/src/agent-session/schemas/sendEvent.ts b/packages/trueforge-core/src/agent-session/schemas/sendEvent.ts index f25a46587..b1f9107ec 100644 --- a/packages/trueforge-core/src/agent-session/schemas/sendEvent.ts +++ b/packages/trueforge-core/src/agent-session/schemas/sendEvent.ts @@ -1,17 +1,26 @@ /** - * Inbound send-event payloads for tip HITL (client → harness), distinct from the - * stream log ({@link PersistedTurnEvent} / session_event). + * Inbound send-event payloads (client → harness), distinct from the stream log + * ({@link PersistedTurnEvent} / session_event). * - * Public send is session-scoped (`POST …/sessions/{id}/events`) with required - * body `turn_id` (one batch → one tip) plus `SessionInboundEventItem`s; rows stamp that - * tip id. v1 union is tip-only; approval policies may relax `turn_id` later. + * Public send is session-scoped (`POST …/sessions/{id}/events`) with body + * `turn_id` + items. Tip HITL uses approval/tool_response; sticky policies use + * `user.tool_approval_policy` (session `allow_session`, optional expiry; may later + * relax tip binding). Per-call allow/deny stays on `user.tool_approval`. * `user.message` stays on createTurn / steer. */ import { z } from '@hono/zod-openapi'; -import { UserToolApprovalMessageSchema, UserToolResponseMessageSchema } from '../../core/events/schema'; +import { + UserToolApprovalMessageSchema, + UserToolApprovalPolicyMessageSchema, + UserToolResponseMessageSchema, +} from '../../core/events/schema'; export const SessionInboundEventItemSchema = z - .discriminatedUnion('type', [UserToolApprovalMessageSchema, UserToolResponseMessageSchema]) - .openapi('SessionInboundEventItem'); + .discriminatedUnion('type', [ + UserToolApprovalMessageSchema, + UserToolResponseMessageSchema, + UserToolApprovalPolicyMessageSchema, + ]) + .openapi('SendTurnEventItem'); export type SessionInboundEventItem = z.infer; diff --git a/packages/trueforge-core/src/core/events/schema.ts b/packages/trueforge-core/src/core/events/schema.ts index bdced8eda..0f4067c6f 100644 --- a/packages/trueforge-core/src/core/events/schema.ts +++ b/packages/trueforge-core/src/core/events/schema.ts @@ -40,6 +40,7 @@ export const EventType = { TOOL_RESPONSE_REQUIRED: 'tool.response_required', USER_TOOL_APPROVAL: 'user.tool_approval', USER_TOOL_RESPONSE: 'user.tool_response', + USER_TOOL_APPROVAL_POLICY: 'user.tool_approval_policy', USER_MESSAGE: 'user.message', } as const; @@ -96,6 +97,37 @@ export const UserToolResponseMessageSchema = z }) .openapi('UserToolResponseEvent'); +export const ToolApprovalPolicyAllowSessionSchema = z + .object({ + type: z.literal('allow_session').describe('Allow matching tool calls for the rest of this session.'), + expire_at: z + .string() + .optional() + .describe('ISO 8601 timestamp when this session allow expires. Omit to allow for the whole session.'), + }) + .openapi('ToolApprovalPolicyAllowSession'); + +export const ToolApprovalPolicyItemSchema = z + .object({ + server: z.string().min(1, 'server is required').describe('MCP server name the tool belongs to.'), + tool_name: z.string().min(1, 'tool_name is required').describe('Tool name this policy applies to.'), + action: z.discriminatedUnion('type', [ToolApprovalPolicyAllowSessionSchema]), + }) + .openapi('ToolApprovalPolicyItem'); + +/** + * Client inbound sticky approval policy (send-event only; not on the durable stream). + * Same pattern as {@link UserToolApprovalMessageSchema} / {@link UserToolResponseMessageSchema}. + */ +export const UserToolApprovalPolicyMessageSchema = z + .object({ + type: z + .literal(EventType.USER_TOOL_APPROVAL_POLICY) + .describe('Sticky allow-session policy for matching tools (optional expiry).'), + policies: z.array(ToolApprovalPolicyItemSchema).min(1).describe('One or more (server, tool_name) policy entries.'), + }) + .openapi('UserToolApprovalPolicyEvent'); + export const TextContentPartSchema = z .object({ type: z.literal('text').describe('Text content part.'), @@ -373,6 +405,8 @@ export type AgentInfo = z.infer; export type ApprovalDecision = z.infer; export type UserToolApprovalMessage = z.infer; export type UserToolResponseMessage = z.infer; +export type ToolApprovalPolicyItem = z.infer; +export type UserToolApprovalPolicyMessage = z.infer; export type AgentApprovalDecisionMessage = z.infer; export type InputTokensBreakdown = z.infer; export type ModelMessageUsage = z.infer; diff --git a/packages/trueforge-core/src/core/index.ts b/packages/trueforge-core/src/core/index.ts index 76538942f..77f629469 100644 --- a/packages/trueforge-core/src/core/index.ts +++ b/packages/trueforge-core/src/core/index.ts @@ -114,6 +114,7 @@ export { ToolResponseEventSchema, ToolResponseRequiredEventSchema, UserToolApprovalMessageSchema, + UserToolApprovalPolicyMessageSchema, UserToolResponseMessageSchema, newEventId, } from './events/schema'; @@ -124,6 +125,7 @@ export type { MCPServerInitInfo, ThreadDoneEvent, ThreadOverwriteContextEvent, + UserToolApprovalPolicyMessage, } from './events/schema'; export { CompletionUsageSchema } from './llm/LLMTypes'; export type { CompletionUsage } from './llm/LLMTypes';