From b23c1c7f7f6aa614047f634db99ad8ffc85fd69e Mon Sep 17 00:00:00 2001 From: Srajan Asthana Date: Fri, 18 Sep 2026 18:31:09 +0530 Subject: [PATCH 1/5] update schema --- .../trueforge-core/src/agent-session/index.ts | 12 ++++++- .../src/agent-session/schemas/events.ts | 24 ++------------ .../src/agent-session/schemas/turn.ts | 31 +++++++++++++------ .../tests/agent-session/testHelpers.ts | 1 - 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/trueforge-core/src/agent-session/index.ts b/packages/trueforge-core/src/agent-session/index.ts index 3b9b5d66c..ec12bc2e6 100644 --- a/packages/trueforge-core/src/agent-session/index.ts +++ b/packages/trueforge-core/src/agent-session/index.ts @@ -7,6 +7,7 @@ export { AgentSpecSchema, DEFAULT_AGENT_CONFIG_ITERATION_LIMIT, RuntimeConfigSch export type { AgentSpec, Skill } from './schemas/agentSpec'; export { + ActionRequiredSchema, CancellationReason, CreateTurnRequestSchema, TurnInputItemSchema, @@ -16,10 +17,19 @@ export { TurnStateCancelledSchema, TurnStateDoneSchema, TurnStateErrorSchema, + TurnStatePausedSchema, TurnStateRunningSchema, TurnStateSchema, } from './schemas/turn'; -export type { TerminalTurnState, Turn, TurnInputItem, TurnMetrics, TurnState } from './schemas/turn'; +export type { + ActionRequired, + TerminalTurnState, + Turn, + TurnInputItem, + TurnMetrics, + TurnState, + TurnStatePaused, +} from './schemas/turn'; export { SessionMetadataSchema, diff --git a/packages/trueforge-core/src/agent-session/schemas/events.ts b/packages/trueforge-core/src/agent-session/schemas/events.ts index 370f209c2..317c227d5 100644 --- a/packages/trueforge-core/src/agent-session/schemas/events.ts +++ b/packages/trueforge-core/src/agent-session/schemas/events.ts @@ -24,6 +24,7 @@ import { TurnStateCancelledSchema, TurnStateDoneSchema, TurnStateErrorSchema, + TurnStatePausedSchema, TurnStateRunningSchema, } from './turn'; @@ -60,29 +61,8 @@ export const TurnDoneEventSchema = z }) .openapi('TurnDoneEvent'); -export const ActionRequiredSchema = z - .object({ - id: EventIdSchema, - }) - .openapi('ActionRequired'); - -export const TurnUpdateStatePausedSchema = z - .object({ - status: z.literal('paused').describe('Turn is paused waiting for required actions.'), - action_required_on_events: z - .array(ActionRequiredSchema) - .describe('Events that still need a user or client action.'), - }) - .openapi('TurnUpdateStatePaused'); - -export const TurnUpdateStateRunningSchema = z - .object({ - status: z.literal('running').describe('Turn is executing.'), - }) - .openapi('TurnUpdateStateRunning'); - export const TurnUpdateStateSchema = z - .discriminatedUnion('status', [TurnUpdateStatePausedSchema, TurnUpdateStateRunningSchema]) + .discriminatedUnion('status', [TurnStatePausedSchema, TurnStateRunningSchema]) .describe('Live non-terminal turn status.') .openapi('TurnUpdateState'); diff --git a/packages/trueforge-core/src/agent-session/schemas/turn.ts b/packages/trueforge-core/src/agent-session/schemas/turn.ts index 59ec0b9a0..453ff2aa6 100644 --- a/packages/trueforge-core/src/agent-session/schemas/turn.ts +++ b/packages/trueforge-core/src/agent-session/schemas/turn.ts @@ -1,8 +1,8 @@ /** Turn product schemas: turn state, input items, and create-turn request. */ import { z } from '@hono/zod-openapi'; import { - ActionRequiredEventSchema, AgentInputUserMessageSchema, + EventIdSchema, ModelMessageEventSchema, UserToolApprovalMessageSchema, UserToolResponseMessageSchema, @@ -91,17 +91,27 @@ export const TurnStateErrorSchema = z }) .openapi('TurnStateError'); +export const ActionRequiredSchema = z + .object({ + id: EventIdSchema, + }) + .openapi('ActionRequired'); + +export const TurnStatePausedSchema = z + .object({ + status: z.literal('paused').describe('Turn is paused waiting for required actions.'), + action_required_on_events: z + .array(ActionRequiredSchema) + .describe('Events that still need a user or client action.'), + }) + .openapi('TurnStatePaused'); + export const TurnStateDoneSchema = z .object({ - status: z.literal('done').describe('Turn finished (possibly paused for required actions).'), + status: z.literal('done').describe('Turn finished with no open required actions.'), output: z .union([ModelMessageEventSchema, z.null()]) - .describe('Final `model.message` for the turn, or null when the turn ended paused without a final message.'), - required_actions: z - .array(ActionRequiredEventSchema) - .describe( - 'Pending actions (`tool.approval_required`, `tool.response_required`, `mcp.auth_required`); empty when none.', - ), + .describe('Final `model.message` for the turn, or null when the turn finished without one.'), completed_at: z.string().describe('ISO 8601 time when the turn reached a terminal state.'), metrics: TurnMetricsSchema.optional().describe('Optional billable aggregate for the whole turn.'), }) @@ -110,6 +120,7 @@ export const TurnStateDoneSchema = z export const TurnStateSchema = z .discriminatedUnion('status', [ TurnStateRunningSchema, + TurnStatePausedSchema, TurnStateDoneSchema, TurnStateCancelledSchema, TurnStateErrorSchema, @@ -179,5 +190,7 @@ export const CreateTurnRequestSchema = z export type Turn = z.infer; export type TurnInputItem = z.infer; export type TurnState = z.infer; -export type TerminalTurnState = Exclude; +export type TerminalTurnState = Exclude; +export type TurnStatePaused = z.infer; +export type ActionRequired = z.infer; export type TurnMetrics = z.infer; diff --git a/packages/trueforge-core/tests/agent-session/testHelpers.ts b/packages/trueforge-core/tests/agent-session/testHelpers.ts index fd1ddc88a..b693005a6 100644 --- a/packages/trueforge-core/tests/agent-session/testHelpers.ts +++ b/packages/trueforge-core/tests/agent-session/testHelpers.ts @@ -195,7 +195,6 @@ export function makeDoneTurnState(): TerminalTurnState { return { status: 'done', output: null, - required_actions: [], completed_at: new Date().toISOString(), }; } From 89a7ad20bc6d9ee8e076fd8564288916550d1b78 Mon Sep 17 00:00:00 2001 From: Srajan Asthana Date: Fri, 18 Sep 2026 18:46:26 +0530 Subject: [PATCH 2/5] nit --- .../src/agent-session/store/ISessionStore.ts | 18 ++-- .../store/InMemorySessionStore.ts | 4 +- .../agent-session/store/storeContractSuite.ts | 89 +++++++++++++++++++ .../tests/agent-session/testHelpers.ts | 21 ++++- .../postgres/session-store/queries/turns.ts | 14 +-- .../db/sqlite/session-store/queries/turns.ts | 14 +-- 6 files changed, 139 insertions(+), 21 deletions(-) diff --git a/packages/trueforge-core/src/agent-session/store/ISessionStore.ts b/packages/trueforge-core/src/agent-session/store/ISessionStore.ts index b11a7ca1d..db62d73bc 100644 --- a/packages/trueforge-core/src/agent-session/store/ISessionStore.ts +++ b/packages/trueforge-core/src/agent-session/store/ISessionStore.ts @@ -12,7 +12,7 @@ import type { TurnRecord } from '../models/TurnRecord'; import type { PersistedTurnEvent, SessionEventItem } from '../schemas/events'; import type { TokenPagination } from '../schemas/pagination'; import type { SessionMetadata } from '../schemas/session'; -import type { CancellationReason, TerminalTurnState } from '../schemas/turn'; +import type { CancellationReason, TurnState } from '../schemas/turn'; /** * Caller-supplied fields for creating a session; the store owns timestamps and tip state. @@ -159,8 +159,12 @@ export interface ListTurnsInput { export interface UpdateTurnStateInput { session_id: string; turn_id: string; - state: TerminalTurnState; - /** Caller-built turn.done; written atomically with the state flip in the same tx. */ + /** Tip after the flip: terminal (`done`/`cancelled`/`error`) or HITL `paused`. */ + state: Exclude; + /** + * Caller-built companion event written atomically with the state flip. + * Terminal → `turn.done`; paused → `turn.update` with paused state. + */ turn_done_event: PersistedTurnEvent; } @@ -330,7 +334,7 @@ export interface ISessionStore< /** * Cancel if still running (persist `turn_done` and fold cost/duration into - * `session.metrics`); already-terminal turns are a read. Missing → {@link TurnNotFoundError}. + * `session.metrics`); paused or already-terminal turns are a read. Missing → {@link TurnNotFoundError}. */ freezeAndGetTurn(input: FreezeAndGetTurnInput): Promise>; @@ -343,9 +347,9 @@ export interface ISessionStore< ): Promise<{ data: TurnRecordWithoutSnapshot[]; pagination: TokenPagination }>; /** - * First terminal write wins (`running` → done/cancelled/error); otherwise 409. - * Winning write also folds cost/duration into `session.metrics`. Missing → 404. - * Must use the same lock/CAS as other turn mutations. + * First non-running write wins (`running` → paused/done/cancelled/error); otherwise 409. + * Terminal winning writes also fold cost/duration into `session.metrics`; pause does not. + * Missing → 404. Must use the same lock/CAS as other turn mutations. */ updateTurnState(input: UpdateTurnStateInput): Promise; diff --git a/packages/trueforge-core/src/agent-session/store/InMemorySessionStore.ts b/packages/trueforge-core/src/agent-session/store/InMemorySessionStore.ts index a2e114b61..1c0cc79c2 100644 --- a/packages/trueforge-core/src/agent-session/store/InMemorySessionStore.ts +++ b/packages/trueforge-core/src/agent-session/store/InMemorySessionStore.ts @@ -471,7 +471,9 @@ export class InMemorySessionStore< if (list) { list.push(deepCopy(input.turn_done_event)); } - this.addTerminalSessionMetrics(input.session_id, turn.created_at, input.state); + if (input.state.status !== 'paused') { + this.addTerminalSessionMetrics(input.session_id, turn.created_at, input.state); + } } async appendToEvents(input: AppendToEventsInput): Promise { diff --git a/packages/trueforge-core/tests/agent-session/store/storeContractSuite.ts b/packages/trueforge-core/tests/agent-session/store/storeContractSuite.ts index 28a85e43f..11912c1b8 100644 --- a/packages/trueforge-core/tests/agent-session/store/storeContractSuite.ts +++ b/packages/trueforge-core/tests/agent-session/store/storeContractSuite.ts @@ -26,8 +26,10 @@ import { makeCreateTurnInput, makeDoneTurnState, makeModelMessageEvent, + makePausedTurnState, makeTurnCreatedEvent, makeTurnDoneEvent, + makeTurnUpdateEvent, TEST_ACTIVE_EXECUTOR_ID, } from '../testHelpers'; @@ -2179,6 +2181,93 @@ export function runStoreContractSuite(createStore: () => ISessionStore) { }), ).rejects.toBeInstanceOf(TurnNotFoundError); }); + + it('allows running → paused and writes turn.update without session metrics', async () => { + const store = createStore(); + await seedSession(store); + await store.createTurn(makeCreateTurnInput({ sessionId, turnId: 'turn-1' })); + const paused = makePausedTurnState([{ id: 'evt-approval' }]); + const turnUpdate = makeTurnUpdateEvent(paused); + await store.updateTurnState({ + session_id: sessionId, + turn_id: 'turn-1', + state: paused, + turn_done_event: turnUpdate, + }); + + const turn = await store.getTurn({ session_id: sessionId, turn_id: 'turn-1' }); + expect(mustGet(turn).state).toEqual(paused); + const { data } = await store.listTurnEvents({ + session_id: sessionId, + turn_id: 'turn-1', + limit: 10, + page_token: undefined, + order: undefined, + }); + const updates = data.filter(e => e.type === EventType.TURN_UPDATE); + expect(updates).toHaveLength(1); + expect(updates[0]).toEqual(turnUpdate); + expect(data.some(e => e.type === EventType.TURN_DONE)).toBe(false); + + const session = await store.getSession({ tenant_id: tenant, session_id: sessionId }); + expect(mustGet(session).metrics).toEqual({ + total_duration_ms: 0, + total_turns: 1, + }); + expect(mustGet(session).metrics.total_cost_in_usd).toBeUndefined(); + }); + + it('rejects a second update after pause (first non-running write wins)', async () => { + const store = createStore(); + await seedSession(store); + await store.createTurn(makeCreateTurnInput({ sessionId, turnId: 'turn-1' })); + const paused = makePausedTurnState(); + await store.updateTurnState({ + session_id: sessionId, + turn_id: 'turn-1', + state: paused, + turn_done_event: makeTurnUpdateEvent(paused), + }); + const doneState = makeDoneTurnState(); + await expect( + store.updateTurnState({ + session_id: sessionId, + turn_id: 'turn-1', + state: doneState, + turn_done_event: makeTurnDoneEvent(doneState), + }), + ).rejects.toBeInstanceOf(SessionStoreConflictError); + const turn = await store.getTurn({ session_id: sessionId, turn_id: 'turn-1' }); + expect(mustGet(turn).state.status).toBe('paused'); + }); + + it('freezeAndGetTurn of a paused tip is a read', async () => { + const store = createStore(); + await seedSession(store); + await store.createTurn(makeCreateTurnInput({ sessionId, turnId: 'turn-1' })); + const paused = makePausedTurnState(); + await store.updateTurnState({ + session_id: sessionId, + turn_id: 'turn-1', + state: paused, + turn_done_event: makeTurnUpdateEvent(paused), + }); + const frozen = await store.freezeAndGetTurn({ + session_id: sessionId, + turn_id: 'turn-1', + reason: CancellationReason.CancelledForNextTurn, + turn_done_event: makeTurnDoneEvent(makeCancelledTurnState(CancellationReason.CancelledForNextTurn)), + }); + expect(frozen.state).toEqual(paused); + const { data } = await store.listTurnEvents({ + session_id: sessionId, + turn_id: 'turn-1', + limit: 10, + page_token: undefined, + order: undefined, + }); + expect(data.filter(e => e.type === EventType.TURN_DONE)).toHaveLength(0); + }); }); describe('events + threads + capability_state', () => { diff --git a/packages/trueforge-core/tests/agent-session/testHelpers.ts b/packages/trueforge-core/tests/agent-session/testHelpers.ts index b693005a6..e3fc020fc 100644 --- a/packages/trueforge-core/tests/agent-session/testHelpers.ts +++ b/packages/trueforge-core/tests/agent-session/testHelpers.ts @@ -3,7 +3,7 @@ import type { ITurnResourceResolver } from '../../src/agent-session/ITurnResourc import { MAIN_THREAD_ID, type TurnRecord } from '../../src/agent-session/models/TurnRecord'; import { AgentSpecSchema, type AgentSpec } from '../../src/agent-session/schemas/agentSpec'; import { EventType } from '../../src/agent-session/schemas/events'; -import { CancellationReason, type TerminalTurnState } from '../../src/agent-session/schemas/turn'; +import { CancellationReason, type TerminalTurnState, type TurnStatePaused } from '../../src/agent-session/schemas/turn'; import type { CreateTurnInput, NewThreadInit, TurnContextAppend } from '../../src/agent-session/store/ISessionStore'; import { TurnResourceResolver } from '../../src/agent-session/TurnResourceResolver'; import type { AgentCapability } from '../../src/core/capabilities/AgentCapability'; @@ -154,6 +154,25 @@ export function makeTurnDoneEvent(state: TerminalTurnState) { }; } +export function makePausedTurnState( + action_required_on_events: { id: string }[] = [{ id: newEventId() }], +): TurnStatePaused { + return { + status: 'paused', + action_required_on_events, + }; +} + +export function makeTurnUpdateEvent(state: TurnStatePaused) { + return { + type: EventType.TURN_UPDATE, + id: newEventId(), + created_at: new Date().toISOString(), + thread_id: null, + state, + }; +} + const defaultRootThread: NewThreadInit = { thread_id: MAIN_THREAD_ID, parent: null, diff --git a/packages/trueforge/src/db/postgres/session-store/queries/turns.ts b/packages/trueforge/src/db/postgres/session-store/queries/turns.ts index 06bcda570..73ed24a60 100644 --- a/packages/trueforge/src/db/postgres/session-store/queries/turns.ts +++ b/packages/trueforge/src/db/postgres/session-store/queries/turns.ts @@ -746,7 +746,7 @@ export async function listTurns(db: Kysely, input: ListTurnsInput): Pr /** * updateTurnState — conditional on state->>'status'='running'. - * 0 rows → SELECT by PK → missing NotFound, present Conflict (first terminal write wins). + * 0 rows → SELECT by PK → missing NotFound, present Conflict (first non-running write wins). */ export async function updateTurnState(db: Kysely, input: UpdateTurnStateInput): Promise { await db.transaction().execute(async trx => { @@ -777,11 +777,13 @@ export async function updateTurnState(db: Kysely, input: UpdateTurnSta throw new TurnNotRunningError(input.turn_id, terminalTurnState(existing.state, input.turn_id)); } - await addSessionCostAndDuration(trx, { - session_id: input.session_id, - turn_created_at: result.created_at, - turn_state: input.state, - }); + if (input.state.status !== 'paused') { + await addSessionCostAndDuration(trx, { + session_id: input.session_id, + turn_created_at: result.created_at, + turn_state: input.state, + }); + } await trx .insertInto('session_event') diff --git a/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts b/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts index db230fd20..136ef795f 100644 --- a/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts +++ b/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts @@ -798,7 +798,7 @@ export async function listTurns(db: Kysely, input: ListTurnsInput): Pr /** * updateTurnState — conditional on state->>'status'='running'. - * 0 rows → SELECT by PK → missing NotFound, present Conflict (first terminal write wins). + * 0 rows → SELECT by PK → missing NotFound, present Conflict (first non-running write wins). */ export async function updateTurnState(db: Kysely, input: UpdateTurnStateInput): Promise { await db.transaction().execute(async trx => { @@ -829,11 +829,13 @@ export async function updateTurnState(db: Kysely, input: UpdateTurnSta throw new TurnNotRunningError(input.turn_id, terminalTurnState(existing.state, input.turn_id)); } - await addSessionCostAndDuration(trx, { - session_id: input.session_id, - turn_created_at: new Date(result.created_at), - turn_state: input.state, - }); + if (input.state.status !== 'paused') { + await addSessionCostAndDuration(trx, { + session_id: input.session_id, + turn_created_at: new Date(result.created_at), + turn_state: input.state, + }); + } await trx .insertInto('session_event') From b0164e22ab4da6035bc04d3ec5e4134c340bec5a Mon Sep 17 00:00:00 2001 From: Srajan Asthana Date: Fri, 18 Sep 2026 18:50:45 +0530 Subject: [PATCH 3/5] bupdate TurnHandle --- .../src/agent-session/TurnHandle.ts | 67 ++++++++++++------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/packages/trueforge-core/src/agent-session/TurnHandle.ts b/packages/trueforge-core/src/agent-session/TurnHandle.ts index f0bb2be03..ad80d0581 100644 --- a/packages/trueforge-core/src/agent-session/TurnHandle.ts +++ b/packages/trueforge-core/src/agent-session/TurnHandle.ts @@ -16,7 +16,13 @@ import { getEmptyCurrentContextUsage } from '../core/runtime/contextUsage'; import type { AgentThreadMetrics } from '../core/runtime/metrics'; import type { ITurnResourceResolver } from './ITurnResourceResolver'; import type { TurnRecord } from './models/TurnRecord'; -import { EventType, type PersistedTurnEvent, type TurnCreatedEvent, type TurnDoneEvent } from './schemas/events'; +import { + EventType, + type PersistedTurnEvent, + type TurnCreatedEvent, + type TurnDoneEvent, + type TurnUpdateEvent, +} from './schemas/events'; import type { TokenPagination } from './schemas/pagination'; import { CancellationReason, @@ -169,10 +175,10 @@ export class TurnHandle> { * Executes the turn. Single consumer, callable ONCE — a second call throws: * this generator IS the execution (persist-before-yield). Execute-only: the * input was already sent and validated in run(); nothing is sent here. - * Sole terminal writer — done/cancelled/error is written to the store from - * inside this generator; honors the AbortSignal passed to run(). On every + * Sole lifecycle writer — paused/done/cancelled/error is written to the store + * from inside this generator; honors the AbortSignal passed to run(). On every * exit path the resolver is closed best-effort in a finally, after the - * terminal write. + * lifecycle write. * * Two consumption patterns (both caller-side; this method is identical for both): * @@ -284,23 +290,23 @@ export class TurnHandle> { const updatedAt = new Date(); const createdAtIso = updatedAt.toISOString(); const metrics = turnMetricsFromAgentThreadMetrics(orchestrator.getMetrics()); - let terminalState: TerminalTurnState; + let nextState: Exclude; if (signal.aborted) { - terminalState = { + nextState = { status: 'cancelled', reason: cancellationReasonFromAbortReason(signal.reason), completed_at: createdAtIso, metrics, }; } else if (caughtError) { - terminalState = { + nextState = { status: 'error', message: caughtError.message, completed_at: createdAtIso, metrics, }; } else if (executeResult?.root_agent_error) { - terminalState = { + nextState = { status: 'error', message: executeResult.root_agent_error.error, completed_at: createdAtIso, @@ -309,39 +315,52 @@ export class TurnHandle> { } else if (executeResult === undefined) { // Consumer abandoned the generator (break/return) without aborting — // no executeResult, no error, signal not aborted. - terminalState = { + nextState = { status: 'cancelled', reason: CancellationReason.ClientCancelled, completed_at: createdAtIso, metrics, }; + } else if (executeResult.required_actions.length > 0) { + nextState = { + status: 'paused', + action_required_on_events: executeResult.required_actions.map(({ id }) => ({ id })), + }; } else { - terminalState = { + nextState = { status: 'done', output: executeResult.output, - required_actions: executeResult.required_actions, completed_at: createdAtIso, metrics, }; } - const turnDone: TurnDoneEvent = { - type: EventType.TURN_DONE, - id: newEventId(), - created_at: createdAtIso, - state: terminalState, - thread_id: null, - }; + const turnStateEvent: TurnDoneEvent | TurnUpdateEvent = + nextState.status === 'paused' + ? { + type: EventType.TURN_UPDATE, + id: newEventId(), + created_at: createdAtIso, + state: nextState, + thread_id: null, + } + : { + type: EventType.TURN_DONE, + id: newEventId(), + created_at: createdAtIso, + state: nextState, + thread_id: null, + }; if (!frozenByStore) { try { await this.store.updateTurnState({ session_id: this.turn.session_id, turn_id: this.turn.turn_id, - state: terminalState, - turn_done_event: turnDone, + state: nextState, + turn_done_event: turnStateEvent, }); - this.turn = { ...this.turn, state: terminalState, updated_at: updatedAt }; + this.turn = { ...this.turn, state: nextState, updated_at: updatedAt }; } catch (persistError) { if (persistError instanceof TurnNotRunningError) { const state: TerminalTurnState = { ...persistError.state, metrics }; @@ -356,14 +375,14 @@ export class TurnHandle> { state, thread_id: null, }; - // eslint-disable-next-line no-unsafe-finally -- deliberate: a concurrent freeze during the terminal write makes the store's state authoritative, so the stream ends here + // eslint-disable-next-line no-unsafe-finally -- deliberate: a concurrent freeze during the lifecycle write makes the store's state authoritative, so the stream ends here return; } // Store-write failures reject the stream (caller drain .catch). await resolver.close().catch(() => { /* no-op */ }); - // eslint-disable-next-line no-unsafe-finally -- deliberate: the terminal-state write runs in finally and its failure must reject the stream + // eslint-disable-next-line no-unsafe-finally -- deliberate: the lifecycle-state write runs in finally and its failure must reject the stream throw persistError; } } @@ -373,7 +392,7 @@ export class TurnHandle> { }); if (!frozenByStore) { - yield turnDone; + yield turnStateEvent; } } } From d124a86f76c8f6eef3adeced1beb706e415f5f6d Mon Sep 17 00:00:00 2001 From: Srajan Asthana Date: Fri, 18 Sep 2026 19:12:26 +0530 Subject: [PATCH 4/5] nit --- .changeset/turn-hitl-paused.md | 6 ++ .../src/agent-session/TurnHandle.ts | 86 +++++++++---------- .../src/agent-session/store/ISessionStore.ts | 5 -- .../agent-session/store/SessionStoreErrors.ts | 6 +- .../tests/agent-session/testHelpers.ts | 4 +- .../tests/agent-session/turnStream.test.ts | 56 ++++++++++++ .../postgres/session-store/queries/turns.ts | 20 ++--- .../db/sqlite/session-store/queries/turns.ts | 20 ++--- 8 files changed, 127 insertions(+), 76 deletions(-) create mode 100644 .changeset/turn-hitl-paused.md diff --git a/.changeset/turn-hitl-paused.md b/.changeset/turn-hitl-paused.md new file mode 100644 index 000000000..0038b5950 --- /dev/null +++ b/.changeset/turn-hitl-paused.md @@ -0,0 +1,6 @@ +--- +"@truefoundry/trueforge": patch +"@truefoundry/trueforge-core": patch +--- + +Pause a turn on HITL (`paused` + `turn.update` with action-required event ids) instead of marking it `done` with required actions. diff --git a/packages/trueforge-core/src/agent-session/TurnHandle.ts b/packages/trueforge-core/src/agent-session/TurnHandle.ts index ad80d0581..669be6b47 100644 --- a/packages/trueforge-core/src/agent-session/TurnHandle.ts +++ b/packages/trueforge-core/src/agent-session/TurnHandle.ts @@ -24,13 +24,7 @@ import { type TurnUpdateEvent, } from './schemas/events'; import type { TokenPagination } from './schemas/pagination'; -import { - CancellationReason, - type TerminalTurnState, - type TurnInputItem, - type TurnMetrics, - type TurnState, -} from './schemas/turn'; +import { CancellationReason, type TurnInputItem, type TurnMetrics, type TurnState } from './schemas/turn'; import type { ISessionStore } from './store/ISessionStore'; import { TurnNotRunningError } from './store/SessionStoreErrors'; @@ -91,6 +85,29 @@ function turnMetricsFromAgentThreadMetrics(metrics: AgentThreadMetrics): TurnMet }; } +function streamEventForStoreState(input: { + state: Exclude; + metrics: TurnMetrics; + created_at: string; +}): TurnDoneEvent | TurnUpdateEvent { + if (input.state.status === 'paused') { + return { + type: EventType.TURN_UPDATE, + id: newEventId(), + created_at: input.created_at, + state: input.state, + thread_id: null, + }; + } + return { + type: EventType.TURN_DONE, + id: newEventId(), + created_at: input.created_at, + state: { ...input.state, metrics: input.metrics }, + thread_id: null, + }; +} + export class TurnHandle> { private readonly store: ISessionStore; private turn: TurnRecord; @@ -257,18 +274,14 @@ export class TurnHandle> { generator = undefined; const updatedAt = new Date(); const createdAtIso = updatedAt.toISOString(); - const state: TerminalTurnState = { - ...error.state, - metrics: turnMetricsFromAgentThreadMetrics(orchestrator.getMetrics()), - }; - this.turn = { ...this.turn, state, updated_at: updatedAt }; - yield { - type: EventType.TURN_DONE, - id: newEventId(), + const metrics = turnMetricsFromAgentThreadMetrics(orchestrator.getMetrics()); + const yielded = streamEventForStoreState({ + state: error.state, + metrics, created_at: createdAtIso, - state, - thread_id: null, - }; + }); + this.turn = { ...this.turn, state: yielded.state, updated_at: updatedAt }; + yield yielded; return; } throw error; @@ -335,22 +348,11 @@ export class TurnHandle> { }; } - const turnStateEvent: TurnDoneEvent | TurnUpdateEvent = - nextState.status === 'paused' - ? { - type: EventType.TURN_UPDATE, - id: newEventId(), - created_at: createdAtIso, - state: nextState, - thread_id: null, - } - : { - type: EventType.TURN_DONE, - id: newEventId(), - created_at: createdAtIso, - state: nextState, - thread_id: null, - }; + const turnStateEvent = streamEventForStoreState({ + state: nextState, + metrics, + created_at: createdAtIso, + }); if (!frozenByStore) { try { @@ -363,18 +365,16 @@ export class TurnHandle> { this.turn = { ...this.turn, state: nextState, updated_at: updatedAt }; } catch (persistError) { if (persistError instanceof TurnNotRunningError) { - const state: TerminalTurnState = { ...persistError.state, metrics }; - this.turn = { ...this.turn, state, updated_at: updatedAt }; + const yielded = streamEventForStoreState({ + state: persistError.state, + metrics, + created_at: createdAtIso, + }); + this.turn = { ...this.turn, state: yielded.state, updated_at: updatedAt }; await resolver.close().catch(() => { /* no-op */ }); - yield { - type: EventType.TURN_DONE, - id: newEventId(), - created_at: createdAtIso, - state, - thread_id: null, - }; + yield yielded; // eslint-disable-next-line no-unsafe-finally -- deliberate: a concurrent freeze during the lifecycle write makes the store's state authoritative, so the stream ends here return; } diff --git a/packages/trueforge-core/src/agent-session/store/ISessionStore.ts b/packages/trueforge-core/src/agent-session/store/ISessionStore.ts index db62d73bc..83633546d 100644 --- a/packages/trueforge-core/src/agent-session/store/ISessionStore.ts +++ b/packages/trueforge-core/src/agent-session/store/ISessionStore.ts @@ -159,12 +159,7 @@ export interface ListTurnsInput { export interface UpdateTurnStateInput { session_id: string; turn_id: string; - /** Tip after the flip: terminal (`done`/`cancelled`/`error`) or HITL `paused`. */ state: Exclude; - /** - * Caller-built companion event written atomically with the state flip. - * Terminal → `turn.done`; paused → `turn.update` with paused state. - */ turn_done_event: PersistedTurnEvent; } diff --git a/packages/trueforge-core/src/agent-session/store/SessionStoreErrors.ts b/packages/trueforge-core/src/agent-session/store/SessionStoreErrors.ts index 89b31ee90..88cdfe0eb 100644 --- a/packages/trueforge-core/src/agent-session/store/SessionStoreErrors.ts +++ b/packages/trueforge-core/src/agent-session/store/SessionStoreErrors.ts @@ -1,4 +1,4 @@ -import type { TerminalTurnState } from '../schemas/turn'; +import type { TurnState } from '../schemas/turn'; /** Store conflict (e.g. first-terminal-wins violation, concurrent createTurn). */ export abstract class SessionStoreConflictError extends Error { @@ -88,9 +88,9 @@ export class PreviousTurnRunningError extends SessionStoreConflictError { export class TurnNotRunningError extends SessionStoreConflictError { readonly turn_id: string; - readonly state: TerminalTurnState; + readonly state: Exclude; - constructor(turn_id: string, state: TerminalTurnState) { + constructor(turn_id: string, state: Exclude) { super(`Turn ${turn_id} is not running (${state.status})`); this.name = 'TurnNotRunningError'; this.turn_id = turn_id; diff --git a/packages/trueforge-core/tests/agent-session/testHelpers.ts b/packages/trueforge-core/tests/agent-session/testHelpers.ts index e3fc020fc..0bf46eab1 100644 --- a/packages/trueforge-core/tests/agent-session/testHelpers.ts +++ b/packages/trueforge-core/tests/agent-session/testHelpers.ts @@ -89,11 +89,13 @@ export function makeTestResolver Promise; usage?: CompletionUsage; + /** Override the mock LLM stream; defaults to {@link emptyLlmStream}. */ + create?: () => AsyncGenerator; /** Named-agent lookup for sessions bound by agent_id. */ agent?: ((agentId: string) => Promise) | undefined; }): ITurnResourceResolver { const llm = makeMockILLM({ - create: jest.fn().mockImplementation(() => emptyLlmStream(options?.usage)), + create: jest.fn().mockImplementation(() => options?.create?.() ?? emptyLlmStream(options?.usage)), }); const base = new TurnResourceResolver({ llm: () => Promise.resolve({ modelClient: llm, defaultModelParams: {} }), diff --git a/packages/trueforge-core/tests/agent-session/turnStream.test.ts b/packages/trueforge-core/tests/agent-session/turnStream.test.ts index 15b857110..4e52836c0 100644 --- a/packages/trueforge-core/tests/agent-session/turnStream.test.ts +++ b/packages/trueforge-core/tests/agent-session/turnStream.test.ts @@ -5,6 +5,7 @@ import { InMemorySessionStore } from '../../src/agent-session/store/InMemorySess import { TurnResourceResolver } from '../../src/agent-session/TurnResourceResolver'; import { RemoteMCP } from '../../src/core/mcp/RemoteMCP'; import { makeStubPublicSandbox } from '../core/harnessMocks'; +import { makeApprovalGatedWriteNoteToolSet, writeNoteToolCallStream } from '../orchestration/helpers/helpers'; import { emptyLlmStream, makeAgentSpec, @@ -81,6 +82,61 @@ describe('TurnHandle.stream()', () => { expect(stored?.state.status).toBe('done'); }); + it('HITL required actions write paused + turn.update, not done', async () => { + const { store, session } = await createSession(); + const { toolSet } = makeApprovalGatedWriteNoteToolSet(); + const turn = await session.createTurn({ + turn_id: mintTestTurnId(), + active_executor_id: TEST_ACTIVE_EXECUTOR_ID, + input: [{ type: EventType.USER_MESSAGE, content: 'hello' }], + previous_turn_id: 'none', + signal: new AbortController().signal, + resolver: makeTestResolver({ + extraCapabilities: [{ systemToolSets: [toolSet] }], + create: () => writeNoteToolCallStream(), + }), + }); + + const types: string[] = []; + for await (const event of turn.stream()) { + types.push(event.type); + } + + expect(types[0]).toBe(EventType.TURN_CREATED); + expect(types).toContain(EventType.TOOL_APPROVAL_REQUIRED); + expect(types[types.length - 1]).toBe(EventType.TURN_UPDATE); + expect(types).not.toContain(EventType.TURN_DONE); + expect(turn.state.status).toBe('paused'); + + const { data } = await turn.listEvents({ limit: 50 }); + const approval = data.find(e => e.type === EventType.TOOL_APPROVAL_REQUIRED); + const updates = data.filter(e => e.type === EventType.TURN_UPDATE); + expect(approval).toBeDefined(); + expect(data.some(e => e.type === EventType.TURN_DONE)).toBe(false); + expect(updates).toHaveLength(1); + if (turn.state.status === 'paused' && approval) { + expect(turn.state.action_required_on_events).toEqual([{ id: approval.id }]); + expect(updates[0]).toMatchObject({ + type: EventType.TURN_UPDATE, + state: { + status: 'paused', + action_required_on_events: [{ id: approval.id }], + }, + }); + } + + const stored = await store.getTurn({ + session_id: 's1', + turn_id: turn.id, + }); + expect(stored?.state.status).toBe('paused'); + const sessionRow = await store.getSession({ tenant_id: tenant, session_id: 's1' }); + expect(sessionRow?.metrics).toEqual({ + total_duration_ms: 0, + total_turns: 1, + }); + }); + it('persists final turn usage from orchestrator metrics', async () => { const { session } = await createSession(); const turn = await session.createTurn({ diff --git a/packages/trueforge/src/db/postgres/session-store/queries/turns.ts b/packages/trueforge/src/db/postgres/session-store/queries/turns.ts index 73ed24a60..e407c7095 100644 --- a/packages/trueforge/src/db/postgres/session-store/queries/turns.ts +++ b/packages/trueforge/src/db/postgres/session-store/queries/turns.ts @@ -150,15 +150,11 @@ async function addSessionCostAndDuration( .execute(); } -function terminalTurnState(state: TurnState, turn_id: string): TerminalTurnState { - switch (state.status) { - case 'running': - throw new SessionStoreInvariantError(`expected terminal state for turn ${turn_id}, got running`); - case 'done': - case 'cancelled': - case 'error': - return state; +function nonRunningTurnState(state: TurnState, turn_id: string): Exclude { + if (state.status === 'running') { + throw new SessionStoreInvariantError(`expected non-running state for turn ${turn_id}, got running`); } + return state; } /** @@ -190,7 +186,7 @@ export async function classifyTurnFenceWriteFailure(db: Kysely, keys: if (!row) { throw new TurnNotFoundError(keys.turn_id); } - throw new TurnNotRunningError(keys.turn_id, terminalTurnState(row.state, keys.turn_id)); + throw new TurnNotRunningError(keys.turn_id, nonRunningTurnState(row.state, keys.turn_id)); } /** @@ -212,7 +208,7 @@ export async function classifyTurnThreadWriteFailure( throw new TurnNotFoundError(keys.turn_id); } if (row.state.status !== 'running') { - throw new TurnNotRunningError(keys.turn_id, terminalTurnState(row.state, keys.turn_id)); + throw new TurnNotRunningError(keys.turn_id, nonRunningTurnState(row.state, keys.turn_id)); } throw new SessionStoreInvariantError(`thread ${thread_id} not found in turn ${keys.turn_id}`); } @@ -231,7 +227,7 @@ export async function assertTurnRunning(db: DbOrTrx, keys: TurnKeys): Promise, input: UpdateTurnSta if (!existing) { throw new TurnNotFoundError(input.turn_id); } - throw new TurnNotRunningError(input.turn_id, terminalTurnState(existing.state, input.turn_id)); + throw new TurnNotRunningError(input.turn_id, nonRunningTurnState(existing.state, input.turn_id)); } if (input.state.status !== 'paused') { diff --git a/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts b/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts index 136ef795f..d222b8505 100644 --- a/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts +++ b/packages/trueforge/src/db/sqlite/session-store/queries/turns.ts @@ -140,15 +140,11 @@ async function addSessionCostAndDuration( .execute(); } -function terminalTurnState(state: TurnState, turn_id: string): TerminalTurnState { - switch (state.status) { - case 'running': - throw new SessionStoreInvariantError(`expected terminal state for turn ${turn_id}, got running`); - case 'done': - case 'cancelled': - case 'error': - return state; +function nonRunningTurnState(state: TurnState, turn_id: string): Exclude { + if (state.status === 'running') { + throw new SessionStoreInvariantError(`expected non-running state for turn ${turn_id}, got running`); } + return state; } async function loadTurnState(db: DbOrTrx, keys: TurnKeys): Promise { @@ -167,7 +163,7 @@ export async function classifyTurnFenceWriteFailure(db: DbOrTrx, keys: TurnKeys) if (!state) { throw new TurnNotFoundError(keys.turn_id); } - throw new TurnNotRunningError(keys.turn_id, terminalTurnState(state, keys.turn_id)); + throw new TurnNotRunningError(keys.turn_id, nonRunningTurnState(state, keys.turn_id)); } /** @@ -179,7 +175,7 @@ export async function classifyTurnThreadWriteFailure(db: DbOrTrx, keys: TurnKeys throw new TurnNotFoundError(keys.turn_id); } if (state.status !== 'running') { - throw new TurnNotRunningError(keys.turn_id, terminalTurnState(state, keys.turn_id)); + throw new TurnNotRunningError(keys.turn_id, nonRunningTurnState(state, keys.turn_id)); } throw new SessionStoreInvariantError(`thread ${thread_id} not found in turn ${keys.turn_id}`); } @@ -190,7 +186,7 @@ export async function assertTurnRunning(db: DbOrTrx, keys: TurnKeys): Promise, input: UpdateTurnSta if (!existing) { throw new TurnNotFoundError(input.turn_id); } - throw new TurnNotRunningError(input.turn_id, terminalTurnState(existing.state, input.turn_id)); + throw new TurnNotRunningError(input.turn_id, nonRunningTurnState(existing.state, input.turn_id)); } if (input.state.status !== 'paused') { From 095ddcc18308f88e10362ceb665df08a008a852b Mon Sep 17 00:00:00 2001 From: "trueforge-dev-bot[bot]" Date: Fri, 18 Sep 2026 13:46:15 +0000 Subject: [PATCH 5/5] Regenerate OpenAPI document and SDKs --- ...60918134615-regenerate-sdk-from-openapi.md | 5 + .github/fern/openapi/openapi.json | 106 ++++++------------ docs/openapi.json | 106 ++++++------------ .../src/api/types/ActionRequiredEvent.ts | 8 -- .../trueforge-sdk/src/api/types/TurnState.ts | 1 + .../src/api/types/TurnStateDone.ts | 4 +- ...pdateStatePaused.ts => TurnStatePaused.ts} | 2 +- .../src/api/types/TurnUpdateState.ts | 2 +- .../src/api/types/TurnUpdateStateRunning.ts | 5 - packages/trueforge-sdk/src/api/types/index.ts | 4 +- .../types/ActionRequiredEvent.ts | 21 ---- .../src/serialization/types/TurnState.ts | 16 ++- .../src/serialization/types/TurnStateDone.ts | 3 - ...pdateStatePaused.ts => TurnStatePaused.ts} | 8 +- .../serialization/types/TurnUpdateState.ts | 8 +- .../types/TurnUpdateStateRunning.ts | 18 --- .../src/serialization/types/index.ts | 4 +- .../src/trueforge_sdk/__init__.py | 12 +- .../src/trueforge_sdk/types/__init__.py | 12 +- .../types/action_required_event.py | 9 -- .../src/trueforge_sdk/types/turn_state.py | 3 +- .../trueforge_sdk/types/turn_state_done.py | 8 +- ...e_state_paused.py => turn_state_paused.py} | 2 +- .../trueforge_sdk/types/turn_update_state.py | 6 +- .../types/turn_update_state_running.py | 19 ---- 25 files changed, 112 insertions(+), 280 deletions(-) create mode 100644 .changeset/20260918134615-regenerate-sdk-from-openapi.md delete mode 100644 packages/trueforge-sdk/src/api/types/ActionRequiredEvent.ts rename packages/trueforge-sdk/src/api/types/{TurnUpdateStatePaused.ts => TurnStatePaused.ts} (86%) delete mode 100644 packages/trueforge-sdk/src/api/types/TurnUpdateStateRunning.ts delete mode 100644 packages/trueforge-sdk/src/serialization/types/ActionRequiredEvent.ts rename packages/trueforge-sdk/src/serialization/types/{TurnUpdateStatePaused.ts => TurnStatePaused.ts} (76%) delete mode 100644 packages/trueforge-sdk/src/serialization/types/TurnUpdateStateRunning.ts delete mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/action_required_event.py rename python/trueforge_sdk/src/trueforge_sdk/types/{turn_update_state_paused.py => turn_state_paused.py} (93%) delete mode 100644 python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state_running.py diff --git a/.changeset/20260918134615-regenerate-sdk-from-openapi.md b/.changeset/20260918134615-regenerate-sdk-from-openapi.md new file mode 100644 index 000000000..efd8ff00f --- /dev/null +++ b/.changeset/20260918134615-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 7a06b406e..e067c566c 100644 --- a/.github/fern/openapi/openapi.json +++ b/.github/fern/openapi/openapi.json @@ -14,27 +14,6 @@ ], "type": "object" }, - "ActionRequiredEvent": { - "discriminator": { - "mapping": { - "mcp.auth_required": "#/components/schemas/MCPAuthRequiredEvent", - "tool.approval_required": "#/components/schemas/ToolApprovalRequiredEvent", - "tool.response_required": "#/components/schemas/ToolResponseRequiredEvent" - }, - "propertyName": "type" - }, - "oneOf": [ - { - "$ref": "#/components/schemas/ToolApprovalRequiredEvent" - }, - { - "$ref": "#/components/schemas/ToolResponseRequiredEvent" - }, - { - "$ref": "#/components/schemas/MCPAuthRequiredEvent" - } - ] - }, "Agent": { "additionalProperties": false, "properties": { @@ -5224,6 +5203,7 @@ "cancelled": "#/components/schemas/TurnStateCancelled", "done": "#/components/schemas/TurnStateDone", "error": "#/components/schemas/TurnStateError", + "paused": "#/components/schemas/TurnStatePaused", "running": "#/components/schemas/TurnStateRunning" }, "propertyName": "status" @@ -5232,6 +5212,9 @@ { "$ref": "#/components/schemas/TurnStateRunning" }, + { + "$ref": "#/components/schemas/TurnStatePaused" + }, { "$ref": "#/components/schemas/TurnStateDone" }, @@ -5305,17 +5288,10 @@ "type": "null" } ], - "description": "Final `model.message` for the turn, or null when the turn ended paused without a final message." - }, - "required_actions": { - "description": "Pending actions (`tool.approval_required`, `tool.response_required`, `mcp.auth_required`); empty when none.", - "items": { - "$ref": "#/components/schemas/ActionRequiredEvent" - }, - "type": "array" + "description": "Final `model.message` for the turn, or null when the turn finished without one." }, "status": { - "description": "Turn finished (possibly paused for required actions).", + "description": "Turn finished with no open required actions.", "enum": [ "done" ], @@ -5325,7 +5301,6 @@ "required": [ "status", "output", - "required_actions", "completed_at" ], "type": "object" @@ -5365,6 +5340,29 @@ ], "type": "object" }, + "TurnStatePaused": { + "properties": { + "action_required_on_events": { + "description": "Events that still need a user or client action.", + "items": { + "$ref": "#/components/schemas/ActionRequired" + }, + "type": "array" + }, + "status": { + "description": "Turn is paused waiting for required actions.", + "enum": [ + "paused" + ], + "type": "string" + } + }, + "required": [ + "status", + "action_required_on_events" + ], + "type": "object" + }, "TurnStateRunning": { "properties": { "status": { @@ -5482,58 +5480,20 @@ "description": "Live non-terminal turn status.", "discriminator": { "mapping": { - "paused": "#/components/schemas/TurnUpdateStatePaused", - "running": "#/components/schemas/TurnUpdateStateRunning" + "paused": "#/components/schemas/TurnStatePaused", + "running": "#/components/schemas/TurnStateRunning" }, "propertyName": "status" }, "oneOf": [ { - "$ref": "#/components/schemas/TurnUpdateStatePaused" + "$ref": "#/components/schemas/TurnStatePaused" }, { - "$ref": "#/components/schemas/TurnUpdateStateRunning" + "$ref": "#/components/schemas/TurnStateRunning" } ] }, - "TurnUpdateStatePaused": { - "properties": { - "action_required_on_events": { - "description": "Events that still need a user or client action.", - "items": { - "$ref": "#/components/schemas/ActionRequired" - }, - "type": "array" - }, - "status": { - "description": "Turn is paused waiting for required actions.", - "enum": [ - "paused" - ], - "type": "string" - } - }, - "required": [ - "status", - "action_required_on_events" - ], - "type": "object" - }, - "TurnUpdateStateRunning": { - "properties": { - "status": { - "description": "Turn is executing.", - "enum": [ - "running" - ], - "type": "string" - } - }, - "required": [ - "status" - ], - "type": "object" - }, "UpdateAgentRequest": { "additionalProperties": false, "properties": { diff --git a/docs/openapi.json b/docs/openapi.json index 7a06b406e..e067c566c 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -14,27 +14,6 @@ ], "type": "object" }, - "ActionRequiredEvent": { - "discriminator": { - "mapping": { - "mcp.auth_required": "#/components/schemas/MCPAuthRequiredEvent", - "tool.approval_required": "#/components/schemas/ToolApprovalRequiredEvent", - "tool.response_required": "#/components/schemas/ToolResponseRequiredEvent" - }, - "propertyName": "type" - }, - "oneOf": [ - { - "$ref": "#/components/schemas/ToolApprovalRequiredEvent" - }, - { - "$ref": "#/components/schemas/ToolResponseRequiredEvent" - }, - { - "$ref": "#/components/schemas/MCPAuthRequiredEvent" - } - ] - }, "Agent": { "additionalProperties": false, "properties": { @@ -5224,6 +5203,7 @@ "cancelled": "#/components/schemas/TurnStateCancelled", "done": "#/components/schemas/TurnStateDone", "error": "#/components/schemas/TurnStateError", + "paused": "#/components/schemas/TurnStatePaused", "running": "#/components/schemas/TurnStateRunning" }, "propertyName": "status" @@ -5232,6 +5212,9 @@ { "$ref": "#/components/schemas/TurnStateRunning" }, + { + "$ref": "#/components/schemas/TurnStatePaused" + }, { "$ref": "#/components/schemas/TurnStateDone" }, @@ -5305,17 +5288,10 @@ "type": "null" } ], - "description": "Final `model.message` for the turn, or null when the turn ended paused without a final message." - }, - "required_actions": { - "description": "Pending actions (`tool.approval_required`, `tool.response_required`, `mcp.auth_required`); empty when none.", - "items": { - "$ref": "#/components/schemas/ActionRequiredEvent" - }, - "type": "array" + "description": "Final `model.message` for the turn, or null when the turn finished without one." }, "status": { - "description": "Turn finished (possibly paused for required actions).", + "description": "Turn finished with no open required actions.", "enum": [ "done" ], @@ -5325,7 +5301,6 @@ "required": [ "status", "output", - "required_actions", "completed_at" ], "type": "object" @@ -5365,6 +5340,29 @@ ], "type": "object" }, + "TurnStatePaused": { + "properties": { + "action_required_on_events": { + "description": "Events that still need a user or client action.", + "items": { + "$ref": "#/components/schemas/ActionRequired" + }, + "type": "array" + }, + "status": { + "description": "Turn is paused waiting for required actions.", + "enum": [ + "paused" + ], + "type": "string" + } + }, + "required": [ + "status", + "action_required_on_events" + ], + "type": "object" + }, "TurnStateRunning": { "properties": { "status": { @@ -5482,58 +5480,20 @@ "description": "Live non-terminal turn status.", "discriminator": { "mapping": { - "paused": "#/components/schemas/TurnUpdateStatePaused", - "running": "#/components/schemas/TurnUpdateStateRunning" + "paused": "#/components/schemas/TurnStatePaused", + "running": "#/components/schemas/TurnStateRunning" }, "propertyName": "status" }, "oneOf": [ { - "$ref": "#/components/schemas/TurnUpdateStatePaused" + "$ref": "#/components/schemas/TurnStatePaused" }, { - "$ref": "#/components/schemas/TurnUpdateStateRunning" + "$ref": "#/components/schemas/TurnStateRunning" } ] }, - "TurnUpdateStatePaused": { - "properties": { - "action_required_on_events": { - "description": "Events that still need a user or client action.", - "items": { - "$ref": "#/components/schemas/ActionRequired" - }, - "type": "array" - }, - "status": { - "description": "Turn is paused waiting for required actions.", - "enum": [ - "paused" - ], - "type": "string" - } - }, - "required": [ - "status", - "action_required_on_events" - ], - "type": "object" - }, - "TurnUpdateStateRunning": { - "properties": { - "status": { - "description": "Turn is executing.", - "enum": [ - "running" - ], - "type": "string" - } - }, - "required": [ - "status" - ], - "type": "object" - }, "UpdateAgentRequest": { "additionalProperties": false, "properties": { diff --git a/packages/trueforge-sdk/src/api/types/ActionRequiredEvent.ts b/packages/trueforge-sdk/src/api/types/ActionRequiredEvent.ts deleted file mode 100644 index b9ab188ac..000000000 --- a/packages/trueforge-sdk/src/api/types/ActionRequiredEvent.ts +++ /dev/null @@ -1,8 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type * as TrueForge from "../index.js"; - -export type ActionRequiredEvent = - | TrueForge.McpAuthRequiredEvent - | TrueForge.ToolApprovalRequiredEvent - | TrueForge.ToolResponseRequiredEvent; diff --git a/packages/trueforge-sdk/src/api/types/TurnState.ts b/packages/trueforge-sdk/src/api/types/TurnState.ts index 1d1ff9611..eef55f805 100644 --- a/packages/trueforge-sdk/src/api/types/TurnState.ts +++ b/packages/trueforge-sdk/src/api/types/TurnState.ts @@ -6,4 +6,5 @@ export type TurnState = | TrueForge.TurnStateCancelled | TrueForge.TurnStateDone | TrueForge.TurnStateError + | TrueForge.TurnStatePaused | TrueForge.TurnStateRunning; diff --git a/packages/trueforge-sdk/src/api/types/TurnStateDone.ts b/packages/trueforge-sdk/src/api/types/TurnStateDone.ts index d8d10966a..2ceaf992f 100644 --- a/packages/trueforge-sdk/src/api/types/TurnStateDone.ts +++ b/packages/trueforge-sdk/src/api/types/TurnStateDone.ts @@ -6,9 +6,7 @@ export interface TurnStateDone { /** ISO 8601 time when the turn reached a terminal state. */ completedAt: string; metrics?: TrueForge.TurnMetrics; - /** Final `model.message` for the turn, or null when the turn ended paused without a final message. */ + /** Final `model.message` for the turn, or null when the turn finished without one. */ output: TrueForge.ModelMessageEvent | null; - /** Pending actions (`tool.approval_required`, `tool.response_required`, `mcp.auth_required`); empty when none. */ - requiredActions: TrueForge.ActionRequiredEvent[]; status: "done"; } diff --git a/packages/trueforge-sdk/src/api/types/TurnUpdateStatePaused.ts b/packages/trueforge-sdk/src/api/types/TurnStatePaused.ts similarity index 86% rename from packages/trueforge-sdk/src/api/types/TurnUpdateStatePaused.ts rename to packages/trueforge-sdk/src/api/types/TurnStatePaused.ts index 6610360e2..20725659e 100644 --- a/packages/trueforge-sdk/src/api/types/TurnUpdateStatePaused.ts +++ b/packages/trueforge-sdk/src/api/types/TurnStatePaused.ts @@ -2,7 +2,7 @@ import type * as TrueForge from "../index.js"; -export interface TurnUpdateStatePaused { +export interface TurnStatePaused { /** Events that still need a user or client action. */ actionRequiredOnEvents: TrueForge.ActionRequired[]; status: "paused"; diff --git a/packages/trueforge-sdk/src/api/types/TurnUpdateState.ts b/packages/trueforge-sdk/src/api/types/TurnUpdateState.ts index 86c4d0e0e..f99843dd7 100644 --- a/packages/trueforge-sdk/src/api/types/TurnUpdateState.ts +++ b/packages/trueforge-sdk/src/api/types/TurnUpdateState.ts @@ -5,4 +5,4 @@ import type * as TrueForge from "../index.js"; /** * Live non-terminal turn status. */ -export type TurnUpdateState = TrueForge.TurnUpdateStatePaused | TrueForge.TurnUpdateStateRunning; +export type TurnUpdateState = TrueForge.TurnStatePaused | TrueForge.TurnStateRunning; diff --git a/packages/trueforge-sdk/src/api/types/TurnUpdateStateRunning.ts b/packages/trueforge-sdk/src/api/types/TurnUpdateStateRunning.ts deleted file mode 100644 index bb4be8e7f..000000000 --- a/packages/trueforge-sdk/src/api/types/TurnUpdateStateRunning.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -export interface TurnUpdateStateRunning { - status: "running"; -} diff --git a/packages/trueforge-sdk/src/api/types/index.ts b/packages/trueforge-sdk/src/api/types/index.ts index 9b29138b5..77c413db5 100644 --- a/packages/trueforge-sdk/src/api/types/index.ts +++ b/packages/trueforge-sdk/src/api/types/index.ts @@ -1,5 +1,4 @@ export * from "./ActionRequired.js"; -export * from "./ActionRequiredEvent.js"; export * from "./Agent.js"; export * from "./AgentCodeSnippet.js"; export * from "./AgentCodeSnippetSampleCode.js"; @@ -221,12 +220,11 @@ export * from "./TurnStateCancelledReason.js"; export * from "./TurnStateDone.js"; export * from "./TurnStateError.js"; export * from "./TurnStateErrorMetrics.js"; +export * from "./TurnStatePaused.js"; export * from "./TurnStateRunning.js"; export * from "./TurnStreamingEvent.js"; export * from "./TurnUpdateEvent.js"; export * from "./TurnUpdateState.js"; -export * from "./TurnUpdateStatePaused.js"; -export * from "./TurnUpdateStateRunning.js"; export * from "./UserMessage.js"; export * from "./UserMessageContent.js"; export * from "./UserMessageContentItem.js"; diff --git a/packages/trueforge-sdk/src/serialization/types/ActionRequiredEvent.ts b/packages/trueforge-sdk/src/serialization/types/ActionRequiredEvent.ts deleted file mode 100644 index 73e8c12c3..000000000 --- a/packages/trueforge-sdk/src/serialization/types/ActionRequiredEvent.ts +++ /dev/null @@ -1,21 +0,0 @@ -// 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 { McpAuthRequiredEvent } from "./McpAuthRequiredEvent.js"; -import { ToolApprovalRequiredEvent } from "./ToolApprovalRequiredEvent.js"; -import { ToolResponseRequiredEvent } from "./ToolResponseRequiredEvent.js"; - -export const ActionRequiredEvent: core.serialization.Schema< - serializers.ActionRequiredEvent.Raw, - TrueForge.ActionRequiredEvent -> = core.serialization.undiscriminatedUnion([ - McpAuthRequiredEvent, - ToolApprovalRequiredEvent, - ToolResponseRequiredEvent, -]); - -export declare namespace ActionRequiredEvent { - export type Raw = McpAuthRequiredEvent.Raw | ToolApprovalRequiredEvent.Raw | ToolResponseRequiredEvent.Raw; -} diff --git a/packages/trueforge-sdk/src/serialization/types/TurnState.ts b/packages/trueforge-sdk/src/serialization/types/TurnState.ts index 252cd55c6..189a23194 100644 --- a/packages/trueforge-sdk/src/serialization/types/TurnState.ts +++ b/packages/trueforge-sdk/src/serialization/types/TurnState.ts @@ -6,11 +6,23 @@ import type * as serializers from "../index.js"; import { TurnStateCancelled } from "./TurnStateCancelled.js"; import { TurnStateDone } from "./TurnStateDone.js"; import { TurnStateError } from "./TurnStateError.js"; +import { TurnStatePaused } from "./TurnStatePaused.js"; import { TurnStateRunning } from "./TurnStateRunning.js"; export const TurnState: core.serialization.Schema = - core.serialization.undiscriminatedUnion([TurnStateCancelled, TurnStateDone, TurnStateError, TurnStateRunning]); + core.serialization.undiscriminatedUnion([ + TurnStateCancelled, + TurnStateDone, + TurnStateError, + TurnStatePaused, + TurnStateRunning, + ]); export declare namespace TurnState { - export type Raw = TurnStateCancelled.Raw | TurnStateDone.Raw | TurnStateError.Raw | TurnStateRunning.Raw; + export type Raw = + | TurnStateCancelled.Raw + | TurnStateDone.Raw + | TurnStateError.Raw + | TurnStatePaused.Raw + | TurnStateRunning.Raw; } diff --git a/packages/trueforge-sdk/src/serialization/types/TurnStateDone.ts b/packages/trueforge-sdk/src/serialization/types/TurnStateDone.ts index 60b9f0404..cf77b3131 100644 --- a/packages/trueforge-sdk/src/serialization/types/TurnStateDone.ts +++ b/packages/trueforge-sdk/src/serialization/types/TurnStateDone.ts @@ -3,7 +3,6 @@ import type * as TrueForge from "../../api/index.js"; import * as core from "../../core/index.js"; import type * as serializers from "../index.js"; -import { ActionRequiredEvent } from "./ActionRequiredEvent.js"; import { ModelMessageEvent } from "./ModelMessageEvent.js"; import { TurnMetrics } from "./TurnMetrics.js"; @@ -12,7 +11,6 @@ export const TurnStateDone: core.serialization.ObjectSchema = core.serialization.object({ actionRequiredOnEvents: core.serialization.property( "action_required_on_events", @@ -16,7 +16,7 @@ export const TurnUpdateStatePaused: core.serialization.ObjectSchema< status: core.serialization.stringLiteral("paused"), }); -export declare namespace TurnUpdateStatePaused { +export declare namespace TurnStatePaused { export interface Raw { action_required_on_events: ActionRequired.Raw[]; status: "paused"; diff --git a/packages/trueforge-sdk/src/serialization/types/TurnUpdateState.ts b/packages/trueforge-sdk/src/serialization/types/TurnUpdateState.ts index d6f59a863..28cf21a8b 100644 --- a/packages/trueforge-sdk/src/serialization/types/TurnUpdateState.ts +++ b/packages/trueforge-sdk/src/serialization/types/TurnUpdateState.ts @@ -3,12 +3,12 @@ import type * as TrueForge from "../../api/index.js"; import * as core from "../../core/index.js"; import type * as serializers from "../index.js"; -import { TurnUpdateStatePaused } from "./TurnUpdateStatePaused.js"; -import { TurnUpdateStateRunning } from "./TurnUpdateStateRunning.js"; +import { TurnStatePaused } from "./TurnStatePaused.js"; +import { TurnStateRunning } from "./TurnStateRunning.js"; export const TurnUpdateState: core.serialization.Schema = - core.serialization.undiscriminatedUnion([TurnUpdateStatePaused, TurnUpdateStateRunning]); + core.serialization.undiscriminatedUnion([TurnStatePaused, TurnStateRunning]); export declare namespace TurnUpdateState { - export type Raw = TurnUpdateStatePaused.Raw | TurnUpdateStateRunning.Raw; + export type Raw = TurnStatePaused.Raw | TurnStateRunning.Raw; } diff --git a/packages/trueforge-sdk/src/serialization/types/TurnUpdateStateRunning.ts b/packages/trueforge-sdk/src/serialization/types/TurnUpdateStateRunning.ts deleted file mode 100644 index 426582c85..000000000 --- a/packages/trueforge-sdk/src/serialization/types/TurnUpdateStateRunning.ts +++ /dev/null @@ -1,18 +0,0 @@ -// 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 TurnUpdateStateRunning: core.serialization.ObjectSchema< - serializers.TurnUpdateStateRunning.Raw, - TrueForge.TurnUpdateStateRunning -> = core.serialization.object({ - status: core.serialization.stringLiteral("running"), -}); - -export declare namespace TurnUpdateStateRunning { - export interface Raw { - status: "running"; - } -} diff --git a/packages/trueforge-sdk/src/serialization/types/index.ts b/packages/trueforge-sdk/src/serialization/types/index.ts index 9b29138b5..77c413db5 100644 --- a/packages/trueforge-sdk/src/serialization/types/index.ts +++ b/packages/trueforge-sdk/src/serialization/types/index.ts @@ -1,5 +1,4 @@ export * from "./ActionRequired.js"; -export * from "./ActionRequiredEvent.js"; export * from "./Agent.js"; export * from "./AgentCodeSnippet.js"; export * from "./AgentCodeSnippetSampleCode.js"; @@ -221,12 +220,11 @@ export * from "./TurnStateCancelledReason.js"; export * from "./TurnStateDone.js"; export * from "./TurnStateError.js"; export * from "./TurnStateErrorMetrics.js"; +export * from "./TurnStatePaused.js"; export * from "./TurnStateRunning.js"; export * from "./TurnStreamingEvent.js"; export * from "./TurnUpdateEvent.js"; export * from "./TurnUpdateState.js"; -export * from "./TurnUpdateStatePaused.js"; -export * from "./TurnUpdateStateRunning.js"; export * from "./UserMessage.js"; export * from "./UserMessageContent.js"; export * from "./UserMessageContentItem.js"; diff --git a/python/trueforge_sdk/src/trueforge_sdk/__init__.py b/python/trueforge_sdk/src/trueforge_sdk/__init__.py index 7242c3c63..e41efcd7e 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/__init__.py +++ b/python/trueforge_sdk/src/trueforge_sdk/__init__.py @@ -8,7 +8,6 @@ if typing.TYPE_CHECKING: from .types import ( ActionRequired, - ActionRequiredEvent, Agent, AgentCodeSnippet, AgentCodeSnippetSampleCode, @@ -230,12 +229,11 @@ TurnStateDone, TurnStateError, TurnStateErrorMetrics, + TurnStatePaused, TurnStateRunning, TurnStreamingEvent, TurnUpdateEvent, TurnUpdateState, - TurnUpdateStatePaused, - TurnUpdateStateRunning, UserMessage, UserMessageContent, UserMessageContentItem, @@ -265,7 +263,6 @@ from .version import __version__ _dynamic_imports: typing.Dict[str, str] = { "ActionRequired": ".types", - "ActionRequiredEvent": ".types", "Agent": ".types", "AgentCodeSnippet": ".types", "AgentCodeSnippetSampleCode": ".types", @@ -501,12 +498,11 @@ "TurnStateDone": ".types", "TurnStateError": ".types", "TurnStateErrorMetrics": ".types", + "TurnStatePaused": ".types", "TurnStateRunning": ".types", "TurnStreamingEvent": ".types", "TurnUpdateEvent": ".types", "TurnUpdateState": ".types", - "TurnUpdateStatePaused": ".types", - "TurnUpdateStateRunning": ".types", "UnauthorizedError": ".errors", "UnprocessableEntityError": ".errors", "UserMessage": ".types", @@ -555,7 +551,6 @@ def __dir__(): __all__ = [ "ActionRequired", - "ActionRequiredEvent", "Agent", "AgentCodeSnippet", "AgentCodeSnippetSampleCode", @@ -791,12 +786,11 @@ def __dir__(): "TurnStateDone", "TurnStateError", "TurnStateErrorMetrics", + "TurnStatePaused", "TurnStateRunning", "TurnStreamingEvent", "TurnUpdateEvent", "TurnUpdateState", - "TurnUpdateStatePaused", - "TurnUpdateStateRunning", "UnauthorizedError", "UnprocessableEntityError", "UserMessage", diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py b/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py index 2e83715b6..00b95ccb5 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/__init__.py @@ -7,7 +7,6 @@ if typing.TYPE_CHECKING: from .action_required import ActionRequired - from .action_required_event import ActionRequiredEvent from .agent import Agent from .agent_code_snippet import AgentCodeSnippet from .agent_code_snippet_sample_code import AgentCodeSnippetSampleCode @@ -229,12 +228,11 @@ from .turn_state_done import TurnStateDone from .turn_state_error import TurnStateError from .turn_state_error_metrics import TurnStateErrorMetrics + from .turn_state_paused import TurnStatePaused from .turn_state_running import TurnStateRunning from .turn_streaming_event import TurnStreamingEvent from .turn_update_event import TurnUpdateEvent from .turn_update_state import TurnUpdateState - from .turn_update_state_paused import TurnUpdateStatePaused - from .turn_update_state_running import TurnUpdateStateRunning from .user_message import UserMessage from .user_message_content import UserMessageContent from .user_message_content_item import UserMessageContentItem @@ -245,7 +243,6 @@ from .zai_model_provider import ZaiModelProvider _dynamic_imports: typing.Dict[str, str] = { "ActionRequired": ".action_required", - "ActionRequiredEvent": ".action_required_event", "Agent": ".agent", "AgentCodeSnippet": ".agent_code_snippet", "AgentCodeSnippetSampleCode": ".agent_code_snippet_sample_code", @@ -467,12 +464,11 @@ "TurnStateDone": ".turn_state_done", "TurnStateError": ".turn_state_error", "TurnStateErrorMetrics": ".turn_state_error_metrics", + "TurnStatePaused": ".turn_state_paused", "TurnStateRunning": ".turn_state_running", "TurnStreamingEvent": ".turn_streaming_event", "TurnUpdateEvent": ".turn_update_event", "TurnUpdateState": ".turn_update_state", - "TurnUpdateStatePaused": ".turn_update_state_paused", - "TurnUpdateStateRunning": ".turn_update_state_running", "UserMessage": ".user_message", "UserMessageContent": ".user_message_content", "UserMessageContentItem": ".user_message_content_item", @@ -507,7 +503,6 @@ def __dir__(): __all__ = [ "ActionRequired", - "ActionRequiredEvent", "Agent", "AgentCodeSnippet", "AgentCodeSnippetSampleCode", @@ -729,12 +724,11 @@ def __dir__(): "TurnStateDone", "TurnStateError", "TurnStateErrorMetrics", + "TurnStatePaused", "TurnStateRunning", "TurnStreamingEvent", "TurnUpdateEvent", "TurnUpdateState", - "TurnUpdateStatePaused", - "TurnUpdateStateRunning", "UserMessage", "UserMessageContent", "UserMessageContentItem", diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/action_required_event.py b/python/trueforge_sdk/src/trueforge_sdk/types/action_required_event.py deleted file mode 100644 index 31aa07509..000000000 --- a/python/trueforge_sdk/src/trueforge_sdk/types/action_required_event.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .mcp_auth_required_event import McpAuthRequiredEvent -from .tool_approval_required_event import ToolApprovalRequiredEvent -from .tool_response_required_event import ToolResponseRequiredEvent - -ActionRequiredEvent = typing.Union[McpAuthRequiredEvent, ToolApprovalRequiredEvent, ToolResponseRequiredEvent] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/turn_state.py b/python/trueforge_sdk/src/trueforge_sdk/types/turn_state.py index 868df1ae3..fc4a3f755 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/turn_state.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/turn_state.py @@ -5,6 +5,7 @@ from .turn_state_cancelled import TurnStateCancelled from .turn_state_done import TurnStateDone from .turn_state_error import TurnStateError +from .turn_state_paused import TurnStatePaused from .turn_state_running import TurnStateRunning -TurnState = typing.Union[TurnStateCancelled, TurnStateDone, TurnStateError, TurnStateRunning] +TurnState = typing.Union[TurnStateCancelled, TurnStateDone, TurnStateError, TurnStatePaused, TurnStateRunning] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/turn_state_done.py b/python/trueforge_sdk/src/trueforge_sdk/types/turn_state_done.py index 17ab1bf82..1080fcc62 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/turn_state_done.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/turn_state_done.py @@ -5,7 +5,6 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.unchecked_base_model import UncheckedBaseModel -from .action_required_event import ActionRequiredEvent from .model_message_event import ModelMessageEvent from .turn_metrics import TurnMetrics @@ -19,12 +18,7 @@ class TurnStateDone(UncheckedBaseModel): metrics: typing.Optional[TurnMetrics] = None output: typing.Optional[ModelMessageEvent] = pydantic.Field(default=None) """ - Final `model.message` for the turn, or null when the turn ended paused without a final message. - """ - - required_actions: typing.List[ActionRequiredEvent] = pydantic.Field() - """ - Pending actions (`tool.approval_required`, `tool.response_required`, `mcp.auth_required`); empty when none. + Final `model.message` for the turn, or null when the turn finished without one. """ status: typing.Literal["done"] = "done" diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state_paused.py b/python/trueforge_sdk/src/trueforge_sdk/types/turn_state_paused.py similarity index 93% rename from python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state_paused.py rename to python/trueforge_sdk/src/trueforge_sdk/types/turn_state_paused.py index 8edcce45f..a17fb2084 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state_paused.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/turn_state_paused.py @@ -8,7 +8,7 @@ from .action_required import ActionRequired -class TurnUpdateStatePaused(UncheckedBaseModel): +class TurnStatePaused(UncheckedBaseModel): action_required_on_events: typing.List[ActionRequired] = pydantic.Field() """ Events that still need a user or client action. diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state.py b/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state.py index 931194b20..5dde446ee 100644 --- a/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state.py +++ b/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state.py @@ -2,7 +2,7 @@ import typing -from .turn_update_state_paused import TurnUpdateStatePaused -from .turn_update_state_running import TurnUpdateStateRunning +from .turn_state_paused import TurnStatePaused +from .turn_state_running import TurnStateRunning -TurnUpdateState = typing.Union[TurnUpdateStatePaused, TurnUpdateStateRunning] +TurnUpdateState = typing.Union[TurnStatePaused, TurnStateRunning] diff --git a/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state_running.py b/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state_running.py deleted file mode 100644 index d0b67f00d..000000000 --- a/python/trueforge_sdk/src/trueforge_sdk/types/turn_update_state_running.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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 TurnUpdateStateRunning(UncheckedBaseModel): - status: typing.Literal["running"] = "running" - - 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