File size: 6,093 Bytes
68722e4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | import { StudioApiRequestError, getStudioAuthHeaders, studioRequest } from './client'
import type {
StudioCreateRunInput,
StudioCreateSceneRunInput,
StudioCreateSceneRunResponse,
StudioCreateSessionInput,
StudioRun,
StudioScene,
StudioSceneSnapshot,
StudioSession,
StudioSessionSnapshot,
} from '../protocol/studio-agent-types'
/**
* Optional request options. Every Scene call accepts an `AbortSignal` so a Session switch can drop
* a response that is no longer needed; the parameter is optional, so the existing call sites stay
* valid and keep their exact behavior.
*/
export interface StudioRequestOptions {
signal?: AbortSignal
}
interface CreateSessionResponse {
session: StudioSession
}
export interface CreateRunResponse extends Omit<StudioSessionSnapshot, 'session'> {
run: StudioRun
assistantMessage?: unknown
text?: string
}
interface CancelRunResponse {
run?: StudioRun
status: 'cancelled' | 'completed' | 'failed' | 'running' | 'pending'
message: string
}
export async function createStudioSession(input: StudioCreateSessionInput): Promise<StudioSession> {
const data = await studioRequest<CreateSessionResponse>('/sessions', {
method: 'POST',
headers: getStudioAuthHeaders('application/json'),
body: JSON.stringify(input),
})
return data.session
}
/**
* Session snapshot. The optional `signal` lets a Session switch drop a response nobody needs any
* more; the parameter is optional, so every existing call site keeps its exact behavior.
*/
export async function getStudioSessionSnapshot(
sessionId: string,
options?: StudioRequestOptions,
): Promise<StudioSessionSnapshot> {
return studioRequest<StudioSessionSnapshot>(`/sessions/${encodeURIComponent(sessionId)}`, {
headers: getStudioAuthHeaders(),
signal: options?.signal,
})
}
/**
* Delete one Session and everything that points at it (the schema cascades: Scenes, messages, parts,
* Runs, renders, session events). An answer of `NOT_FOUND` means the Session is already gone for this
* owner, which is the outcome the caller wanted, so it resolves `true` too and a stale local id can
* still be forgotten. Any other failure rejects, and the caller keeps its entry.
*/
export async function deleteStudioSession(sessionId: string): Promise<boolean> {
try {
const data = await studioRequest<{ deleted: boolean }>(
`/sessions/${encodeURIComponent(sessionId)}`,
{
method: 'DELETE',
headers: getStudioAuthHeaders('application/json'),
},
)
return data.deleted === true
} catch (error) {
if (error instanceof StudioApiRequestError && error.code === 'NOT_FOUND') {
return true
}
throw error
}
}
/**
* One Scene and only its own records. An absent, foreign, or foreign-Session Scene all arrive
* as the same 404, so callers cannot tell them apart.
*/
export async function getStudioSceneSnapshot(
sessionId: string,
sceneId: string,
options?: StudioRequestOptions,
): Promise<StudioSceneSnapshot> {
return studioRequest<StudioSceneSnapshot>(
`/sessions/${encodeURIComponent(sessionId)}/scenes/${encodeURIComponent(sceneId)}`,
{
headers: getStudioAuthHeaders(),
signal: options?.signal,
},
)
}
interface CreateSceneResponse {
scene: StudioScene
}
interface ReorderScenesResponse {
scenes: StudioScene[]
}
/**
* Append one Scene to a Session. Position and source path are server decisions: the body is empty
* and never carries an identity, a directory or a file path.
*/
export async function createStudioScene(
sessionId: string,
options?: StudioRequestOptions,
): Promise<StudioScene> {
const data = await studioRequest<CreateSceneResponse>(
`/sessions/${encodeURIComponent(sessionId)}/scenes`,
{
method: 'POST',
headers: getStudioAuthHeaders('application/json'),
body: JSON.stringify({}),
signal: options?.signal,
},
)
return data.scene
}
/**
* Replace the Scene order of a Session. The order must name exactly the persisted Scene set, so a
* rejected request is a conflict and never a partial reorder.
*/
export async function reorderStudioScenes(
sessionId: string,
sceneIds: string[],
options?: StudioRequestOptions,
): Promise<StudioScene[]> {
const data = await studioRequest<ReorderScenesResponse>(
`/sessions/${encodeURIComponent(sessionId)}/scenes/order`,
{
method: 'PUT',
headers: getStudioAuthHeaders('application/json'),
body: JSON.stringify({ sceneIds }),
signal: options?.signal,
},
)
return data.scenes
}
export async function createStudioRun(input: StudioCreateRunInput): Promise<CreateRunResponse> {
return studioRequest<CreateRunResponse>('/runs', {
method: 'POST',
headers: getStudioAuthHeaders('application/json'),
body: JSON.stringify(input),
})
}
/** Nested Scene Run path: Session and Scene identity live in the URL, never in the body. */
export function buildStudioSceneRunPath(sessionId: string, sceneId: string): string {
return `/sessions/${encodeURIComponent(sessionId)}/scenes/${encodeURIComponent(sceneId)}/runs`
}
/**
* Start a Run for one Scene. The response carries only that Scene's records plus the accepted Run,
* so a Scene client never receives sibling messages, Runs or renders.
*/
export async function createStudioSceneRun(
sessionId: string,
sceneId: string,
input: StudioCreateSceneRunInput,
options?: StudioRequestOptions,
): Promise<StudioCreateSceneRunResponse> {
return studioRequest<StudioCreateSceneRunResponse>(buildStudioSceneRunPath(sessionId, sceneId), {
method: 'POST',
headers: getStudioAuthHeaders('application/json'),
body: JSON.stringify(input),
signal: options?.signal,
})
}
export async function cancelStudioRun(input: {
runId: string
reason?: string
}): Promise<CancelRunResponse> {
return studioRequest<CancelRunResponse>(`/runs/${encodeURIComponent(input.runId)}/cancel`, {
method: 'POST',
headers: getStudioAuthHeaders('application/json'),
body: JSON.stringify({ reason: input.reason }),
})
} |