From 900a4dd7a0b185bc3d6dd81a2c264f2baacdfb49 Mon Sep 17 00:00:00 2001 From: Heer Ambavi Date: Wed, 23 Sep 2026 10:13:36 +0530 Subject: [PATCH 1/2] add POST events API and add events to session stream --- .changeset/turn-inbound-events-api.md | 6 + .../src/agent-session/schemas/events.ts | 6 + .../trueforge-core/src/core/events/schema.ts | 32 +++- packages/trueforge-core/src/core/index.ts | 6 + packages/trueforge/src/apis/turns.ts | 53 ++++++ packages/trueforge/src/routes/turnRoutes.ts | 50 +++++- packages/trueforge/src/schemas/events.ts | 34 ++++ .../tests/unit/apis/sessionHttp.test.ts | 162 ++++++++++++++++++ .../trueforge/tests/unit/apis/turns.test.ts | 35 ++++ 9 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 .changeset/turn-inbound-events-api.md diff --git a/.changeset/turn-inbound-events-api.md b/.changeset/turn-inbound-events-api.md new file mode 100644 index 000000000..909824e72 --- /dev/null +++ b/.changeset/turn-inbound-events-api.md @@ -0,0 +1,6 @@ +--- +"@truefoundry/trueforge": patch +"@truefoundry/trueforge-core": patch +--- + +Add `POST /sessions/{session_id}/turns/{turn_id}/events` to persist inbound tip HITL / policy events. Response items use the same `UserToolApprovalEvent` / `UserToolResponseEvent` / `UserToolApprovalPolicyEvent` shapes as `SessionEvent` and the turn SSE stream. diff --git a/packages/trueforge-core/src/agent-session/schemas/events.ts b/packages/trueforge-core/src/agent-session/schemas/events.ts index 370f209c2..0a098f602 100644 --- a/packages/trueforge-core/src/agent-session/schemas/events.ts +++ b/packages/trueforge-core/src/agent-session/schemas/events.ts @@ -18,6 +18,9 @@ import { ToolApprovalRequiredEventSchema, ToolResponseEventSchema, ToolResponseRequiredEventSchema, + UserToolApprovalEventSchema, + UserToolApprovalPolicyEventSchema, + UserToolResponseEventSchema, } from '../../core/events/schema'; import { TurnInputItemSchema, @@ -111,6 +114,9 @@ export const SessionEventSchema = z SandboxCreatedEventSchema, ToolApprovalRequiredEventSchema, ToolResponseRequiredEventSchema, + UserToolApprovalEventSchema, + UserToolResponseEventSchema, + UserToolApprovalPolicyEventSchema, ]) .openapi('SessionEvent'); diff --git a/packages/trueforge-core/src/core/events/schema.ts b/packages/trueforge-core/src/core/events/schema.ts index d36fe5721..fbb9deed2 100644 --- a/packages/trueforge-core/src/core/events/schema.ts +++ b/packages/trueforge-core/src/core/events/schema.ts @@ -98,7 +98,7 @@ export const UserToolResponseMessageSchema = z }) .openapi('UserToolResponseMessage'); - export const ToolApprovalPolicyAllowSessionSchema = z +export const ToolApprovalPolicyAllowSessionSchema = z .object({ type: z.literal('allow_session').describe('Allow matching tool calls for the rest of this session.'), expire_at: z @@ -125,6 +125,33 @@ export const UserToolApprovalPolicyMessageSchema = z }) .openapi('UserToolApprovalPolicyMessage'); +/** Durable / SSE form of {@link UserToolApprovalMessageSchema}. */ +export const UserToolApprovalEventSchema = z + .object({ + ...UserToolApprovalMessageSchema.shape, + id: EventIdSchema, + created_at: z.string().describe('ISO 8601 event timestamp.'), + }) + .openapi('UserToolApprovalEvent'); + +/** Durable / SSE form of {@link UserToolResponseMessageSchema}. */ +export const UserToolResponseEventSchema = z + .object({ + ...UserToolResponseMessageSchema.shape, + id: EventIdSchema, + created_at: z.string().describe('ISO 8601 event timestamp.'), + }) + .openapi('UserToolResponseEvent'); + +/** Durable / SSE form of {@link UserToolApprovalPolicyMessageSchema}. */ +export const UserToolApprovalPolicyEventSchema = z + .object({ + ...UserToolApprovalPolicyMessageSchema.shape, + id: EventIdSchema, + created_at: z.string().describe('ISO 8601 event timestamp.'), + }) + .openapi('UserToolApprovalPolicyEvent'); + export const TextContentPartSchema = z .object({ type: z.literal('text').describe('Text content part.'), @@ -404,6 +431,9 @@ export type UserToolApprovalMessage = z.infer; export type ToolApprovalPolicyItem = z.infer; export type UserToolApprovalPolicyMessage = z.infer; +export type UserToolApprovalEvent = z.infer; +export type UserToolResponseEvent = z.infer; +export type UserToolApprovalPolicyEvent = 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 c9056a503..2a6fdf465 100644 --- a/packages/trueforge-core/src/core/index.ts +++ b/packages/trueforge-core/src/core/index.ts @@ -113,8 +113,11 @@ export { ToolApprovalRequiredEventSchema, ToolResponseEventSchema, ToolResponseRequiredEventSchema, + UserToolApprovalEventSchema, UserToolApprovalMessageSchema, + UserToolApprovalPolicyEventSchema, UserToolApprovalPolicyMessageSchema, + UserToolResponseEventSchema, UserToolResponseMessageSchema, newEventId, } from './events/schema'; @@ -125,7 +128,10 @@ export type { MCPServerInitInfo, ThreadDoneEvent, ThreadOverwriteContextEvent, + UserToolApprovalEvent, + UserToolApprovalPolicyEvent, UserToolApprovalPolicyMessage, + UserToolResponseEvent, } from './events/schema'; export { CompletionUsageSchema } from './llm/LLMTypes'; export type { CompletionUsage } from './llm/LLMTypes'; diff --git a/packages/trueforge/src/apis/turns.ts b/packages/trueforge/src/apis/turns.ts index 4ffc8994c..a25f162a1 100644 --- a/packages/trueforge/src/apis/turns.ts +++ b/packages/trueforge/src/apis/turns.ts @@ -22,6 +22,7 @@ import { isAgentInputUserMessage, isFileContentPart, McpConnectionError, + newEventId, rawSandboxId, redisKey, SandboxError, @@ -42,6 +43,7 @@ import type { ISkillStore } from '../db/skillStore'; import type { IWebSearchProviderStore } from '../db/webSearchProviderStore'; import { createAndExecuteTurnRoute, + createTurnInboundEventRoute, downloadSandboxFileRoute, getTurnRoute, listTurnEventsRoute, @@ -923,12 +925,63 @@ export function createTurnsRouter(deps: TurnsRouterDeps) { }); }; + /** Persist-only inbox write; apply / wake is follow-up work. */ + const createTurnInboundEventHandler: RouteHandler = async c => { + const { session_id: sessionId, turn_id: turnId } = c.req.valid('param'); + const body = c.req.valid('json'); + const requestContext = deps.resolveRequestContext(c); + const session = await deps.sessions.get({ + tenant_id: requestContext.tenant_id, + session_id: sessionId, + }); + if (!session) { + return c.json({ error: { message: `Session not found: ${sessionId}` } }, 404); + } + if ( + !isSessionOwner({ + subject_id: requestContext.subject.id, + created_by_subject: session.record.created_by_subject, + }) + ) { + return c.json({ error: { message: FORBIDDEN_SESSION_ACCESS } }, 403); + } + + const createdAt = new Date().toISOString(); + const events = body.events.map(payload => { + const id = newEventId(); + return { + event_id: id, + payload, + created_at: createdAt, + created: { ...payload, id, created_at: createdAt }, + }; + }); + + try { + await deps.sessionStore.insertTurnInboundEvents({ + session_id: sessionId, + turn_id: turnId, + events: events.map(({ event_id, payload, created_at }) => ({ event_id, payload, created_at })), + }); + return c.json({ data: events.map(e => e.created) }, 201); + } catch (error) { + if (error instanceof SessionStoreNotFoundError) { + return c.json({ error: { message: error.message } }, 404); + } + if (error instanceof SessionStoreConflictError) { + return c.json({ error: { message: error.message } }, 409); + } + throw error; + } + }; + const router = new OpenAPIHono(); router.openapi(createAndExecuteTurnRoute, createAndExecuteTurnHandler); router.openapi(listTurnsRoute, listTurnsHandler); router.openapi(getTurnRoute, getTurnHandler); router.openapi(downloadSandboxFileRoute, downloadSandboxFileHandler); router.openapi(listTurnEventsRoute, listTurnEventsHandler); + router.openapi(createTurnInboundEventRoute, createTurnInboundEventHandler); router.openapi(subscribeTurnRoute, subscribeTurnHandler); return router; } diff --git a/packages/trueforge/src/routes/turnRoutes.ts b/packages/trueforge/src/routes/turnRoutes.ts index 14f9b9398..641d2b2ca 100644 --- a/packages/trueforge/src/routes/turnRoutes.ts +++ b/packages/trueforge/src/routes/turnRoutes.ts @@ -5,7 +5,11 @@ */ import { createRoute, z } from '@hono/zod-openapi'; import { RequestErrorResponseSchema } from '../schemas/errors'; -import { TurnStreamingEventSchema } from '../schemas/events'; +import { + CreateTurnInboundEventRequestSchema, + CreateTurnInboundEventResponseSchema, + TurnStreamingEventSchema, +} from '../schemas/events'; import { CreateTurnRequestSchema, DownloadSandboxFileRequestQuerySchema, @@ -168,6 +172,50 @@ export const listTurnEventsRoute = createRoute({ }, }); +/** + * Create inbound tip HITL / policy events in `turn_inbound_events`. + * Apply / wake / auto-continue land in follow-up work — this route only inserts. + */ +export const createTurnInboundEventRoute = createRoute({ + method: 'post', + path: '/{session_id}/turns/{turn_id}/events', + tags: [OpenApiTag.AGENT_SESSIONS], + summary: 'Create turn inbound events', + description: + 'Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step.', + 'x-fern-sdk-group-name': ['sessions'], + 'x-fern-sdk-method-name': 'create_turn_inbound_event', + request: { + params: TurnIdParamsSchema, + body: { + content: { 'application/json': { schema: CreateTurnInboundEventRequestSchema } }, + required: true, + }, + }, + responses: { + 201: { + content: { 'application/json': { schema: CreateTurnInboundEventResponseSchema } }, + description: 'Events created in the turn inbox.', + }, + 400: { + content: { 'application/json': { schema: RequestErrorResponseSchema } }, + description: 'Invalid request body.', + }, + 403: { + content: { 'application/json': { schema: RequestErrorResponseSchema } }, + description: 'Caller is not the session creator.', + }, + 404: { + content: { 'application/json': { schema: RequestErrorResponseSchema } }, + description: 'Session or turn not found.', + }, + 409: { + content: { 'application/json': { schema: RequestErrorResponseSchema } }, + description: 'Turn is terminal, or an event id already exists.', + }, + }, +}); + export const createAndExecuteTurnRoute = createRoute({ method: 'post', path: '/{session_id}/turns', diff --git a/packages/trueforge/src/schemas/events.ts b/packages/trueforge/src/schemas/events.ts index 4b702a6e1..2717cecd1 100644 --- a/packages/trueforge/src/schemas/events.ts +++ b/packages/trueforge/src/schemas/events.ts @@ -9,6 +9,7 @@ import { TokenPaginationSchema, TurnCreatedEventSchema, TurnDoneEventSchema, + TurnInboundEventItemSchema, TurnUpdateEventSchema, } from '@truefoundry/trueforge-core/agent-session'; import { @@ -22,12 +23,42 @@ import { ToolApprovalRequiredEventSchema, ToolResponseEventSchema, ToolResponseRequiredEventSchema, + UserToolApprovalEventSchema, + UserToolApprovalPolicyEventSchema, + UserToolResponseEventSchema, } from '@truefoundry/trueforge-core/core'; import { EVENTS_PAGE_LIMIT } from './common'; export type { TurnCreatedEvent } from '@truefoundry/trueforge-core/agent-session'; export { EventType }; +/** Client → harness inbound events (tip HITL / sticky policy). Persisted to the turn inbox. */ +export const CreateTurnInboundEventRequestSchema = z + .object({ + events: z + .array(TurnInboundEventItemSchema) + .min(1) + .describe('One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`).'), + }) + .openapi('CreateTurnInboundEventRequest'); + +/** Same shapes as the durable stream / {@link SessionEvent} members for these types. */ +export const CreatedTurnInboundEventSchema = z + .discriminatedUnion('type', [ + UserToolApprovalEventSchema, + UserToolResponseEventSchema, + UserToolApprovalPolicyEventSchema, + ]) + .openapi('CreatedTurnInboundEvent'); + +export const CreateTurnInboundEventResponseSchema = z + .object({ + data: z + .array(CreatedTurnInboundEventSchema) + .describe('Created inbox events with server-minted `id` and `created_at`, in request order.'), + }) + .openapi('CreateTurnInboundEventResponse'); + /** Live SSE stream for session turns — content events, deltas and lifecycle. */ export const TurnStreamingEventSchema = z .discriminatedUnion('type', [ @@ -41,6 +72,9 @@ export const TurnStreamingEventSchema = z SandboxCreatedEventSchema, ToolApprovalRequiredEventSchema, ToolResponseRequiredEventSchema, + UserToolApprovalEventSchema, + UserToolResponseEventSchema, + UserToolApprovalPolicyEventSchema, TurnCreatedEventSchema, TurnUpdateEventSchema, TurnDoneEventSchema, diff --git a/packages/trueforge/tests/unit/apis/sessionHttp.test.ts b/packages/trueforge/tests/unit/apis/sessionHttp.test.ts index 5e64789f2..496a50ac8 100644 --- a/packages/trueforge/tests/unit/apis/sessionHttp.test.ts +++ b/packages/trueforge/tests/unit/apis/sessionHttp.test.ts @@ -3,14 +3,21 @@ import { AgentSpecSchema, Sessions } from '@truefoundry/trueforge-core/agent-ses import { RequestReplyRouter } from '@truefoundry/trueforge-core/request-reply'; import { createClient } from 'redis'; import { createLogger } from 'winston'; +import { + makeCreateTurnInput, + makeDoneTurnState, + makeTurnDoneEvent, +} from '../../../../trueforge-core/tests/agent-session/testHelpers'; import { createInternalMetricsRouter } from '../../../src/apis/sessionMetrics'; import { createInternalSessionsRouter, createSessionsRouter, type SessionsRouterDeps, } from '../../../src/apis/sessions'; +import { createTurnsRouter } from '../../../src/apis/turns'; import { TrueForgeAuthorizer, type Authorizer } from '../../../src/auth/authorizer'; import { STANDALONE_REQUEST_CONTEXT } from '../../../src/auth/identity'; +import { McpServerWithAuthStore } from '../../../src/db/McpServerWithAuthStore'; import { migrateSqliteToLatest } from '../../../src/db/migrateSqlite'; import { SqliteAgentStore } from '../../../src/db/sqlite/agent-store/SqliteAgentStore'; import { createSqliteDb } from '../../../src/db/sqlite/client'; @@ -20,8 +27,10 @@ import { SqliteSandboxProviderStore } from '../../../src/db/sqlite/sandbox-provi import { SqliteSessionMetricsStore } from '../../../src/db/sqlite/session-metrics/SqliteSessionMetricsStore'; import { SqliteSessionStore } from '../../../src/db/sqlite/session-store/SqliteSessionStore'; import { SqliteSkillStore } from '../../../src/db/sqlite/skill-store/SqliteSkillStore'; +import { SqliteOAuthTokenStore } from '../../../src/db/sqlite/token-store/SqliteOAuthTokenStore'; import { SqliteWebSearchProviderStore } from '../../../src/db/sqlite/web-search-provider-store/SqliteWebSearchProviderStore'; import { ActiveTurnRegistry } from '../../../src/runtime/activeTurns'; +import { EventSubscriptionRegistry } from '../../../src/runtime/event-subscription/index.js'; import { ListSessionsResponseSchema } from '../../../src/schemas/session'; import { GetSessionMetricsChartDataResponseSchema, @@ -108,6 +117,30 @@ describe('sessions HTTP agent binding', () => { sessionDeps = deps; app = new OpenAPIHono(); app.route('/', createSessionsRouter(deps)); + const tokenStore = new SqliteOAuthTokenStore(db); + app.route( + '/', + createTurnsRouter({ + sessions, + sessionStore, + activeTurns: deps.activeTurns, + resolveModelProviderStore: () => modelProviderStore, + resolveMcpServerStore: () => + new McpServerWithAuthStore({ + store: mcpServerStore, + tokenStore, + clientName: 'test-client', + }), + resolveSkillStore: () => skillStore, + resolveAgentStore: () => agentStore, + eventSubscriptions: new EventSubscriptionRegistry(undefined), + resolveSandboxProviderStore: () => sandboxProviderStore, + resolveWebSearchProviderStore: () => webSearchProviderStore, + logger: deps.logger, + resolveRequestContext: deps.resolveRequestContext, + authorizer: deps.authorizer, + }), + ); app.route('/api/internal/sessions', createInternalSessionsRouter(deps)); app.route( '/api/internal/metrics', @@ -423,10 +456,139 @@ describe('sessions HTTP agent binding', () => { expect(eventsForbidden.status).toBe(403); expect(await eventsForbidden.json()).toEqual(forbiddenBody); + const sendEventsForbidden = await app.request( + '/other-user-session/turns/any-turn/events', + jsonInit('POST', { + events: [ + { + type: 'user.tool_approval', + thread_id: 'main', + tool_call_id: 'tc-1', + approval: { status: 'allow' }, + }, + ], + }), + ); + expect(sendEventsForbidden.status).toBe(403); + expect(await sendEventsForbidden.json()).toEqual(forbiddenBody); + const allowed = await app.request(`/${json.data.id}`); expect(allowed.status).toBe(200); }); + it('POST /sessions/{id}/turns/{turn_id}/events persists inbox rows for a running tip', async () => { + const created = await app.request('/', jsonInit('POST', { agent: { spec: inlineSpec } })); + expect(created.status).toBe(201); + const { data: session } = (await created.json()) as { data: { id: string } }; + await sessionStore.createTurn(makeCreateTurnInput({ sessionId: session.id, turnId: 'tip-1' })); + + const res = await app.request( + `/${session.id}/turns/tip-1/events`, + jsonInit('POST', { + events: [ + { + type: 'user.tool_approval', + thread_id: 'main', + tool_call_id: 'tc-1', + approval: { status: 'allow' }, + }, + { + type: 'user.tool_approval_policy', + policies: [ + { + server: 'github', + tool_name: 'create_issue', + action: { type: 'allow_session' }, + }, + ], + }, + ], + }), + ); + expect(res.status).toBe(201); + const body = (await res.json()) as { + data: Array<{ + id: string; + created_at: string; + type: string; + thread_id?: string; + tool_call_id?: string; + approval?: { status: string }; + policies?: unknown[]; + }>; + }; + expect(body.data).toHaveLength(2); + expect(body.data[0]).toMatchObject({ + type: 'user.tool_approval', + thread_id: 'main', + tool_call_id: 'tc-1', + approval: { status: 'allow' }, + }); + expect(body.data[0]?.id).toEqual(expect.any(String)); + expect(body.data[0]?.created_at).toEqual(expect.any(String)); + expect(body.data[1]).toMatchObject({ + type: 'user.tool_approval_policy', + policies: [ + { + server: 'github', + tool_name: 'create_issue', + action: { type: 'allow_session' }, + }, + ], + }); + + const pending = await sessionStore.listUnconsumedTurnInboundEvents({ + session_id: session.id, + turn_id: 'tip-1', + }); + expect(pending.map(e => e.event_id)).toEqual(body.data.map(e => e.id)); + expect(pending.map(e => e.payload.type)).toEqual(['user.tool_approval', 'user.tool_approval_policy']); + }); + + it('POST /sessions/{id}/turns/{turn_id}/events returns 404 for unknown tip and 409 for terminal tip', async () => { + const created = await app.request('/', jsonInit('POST', { agent: { spec: inlineSpec } })); + expect(created.status).toBe(201); + const { data: session } = (await created.json()) as { data: { id: string } }; + await sessionStore.createTurn(makeCreateTurnInput({ sessionId: session.id, turnId: 'tip-done' })); + const doneState = makeDoneTurnState(); + await sessionStore.updateTurnState({ + session_id: session.id, + turn_id: 'tip-done', + state: doneState, + turn_done_event: makeTurnDoneEvent(doneState), + }); + + const missing = await app.request( + `/${session.id}/turns/no-such-tip/events`, + jsonInit('POST', { + events: [ + { + type: 'user.tool_approval', + thread_id: 'main', + tool_call_id: 'tc-1', + approval: { status: 'allow' }, + }, + ], + }), + ); + expect(missing.status).toBe(404); + + const terminal = await app.request( + `/${session.id}/turns/tip-done/events`, + jsonInit('POST', { + events: [ + { + type: 'user.tool_approval', + thread_id: 'main', + tool_call_id: 'tc-1', + approval: { status: 'allow' }, + }, + ], + }), + ); + expect(terminal.status).toBe(409); + }); + it('rejects PATCH agent on a named session', async () => { const agent = await agentStore.createAgent({ tenant_id: 'default', diff --git a/packages/trueforge/tests/unit/apis/turns.test.ts b/packages/trueforge/tests/unit/apis/turns.test.ts index b8bdc9be2..db85a17a8 100644 --- a/packages/trueforge/tests/unit/apis/turns.test.ts +++ b/packages/trueforge/tests/unit/apis/turns.test.ts @@ -107,6 +107,23 @@ describe('turns', () => { expect(eventsResponse.status).toBe(403); expect(await eventsResponse.json()).toEqual(forbiddenAccess); + const createEventsResponse = await app.request('/s1/turns/any-turn/events', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + events: [ + { + type: 'user.tool_approval', + thread_id: 'main', + tool_call_id: 'tc-1', + approval: { status: 'allow' }, + }, + ], + }), + }); + expect(createEventsResponse.status).toBe(403); + expect(await createEventsResponse.json()).toEqual(forbiddenAccess); + const subscribeResponse = await app.request('/s1/turns/any-turn/subscribe'); expect(subscribeResponse.status).toBe(403); expect(await subscribeResponse.json()).toEqual(forbiddenAccess); @@ -193,6 +210,24 @@ describe('turns', () => { }) ).status, ).toBe(403); + expect( + ( + await app.request('/managed-session/turns/missing/events', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + events: [ + { + type: 'user.tool_approval', + thread_id: 'main', + tool_call_id: 'tc-1', + approval: { status: 'allow' }, + }, + ], + }), + }) + ).status, + ).toBe(403); }); }); From e8f80f1cd17333c9f115bbcdd2d7ad7d7c973ee0 Mon Sep 17 00:00:00 2001 From: "trueforge-dev-bot[bot]" Date: Wed, 23 Sep 2026 04:46:31 +0000 Subject: [PATCH 2/2] Regenerate OpenAPI document and SDKs --- ...60923044631-regenerate-sdk-from-openapi.md | 5 + .github/fern/openapi/openapi.json | 393 +++++++++++++++++- docs/openapi.json | 393 +++++++++++++++++- packages/trueforge-sdk/reference.md | 88 ++++ .../api/resources/sessions/client/Client.ts | 152 +++++++ .../requests/CreateTurnInboundEventRequest.ts | 21 + .../sessions/client/requests/index.ts | 1 + .../types/CreateTurnInboundEventResponse.ts | 8 + .../src/api/types/CreatedTurnInboundEvent.ts | 8 + .../src/api/types/SessionEvent.ts | 5 +- .../types/ToolApprovalPolicyAllowSession.ts | 7 + .../src/api/types/ToolApprovalPolicyItem.ts | 11 + .../src/api/types/TurnInboundEventItem.ts | 8 + .../src/api/types/TurnStreamingEvent.ts | 5 +- .../src/api/types/UserToolApprovalEvent.ts | 16 + .../api/types/UserToolApprovalPolicyEvent.ts | 13 + .../types/UserToolApprovalPolicyMessage.ts | 9 + .../src/api/types/UserToolResponseEvent.ts | 15 + packages/trueforge-sdk/src/api/types/index.ts | 9 + .../requests/CreateTurnInboundEventRequest.ts | 19 + .../sessions/client/requests/index.ts | 1 + .../types/CreateTurnInboundEventResponse.ts | 19 + .../types/CreatedTurnInboundEvent.ts | 21 + .../src/serialization/types/SessionEvent.ts | 11 +- .../types/ToolApprovalPolicyAllowSession.ts | 20 + .../types/ToolApprovalPolicyItem.ts | 23 + .../types/TurnInboundEventItem.ts | 21 + .../serialization/types/TurnStreamingEvent.ts | 11 +- .../types/UserToolApprovalEvent.ts | 29 ++ .../types/UserToolApprovalPolicyEvent.ts | 25 ++ .../types/UserToolApprovalPolicyMessage.ts | 21 + .../types/UserToolResponseEvent.ts | 28 ++ .../src/serialization/types/index.ts | 9 + .../trueforge-sdk/tests/wire/sessions.test.ts | 239 +++++++++++ python/trueforge_sdk/reference.md | 99 +++++ .../src/trueforge_sdk/__init__.py | 27 ++ .../src/trueforge_sdk/sessions/client.py | 120 ++++++ .../src/trueforge_sdk/sessions/raw_client.py | 217 ++++++++++ .../src/trueforge_sdk/types/__init__.py | 27 ++ .../create_turn_inbound_event_response.py | 23 + .../types/created_turn_inbound_event.py | 9 + .../src/trueforge_sdk/types/session_event.py | 6 + .../tool_approval_policy_allow_session.py | 24 ++ .../types/tool_approval_policy_item.py | 29 ++ .../types/turn_inbound_event_item.py | 9 + .../types/turn_streaming_event.py | 6 + .../types/user_tool_approval_event.py | 41 ++ .../types/user_tool_approval_policy_event.py | 35 ++ .../user_tool_approval_policy_message.py | 25 ++ .../types/user_tool_response_event.py | 44 ++ 50 files changed, 2397 insertions(+), 8 deletions(-) create mode 100644 .changeset/20260923044631-regenerate-sdk-from-openapi.md create mode 100644 packages/trueforge-sdk/src/api/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts create mode 100644 packages/trueforge-sdk/src/api/types/CreateTurnInboundEventResponse.ts create mode 100644 packages/trueforge-sdk/src/api/types/CreatedTurnInboundEvent.ts create mode 100644 packages/trueforge-sdk/src/api/types/ToolApprovalPolicyAllowSession.ts create mode 100644 packages/trueforge-sdk/src/api/types/ToolApprovalPolicyItem.ts create mode 100644 packages/trueforge-sdk/src/api/types/TurnInboundEventItem.ts create mode 100644 packages/trueforge-sdk/src/api/types/UserToolApprovalEvent.ts create mode 100644 packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyEvent.ts create mode 100644 packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyMessage.ts create mode 100644 packages/trueforge-sdk/src/api/types/UserToolResponseEvent.ts create mode 100644 packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/CreateTurnInboundEventResponse.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/CreatedTurnInboundEvent.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/ToolApprovalPolicyAllowSession.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/ToolApprovalPolicyItem.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/TurnInboundEventItem.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/UserToolApprovalEvent.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyEvent.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyMessage.ts create mode 100644 packages/trueforge-sdk/src/serialization/types/UserToolResponseEvent.ts create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/create_turn_inbound_event_response.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/created_turn_inbound_event.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_allow_session.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_item.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/turn_inbound_event_item.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_event.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_event.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_message.py create mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/user_tool_response_event.py diff --git a/.changeset/20260923044631-regenerate-sdk-from-openapi.md b/.changeset/20260923044631-regenerate-sdk-from-openapi.md new file mode 100644 index 000000000..efd8ff00f --- /dev/null +++ b/.changeset/20260923044631-regenerate-sdk-from-openapi.md @@ -0,0 +1,5 @@ +--- +"@truefoundry/trueforge-sdk": patch +--- + +Regenerate SDK from updated OpenAPI spec. diff --git a/.github/fern/openapi/openapi.json b/.github/fern/openapi/openapi.json index 114708b55..a98af15c3 100644 --- a/.github/fern/openapi/openapi.json +++ b/.github/fern/openapi/openapi.json @@ -1163,6 +1163,37 @@ ], "type": "object" }, + "CreateTurnInboundEventRequest": { + "properties": { + "events": { + "description": "One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`).", + "items": { + "$ref": "#/components/schemas/TurnInboundEventItem" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "events" + ], + "type": "object" + }, + "CreateTurnInboundEventResponse": { + "properties": { + "data": { + "description": "Created inbox events with server-minted `id` and `created_at`, in request order.", + "items": { + "$ref": "#/components/schemas/CreatedTurnInboundEvent" + }, + "type": "array" + } + }, + "required": [ + "data" + ], + "type": "object" + }, "CreateTurnRequest": { "properties": { "input": { @@ -1210,6 +1241,27 @@ ], "type": "object" }, + "CreatedTurnInboundEvent": { + "discriminator": { + "mapping": { + "user.tool_approval": "#/components/schemas/UserToolApprovalEvent", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyEvent", + "user.tool_response": "#/components/schemas/UserToolResponseEvent" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/UserToolApprovalEvent" + }, + { + "$ref": "#/components/schemas/UserToolResponseEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyEvent" + } + ] + }, "CronExpression": { "description": "Standard 5-field cron expression, evaluated in `timezone`.", "pattern": "^[\\d*,\\-/]+(?:\\s+[\\d*,\\-/]+){4}$", @@ -4108,7 +4160,10 @@ "tool.response_required": "#/components/schemas/ToolResponseRequiredEvent", "turn.created": "#/components/schemas/TurnCreatedEvent", "turn.done": "#/components/schemas/TurnDoneEvent", - "turn.update": "#/components/schemas/TurnUpdateEvent" + "turn.update": "#/components/schemas/TurnUpdateEvent", + "user.tool_approval": "#/components/schemas/UserToolApprovalEvent", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyEvent", + "user.tool_response": "#/components/schemas/UserToolResponseEvent" }, "propertyName": "type" }, @@ -4148,6 +4203,15 @@ }, { "$ref": "#/components/schemas/ToolResponseRequiredEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalEvent" + }, + { + "$ref": "#/components/schemas/UserToolResponseEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyEvent" } ] }, @@ -4778,6 +4842,58 @@ ], "type": "object" }, + "ToolApprovalPolicyAllowSession": { + "properties": { + "expire_at": { + "description": "ISO 8601 timestamp when this session allow expires. Omit to allow for the whole session.", + "type": "string" + }, + "type": { + "description": "Allow matching tool calls for the rest of this session.", + "enum": [ + "allow_session" + ], + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "ToolApprovalPolicyItem": { + "properties": { + "action": { + "discriminator": { + "mapping": { + "allow_session": "#/components/schemas/ToolApprovalPolicyAllowSession" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/ToolApprovalPolicyAllowSession" + } + ] + }, + "server": { + "description": "MCP server name the tool belongs to.", + "minLength": 1, + "type": "string" + }, + "tool_name": { + "description": "Tool name this policy applies to.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "server", + "tool_name", + "action" + ], + "type": "object" + }, "ToolApprovalRequiredEvent": { "properties": { "created_at": { @@ -5228,6 +5344,27 @@ ], "type": "object" }, + "TurnInboundEventItem": { + "discriminator": { + "mapping": { + "user.tool_approval": "#/components/schemas/UserToolApprovalMessage", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyMessage", + "user.tool_response": "#/components/schemas/UserToolResponseMessage" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/UserToolApprovalMessage" + }, + { + "$ref": "#/components/schemas/UserToolResponseMessage" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyMessage" + } + ] + }, "TurnInputItem": { "discriminator": { "mapping": { @@ -5467,7 +5604,10 @@ "tool.response_required": "#/components/schemas/ToolResponseRequiredEvent", "turn.created": "#/components/schemas/TurnCreatedEvent", "turn.done": "#/components/schemas/TurnDoneEvent", - "turn.update": "#/components/schemas/TurnUpdateEvent" + "turn.update": "#/components/schemas/TurnUpdateEvent", + "user.tool_approval": "#/components/schemas/UserToolApprovalEvent", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyEvent", + "user.tool_response": "#/components/schemas/UserToolResponseEvent" }, "propertyName": "type" }, @@ -5502,6 +5642,15 @@ { "$ref": "#/components/schemas/ToolResponseRequiredEvent" }, + { + "$ref": "#/components/schemas/UserToolApprovalEvent" + }, + { + "$ref": "#/components/schemas/UserToolResponseEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyEvent" + }, { "$ref": "#/components/schemas/TurnCreatedEvent" }, @@ -5764,6 +5913,47 @@ } ] }, + "UserToolApprovalEvent": { + "properties": { + "approval": { + "$ref": "#/components/schemas/ApprovalDecision" + }, + "created_at": { + "description": "ISO 8601 event timestamp.", + "type": "string" + }, + "id": { + "description": "Unique identifier for the event (monotonic ULID).", + "type": "string" + }, + "thread_id": { + "description": "Thread that owns the pending tool call.", + "minLength": 1, + "type": "string" + }, + "tool_call_id": { + "description": "Tool call id being approved or denied.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Client resume after tool.approval_required.", + "enum": [ + "user.tool_approval" + ], + "type": "string" + } + }, + "required": [ + "type", + "thread_id", + "tool_call_id", + "approval", + "id", + "created_at" + ], + "type": "object" + }, "UserToolApprovalMessage": { "properties": { "approval": { @@ -5795,6 +5985,107 @@ ], "type": "object" }, + "UserToolApprovalPolicyEvent": { + "properties": { + "created_at": { + "description": "ISO 8601 event timestamp.", + "type": "string" + }, + "id": { + "description": "Unique identifier for the event (monotonic ULID).", + "type": "string" + }, + "policies": { + "description": "One or more (server, tool_name) policy entries.", + "items": { + "$ref": "#/components/schemas/ToolApprovalPolicyItem" + }, + "minItems": 1, + "type": "array" + }, + "type": { + "description": "Sticky allow-session policy for matching tools (optional expiry).", + "enum": [ + "user.tool_approval_policy" + ], + "type": "string" + } + }, + "required": [ + "type", + "policies", + "id", + "created_at" + ], + "type": "object" + }, + "UserToolApprovalPolicyMessage": { + "properties": { + "policies": { + "description": "One or more (server, tool_name) policy entries.", + "items": { + "$ref": "#/components/schemas/ToolApprovalPolicyItem" + }, + "minItems": 1, + "type": "array" + }, + "type": { + "description": "Sticky allow-session policy for matching tools (optional expiry).", + "enum": [ + "user.tool_approval_policy" + ], + "type": "string" + } + }, + "required": [ + "type", + "policies" + ], + "type": "object" + }, + "UserToolResponseEvent": { + "properties": { + "content": { + "description": "Client-side tool result content.", + "minLength": 1, + "type": "string" + }, + "created_at": { + "description": "ISO 8601 event timestamp.", + "type": "string" + }, + "id": { + "description": "Unique identifier for the event (monotonic ULID).", + "type": "string" + }, + "thread_id": { + "description": "Thread that owns the pending tool call.", + "minLength": 1, + "type": "string" + }, + "tool_call_id": { + "description": "Tool call id receiving the client response.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Client resume after tool.response_required.", + "enum": [ + "user.tool_response" + ], + "type": "string" + } + }, + "required": [ + "type", + "thread_id", + "tool_call_id", + "content", + "id", + "created_at" + ], + "type": "object" + }, "UserToolResponseMessage": { "properties": { "content": { @@ -9349,6 +9640,104 @@ "sessions" ], "x-fern-sdk-method-name": "list_turn_events" + }, + "post": { + "description": "Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step.", + "parameters": [ + { + "description": "Session identifier.", + "in": "path", + "name": "session_id", + "required": true, + "schema": { + "description": "Session identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + }, + { + "description": "Turn identifier.", + "in": "path", + "name": "turn_id", + "required": true, + "schema": { + "description": "Turn identifier.", + "minLength": 1, + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTurnInboundEventRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTurnInboundEventResponse" + } + } + }, + "description": "Events created in the turn inbox." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid request body." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Caller is not the session creator." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Session or turn not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Turn is terminal, or an event id already exists." + } + }, + "summary": "Create turn inbound events", + "tags": [ + "Agent Sessions" + ], + "x-fern-sdk-group-name": [ + "sessions" + ], + "x-fern-sdk-method-name": "create_turn_inbound_event" } }, "/api/v1/sessions/{session_id}/turns/{turn_id}/subscribe": { diff --git a/docs/openapi.json b/docs/openapi.json index 114708b55..a98af15c3 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1163,6 +1163,37 @@ ], "type": "object" }, + "CreateTurnInboundEventRequest": { + "properties": { + "events": { + "description": "One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`).", + "items": { + "$ref": "#/components/schemas/TurnInboundEventItem" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "events" + ], + "type": "object" + }, + "CreateTurnInboundEventResponse": { + "properties": { + "data": { + "description": "Created inbox events with server-minted `id` and `created_at`, in request order.", + "items": { + "$ref": "#/components/schemas/CreatedTurnInboundEvent" + }, + "type": "array" + } + }, + "required": [ + "data" + ], + "type": "object" + }, "CreateTurnRequest": { "properties": { "input": { @@ -1210,6 +1241,27 @@ ], "type": "object" }, + "CreatedTurnInboundEvent": { + "discriminator": { + "mapping": { + "user.tool_approval": "#/components/schemas/UserToolApprovalEvent", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyEvent", + "user.tool_response": "#/components/schemas/UserToolResponseEvent" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/UserToolApprovalEvent" + }, + { + "$ref": "#/components/schemas/UserToolResponseEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyEvent" + } + ] + }, "CronExpression": { "description": "Standard 5-field cron expression, evaluated in `timezone`.", "pattern": "^[\\d*,\\-/]+(?:\\s+[\\d*,\\-/]+){4}$", @@ -4108,7 +4160,10 @@ "tool.response_required": "#/components/schemas/ToolResponseRequiredEvent", "turn.created": "#/components/schemas/TurnCreatedEvent", "turn.done": "#/components/schemas/TurnDoneEvent", - "turn.update": "#/components/schemas/TurnUpdateEvent" + "turn.update": "#/components/schemas/TurnUpdateEvent", + "user.tool_approval": "#/components/schemas/UserToolApprovalEvent", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyEvent", + "user.tool_response": "#/components/schemas/UserToolResponseEvent" }, "propertyName": "type" }, @@ -4148,6 +4203,15 @@ }, { "$ref": "#/components/schemas/ToolResponseRequiredEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalEvent" + }, + { + "$ref": "#/components/schemas/UserToolResponseEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyEvent" } ] }, @@ -4778,6 +4842,58 @@ ], "type": "object" }, + "ToolApprovalPolicyAllowSession": { + "properties": { + "expire_at": { + "description": "ISO 8601 timestamp when this session allow expires. Omit to allow for the whole session.", + "type": "string" + }, + "type": { + "description": "Allow matching tool calls for the rest of this session.", + "enum": [ + "allow_session" + ], + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "ToolApprovalPolicyItem": { + "properties": { + "action": { + "discriminator": { + "mapping": { + "allow_session": "#/components/schemas/ToolApprovalPolicyAllowSession" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/ToolApprovalPolicyAllowSession" + } + ] + }, + "server": { + "description": "MCP server name the tool belongs to.", + "minLength": 1, + "type": "string" + }, + "tool_name": { + "description": "Tool name this policy applies to.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "server", + "tool_name", + "action" + ], + "type": "object" + }, "ToolApprovalRequiredEvent": { "properties": { "created_at": { @@ -5228,6 +5344,27 @@ ], "type": "object" }, + "TurnInboundEventItem": { + "discriminator": { + "mapping": { + "user.tool_approval": "#/components/schemas/UserToolApprovalMessage", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyMessage", + "user.tool_response": "#/components/schemas/UserToolResponseMessage" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/UserToolApprovalMessage" + }, + { + "$ref": "#/components/schemas/UserToolResponseMessage" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyMessage" + } + ] + }, "TurnInputItem": { "discriminator": { "mapping": { @@ -5467,7 +5604,10 @@ "tool.response_required": "#/components/schemas/ToolResponseRequiredEvent", "turn.created": "#/components/schemas/TurnCreatedEvent", "turn.done": "#/components/schemas/TurnDoneEvent", - "turn.update": "#/components/schemas/TurnUpdateEvent" + "turn.update": "#/components/schemas/TurnUpdateEvent", + "user.tool_approval": "#/components/schemas/UserToolApprovalEvent", + "user.tool_approval_policy": "#/components/schemas/UserToolApprovalPolicyEvent", + "user.tool_response": "#/components/schemas/UserToolResponseEvent" }, "propertyName": "type" }, @@ -5502,6 +5642,15 @@ { "$ref": "#/components/schemas/ToolResponseRequiredEvent" }, + { + "$ref": "#/components/schemas/UserToolApprovalEvent" + }, + { + "$ref": "#/components/schemas/UserToolResponseEvent" + }, + { + "$ref": "#/components/schemas/UserToolApprovalPolicyEvent" + }, { "$ref": "#/components/schemas/TurnCreatedEvent" }, @@ -5764,6 +5913,47 @@ } ] }, + "UserToolApprovalEvent": { + "properties": { + "approval": { + "$ref": "#/components/schemas/ApprovalDecision" + }, + "created_at": { + "description": "ISO 8601 event timestamp.", + "type": "string" + }, + "id": { + "description": "Unique identifier for the event (monotonic ULID).", + "type": "string" + }, + "thread_id": { + "description": "Thread that owns the pending tool call.", + "minLength": 1, + "type": "string" + }, + "tool_call_id": { + "description": "Tool call id being approved or denied.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Client resume after tool.approval_required.", + "enum": [ + "user.tool_approval" + ], + "type": "string" + } + }, + "required": [ + "type", + "thread_id", + "tool_call_id", + "approval", + "id", + "created_at" + ], + "type": "object" + }, "UserToolApprovalMessage": { "properties": { "approval": { @@ -5795,6 +5985,107 @@ ], "type": "object" }, + "UserToolApprovalPolicyEvent": { + "properties": { + "created_at": { + "description": "ISO 8601 event timestamp.", + "type": "string" + }, + "id": { + "description": "Unique identifier for the event (monotonic ULID).", + "type": "string" + }, + "policies": { + "description": "One or more (server, tool_name) policy entries.", + "items": { + "$ref": "#/components/schemas/ToolApprovalPolicyItem" + }, + "minItems": 1, + "type": "array" + }, + "type": { + "description": "Sticky allow-session policy for matching tools (optional expiry).", + "enum": [ + "user.tool_approval_policy" + ], + "type": "string" + } + }, + "required": [ + "type", + "policies", + "id", + "created_at" + ], + "type": "object" + }, + "UserToolApprovalPolicyMessage": { + "properties": { + "policies": { + "description": "One or more (server, tool_name) policy entries.", + "items": { + "$ref": "#/components/schemas/ToolApprovalPolicyItem" + }, + "minItems": 1, + "type": "array" + }, + "type": { + "description": "Sticky allow-session policy for matching tools (optional expiry).", + "enum": [ + "user.tool_approval_policy" + ], + "type": "string" + } + }, + "required": [ + "type", + "policies" + ], + "type": "object" + }, + "UserToolResponseEvent": { + "properties": { + "content": { + "description": "Client-side tool result content.", + "minLength": 1, + "type": "string" + }, + "created_at": { + "description": "ISO 8601 event timestamp.", + "type": "string" + }, + "id": { + "description": "Unique identifier for the event (monotonic ULID).", + "type": "string" + }, + "thread_id": { + "description": "Thread that owns the pending tool call.", + "minLength": 1, + "type": "string" + }, + "tool_call_id": { + "description": "Tool call id receiving the client response.", + "minLength": 1, + "type": "string" + }, + "type": { + "description": "Client resume after tool.response_required.", + "enum": [ + "user.tool_response" + ], + "type": "string" + } + }, + "required": [ + "type", + "thread_id", + "tool_call_id", + "content", + "id", + "created_at" + ], + "type": "object" + }, "UserToolResponseMessage": { "properties": { "content": { @@ -9349,6 +9640,104 @@ "sessions" ], "x-fern-sdk-method-name": "list_turn_events" + }, + "post": { + "description": "Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step.", + "parameters": [ + { + "description": "Session identifier.", + "in": "path", + "name": "session_id", + "required": true, + "schema": { + "description": "Session identifier.", + "maxLength": 64, + "minLength": 1, + "type": "string" + } + }, + { + "description": "Turn identifier.", + "in": "path", + "name": "turn_id", + "required": true, + "schema": { + "description": "Turn identifier.", + "minLength": 1, + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTurnInboundEventRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTurnInboundEventResponse" + } + } + }, + "description": "Events created in the turn inbox." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Invalid request body." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Caller is not the session creator." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Session or turn not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestErrorResponse" + } + } + }, + "description": "Turn is terminal, or an event id already exists." + } + }, + "summary": "Create turn inbound events", + "tags": [ + "Agent Sessions" + ], + "x-fern-sdk-group-name": [ + "sessions" + ], + "x-fern-sdk-method-name": "create_turn_inbound_event" } }, "/api/v1/sessions/{session_id}/turns/{turn_id}/subscribe": { diff --git a/packages/trueforge-sdk/reference.md b/packages/trueforge-sdk/reference.md index d4d1e893f..7ca82eba3 100644 --- a/packages/trueforge-sdk/reference.md +++ b/packages/trueforge-sdk/reference.md @@ -2366,6 +2366,94 @@ const response = page.response; + + + + +
client.sessions.createTurnInboundEvent(session_id, turn_id, { ...params }) -> TrueForge.CreateTurnInboundEventResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.sessions.createTurnInboundEvent("session_id", "turn_id", { + events: [{ + approval: { + status: "allow" + }, + threadId: "thread_id", + toolCallId: "tool_call_id", + type: "user.tool_approval" + }] +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**session_id:** `string` — Session identifier. + +
+
+ +
+
+ +**turn_id:** `string` — Turn identifier. + +
+
+ +
+
+ +**request:** `TrueForge.CreateTurnInboundEventRequest` + +
+
+ +
+
+ +**requestOptions:** `SessionsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/packages/trueforge-sdk/src/api/resources/sessions/client/Client.ts b/packages/trueforge-sdk/src/api/resources/sessions/client/Client.ts index 422c38ef2..b4555d19d 100644 --- a/packages/trueforge-sdk/src/api/resources/sessions/client/Client.ts +++ b/packages/trueforge-sdk/src/api/resources/sessions/client/Client.ts @@ -1742,6 +1742,158 @@ export class SessionsClient { }); } + /** + * Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step. + * + * @param {string} session_id - Session identifier. + * @param {string} turn_id - Turn identifier. + * @param {TrueForge.CreateTurnInboundEventRequest} request + * @param {SessionsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link TrueForge.BadRequestError} + * @throws {@link TrueForge.ForbiddenError} + * @throws {@link TrueForge.NotFoundError} + * @throws {@link TrueForge.ConflictError} + * @throws {@link errors.TrueForgeError} + * @throws {@link errors.TrueForgeTimeoutError} + * + * @example + * await client.sessions.createTurnInboundEvent("session_id", "turn_id", { + * events: [{ + * approval: { + * status: "allow" + * }, + * threadId: "thread_id", + * toolCallId: "tool_call_id", + * type: "user.tool_approval" + * }] + * }) + */ + public createTurnInboundEvent( + session_id: string, + turn_id: string, + request: TrueForge.CreateTurnInboundEventRequest, + requestOptions?: SessionsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__createTurnInboundEvent(session_id, turn_id, request, requestOptions), + ); + } + + private async __createTurnInboundEvent( + session_id: string, + turn_id: string, + request: TrueForge.CreateTurnInboundEventRequest, + requestOptions?: SessionsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `api/v1/sessions/${core.url.encodePathParam(session_id)}/turns/${core.url.encodePathParam(turn_id)}/events`, + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: mergeAdditionalBodyParameters( + serializers.CreateTurnInboundEventRequest.jsonOrThrow(request, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + omitUndefined: true, + }), + requestOptions?.additionalBodyParameters, + ), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.CreateTurnInboundEventResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new TrueForge.BadRequestError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 403: + throw new TrueForge.ForbiddenError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 404: + throw new TrueForge.NotFoundError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + case 409: + throw new TrueForge.ConflictError( + serializers.RequestErrorResponse.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.TrueForgeError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/api/v1/sessions/{session_id}/turns/{turn_id}/events", + ); + } + /** * Subscribe to the live SSE stream for a turn. Only the session creator may subscribe. Pass `after_sequence_number` to resume after a disconnect (exclusive — events after this sequence number are replayed). */ diff --git a/packages/trueforge-sdk/src/api/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts b/packages/trueforge-sdk/src/api/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts new file mode 100644 index 000000000..89bcebb2e --- /dev/null +++ b/packages/trueforge-sdk/src/api/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../../../index.js"; + +/** + * @example + * { + * events: [{ + * approval: { + * status: "allow" + * }, + * threadId: "thread_id", + * toolCallId: "tool_call_id", + * type: "user.tool_approval" + * }] + * } + */ +export interface CreateTurnInboundEventRequest { + /** One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`). */ + events: TrueForge.TurnInboundEventItem[]; +} diff --git a/packages/trueforge-sdk/src/api/resources/sessions/client/requests/index.ts b/packages/trueforge-sdk/src/api/resources/sessions/client/requests/index.ts index 5299c014d..a113751e0 100644 --- a/packages/trueforge-sdk/src/api/resources/sessions/client/requests/index.ts +++ b/packages/trueforge-sdk/src/api/resources/sessions/client/requests/index.ts @@ -1,5 +1,6 @@ export type { CancelSessionRequest } from "./CancelSessionRequest.js"; export type { CreateSessionRequest } from "./CreateSessionRequest.js"; +export type { CreateTurnInboundEventRequest } from "./CreateTurnInboundEventRequest.js"; export type { CreateTurnSessionsRequest } from "./CreateTurnSessionsRequest.js"; export type { CreateTurnSessionsStreamRequest } from "./CreateTurnSessionsStreamRequest.js"; export type { DownloadSandboxFileSessionsRequest } from "./DownloadSandboxFileSessionsRequest.js"; diff --git a/packages/trueforge-sdk/src/api/types/CreateTurnInboundEventResponse.ts b/packages/trueforge-sdk/src/api/types/CreateTurnInboundEventResponse.ts new file mode 100644 index 000000000..af4d86548 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/CreateTurnInboundEventResponse.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface CreateTurnInboundEventResponse { + /** Created inbox events with server-minted `id` and `created_at`, in request order. */ + data: TrueForge.CreatedTurnInboundEvent[]; +} diff --git a/packages/trueforge-sdk/src/api/types/CreatedTurnInboundEvent.ts b/packages/trueforge-sdk/src/api/types/CreatedTurnInboundEvent.ts new file mode 100644 index 000000000..7a065c229 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/CreatedTurnInboundEvent.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export type CreatedTurnInboundEvent = + | TrueForge.UserToolApprovalEvent + | TrueForge.UserToolApprovalPolicyEvent + | TrueForge.UserToolResponseEvent; diff --git a/packages/trueforge-sdk/src/api/types/SessionEvent.ts b/packages/trueforge-sdk/src/api/types/SessionEvent.ts index 8d305b279..d47a36b5b 100644 --- a/packages/trueforge-sdk/src/api/types/SessionEvent.ts +++ b/packages/trueforge-sdk/src/api/types/SessionEvent.ts @@ -14,4 +14,7 @@ export type SessionEvent = | TrueForge.ToolResponseRequiredEvent | TrueForge.TurnCreatedEvent | TrueForge.TurnDoneEvent - | TrueForge.TurnUpdateEvent; + | TrueForge.TurnUpdateEvent + | TrueForge.UserToolApprovalEvent + | TrueForge.UserToolApprovalPolicyEvent + | TrueForge.UserToolResponseEvent; diff --git a/packages/trueforge-sdk/src/api/types/ToolApprovalPolicyAllowSession.ts b/packages/trueforge-sdk/src/api/types/ToolApprovalPolicyAllowSession.ts new file mode 100644 index 000000000..a01e8383b --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/ToolApprovalPolicyAllowSession.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ToolApprovalPolicyAllowSession { + /** ISO 8601 timestamp when this session allow expires. Omit to allow for the whole session. */ + expireAt?: string; + type: "allow_session"; +} diff --git a/packages/trueforge-sdk/src/api/types/ToolApprovalPolicyItem.ts b/packages/trueforge-sdk/src/api/types/ToolApprovalPolicyItem.ts new file mode 100644 index 000000000..ee585d838 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/ToolApprovalPolicyItem.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface ToolApprovalPolicyItem { + action: TrueForge.ToolApprovalPolicyAllowSession; + /** MCP server name the tool belongs to. */ + server: string; + /** Tool name this policy applies to. */ + toolName: string; +} diff --git a/packages/trueforge-sdk/src/api/types/TurnInboundEventItem.ts b/packages/trueforge-sdk/src/api/types/TurnInboundEventItem.ts new file mode 100644 index 000000000..e23d0668c --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/TurnInboundEventItem.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export type TurnInboundEventItem = + | TrueForge.UserToolApprovalMessage + | TrueForge.UserToolApprovalPolicyMessage + | TrueForge.UserToolResponseMessage; diff --git a/packages/trueforge-sdk/src/api/types/TurnStreamingEvent.ts b/packages/trueforge-sdk/src/api/types/TurnStreamingEvent.ts index 61f6515ac..35de83b0b 100644 --- a/packages/trueforge-sdk/src/api/types/TurnStreamingEvent.ts +++ b/packages/trueforge-sdk/src/api/types/TurnStreamingEvent.ts @@ -15,4 +15,7 @@ export type TurnStreamingEvent = | TrueForge.ToolResponseRequiredEvent | TrueForge.TurnCreatedEvent | TrueForge.TurnDoneEvent - | TrueForge.TurnUpdateEvent; + | TrueForge.TurnUpdateEvent + | TrueForge.UserToolApprovalEvent + | TrueForge.UserToolApprovalPolicyEvent + | TrueForge.UserToolResponseEvent; diff --git a/packages/trueforge-sdk/src/api/types/UserToolApprovalEvent.ts b/packages/trueforge-sdk/src/api/types/UserToolApprovalEvent.ts new file mode 100644 index 000000000..d026b2b79 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/UserToolApprovalEvent.ts @@ -0,0 +1,16 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface UserToolApprovalEvent { + approval: TrueForge.ApprovalDecision; + /** ISO 8601 event timestamp. */ + createdAt: string; + /** Unique identifier for the event (monotonic ULID). */ + id: string; + /** Thread that owns the pending tool call. */ + threadId: string; + /** Tool call id being approved or denied. */ + toolCallId: string; + type: "user.tool_approval"; +} diff --git a/packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyEvent.ts b/packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyEvent.ts new file mode 100644 index 000000000..346e5b739 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyEvent.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface UserToolApprovalPolicyEvent { + /** ISO 8601 event timestamp. */ + createdAt: string; + /** Unique identifier for the event (monotonic ULID). */ + id: string; + /** One or more (server, tool_name) policy entries. */ + policies: TrueForge.ToolApprovalPolicyItem[]; + type: "user.tool_approval_policy"; +} diff --git a/packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyMessage.ts b/packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyMessage.ts new file mode 100644 index 000000000..41a7ac8ea --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/UserToolApprovalPolicyMessage.ts @@ -0,0 +1,9 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../index.js"; + +export interface UserToolApprovalPolicyMessage { + /** One or more (server, tool_name) policy entries. */ + policies: TrueForge.ToolApprovalPolicyItem[]; + type: "user.tool_approval_policy"; +} diff --git a/packages/trueforge-sdk/src/api/types/UserToolResponseEvent.ts b/packages/trueforge-sdk/src/api/types/UserToolResponseEvent.ts new file mode 100644 index 000000000..aec481da0 --- /dev/null +++ b/packages/trueforge-sdk/src/api/types/UserToolResponseEvent.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface UserToolResponseEvent { + /** Client-side tool result content. */ + content: string; + /** ISO 8601 event timestamp. */ + createdAt: string; + /** Unique identifier for the event (monotonic ULID). */ + id: string; + /** Thread that owns the pending tool call. */ + threadId: string; + /** Tool call id receiving the client response. */ + toolCallId: string; + type: "user.tool_response"; +} diff --git a/packages/trueforge-sdk/src/api/types/index.ts b/packages/trueforge-sdk/src/api/types/index.ts index b51454d87..1c08669c3 100644 --- a/packages/trueforge-sdk/src/api/types/index.ts +++ b/packages/trueforge-sdk/src/api/types/index.ts @@ -47,8 +47,10 @@ export * from "./ConfiguredSkill.js"; export * from "./ConfiguredWebSearchProvider.js"; export * from "./ContextManagementConfig.js"; export * from "./CreatedBySubject.js"; +export * from "./CreatedTurnInboundEvent.js"; export * from "./CreateScheduleRunResponse.js"; export * from "./CreateSessionAgent.js"; +export * from "./CreateTurnInboundEventResponse.js"; export * from "./CronExpression.js"; export * from "./CustomModelProvider.js"; export * from "./DaytonaSandboxProviderAuth.js"; @@ -203,6 +205,8 @@ export * from "./ThreadStateError.js"; export * from "./Timezone.js"; export * from "./TogetherAiModelProvider.js"; export * from "./TokenPagination.js"; +export * from "./ToolApprovalPolicyAllowSession.js"; +export * from "./ToolApprovalPolicyItem.js"; export * from "./ToolApprovalRequiredEvent.js"; export * from "./ToolCall.js"; export * from "./ToolCallRef.js"; @@ -217,6 +221,7 @@ export * from "./Turn.js"; export * from "./TurnCreatedEvent.js"; export * from "./TurnDoneEvent.js"; export * from "./TurnDoneEventState.js"; +export * from "./TurnInboundEventItem.js"; export * from "./TurnInputItem.js"; export * from "./TurnMetrics.js"; export * from "./TurnState.js"; @@ -235,7 +240,11 @@ export * from "./TurnUpdateStateRunning.js"; export * from "./UserMessage.js"; export * from "./UserMessageContent.js"; export * from "./UserMessageContentItem.js"; +export * from "./UserToolApprovalEvent.js"; export * from "./UserToolApprovalMessage.js"; +export * from "./UserToolApprovalPolicyEvent.js"; +export * from "./UserToolApprovalPolicyMessage.js"; +export * from "./UserToolResponseEvent.js"; export * from "./UserToolResponseMessage.js"; export * from "./WebSearchCapability.js"; export * from "./WebSearchConfig.js"; diff --git a/packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts b/packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts new file mode 100644 index 000000000..69464825a --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/CreateTurnInboundEventRequest.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../../../../api/index.js"; +import * as core from "../../../../../core/index.js"; +import type * as serializers from "../../../../index.js"; +import { TurnInboundEventItem } from "../../../../types/TurnInboundEventItem.js"; + +export const CreateTurnInboundEventRequest: core.serialization.Schema< + serializers.CreateTurnInboundEventRequest.Raw, + TrueForge.CreateTurnInboundEventRequest +> = core.serialization.object({ + events: core.serialization.list(TurnInboundEventItem), +}); + +export declare namespace CreateTurnInboundEventRequest { + export interface Raw { + events: TurnInboundEventItem.Raw[]; + } +} diff --git a/packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/index.ts b/packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/index.ts index 834355641..d5b8969f1 100644 --- a/packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/index.ts +++ b/packages/trueforge-sdk/src/serialization/resources/sessions/client/requests/index.ts @@ -1,5 +1,6 @@ export { CancelSessionRequest } from "./CancelSessionRequest.js"; export { CreateSessionRequest } from "./CreateSessionRequest.js"; +export { CreateTurnInboundEventRequest } from "./CreateTurnInboundEventRequest.js"; export { CreateTurnSessionsRequest } from "./CreateTurnSessionsRequest.js"; export { CreateTurnSessionsStreamRequest } from "./CreateTurnSessionsStreamRequest.js"; export { UpdateSessionRequest } from "./UpdateSessionRequest.js"; diff --git a/packages/trueforge-sdk/src/serialization/types/CreateTurnInboundEventResponse.ts b/packages/trueforge-sdk/src/serialization/types/CreateTurnInboundEventResponse.ts new file mode 100644 index 000000000..af0be6de0 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/CreateTurnInboundEventResponse.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { CreatedTurnInboundEvent } from "./CreatedTurnInboundEvent.js"; + +export const CreateTurnInboundEventResponse: core.serialization.ObjectSchema< + serializers.CreateTurnInboundEventResponse.Raw, + TrueForge.CreateTurnInboundEventResponse +> = core.serialization.object({ + data: core.serialization.list(CreatedTurnInboundEvent), +}); + +export declare namespace CreateTurnInboundEventResponse { + export interface Raw { + data: CreatedTurnInboundEvent.Raw[]; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/CreatedTurnInboundEvent.ts b/packages/trueforge-sdk/src/serialization/types/CreatedTurnInboundEvent.ts new file mode 100644 index 000000000..ae2db93d7 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/CreatedTurnInboundEvent.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { UserToolApprovalEvent } from "./UserToolApprovalEvent.js"; +import { UserToolApprovalPolicyEvent } from "./UserToolApprovalPolicyEvent.js"; +import { UserToolResponseEvent } from "./UserToolResponseEvent.js"; + +export const CreatedTurnInboundEvent: core.serialization.Schema< + serializers.CreatedTurnInboundEvent.Raw, + TrueForge.CreatedTurnInboundEvent +> = core.serialization.undiscriminatedUnion([ + UserToolApprovalEvent, + UserToolApprovalPolicyEvent, + UserToolResponseEvent, +]); + +export declare namespace CreatedTurnInboundEvent { + export type Raw = UserToolApprovalEvent.Raw | UserToolApprovalPolicyEvent.Raw | UserToolResponseEvent.Raw; +} diff --git a/packages/trueforge-sdk/src/serialization/types/SessionEvent.ts b/packages/trueforge-sdk/src/serialization/types/SessionEvent.ts index bd14830f2..68d0482fc 100644 --- a/packages/trueforge-sdk/src/serialization/types/SessionEvent.ts +++ b/packages/trueforge-sdk/src/serialization/types/SessionEvent.ts @@ -15,6 +15,9 @@ import { ToolResponseRequiredEvent } from "./ToolResponseRequiredEvent.js"; import { TurnCreatedEvent } from "./TurnCreatedEvent.js"; import { TurnDoneEvent } from "./TurnDoneEvent.js"; import { TurnUpdateEvent } from "./TurnUpdateEvent.js"; +import { UserToolApprovalEvent } from "./UserToolApprovalEvent.js"; +import { UserToolApprovalPolicyEvent } from "./UserToolApprovalPolicyEvent.js"; +import { UserToolResponseEvent } from "./UserToolResponseEvent.js"; export const SessionEvent: core.serialization.Schema = core.serialization.undiscriminatedUnion([ @@ -30,6 +33,9 @@ export const SessionEvent: core.serialization.Schema = core.serialization.object({ + expireAt: core.serialization.property("expire_at", core.serialization.string().optional()), + type: core.serialization.stringLiteral("allow_session"), +}); + +export declare namespace ToolApprovalPolicyAllowSession { + export interface Raw { + expire_at?: string | null; + type: "allow_session"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/ToolApprovalPolicyItem.ts b/packages/trueforge-sdk/src/serialization/types/ToolApprovalPolicyItem.ts new file mode 100644 index 000000000..0390bc75c --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/ToolApprovalPolicyItem.ts @@ -0,0 +1,23 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { ToolApprovalPolicyAllowSession } from "./ToolApprovalPolicyAllowSession.js"; + +export const ToolApprovalPolicyItem: core.serialization.ObjectSchema< + serializers.ToolApprovalPolicyItem.Raw, + TrueForge.ToolApprovalPolicyItem +> = core.serialization.object({ + action: ToolApprovalPolicyAllowSession, + server: core.serialization.string(), + toolName: core.serialization.property("tool_name", core.serialization.string()), +}); + +export declare namespace ToolApprovalPolicyItem { + export interface Raw { + action: ToolApprovalPolicyAllowSession.Raw; + server: string; + tool_name: string; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/TurnInboundEventItem.ts b/packages/trueforge-sdk/src/serialization/types/TurnInboundEventItem.ts new file mode 100644 index 000000000..e8770bb75 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/TurnInboundEventItem.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { UserToolApprovalMessage } from "./UserToolApprovalMessage.js"; +import { UserToolApprovalPolicyMessage } from "./UserToolApprovalPolicyMessage.js"; +import { UserToolResponseMessage } from "./UserToolResponseMessage.js"; + +export const TurnInboundEventItem: core.serialization.Schema< + serializers.TurnInboundEventItem.Raw, + TrueForge.TurnInboundEventItem +> = core.serialization.undiscriminatedUnion([ + UserToolApprovalMessage, + UserToolApprovalPolicyMessage, + UserToolResponseMessage, +]); + +export declare namespace TurnInboundEventItem { + export type Raw = UserToolApprovalMessage.Raw | UserToolApprovalPolicyMessage.Raw | UserToolResponseMessage.Raw; +} diff --git a/packages/trueforge-sdk/src/serialization/types/TurnStreamingEvent.ts b/packages/trueforge-sdk/src/serialization/types/TurnStreamingEvent.ts index 29756a573..d160ee721 100644 --- a/packages/trueforge-sdk/src/serialization/types/TurnStreamingEvent.ts +++ b/packages/trueforge-sdk/src/serialization/types/TurnStreamingEvent.ts @@ -16,6 +16,9 @@ import { ToolResponseRequiredEvent } from "./ToolResponseRequiredEvent.js"; import { TurnCreatedEvent } from "./TurnCreatedEvent.js"; import { TurnDoneEvent } from "./TurnDoneEvent.js"; import { TurnUpdateEvent } from "./TurnUpdateEvent.js"; +import { UserToolApprovalEvent } from "./UserToolApprovalEvent.js"; +import { UserToolApprovalPolicyEvent } from "./UserToolApprovalPolicyEvent.js"; +import { UserToolResponseEvent } from "./UserToolResponseEvent.js"; export const TurnStreamingEvent: core.serialization.Schema< serializers.TurnStreamingEvent.Raw, @@ -34,6 +37,9 @@ export const TurnStreamingEvent: core.serialization.Schema< TurnCreatedEvent, TurnDoneEvent, TurnUpdateEvent, + UserToolApprovalEvent, + UserToolApprovalPolicyEvent, + UserToolResponseEvent, ]); export declare namespace TurnStreamingEvent { @@ -50,5 +56,8 @@ export declare namespace TurnStreamingEvent { | ToolResponseRequiredEvent.Raw | TurnCreatedEvent.Raw | TurnDoneEvent.Raw - | TurnUpdateEvent.Raw; + | TurnUpdateEvent.Raw + | UserToolApprovalEvent.Raw + | UserToolApprovalPolicyEvent.Raw + | UserToolResponseEvent.Raw; } diff --git a/packages/trueforge-sdk/src/serialization/types/UserToolApprovalEvent.ts b/packages/trueforge-sdk/src/serialization/types/UserToolApprovalEvent.ts new file mode 100644 index 000000000..edce2158c --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/UserToolApprovalEvent.ts @@ -0,0 +1,29 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { ApprovalDecision } from "./ApprovalDecision.js"; + +export const UserToolApprovalEvent: core.serialization.ObjectSchema< + serializers.UserToolApprovalEvent.Raw, + TrueForge.UserToolApprovalEvent +> = core.serialization.object({ + approval: ApprovalDecision, + createdAt: core.serialization.property("created_at", core.serialization.string()), + id: core.serialization.string(), + threadId: core.serialization.property("thread_id", core.serialization.string()), + toolCallId: core.serialization.property("tool_call_id", core.serialization.string()), + type: core.serialization.stringLiteral("user.tool_approval"), +}); + +export declare namespace UserToolApprovalEvent { + export interface Raw { + approval: ApprovalDecision.Raw; + created_at: string; + id: string; + thread_id: string; + tool_call_id: string; + type: "user.tool_approval"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyEvent.ts b/packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyEvent.ts new file mode 100644 index 000000000..80d201d3c --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyEvent.ts @@ -0,0 +1,25 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { ToolApprovalPolicyItem } from "./ToolApprovalPolicyItem.js"; + +export const UserToolApprovalPolicyEvent: core.serialization.ObjectSchema< + serializers.UserToolApprovalPolicyEvent.Raw, + TrueForge.UserToolApprovalPolicyEvent +> = core.serialization.object({ + createdAt: core.serialization.property("created_at", core.serialization.string()), + id: core.serialization.string(), + policies: core.serialization.list(ToolApprovalPolicyItem), + type: core.serialization.stringLiteral("user.tool_approval_policy"), +}); + +export declare namespace UserToolApprovalPolicyEvent { + export interface Raw { + created_at: string; + id: string; + policies: ToolApprovalPolicyItem.Raw[]; + type: "user.tool_approval_policy"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyMessage.ts b/packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyMessage.ts new file mode 100644 index 000000000..a8ca2cea7 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/UserToolApprovalPolicyMessage.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; +import { ToolApprovalPolicyItem } from "./ToolApprovalPolicyItem.js"; + +export const UserToolApprovalPolicyMessage: core.serialization.ObjectSchema< + serializers.UserToolApprovalPolicyMessage.Raw, + TrueForge.UserToolApprovalPolicyMessage +> = core.serialization.object({ + policies: core.serialization.list(ToolApprovalPolicyItem), + type: core.serialization.stringLiteral("user.tool_approval_policy"), +}); + +export declare namespace UserToolApprovalPolicyMessage { + export interface Raw { + policies: ToolApprovalPolicyItem.Raw[]; + type: "user.tool_approval_policy"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/UserToolResponseEvent.ts b/packages/trueforge-sdk/src/serialization/types/UserToolResponseEvent.ts new file mode 100644 index 000000000..bcce18b61 --- /dev/null +++ b/packages/trueforge-sdk/src/serialization/types/UserToolResponseEvent.ts @@ -0,0 +1,28 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as TrueForge from "../../api/index.js"; +import * as core from "../../core/index.js"; +import type * as serializers from "../index.js"; + +export const UserToolResponseEvent: core.serialization.ObjectSchema< + serializers.UserToolResponseEvent.Raw, + TrueForge.UserToolResponseEvent +> = core.serialization.object({ + content: core.serialization.string(), + createdAt: core.serialization.property("created_at", core.serialization.string()), + id: core.serialization.string(), + threadId: core.serialization.property("thread_id", core.serialization.string()), + toolCallId: core.serialization.property("tool_call_id", core.serialization.string()), + type: core.serialization.stringLiteral("user.tool_response"), +}); + +export declare namespace UserToolResponseEvent { + export interface Raw { + content: string; + created_at: string; + id: string; + thread_id: string; + tool_call_id: string; + type: "user.tool_response"; + } +} diff --git a/packages/trueforge-sdk/src/serialization/types/index.ts b/packages/trueforge-sdk/src/serialization/types/index.ts index b51454d87..1c08669c3 100644 --- a/packages/trueforge-sdk/src/serialization/types/index.ts +++ b/packages/trueforge-sdk/src/serialization/types/index.ts @@ -47,8 +47,10 @@ export * from "./ConfiguredSkill.js"; export * from "./ConfiguredWebSearchProvider.js"; export * from "./ContextManagementConfig.js"; export * from "./CreatedBySubject.js"; +export * from "./CreatedTurnInboundEvent.js"; export * from "./CreateScheduleRunResponse.js"; export * from "./CreateSessionAgent.js"; +export * from "./CreateTurnInboundEventResponse.js"; export * from "./CronExpression.js"; export * from "./CustomModelProvider.js"; export * from "./DaytonaSandboxProviderAuth.js"; @@ -203,6 +205,8 @@ export * from "./ThreadStateError.js"; export * from "./Timezone.js"; export * from "./TogetherAiModelProvider.js"; export * from "./TokenPagination.js"; +export * from "./ToolApprovalPolicyAllowSession.js"; +export * from "./ToolApprovalPolicyItem.js"; export * from "./ToolApprovalRequiredEvent.js"; export * from "./ToolCall.js"; export * from "./ToolCallRef.js"; @@ -217,6 +221,7 @@ export * from "./Turn.js"; export * from "./TurnCreatedEvent.js"; export * from "./TurnDoneEvent.js"; export * from "./TurnDoneEventState.js"; +export * from "./TurnInboundEventItem.js"; export * from "./TurnInputItem.js"; export * from "./TurnMetrics.js"; export * from "./TurnState.js"; @@ -235,7 +240,11 @@ export * from "./TurnUpdateStateRunning.js"; export * from "./UserMessage.js"; export * from "./UserMessageContent.js"; export * from "./UserMessageContentItem.js"; +export * from "./UserToolApprovalEvent.js"; export * from "./UserToolApprovalMessage.js"; +export * from "./UserToolApprovalPolicyEvent.js"; +export * from "./UserToolApprovalPolicyMessage.js"; +export * from "./UserToolResponseEvent.js"; export * from "./UserToolResponseMessage.js"; export * from "./WebSearchCapability.js"; export * from "./WebSearchConfig.js"; diff --git a/packages/trueforge-sdk/tests/wire/sessions.test.ts b/packages/trueforge-sdk/tests/wire/sessions.test.ts index 0eb6c2dc9..9686a1422 100644 --- a/packages/trueforge-sdk/tests/wire/sessions.test.ts +++ b/packages/trueforge-sdk/tests/wire/sessions.test.ts @@ -1398,6 +1398,245 @@ describe("SessionsClient", () => { }).rejects.toThrow(TrueForgeTypes.NotFoundError); }); + test("create_turn_inbound_event (1)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + events: [ + { + approval: { status: "allow" }, + thread_id: "thread_id", + tool_call_id: "tool_call_id", + type: "user.tool_approval", + }, + ], + }; + const rawResponseBody = { + data: [ + { + approval: { status: "allow" }, + created_at: "created_at", + id: "id", + thread_id: "thread_id", + tool_call_id: "tool_call_id", + type: "user.tool_approval", + }, + ], + }; + + server + .mockEndpoint() + .post("/api/v1/sessions/session_id/turns/turn_id/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.sessions.createTurnInboundEvent("session_id", "turn_id", { + events: [ + { + approval: { + status: "allow", + }, + threadId: "thread_id", + toolCallId: "tool_call_id", + type: "user.tool_approval", + }, + ], + }); + expect(response).toEqual({ + data: [ + { + approval: { + status: "allow", + }, + createdAt: "created_at", + id: "id", + threadId: "thread_id", + toolCallId: "tool_call_id", + type: "user.tool_approval", + }, + ], + }); + }); + + test("create_turn_inbound_event (2)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + events: [ + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + ], + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sessions/session_id/turns/turn_id/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sessions.createTurnInboundEvent("session_id", "turn_id", { + events: [ + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + ], + }); + }).rejects.toThrow(TrueForgeTypes.BadRequestError); + }); + + test("create_turn_inbound_event (3)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + events: [ + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + ], + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sessions/session_id/turns/turn_id/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sessions.createTurnInboundEvent("session_id", "turn_id", { + events: [ + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + ], + }); + }).rejects.toThrow(TrueForgeTypes.ForbiddenError); + }); + + test("create_turn_inbound_event (4)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + events: [ + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + ], + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sessions/session_id/turns/turn_id/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sessions.createTurnInboundEvent("session_id", "turn_id", { + events: [ + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + ], + }); + }).rejects.toThrow(TrueForgeTypes.NotFoundError); + }); + + test("create_turn_inbound_event (5)", async () => { + const server = mockServerPool.createServer(); + const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); + const rawRequestBody = { + events: [ + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + { approval: { status: "allow" }, thread_id: "x", tool_call_id: "x", type: "user.tool_approval" }, + ], + }; + const rawResponseBody = { error: { message: "message" } }; + + server + .mockEndpoint() + .post("/api/v1/sessions/session_id/turns/turn_id/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.sessions.createTurnInboundEvent("session_id", "turn_id", { + events: [ + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + { + approval: { + status: "allow", + }, + threadId: "x", + toolCallId: "x", + type: "user.tool_approval", + }, + ], + }); + }).rejects.toThrow(TrueForgeTypes.ConflictError); + }); + test("subscribe_to_turn (1)", async () => { const server = mockServerPool.createServer(); const client = new TrueForge({ maxRetries: 0, token: "test", baseUrl: server.baseUrl }); diff --git a/python/trueforge_sdk/reference.md b/python/trueforge_sdk/reference.md index e6f916a46..1a8f9cb15 100644 --- a/python/trueforge_sdk/reference.md +++ b/python/trueforge_sdk/reference.md @@ -2827,6 +2827,105 @@ client.sessions.list_turn_events( + + + + +
client.sessions.create_turn_inbound_event(...) -> CreateTurnInboundEventResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from trueforge_sdk import TrueForge, UserToolApprovalMessage, ApprovalAllow + +client = TrueForge( + token="", + base_url="https://yourhost.com/path/to/api", +) + +client.sessions.create_turn_inbound_event( + session_id="session_id", + turn_id="turn_id", + events=[ + UserToolApprovalMessage( + approval=ApprovalAllow( + status="allow", + ), + thread_id="thread_id", + tool_call_id="tool_call_id", + type="user.tool_approval", + ) + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**session_id:** `str` — Session identifier. + +
+
+ +
+
+ +**turn_id:** `str` — Turn identifier. + +
+
+ +
+
+ +**events:** `typing.List[TurnInboundEventItem]` — One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`). + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/python/trueforge_sdk/src/trueforge_sdk/__init__.py b/python/trueforge_sdk/src/trueforge_sdk/__init__.py index 700b078bc..f218965e9 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/__init__.py +++ b/python/trueforge_sdk/src/trueforge_sdk/__init__.py @@ -57,7 +57,9 @@ ContextManagementConfig, CreateScheduleRunResponse, CreateSessionAgent, + CreateTurnInboundEventResponse, CreatedBySubject, + CreatedTurnInboundEvent, CronExpression, CustomModelProvider, DaytonaSandboxProviderAuth, @@ -212,6 +214,8 @@ Timezone, TogetherAiModelProvider, TokenPagination, + ToolApprovalPolicyAllowSession, + ToolApprovalPolicyItem, ToolApprovalRequiredEvent, ToolCall, ToolCallRef, @@ -226,6 +230,7 @@ TurnCreatedEvent, TurnDoneEvent, TurnDoneEventState, + TurnInboundEventItem, TurnInputItem, TurnMetrics, TurnState, @@ -244,7 +249,11 @@ UserMessage, UserMessageContent, UserMessageContentItem, + UserToolApprovalEvent, UserToolApprovalMessage, + UserToolApprovalPolicyEvent, + UserToolApprovalPolicyMessage, + UserToolResponseEvent, UserToolResponseMessage, WebSearchCapability, WebSearchConfig, @@ -325,7 +334,9 @@ "ContextManagementConfig": ".types", "CreateScheduleRunResponse": ".types", "CreateSessionAgent": ".types", + "CreateTurnInboundEventResponse": ".types", "CreatedBySubject": ".types", + "CreatedTurnInboundEvent": ".types", "CronExpression": ".types", "CustomModelProvider": ".types", "DaytonaSandboxProviderAuth": ".types", @@ -488,6 +499,8 @@ "Timezone": ".types", "TogetherAiModelProvider": ".types", "TokenPagination": ".types", + "ToolApprovalPolicyAllowSession": ".types", + "ToolApprovalPolicyItem": ".types", "ToolApprovalRequiredEvent": ".types", "ToolCall": ".types", "ToolCallRef": ".types", @@ -503,6 +516,7 @@ "TurnCreatedEvent": ".types", "TurnDoneEvent": ".types", "TurnDoneEventState": ".types", + "TurnInboundEventItem": ".types", "TurnInputItem": ".types", "TurnMetrics": ".types", "TurnState": ".types", @@ -523,7 +537,11 @@ "UserMessage": ".types", "UserMessageContent": ".types", "UserMessageContentItem": ".types", + "UserToolApprovalEvent": ".types", "UserToolApprovalMessage": ".types", + "UserToolApprovalPolicyEvent": ".types", + "UserToolApprovalPolicyMessage": ".types", + "UserToolResponseEvent": ".types", "UserToolResponseMessage": ".types", "WebSearchCapability": ".types", "WebSearchConfig": ".types", @@ -621,7 +639,9 @@ def __dir__(): "ContextManagementConfig", "CreateScheduleRunResponse", "CreateSessionAgent", + "CreateTurnInboundEventResponse", "CreatedBySubject", + "CreatedTurnInboundEvent", "CronExpression", "CustomModelProvider", "DaytonaSandboxProviderAuth", @@ -784,6 +804,8 @@ def __dir__(): "Timezone", "TogetherAiModelProvider", "TokenPagination", + "ToolApprovalPolicyAllowSession", + "ToolApprovalPolicyItem", "ToolApprovalRequiredEvent", "ToolCall", "ToolCallRef", @@ -799,6 +821,7 @@ def __dir__(): "TurnCreatedEvent", "TurnDoneEvent", "TurnDoneEventState", + "TurnInboundEventItem", "TurnInputItem", "TurnMetrics", "TurnState", @@ -819,7 +842,11 @@ def __dir__(): "UserMessage", "UserMessageContent", "UserMessageContentItem", + "UserToolApprovalEvent", "UserToolApprovalMessage", + "UserToolApprovalPolicyEvent", + "UserToolApprovalPolicyMessage", + "UserToolResponseEvent", "UserToolResponseMessage", "WebSearchCapability", "WebSearchConfig", diff --git a/python/trueforge_sdk/src/trueforge_sdk/sessions/client.py b/python/trueforge_sdk/src/trueforge_sdk/sessions/client.py index ce744a779..d1ad04565 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/sessions/client.py +++ b/python/trueforge_sdk/src/trueforge_sdk/sessions/client.py @@ -9,6 +9,7 @@ from ..core.stream import AsyncStream, Stream, StreamEvent from ..types.cancel_session_response import CancelSessionResponse from ..types.create_session_agent import CreateSessionAgent +from ..types.create_turn_inbound_event_response import CreateTurnInboundEventResponse from ..types.get_session_response import GetSessionResponse from ..types.get_turn_response import GetTurnResponse from ..types.list_session_events_response import ListSessionEventsResponse @@ -25,6 +26,7 @@ from ..types.session_metadata import SessionMetadata from ..types.session_source_type import SessionSourceType from ..types.turn import Turn +from ..types.turn_inbound_event_item import TurnInboundEventItem from ..types.turn_input_item import TurnInputItem from ..types.turn_streaming_event import TurnStreamingEvent from .raw_client import AsyncRawSessionsClient, RawSessionsClient @@ -685,6 +687,61 @@ def list_turn_events( request_options=request_options, ) + def create_turn_inbound_event( + self, + *, + session_id: str, + turn_id: str, + events: typing.Sequence[TurnInboundEventItem], + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateTurnInboundEventResponse: + """ + Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step. + + Parameters + ---------- + session_id : str + Session identifier. + + turn_id : str + Turn identifier. + + events : typing.Sequence[TurnInboundEventItem] + One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateTurnInboundEventResponse + Events created in the turn inbox. + + Examples + -------- + from trueforge_sdk import ApprovalAllow, TrueForge, UserToolApprovalMessage + + client = TrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.sessions.create_turn_inbound_event( + session_id="session_id", + turn_id="turn_id", + events=[ + UserToolApprovalMessage( + approval=ApprovalAllow(), + thread_id="thread_id", + tool_call_id="tool_call_id", + ) + ], + ) + """ + _response = self._raw_client.create_turn_inbound_event( + session_id=session_id, turn_id=turn_id, events=events, request_options=request_options + ) + return _response.data + def subscribe_to_turn( self, *, @@ -1509,6 +1566,69 @@ async def main() -> None: request_options=request_options, ) + async def create_turn_inbound_event( + self, + *, + session_id: str, + turn_id: str, + events: typing.Sequence[TurnInboundEventItem], + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateTurnInboundEventResponse: + """ + Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step. + + Parameters + ---------- + session_id : str + Session identifier. + + turn_id : str + Turn identifier. + + events : typing.Sequence[TurnInboundEventItem] + One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateTurnInboundEventResponse + Events created in the turn inbox. + + Examples + -------- + import asyncio + + from trueforge_sdk import ApprovalAllow, AsyncTrueForge, UserToolApprovalMessage + + client = AsyncTrueForge( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.sessions.create_turn_inbound_event( + session_id="session_id", + turn_id="turn_id", + events=[ + UserToolApprovalMessage( + approval=ApprovalAllow(), + thread_id="thread_id", + tool_call_id="tool_call_id", + ) + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_turn_inbound_event( + session_id=session_id, turn_id=turn_id, events=events, request_options=request_options + ) + return _response.data + def subscribe_to_turn( self, *, diff --git a/python/trueforge_sdk/src/trueforge_sdk/sessions/raw_client.py b/python/trueforge_sdk/src/trueforge_sdk/sessions/raw_client.py index 1a8065d01..616b3c0fd 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/sessions/raw_client.py +++ b/python/trueforge_sdk/src/trueforge_sdk/sessions/raw_client.py @@ -20,6 +20,7 @@ from ..core.stream import AsyncStream, Stream, StreamEvent from ..core.unchecked_base_model import construct_type from ..errors.bad_request_error import BadRequestError +from ..errors.conflict_error import ConflictError from ..errors.content_too_large_error import ContentTooLargeError from ..errors.failed_dependency_error import FailedDependencyError from ..errors.forbidden_error import ForbiddenError @@ -30,6 +31,7 @@ from ..errors.unprocessable_entity_error import UnprocessableEntityError from ..types.cancel_session_response import CancelSessionResponse from ..types.create_session_agent import CreateSessionAgent +from ..types.create_turn_inbound_event_response import CreateTurnInboundEventResponse from ..types.get_session_response import GetSessionResponse from ..types.get_turn_response import GetTurnResponse from ..types.list_session_events_response import ListSessionEventsResponse @@ -47,6 +49,7 @@ from ..types.session_metadata import SessionMetadata from ..types.session_source_type import SessionSourceType from ..types.turn import Turn +from ..types.turn_inbound_event_item import TurnInboundEventItem from ..types.turn_input_item import TurnInputItem from ..types.turn_streaming_event import TurnStreamingEvent from pydantic import ValidationError @@ -1436,6 +1439,113 @@ def list_turn_events( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def create_turn_inbound_event( + self, + *, + session_id: str, + turn_id: str, + events: typing.Sequence[TurnInboundEventItem], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CreateTurnInboundEventResponse]: + """ + Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step. + + Parameters + ---------- + session_id : str + Session identifier. + + turn_id : str + Turn identifier. + + events : typing.Sequence[TurnInboundEventItem] + One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CreateTurnInboundEventResponse] + Events created in the turn inbox. + """ + _response = self._client_wrapper.httpx_client.request( + f"api/v1/sessions/{encode_path_param(session_id)}/turns/{encode_path_param(turn_id)}/events", + method="POST", + json={ + "events": convert_and_respect_annotation_metadata( + object_=events, annotation=typing.Sequence[TurnInboundEventItem], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateTurnInboundEventResponse, + construct_type( + type_=CreateTurnInboundEventResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + @contextlib.contextmanager def subscribe_to_turn( self, @@ -2971,6 +3081,113 @@ async def _get_next(): ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + async def create_turn_inbound_event( + self, + *, + session_id: str, + turn_id: str, + events: typing.Sequence[TurnInboundEventItem], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CreateTurnInboundEventResponse]: + """ + Create inbound events (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`) in the durable turn inbox. Only the session creator may create. Events are stored unconsumed; applying them to the turn is a separate step. + + Parameters + ---------- + session_id : str + Session identifier. + + turn_id : str + Turn identifier. + + events : typing.Sequence[TurnInboundEventItem] + One or more inbound items (`user.tool_approval`, `user.tool_response`, `user.tool_approval_policy`). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CreateTurnInboundEventResponse] + Events created in the turn inbox. + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/v1/sessions/{encode_path_param(session_id)}/turns/{encode_path_param(turn_id)}/events", + method="POST", + json={ + "events": convert_and_respect_annotation_metadata( + object_=events, annotation=typing.Sequence[TurnInboundEventItem], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateTurnInboundEventResponse, + construct_type( + type_=CreateTurnInboundEventResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + RequestErrorResponse, + construct_type( + type_=RequestErrorResponse, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + @contextlib.asynccontextmanager async def subscribe_to_turn( self, diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py b/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py index f1fb9e3ef..6569a7a2c 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py @@ -56,7 +56,9 @@ from .context_management_config import ContextManagementConfig from .create_schedule_run_response import CreateScheduleRunResponse from .create_session_agent import CreateSessionAgent + from .create_turn_inbound_event_response import CreateTurnInboundEventResponse from .created_by_subject import CreatedBySubject + from .created_turn_inbound_event import CreatedTurnInboundEvent from .cron_expression import CronExpression from .custom_model_provider import CustomModelProvider from .daytona_sandbox_provider_auth import DaytonaSandboxProviderAuth @@ -211,6 +213,8 @@ from .timezone import Timezone from .together_ai_model_provider import TogetherAiModelProvider from .token_pagination import TokenPagination + from .tool_approval_policy_allow_session import ToolApprovalPolicyAllowSession + from .tool_approval_policy_item import ToolApprovalPolicyItem from .tool_approval_required_event import ToolApprovalRequiredEvent from .tool_call import ToolCall from .tool_call_ref import ToolCallRef @@ -225,6 +229,7 @@ from .turn_created_event import TurnCreatedEvent from .turn_done_event import TurnDoneEvent from .turn_done_event_state import TurnDoneEventState + from .turn_inbound_event_item import TurnInboundEventItem from .turn_input_item import TurnInputItem from .turn_metrics import TurnMetrics from .turn_state import TurnState @@ -243,7 +248,11 @@ from .user_message import UserMessage from .user_message_content import UserMessageContent from .user_message_content_item import UserMessageContentItem + from .user_tool_approval_event import UserToolApprovalEvent from .user_tool_approval_message import UserToolApprovalMessage + from .user_tool_approval_policy_event import UserToolApprovalPolicyEvent + from .user_tool_approval_policy_message import UserToolApprovalPolicyMessage + from .user_tool_response_event import UserToolResponseEvent from .user_tool_response_message import UserToolResponseMessage from .web_search_capability import WebSearchCapability from .web_search_config import WebSearchConfig @@ -300,7 +309,9 @@ "ContextManagementConfig": ".context_management_config", "CreateScheduleRunResponse": ".create_schedule_run_response", "CreateSessionAgent": ".create_session_agent", + "CreateTurnInboundEventResponse": ".create_turn_inbound_event_response", "CreatedBySubject": ".created_by_subject", + "CreatedTurnInboundEvent": ".created_turn_inbound_event", "CronExpression": ".cron_expression", "CustomModelProvider": ".custom_model_provider", "DaytonaSandboxProviderAuth": ".daytona_sandbox_provider_auth", @@ -455,6 +466,8 @@ "Timezone": ".timezone", "TogetherAiModelProvider": ".together_ai_model_provider", "TokenPagination": ".token_pagination", + "ToolApprovalPolicyAllowSession": ".tool_approval_policy_allow_session", + "ToolApprovalPolicyItem": ".tool_approval_policy_item", "ToolApprovalRequiredEvent": ".tool_approval_required_event", "ToolCall": ".tool_call", "ToolCallRef": ".tool_call_ref", @@ -469,6 +482,7 @@ "TurnCreatedEvent": ".turn_created_event", "TurnDoneEvent": ".turn_done_event", "TurnDoneEventState": ".turn_done_event_state", + "TurnInboundEventItem": ".turn_inbound_event_item", "TurnInputItem": ".turn_input_item", "TurnMetrics": ".turn_metrics", "TurnState": ".turn_state", @@ -487,7 +501,11 @@ "UserMessage": ".user_message", "UserMessageContent": ".user_message_content", "UserMessageContentItem": ".user_message_content_item", + "UserToolApprovalEvent": ".user_tool_approval_event", "UserToolApprovalMessage": ".user_tool_approval_message", + "UserToolApprovalPolicyEvent": ".user_tool_approval_policy_event", + "UserToolApprovalPolicyMessage": ".user_tool_approval_policy_message", + "UserToolResponseEvent": ".user_tool_response_event", "UserToolResponseMessage": ".user_tool_response_message", "WebSearchCapability": ".web_search_capability", "WebSearchConfig": ".web_search_config", @@ -568,7 +586,9 @@ def __dir__(): "ContextManagementConfig", "CreateScheduleRunResponse", "CreateSessionAgent", + "CreateTurnInboundEventResponse", "CreatedBySubject", + "CreatedTurnInboundEvent", "CronExpression", "CustomModelProvider", "DaytonaSandboxProviderAuth", @@ -723,6 +743,8 @@ def __dir__(): "Timezone", "TogetherAiModelProvider", "TokenPagination", + "ToolApprovalPolicyAllowSession", + "ToolApprovalPolicyItem", "ToolApprovalRequiredEvent", "ToolCall", "ToolCallRef", @@ -737,6 +759,7 @@ def __dir__(): "TurnCreatedEvent", "TurnDoneEvent", "TurnDoneEventState", + "TurnInboundEventItem", "TurnInputItem", "TurnMetrics", "TurnState", @@ -755,7 +778,11 @@ def __dir__(): "UserMessage", "UserMessageContent", "UserMessageContentItem", + "UserToolApprovalEvent", "UserToolApprovalMessage", + "UserToolApprovalPolicyEvent", + "UserToolApprovalPolicyMessage", + "UserToolResponseEvent", "UserToolResponseMessage", "WebSearchCapability", "WebSearchConfig", diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/create_turn_inbound_event_response.py b/python/trueforge_sdk/src/trueforge_sdk/types/create_turn_inbound_event_response.py new file mode 100644 index 000000000..ff5de2a43 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/create_turn_inbound_event_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .created_turn_inbound_event import CreatedTurnInboundEvent + + +class CreateTurnInboundEventResponse(UncheckedBaseModel): + data: typing.List[CreatedTurnInboundEvent] = pydantic.Field() + """ + Created inbox events with server-minted `id` and `created_at`, in request order. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/created_turn_inbound_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/created_turn_inbound_event.py new file mode 100644 index 000000000..81ec54077 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/created_turn_inbound_event.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .user_tool_approval_event import UserToolApprovalEvent +from .user_tool_approval_policy_event import UserToolApprovalPolicyEvent +from .user_tool_response_event import UserToolResponseEvent + +CreatedTurnInboundEvent = typing.Union[UserToolApprovalEvent, UserToolApprovalPolicyEvent, UserToolResponseEvent] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/session_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/session_event.py index 6119fc25c..cbedb13d2 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/session_event.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/session_event.py @@ -14,6 +14,9 @@ from .turn_created_event import TurnCreatedEvent from .turn_done_event import TurnDoneEvent from .turn_update_event import TurnUpdateEvent +from .user_tool_approval_event import UserToolApprovalEvent +from .user_tool_approval_policy_event import UserToolApprovalPolicyEvent +from .user_tool_response_event import UserToolResponseEvent SessionEvent = typing.Union[ McpAuthRequiredEvent, @@ -28,4 +31,7 @@ TurnCreatedEvent, TurnDoneEvent, TurnUpdateEvent, + UserToolApprovalEvent, + UserToolApprovalPolicyEvent, + UserToolResponseEvent, ] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_allow_session.py b/python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_allow_session.py new file mode 100644 index 000000000..93c9ebf9f --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_allow_session.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ToolApprovalPolicyAllowSession(UncheckedBaseModel): + expire_at: typing.Optional[str] = pydantic.Field(default=None) + """ + ISO 8601 timestamp when this session allow expires. Omit to allow for the whole session. + """ + + type: typing.Literal["allow_session"] = "allow_session" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_item.py b/python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_item.py new file mode 100644 index 000000000..54ba130d5 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/tool_approval_policy_item.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .tool_approval_policy_allow_session import ToolApprovalPolicyAllowSession + + +class ToolApprovalPolicyItem(UncheckedBaseModel): + action: ToolApprovalPolicyAllowSession + server: str = pydantic.Field() + """ + MCP server name the tool belongs to. + """ + + tool_name: str = pydantic.Field() + """ + Tool name this policy applies to. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/turn_inbound_event_item.py b/python/trueforge_sdk/src/trueforge_sdk/types/turn_inbound_event_item.py new file mode 100644 index 000000000..6cb951a28 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/turn_inbound_event_item.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .user_tool_approval_message import UserToolApprovalMessage +from .user_tool_approval_policy_message import UserToolApprovalPolicyMessage +from .user_tool_response_message import UserToolResponseMessage + +TurnInboundEventItem = typing.Union[UserToolApprovalMessage, UserToolApprovalPolicyMessage, UserToolResponseMessage] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/turn_streaming_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/turn_streaming_event.py index 3570d5ed6..1a9d40b9a 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/turn_streaming_event.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/turn_streaming_event.py @@ -15,6 +15,9 @@ from .turn_created_event import TurnCreatedEvent from .turn_done_event import TurnDoneEvent from .turn_update_event import TurnUpdateEvent +from .user_tool_approval_event import UserToolApprovalEvent +from .user_tool_approval_policy_event import UserToolApprovalPolicyEvent +from .user_tool_response_event import UserToolResponseEvent TurnStreamingEvent = typing.Union[ McpAuthRequiredEvent, @@ -30,4 +33,7 @@ TurnCreatedEvent, TurnDoneEvent, TurnUpdateEvent, + UserToolApprovalEvent, + UserToolApprovalPolicyEvent, + UserToolResponseEvent, ] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_event.py new file mode 100644 index 000000000..a57a02864 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_event.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .approval_decision import ApprovalDecision + + +class UserToolApprovalEvent(UncheckedBaseModel): + approval: ApprovalDecision + created_at: str = pydantic.Field() + """ + ISO 8601 event timestamp. + """ + + id: str = pydantic.Field() + """ + Unique identifier for the event (monotonic ULID). + """ + + thread_id: str = pydantic.Field() + """ + Thread that owns the pending tool call. + """ + + tool_call_id: str = pydantic.Field() + """ + Tool call id being approved or denied. + """ + + type: typing.Literal["user.tool_approval"] = "user.tool_approval" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_event.py new file mode 100644 index 000000000..13ae72c36 --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_event.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .tool_approval_policy_item import ToolApprovalPolicyItem + + +class UserToolApprovalPolicyEvent(UncheckedBaseModel): + created_at: str = pydantic.Field() + """ + ISO 8601 event timestamp. + """ + + id: str = pydantic.Field() + """ + Unique identifier for the event (monotonic ULID). + """ + + policies: typing.List[ToolApprovalPolicyItem] = pydantic.Field() + """ + One or more (server, tool_name) policy entries. + """ + + type: typing.Literal["user.tool_approval_policy"] = "user.tool_approval_policy" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_message.py b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_message.py new file mode 100644 index 000000000..1b5bcefcf --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_approval_policy_message.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .tool_approval_policy_item import ToolApprovalPolicyItem + + +class UserToolApprovalPolicyMessage(UncheckedBaseModel): + policies: typing.List[ToolApprovalPolicyItem] = pydantic.Field() + """ + One or more (server, tool_name) policy entries. + """ + + type: typing.Literal["user.tool_approval_policy"] = "user.tool_approval_policy" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_response_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_response_event.py new file mode 100644 index 000000000..f902e885e --- /dev/null +++ b/python/trueforge_sdk/src/trueforge_sdk/types/user_tool_response_event.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class UserToolResponseEvent(UncheckedBaseModel): + content: str = pydantic.Field() + """ + Client-side tool result content. + """ + + created_at: str = pydantic.Field() + """ + ISO 8601 event timestamp. + """ + + id: str = pydantic.Field() + """ + Unique identifier for the event (monotonic ULID). + """ + + thread_id: str = pydantic.Field() + """ + Thread that owns the pending tool call. + """ + + tool_call_id: str = pydantic.Field() + """ + Tool call id receiving the client response. + """ + + type: typing.Literal["user.tool_response"] = "user.tool_response" + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + smart_union = True + extra = pydantic.Extra.allow