@volter/twin-openai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2100 @@
1
+ // OpenAI twin REQUEST HANDLER — the canonical OpenAI API surface for the twin.
2
+ // Contract: handleOpenAITwinRequest({method, path, body}) -> {status, body}. It is the
3
+ // faithful OpenAI API the real `openai` SDK (pointed at this baseURL) talks to UNMODIFIED.
4
+ //
5
+ // THE HONEST DESIGN: the twin cannot run the model, so `POST /v1/chat/completions` and
6
+ // `POST /v1/responses` return a DETERMINISTIC STUB completion (openai-stub.ts) clearly labeled
7
+ // a twin stub — it NEVER pretends to be real model output. `POST /v1/embeddings` returns
8
+ // DETERMINISTIC pseudo-vectors (never real embedding values). But the ENTIRE PROTOCOL ENVELOPE
9
+ // is vendor-faithful: response shapes, streaming SSE chunks, tool_calls, finish_reason,
10
+ // deterministic usage. The genuinely stateful + static surface is real, not stubbed:
11
+ // • GET /v1/models (+ /:id) — static catalog (openai-models.ts)
12
+ // • POST/GET/DELETE /v1/files — stateful (kernel action log)
13
+ // • POST/GET /v1/batches (+ cancel) — stateful
14
+ // • POST /v1/moderations — deterministic classifier
15
+ // • Fine-tuning jobs (create/get/list/cancel/events) — stateful
16
+ // • Vector stores (+ files) — stateful
17
+ // • POST /v1/images/generations — response shape with a placeholder URL (real pixels
18
+ // are out of scope)
19
+ //
20
+ // State lives in the kernel action log (D1): all writes are local actions, reads are the
21
+ // projection. No real OpenAI is ever called from this path (D4). Streaming uses an INJECTED
22
+ // sink — no real sockets / setTimeout (D5 verify is offline + deterministic).
23
+ import { applyTwinWrite, projectResources } from '@volter/twin';
24
+ import { OPENAI_MODELS, findModel } from './openai-models.ts';
25
+ import {
26
+ buildLogprobs,
27
+ countPromptTokens,
28
+ estimateTokens,
29
+ moderateText,
30
+ pseudoEmbedding,
31
+ stubAssistantText,
32
+ stubJsonObject,
33
+ stubToolCall,
34
+ } from './openai-stub.ts';
35
+ import { type OpenAIScenarioEngine, type OpenAIScenarioRespond, realizeOpenAIRespond, type ScriptedResult } from './openai-scenario.ts';
36
+ import type {
37
+ ChatChoice,
38
+ ChatCompletion,
39
+ ChatMessageParam,
40
+ ChatToolCall,
41
+ ChatUsage,
42
+ Embedding,
43
+ EmbeddingResponse,
44
+ OpenAIResponse,
45
+ ResponseMessageItem,
46
+ ResponseOutputItem,
47
+ ResponseReasoningItem,
48
+ ResponseUsage,
49
+ SseSink,
50
+ } from './openai-types.ts';
51
+
52
+ const SERVICE = 'openai';
53
+
54
+ export type OpenAIRequest = {
55
+ /** The scenario engine (kernel grammar + this pack's vocabulary) — scripts chat turns. */
56
+ scenarioEngine?: OpenAIScenarioEngine;
57
+ method: string;
58
+ path: string;
59
+ body?: string;
60
+ occurredAt?: string;
61
+ root?: string;
62
+ readOnly?: boolean;
63
+ /** The credential the caller presents (the SDK's bearer `Authorization` header). When a request
64
+ * carries an auth SURFACE (this field set, or `headers` present), the twin holds it to the real
65
+ * vendor rule: a credential is required → 401 on missing/invalid. In-process trusted calls
66
+ * (capability verify, connector) omit BOTH and are not auth-gated — the twin can't validate
67
+ * against real keys, so the modeled failure is the CHECKABLE missing/sentinel-invalid case. */
68
+ apiKey?: string;
69
+ /** Lower-cased request headers (e.g. `authorization`, `idempotency-key`) the HTTP server passes
70
+ * through so the handler can model auth (401), the rate-limit trigger (429), and idempotency. */
71
+ headers?: Record<string, string>;
72
+ /** When set on a streaming POST, chunks are written here (no sockets). */
73
+ sseSink?: SseSink;
74
+ };
75
+ /** The handler response. `headers` (when present) are response headers the HTTP server should set
76
+ * — e.g. `Retry-After` + the `x-ratelimit-*` family on a modeled 429. */
77
+ export type OpenAIResponseEnvelope = { status: number; body: unknown; headers?: Record<string, string> };
78
+
79
+ // ── vendor-shaped errors ──────────────────────────────────────────────────────────────
80
+ function errBody(type: string, message: string, code: string | null = null, param: string | null = null) {
81
+ return { error: { message, type, param, code } };
82
+ }
83
+ function invalidRequest(message: string, param: string | null = null, code: string | null = null): OpenAIResponseEnvelope {
84
+ return { status: 400, body: errBody('invalid_request_error', message, code, param) };
85
+ }
86
+ function notFound(message: string, code: string | null = null): OpenAIResponseEnvelope {
87
+ return { status: 404, body: errBody('invalid_request_error', message, code) };
88
+ }
89
+ function authError(message: string, code = 'invalid_api_key'): OpenAIResponseEnvelope {
90
+ // Real OpenAI 401s carry error.type:'invalid_request_error' with a code like 'invalid_api_key'.
91
+ return { status: 401, body: errBody('invalid_request_error', message, code) };
92
+ }
93
+
94
+ // ── modeled authentication (401) ────────────────────────────────────────────────────────
95
+ // Real OpenAI requires a bearer credential on every request and returns 401 when it is missing
96
+ // or invalid. The twin can't validate against real keys, so it models the CHECKABLE failures: a
97
+ // missing credential, and a reserved sentinel ('sk-invalid'/'invalid') for the invalid-key path.
98
+ // Any other non-empty key is accepted. Trusted in-process calls (verify/connector) carry NEITHER
99
+ // `headers` nor `apiKey` and are NOT auth-gated; the real `openai` SDK always sends a key → passes.
100
+ function checkAuth(req: OpenAIRequest): OpenAIResponseEnvelope | null {
101
+ const auth = req.headers?.['authorization'];
102
+ const bearer = typeof auth === 'string' && auth.toLowerCase().startsWith('bearer ') ? auth.slice(7).trim() : '';
103
+ const key = (req.apiKey ?? '').trim() || bearer;
104
+ if (!key) return authError('You didn\'t provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY).', 'invalid_api_key');
105
+ if (key === 'sk-invalid' || key === 'invalid') return authError('Incorrect API key provided. You can find your API key at https://platform.openai.com/account/api-keys.', 'invalid_api_key');
106
+ return null;
107
+ }
108
+
109
+ // ── modeled rate limiting (429) ─────────────────────────────────────────────────────────
110
+ // Rate limits are non-deterministic in production, so the twin exposes a DETERMINISTIC opt-in
111
+ // trigger: a request carrying `x-twin-force-rate-limit: 1` (or `true`) returns the faithful 429
112
+ // envelope (error.type:'rate_limit_exceeded') + Retry-After and the x-ratelimit-* header family.
113
+ // (No real timing/quotas — the twin can't reproduce them; this is the checkable plumbing.)
114
+ function rateLimitError(): OpenAIResponseEnvelope {
115
+ return {
116
+ status: 429,
117
+ body: errBody('rate_limit_exceeded', 'Rate limit reached for requests. Limit your request rate or retry after the indicated delay.', 'rate_limit_exceeded'),
118
+ headers: {
119
+ 'retry-after': '1',
120
+ 'x-ratelimit-limit-requests': '10000',
121
+ 'x-ratelimit-remaining-requests': '0',
122
+ 'x-ratelimit-reset-requests': '1s',
123
+ 'x-ratelimit-limit-tokens': '2000000',
124
+ 'x-ratelimit-remaining-tokens': '0',
125
+ 'x-ratelimit-reset-tokens': '6ms',
126
+ },
127
+ };
128
+ }
129
+ function rateLimitTriggered(req: OpenAIRequest): boolean {
130
+ const v = req.headers?.['x-twin-force-rate-limit'];
131
+ return v === '1' || v === 'true';
132
+ }
133
+
134
+ function nowEpoch(occurredAt?: string): number {
135
+ return Math.floor((occurredAt ? Date.parse(occurredAt) : 0) / 1000);
136
+ }
137
+ function nowIso(occurredAt?: string): string {
138
+ return occurredAt ? new Date(Date.parse(occurredAt)).toISOString() : '1970-01-01T00:00:00.000Z';
139
+ }
140
+
141
+ // ── kernel helpers ──────────────────────────────────────────────────────────────────────
142
+ function rows(type: string, root?: string): Array<Record<string, unknown>> {
143
+ return projectResources(SERVICE, root).filter((r) => r.type === type);
144
+ }
145
+ function nextId(type: string, prefix: string, root?: string): string {
146
+ let max = 0;
147
+ for (const r of rows(type, root)) {
148
+ const m = new RegExp(`^${prefix}-twin-(\\d+)$`).exec(String(r.id));
149
+ if (m) max = Math.max(max, Number(m[1]));
150
+ }
151
+ return `${prefix}-twin-${max + 1}`;
152
+ }
153
+ function getRow(type: string, id: string, root?: string): Record<string, unknown> | undefined {
154
+ return rows(type, root).find((r) => r.id === id);
155
+ }
156
+ // The kernel reserves the field name `type` as its resource-type discriminator, so the stored
157
+ // vendor `object` field survives projection but the kernel `type` shadows nothing — we strip
158
+ // the kernel housekeeping fields and re-shape the served view per resource.
159
+ function strip(r: Record<string, unknown>): Record<string, unknown> {
160
+ const out: Record<string, unknown> = {};
161
+ for (const [k, v] of Object.entries(r)) {
162
+ // drop the kernel housekeeping field + the twin's private underscore-prefixed fields.
163
+ if (k === 'type' || k === 'updatedAt' || k.startsWith('_')) continue;
164
+ out[k] = v;
165
+ }
166
+ return out;
167
+ }
168
+
169
+ // ── idempotency (Idempotency-Key header dedup) ───────────────────────────────────────────
170
+ // A mutation carrying an Idempotency-Key is replayed verbatim on re-issue with the same key — the
171
+ // vendor returns the original response (same id) and never re-applies the side effect. The twin
172
+ // stores the serialized {status,body,headers} keyed by a hash of the key (type-prefixed id, so it
173
+ // can't collide with another resource's numeric id — kernel (type,id) caveat).
174
+ function idemId(key: string): string {
175
+ let h = 0x811c9dc5;
176
+ for (let i = 0; i < key.length; i++) { h ^= key.charCodeAt(i); h = Math.imul(h, 0x01000193); }
177
+ return `idem-${(h >>> 0).toString(36)}`;
178
+ }
179
+ function getIdempotentResult(key: string, root?: string): OpenAIResponseEnvelope | null {
180
+ const row = getRow('idempotency_record', idemId(key), root);
181
+ if (!row || typeof row._result !== 'string') return null;
182
+ try { return JSON.parse(row._result as string) as OpenAIResponseEnvelope; } catch { return null; }
183
+ }
184
+ async function storeIdempotentResult(key: string, result: OpenAIResponseEnvelope, req: OpenAIRequest): Promise<void> {
185
+ const id = idemId(key);
186
+ if (getRow('idempotency_record', id, req.root)) return; // a concurrent insert won
187
+ await applyTwinWrite(SERVICE, {
188
+ operation: 'idempotency_record.create',
189
+ subjectType: 'idempotency_record',
190
+ subjectId: id,
191
+ fields: { object: 'idempotency_record', _key: key, _result: JSON.stringify(result) },
192
+ ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}),
193
+ actor: { kind: 'agent' },
194
+ }, req.root);
195
+ }
196
+
197
+ // ── usage ledger (real recorded usage → the usage/costs reporting endpoints) ──────────────
198
+ // Every billable inference call appends a usage record (model + token counts + a synthetic cost
199
+ // computed from a per-model price table). The /v1/organization/usage|costs endpoints aggregate
200
+ // these REAL recorded rows — nothing is hardcoded; with no traffic the report is genuinely empty.
201
+ // Per-model USD price per 1M tokens (a faithful slice of the published price sheet; deterministic).
202
+ const MODEL_PRICES: Record<string, { input: number; output: number }> = {
203
+ 'gpt-4o': { input: 2.5, output: 10 },
204
+ 'gpt-4o-mini': { input: 0.15, output: 0.6 },
205
+ 'gpt-4.1': { input: 2, output: 8 },
206
+ 'gpt-4.1-mini': { input: 0.4, output: 1.6 },
207
+ 'gpt-4-turbo': { input: 10, output: 30 },
208
+ 'o3': { input: 2, output: 8 },
209
+ 'o4-mini': { input: 1.1, output: 4.4 },
210
+ 'gpt-3.5-turbo': { input: 0.5, output: 1.5 },
211
+ 'text-embedding-3-small': { input: 0.02, output: 0 },
212
+ 'text-embedding-3-large': { input: 0.13, output: 0 },
213
+ 'text-embedding-ada-002': { input: 0.1, output: 0 },
214
+ };
215
+ function modelPrice(model: string): { input: number; output: number } {
216
+ return MODEL_PRICES[model] ?? { input: 0, output: 0 };
217
+ }
218
+ function usageCost(model: string, inputTokens: number, outputTokens: number): number {
219
+ const p = modelPrice(model);
220
+ return (inputTokens / 1e6) * p.input + (outputTokens / 1e6) * p.output;
221
+ }
222
+ async function recordUsage(req: OpenAIRequest, kind: string, model: string, inputTokens: number, outputTokens: number): Promise<void> {
223
+ const id = nextId('usage_record', 'usage', req.root);
224
+ await applyTwinWrite(SERVICE, {
225
+ operation: 'usage_record.create',
226
+ subjectType: 'usage_record',
227
+ subjectId: id,
228
+ fields: {
229
+ object: 'usage_record',
230
+ kind,
231
+ model,
232
+ input_tokens: inputTokens,
233
+ output_tokens: outputTokens,
234
+ num_model_requests: 1,
235
+ cost_usd: usageCost(model, inputTokens, outputTokens),
236
+ created_at: nowEpoch(req.occurredAt),
237
+ },
238
+ ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}),
239
+ actor: { kind: 'agent' },
240
+ }, req.root);
241
+ }
242
+
243
+ // ── cursor pagination (after/limit + has_more, vendor-faithful list envelope) ────────────
244
+ // OpenAI list endpoints page by an opaque cursor: `after=<id>` returns items AFTER that id and
245
+ // `limit` (default 20, capped 100) caps the page; the envelope carries has_more/first_id/last_id.
246
+ function paginate(items: Array<Record<string, unknown>>, rawPath: string): Record<string, unknown> {
247
+ const qIdx = rawPath.indexOf('?');
248
+ const qs = new URLSearchParams(qIdx >= 0 ? rawPath.slice(qIdx + 1) : '');
249
+ const after = qs.get('after');
250
+ let limit = Number(qs.get('limit'));
251
+ if (!Number.isInteger(limit) || limit < 1) limit = 20;
252
+ if (limit > 100) limit = 100;
253
+ let start = 0;
254
+ if (after) {
255
+ const i = items.findIndex((it) => it.id === after);
256
+ start = i >= 0 ? i + 1 : items.length; // unknown cursor → empty page (vendor returns nothing after end)
257
+ }
258
+ const page = items.slice(start, start + limit);
259
+ const hasMore = start + limit < items.length;
260
+ return { object: 'list', data: page, has_more: hasMore, first_id: page[0]?.id ?? null, last_id: page[page.length - 1]?.id ?? null };
261
+ }
262
+
263
+ // ── request parsing ─────────────────────────────────────────────────────────────────────
264
+ function parseJson(body?: string): Record<string, unknown> {
265
+ if (!body || !body.trim()) return {};
266
+ try {
267
+ const v = JSON.parse(body);
268
+ return v && typeof v === 'object' ? (v as Record<string, unknown>) : {};
269
+ } catch {
270
+ return {};
271
+ }
272
+ }
273
+
274
+ const SYSTEM_FINGERPRINT = 'fp_twin_stub';
275
+
276
+ // ── chat completions: validate the request the same way the vendor does ──────────────────
277
+ type ToolChoice = 'auto' | 'none' | 'required' | { name: string };
278
+ type ResponseFormat = { kind: 'text' } | { kind: 'json_object' } | { kind: 'json_schema'; schema: unknown };
279
+ type ChatArgs = {
280
+ model: string;
281
+ messages: ChatMessageParam[];
282
+ tools?: unknown;
283
+ functions?: unknown;
284
+ n: number;
285
+ maxTokens?: number;
286
+ stop?: string[];
287
+ stream: boolean;
288
+ toolChoice?: ToolChoice;
289
+ parallelToolCalls: boolean;
290
+ responseFormat: ResponseFormat;
291
+ includeUsage: boolean;
292
+ logprobs: boolean;
293
+ topLogprobs?: number;
294
+ seed?: number;
295
+ logitBias?: Record<string, number>;
296
+ prediction?: string;
297
+ store: boolean;
298
+ metadata?: Record<string, unknown>;
299
+ /** modalities: ['text'] (default) or ['text','audio'] — audio asks for a spoken output. */
300
+ audioOutput?: { voice: string; format: string };
301
+ };
302
+
303
+ function validateChat(params: Record<string, unknown>): { args: ChatArgs } | { error: OpenAIResponseEnvelope } {
304
+ if (params.model === undefined || params.model === '') return { error: invalidRequest("you must provide a model parameter", 'model') };
305
+ if (typeof params.model !== 'string') return { error: invalidRequest("'model' must be a string", 'model') };
306
+ if (!Array.isArray(params.messages)) return { error: invalidRequest("you must provide a messages parameter", 'messages') };
307
+ if (params.messages.length === 0) return { error: invalidRequest("[] is too short - 'messages'", 'messages') };
308
+ const messages = params.messages as ChatMessageParam[];
309
+ for (const m of messages) {
310
+ if (!m || typeof m !== 'object' || typeof m.role !== 'string') {
311
+ return { error: invalidRequest("each message must have a valid 'role'", 'messages') };
312
+ }
313
+ }
314
+ let n = 1;
315
+ if (params.n !== undefined) {
316
+ n = Number(params.n);
317
+ if (!Number.isInteger(n) || n < 1) return { error: invalidRequest("'n' must be an integer >= 1", 'n') };
318
+ }
319
+ // max_completion_tokens is the current name; max_tokens is the legacy alias.
320
+ const maxRaw = params.max_completion_tokens ?? params.max_tokens;
321
+ let maxTokens: number | undefined;
322
+ if (maxRaw !== undefined) {
323
+ maxTokens = Number(maxRaw);
324
+ if (!Number.isInteger(maxTokens) || maxTokens < 1) return { error: invalidRequest("'max_tokens' must be an integer >= 1", 'max_tokens') };
325
+ }
326
+ const stopRaw = params.stop;
327
+ let stop: string[] | undefined;
328
+ if (stopRaw !== undefined) {
329
+ if (typeof stopRaw === 'string') stop = [stopRaw];
330
+ else if (Array.isArray(stopRaw)) stop = stopRaw as string[];
331
+ else return { error: invalidRequest("'stop' must be a string or array of strings", 'stop') };
332
+ }
333
+ // tool_choice: 'auto' | 'none' | 'required' | { type:'function', function:{ name } }.
334
+ let toolChoice: ToolChoice | undefined;
335
+ const tcRaw = params.tool_choice;
336
+ if (tcRaw !== undefined) {
337
+ if (tcRaw === 'auto' || tcRaw === 'none' || tcRaw === 'required') toolChoice = tcRaw;
338
+ else if (tcRaw && typeof tcRaw === 'object') {
339
+ const fn = (tcRaw as { function?: { name?: unknown } }).function;
340
+ if (typeof fn?.name === 'string') toolChoice = { name: fn.name };
341
+ else return { error: invalidRequest("invalid 'tool_choice' — named choice requires function.name", 'tool_choice') };
342
+ } else return { error: invalidRequest("'tool_choice' must be 'auto'/'none'/'required' or a named function", 'tool_choice') };
343
+ }
344
+ // response_format: { type:'text' | 'json_object' | 'json_schema', json_schema? }.
345
+ let responseFormat: ResponseFormat = { kind: 'text' };
346
+ const rf = params.response_format;
347
+ if (rf !== undefined) {
348
+ if (!rf || typeof rf !== 'object') return { error: invalidRequest("'response_format' must be an object", 'response_format') };
349
+ const t = (rf as { type?: unknown }).type;
350
+ if (t === 'json_object') responseFormat = { kind: 'json_object' };
351
+ else if (t === 'json_schema') responseFormat = { kind: 'json_schema', schema: (rf as { json_schema?: unknown }).json_schema };
352
+ else if (t === 'text' || t === undefined) responseFormat = { kind: 'text' };
353
+ else return { error: invalidRequest("'response_format.type' must be 'text', 'json_object', or 'json_schema'", 'response_format') };
354
+ }
355
+ // stream_options.include_usage → emit a final usage-only chunk in the stream.
356
+ const so = params.stream_options as { include_usage?: unknown } | undefined;
357
+ const includeUsage = !!(so && typeof so === 'object' && so.include_usage === true);
358
+ // logprobs (boolean) + top_logprobs (0..20, requires logprobs:true) → per-token logprob detail.
359
+ const logprobs = params.logprobs === true;
360
+ let topLogprobs: number | undefined;
361
+ if (params.top_logprobs !== undefined) {
362
+ topLogprobs = Number(params.top_logprobs);
363
+ if (!Number.isInteger(topLogprobs) || topLogprobs < 0 || topLogprobs > 20) return { error: invalidRequest("'top_logprobs' must be an integer between 0 and 20", 'top_logprobs') };
364
+ if (!logprobs) return { error: invalidRequest("'top_logprobs' requires 'logprobs' to be true", 'top_logprobs') };
365
+ }
366
+ // seed → reproducible sampling (the twin is already deterministic; we echo it via fingerprint).
367
+ let seed: number | undefined;
368
+ if (params.seed !== undefined) {
369
+ seed = Number(params.seed);
370
+ if (!Number.isInteger(seed)) return { error: invalidRequest("'seed' must be an integer", 'seed') };
371
+ }
372
+ // logit_bias → a map of token-id → bias in [-100, 100]; must be an object of numbers.
373
+ let logitBias: Record<string, number> | undefined;
374
+ if (params.logit_bias !== undefined) {
375
+ const lb = params.logit_bias;
376
+ if (!lb || typeof lb !== 'object' || Array.isArray(lb)) return { error: invalidRequest("'logit_bias' must be an object mapping token ids to bias values", 'logit_bias') };
377
+ logitBias = {};
378
+ for (const [k, v] of Object.entries(lb as Record<string, unknown>)) {
379
+ const num = Number(v);
380
+ if (!Number.isFinite(num) || num < -100 || num > 100) return { error: invalidRequest("each 'logit_bias' value must be a number between -100 and 100", 'logit_bias') };
381
+ logitBias[k] = num;
382
+ }
383
+ }
384
+ // prediction → predicted outputs ({ type:'content', content }); content may be a string or parts.
385
+ let prediction: string | undefined;
386
+ if (params.prediction !== undefined) {
387
+ const p = params.prediction as { type?: unknown; content?: unknown } | undefined;
388
+ if (!p || typeof p !== 'object' || p.type !== 'content' || p.content === undefined) return { error: invalidRequest("'prediction' must be an object with type 'content' and a content field", 'prediction') };
389
+ prediction = typeof p.content === 'string'
390
+ ? p.content
391
+ : Array.isArray(p.content) ? p.content.map((c) => (c && typeof c === 'object' ? String((c as { text?: unknown }).text ?? '') : String(c))).join('') : '';
392
+ }
393
+ // modalities + audio → audio output. modalities is ['text'] (default) or includes 'audio'; when
394
+ // it does, `audio: { voice, format }` is REQUIRED (vendor rule). The twin can't synthesize speech,
395
+ // so the returned bytes are a labeled stub — the envelope (message.audio shape) is faithful.
396
+ let audioOutput: { voice: string; format: string } | undefined;
397
+ const modalities = params.modalities;
398
+ if (modalities !== undefined) {
399
+ if (!Array.isArray(modalities)) return { error: invalidRequest("'modalities' must be an array", 'modalities') };
400
+ if ((modalities as unknown[]).includes('audio')) {
401
+ const a = params.audio as { voice?: unknown; format?: unknown } | undefined;
402
+ if (!a || typeof a !== 'object' || typeof a.voice !== 'string' || typeof a.format !== 'string') {
403
+ return { error: invalidRequest("'audio' with a 'voice' and 'format' is required when 'modalities' includes 'audio'", 'audio') };
404
+ }
405
+ audioOutput = { voice: a.voice, format: a.format };
406
+ }
407
+ }
408
+ // store + metadata → stored completions (retrievable later); metadata must be a flat object.
409
+ const store = params.store === true;
410
+ let metadata: Record<string, unknown> | undefined;
411
+ if (params.metadata !== undefined) {
412
+ if (!params.metadata || typeof params.metadata !== 'object' || Array.isArray(params.metadata)) return { error: invalidRequest("'metadata' must be an object", 'metadata') };
413
+ metadata = params.metadata as Record<string, unknown>;
414
+ }
415
+ return {
416
+ args: {
417
+ model: params.model,
418
+ messages,
419
+ tools: params.tools,
420
+ functions: params.functions,
421
+ n,
422
+ ...(maxTokens !== undefined ? { maxTokens } : {}),
423
+ ...(stop !== undefined ? { stop } : {}),
424
+ stream: params.stream === true,
425
+ ...(toolChoice !== undefined ? { toolChoice } : {}),
426
+ parallelToolCalls: params.parallel_tool_calls !== false,
427
+ responseFormat,
428
+ includeUsage,
429
+ logprobs,
430
+ ...(topLogprobs !== undefined ? { topLogprobs } : {}),
431
+ ...(seed !== undefined ? { seed } : {}),
432
+ ...(logitBias !== undefined ? { logitBias } : {}),
433
+ ...(prediction !== undefined ? { prediction } : {}),
434
+ store,
435
+ ...(metadata !== undefined ? { metadata } : {}),
436
+ ...(audioOutput !== undefined ? { audioOutput } : {}),
437
+ },
438
+ };
439
+ }
440
+
441
+ // Build ONE deterministic stub choice (index `idx`). Honors tools/functions (tool_calls +
442
+ // finish_reason tool_calls), tool_choice (none/required/named), parallel_tool_calls,
443
+ // response_format (json_object/json_schema), max_tokens, and stop sequences.
444
+ function buildChoice(args: ChatArgs, idx: number): { choice: ChatChoice; completionTokens: number } {
445
+ const toolsSource = args.tools ?? args.functions;
446
+ const hasTools = Array.isArray(toolsSource) && toolsSource.length > 0;
447
+ // tool_choice gates whether the stub calls a tool: 'none' forbids it; a named/required choice
448
+ // forces it (even when the heuristic otherwise would not); 'auto'/default calls when tools exist.
449
+ const forbidTools = args.toolChoice === 'none';
450
+ const forcedName = typeof args.toolChoice === 'object' ? args.toolChoice.name : undefined;
451
+ const wantTool = hasTools && !forbidTools;
452
+ if (wantTool) {
453
+ // parallel_tool_calls (default true) → the stub may emit one call per provided tool; a named
454
+ // choice or parallel:false collapses to a single call.
455
+ const list = Array.isArray(toolsSource) ? toolsSource : [];
456
+ const calls: ChatToolCall[] = [];
457
+ if (forcedName || args.parallelToolCalls === false) {
458
+ const tc = stubToolCall(toolsSource, idx + 1, forcedName);
459
+ if (tc) calls.push(tc);
460
+ } else {
461
+ for (let t = 0; t < list.length; t++) {
462
+ const tc = stubToolCall([list[t]], idx * 100 + t + 1);
463
+ if (tc) calls.push(tc);
464
+ }
465
+ }
466
+ if (calls.length) {
467
+ const completionTokens = estimateTokens(JSON.stringify(calls));
468
+ return {
469
+ choice: { index: idx, message: { role: 'assistant', content: null, tool_calls: calls, refusal: null }, logprobs: null, finish_reason: 'tool_calls' },
470
+ completionTokens,
471
+ };
472
+ }
473
+ }
474
+ // json_mode: when response_format requests json_object/json_schema, the content is valid JSON.
475
+ // prediction (predicted outputs): a real model uses the prediction to speed decoding but still
476
+ // returns its own generation — the twin echoes the predicted content (clearly still a stub) so
477
+ // the prediction round-trips, then reports accepted/rejected prediction tokens in usage.
478
+ let text = args.prediction !== undefined
479
+ ? `[twin-stub:${args.model}] predicted-output echo: ${args.prediction}`
480
+ : args.responseFormat.kind === 'json_object'
481
+ ? stubJsonObject(args.messages, args.model)
482
+ : args.responseFormat.kind === 'json_schema'
483
+ ? stubJsonObject(args.messages, args.model, args.responseFormat.schema)
484
+ : stubAssistantText(args.messages, args.model);
485
+ let finish: ChatChoice['finish_reason'] = 'stop';
486
+ // Truncate at the EARLIEST-occurring stop sequence across the whole `stop` list — not
487
+ // whichever sequence happens to be listed first — so multi-element `stop` arrays match the
488
+ // real vendor's "stop generation at the first hit" semantics regardless of array order.
489
+ let stopAt = -1;
490
+ for (const s of args.stop ?? []) {
491
+ if (!s) continue;
492
+ const i = text.indexOf(s);
493
+ if (i >= 0 && (stopAt < 0 || i < stopAt)) stopAt = i;
494
+ }
495
+ if (stopAt >= 0) text = text.slice(0, stopAt);
496
+ if (args.maxTokens !== undefined && estimateTokens(text) > args.maxTokens) {
497
+ text = text.slice(0, args.maxTokens * 4);
498
+ finish = 'length';
499
+ }
500
+ const logprobs = args.logprobs ? buildLogprobs(text, args.topLogprobs ?? 0) : null;
501
+ // modalities:['audio'] → the assistant replies with an audio object (content is null, the text
502
+ // lives in audio.transcript). The twin can't synthesize speech, so `data` is a labeled-stub
503
+ // base64 string; the shape (id/data/transcript/expires_at) is vendor-faithful.
504
+ if (args.audioOutput) {
505
+ const transcript = text;
506
+ const stubBytes = `[twin-stub:${args.model}] no real audio synthesis — voice=${args.audioOutput.voice} format=${args.audioOutput.format}; transcript: ${transcript}`;
507
+ const audio = {
508
+ id: `audio-twin-${stableSuffix(args)}-${idx}`,
509
+ data: Buffer.from(stubBytes, 'utf8').toString('base64'),
510
+ transcript,
511
+ expires_at: nowEpoch() + 3600,
512
+ };
513
+ return {
514
+ choice: { index: idx, message: { role: 'assistant', content: null, refusal: null, audio }, logprobs, finish_reason: finish },
515
+ completionTokens: estimateTokens(transcript),
516
+ };
517
+ }
518
+ return {
519
+ choice: { index: idx, message: { role: 'assistant', content: text, refusal: null }, logprobs, finish_reason: finish },
520
+ completionTokens: estimateTokens(text),
521
+ };
522
+ }
523
+
524
+ function buildChatCompletion(args: ChatArgs, occurredAt?: string, scenarioEngine?: OpenAIScenarioEngine): ChatCompletion {
525
+ const promptTokens = countPromptTokens(args.messages);
526
+ // Scenario handlers: a fired handler scripts the assistant turn; a miss teaches in the stub.
527
+ let scripted: ScriptedResult | null = null;
528
+ let missTeach = '';
529
+ if (scenarioEngine) {
530
+ const decision = scenarioEngine.next({ model: args.model, messages: args.messages, tools: args.tools, maxTokens: args.maxTokens });
531
+ if (decision.kind === 'handler') scripted = realizeOpenAIRespond(decision.respond as OpenAIScenarioRespond);
532
+ else missTeach = `\n[twin-scenario miss — no handler matched. Author one in the world dir's handlers/openai.json (GET /twin explains; GET /twin/scenario lists handlers + misses). Features seen: ${JSON.stringify(decision.miss.features)}]`;
533
+ }
534
+ const choices: ChatChoice[] = [];
535
+ let completionTokens = 0;
536
+ for (let i = 0; i < args.n; i++) {
537
+ if (scripted) {
538
+ const message = scripted.toolCalls.length
539
+ ? { role: 'assistant' as const, content: scripted.text, tool_calls: scripted.toolCalls, refusal: null }
540
+ : { role: 'assistant' as const, content: scripted.text ?? '', refusal: null };
541
+ const ct = estimateTokens(JSON.stringify(scripted.toolCalls.length ? scripted.toolCalls : scripted.text ?? ''));
542
+ choices.push({ index: i, message, logprobs: null, finish_reason: scripted.finishReason });
543
+ completionTokens += ct;
544
+ continue;
545
+ }
546
+ const { choice, completionTokens: ct } = buildChoice(args, i);
547
+ if (missTeach && typeof choice.message.content === 'string') choice.message.content += missTeach;
548
+ choices.push(choice);
549
+ completionTokens += ct;
550
+ }
551
+ const usage: ChatUsage = { prompt_tokens: promptTokens, completion_tokens: completionTokens, total_tokens: promptTokens + completionTokens };
552
+ // prediction (predicted outputs): real responses report how many predicted tokens were accepted
553
+ // vs rejected. The twin echoes the prediction, so all predicted tokens are "accepted".
554
+ if (args.prediction !== undefined) {
555
+ const accepted = estimateTokens(args.prediction);
556
+ usage.completion_tokens_details = { accepted_prediction_tokens: accepted, rejected_prediction_tokens: 0 };
557
+ }
558
+ return {
559
+ id: `chatcmpl-twin-${stableSuffix(args)}`,
560
+ object: 'chat.completion',
561
+ created: nowEpoch(occurredAt),
562
+ model: args.model,
563
+ choices,
564
+ usage,
565
+ system_fingerprint: SYSTEM_FINGERPRINT,
566
+ ...(args.metadata !== undefined ? { metadata: args.metadata } : {}),
567
+ };
568
+ }
569
+
570
+ // Persist a stored chat completion (store:true) so it can be retrieved/listed/deleted later, and
571
+ // keep the request messages for the /messages sub-resource (stored-completions input replay).
572
+ async function storeChatCompletion(resp: ChatCompletion, args: ChatArgs, req: OpenAIRequest): Promise<void> {
573
+ const inputMessages = args.messages.map((m, i) => ({ id: `${resp.id}-msg-${i}`, role: m.role, content: typeof m.content === 'string' ? m.content : (m.content ?? null) }));
574
+ await applyTwinWrite(SERVICE, {
575
+ operation: 'chat_completion.create',
576
+ subjectType: 'chat_completion',
577
+ subjectId: resp.id,
578
+ fields: { ...resp, _stored: true, _input_messages: inputMessages },
579
+ ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}),
580
+ actor: { kind: 'agent' },
581
+ }, req.root);
582
+ }
583
+ function storedChatView(r: Record<string, unknown>): Record<string, unknown> {
584
+ return { id: r.id, ...strip(r) };
585
+ }
586
+
587
+ // A deterministic id suffix from the request (so ids are stable + assertable, like the twin's
588
+ // other deterministic outputs). Hash of the prompt text + model + seed (seed changes sampling,
589
+ // so it changes the response id the way a real seed-distinct request does).
590
+ function stableSuffix(args: ChatArgs): string {
591
+ let h = 0x811c9dc5;
592
+ const s = JSON.stringify(args.messages) + args.model + (args.seed !== undefined ? `|seed=${args.seed}` : '');
593
+ for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193); }
594
+ return (h >>> 0).toString(36);
595
+ }
596
+
597
+ // Split text into deterministic streaming chunks (≤ ~20 chars each), preserving order.
598
+ function chunkText(text: string): string[] {
599
+ if (!text) return [];
600
+ const out: string[] = [];
601
+ for (let i = 0; i < text.length; i += 20) out.push(text.slice(i, i + 20));
602
+ return out;
603
+ }
604
+
605
+ /**
606
+ * Emit the vendor-faithful Chat Completions streaming sequence into the injected sink (NO
607
+ * sockets, NO setTimeout). Real order: a first chunk with `delta:{role:'assistant'}`, then
608
+ * `delta:{content}` chunks (or tool_calls deltas), then a final chunk with `finish_reason`,
609
+ * then `[DONE]`. Deterministic + synchronous so a collector can assert the full sequence.
610
+ */
611
+ export function streamChat(args: ChatArgs, sink: SseSink, occurredAt?: string, scenarioEngine?: OpenAIScenarioEngine): ChatCompletion {
612
+ const full = buildChatCompletion(args, occurredAt, scenarioEngine);
613
+ const base = { id: full.id, object: 'chat.completion.chunk' as const, created: full.created, model: full.model, system_fingerprint: SYSTEM_FINGERPRINT };
614
+ for (const choice of full.choices) {
615
+ const idx = choice.index;
616
+ // role chunk
617
+ sink({ data: { ...base, choices: [{ index: idx, delta: { role: 'assistant', content: '' }, logprobs: null, finish_reason: null }] } });
618
+ if (choice.message.tool_calls && choice.message.tool_calls.length) {
619
+ // one tool_calls delta-pair per call, each carrying its own `index` (parallel tool calls).
620
+ choice.message.tool_calls.forEach((tc, tIdx) => {
621
+ sink({ data: { ...base, choices: [{ index: idx, delta: { tool_calls: [{ index: tIdx, id: tc.id, type: 'function', function: { name: tc.function.name, arguments: '' } }] }, logprobs: null, finish_reason: null }] } });
622
+ sink({ data: { ...base, choices: [{ index: idx, delta: { tool_calls: [{ index: tIdx, function: { arguments: tc.function.arguments } }] }, logprobs: null, finish_reason: null }] } });
623
+ });
624
+ } else {
625
+ for (const piece of chunkText(choice.message.content ?? '')) {
626
+ sink({ data: { ...base, choices: [{ index: idx, delta: { content: piece }, logprobs: null, finish_reason: null }] } });
627
+ }
628
+ }
629
+ sink({ data: { ...base, choices: [{ index: idx, delta: {}, logprobs: null, finish_reason: choice.finish_reason }] } });
630
+ }
631
+ // stream_options.include_usage → a final chunk with an empty choices array carrying `usage`.
632
+ if (args.includeUsage) {
633
+ sink({ data: { ...base, choices: [], usage: full.usage } });
634
+ }
635
+ sink({ done: true });
636
+ return full;
637
+ }
638
+
639
+ // ── Responses API ───────────────────────────────────────────────────────────────────────
640
+ type ResponsesArgs = {
641
+ model: string;
642
+ inputText: string;
643
+ messages: ChatMessageParam[];
644
+ stream: boolean;
645
+ store: boolean;
646
+ previousResponseId?: string;
647
+ /** reasoning.effort ('minimal'|'low'|'medium'|'high') → emit a reasoning item + reasoning_tokens. */
648
+ reasoningEffort?: string;
649
+ /** background:true → the response is created `queued` and processed asynchronously (poll + cancel). */
650
+ background: boolean;
651
+ };
652
+ const REASONING_EFFORTS = new Set(['minimal', 'low', 'medium', 'high']);
653
+
654
+ function validateResponses(params: Record<string, unknown>): { args: ResponsesArgs } | { error: OpenAIResponseEnvelope } {
655
+ if (params.model === undefined || params.model === '') return { error: invalidRequest("you must provide a model parameter", 'model') };
656
+ if (typeof params.model !== 'string') return { error: invalidRequest("'model' must be a string", 'model') };
657
+ if (params.input === undefined) return { error: invalidRequest("you must provide an input parameter", 'input') };
658
+ // input may be a string OR an array of input items (role/content). Reduce to plain text +
659
+ // a messages-shaped view for token counting.
660
+ let inputText = '';
661
+ const messages: ChatMessageParam[] = [];
662
+ if (typeof params.input === 'string') {
663
+ inputText = params.input;
664
+ messages.push({ role: 'user', content: params.input });
665
+ } else if (Array.isArray(params.input)) {
666
+ for (const item of params.input as Array<Record<string, unknown>>) {
667
+ const role = (typeof item?.role === 'string' ? item.role : 'user') as ChatMessageParam['role'];
668
+ const content = item?.content;
669
+ const text = typeof content === 'string' ? content : Array.isArray(content) ? content.map((c) => (c && typeof c === 'object' ? String((c as { text?: unknown }).text ?? JSON.stringify(c)) : String(c))).join('\n') : '';
670
+ inputText += (inputText ? '\n' : '') + text;
671
+ messages.push({ role, content: text });
672
+ }
673
+ } else {
674
+ return { error: invalidRequest("'input' must be a string or an array of input items", 'input') };
675
+ }
676
+ const prev = params.previous_response_id;
677
+ if (prev !== undefined && (typeof prev !== 'string' || !prev)) return { error: invalidRequest("'previous_response_id' must be a string", 'previous_response_id') };
678
+ // reasoning.effort → the model spends a (stubbed) reasoning budget; the item shape is faithful.
679
+ let reasoningEffort: string | undefined;
680
+ if (params.reasoning !== undefined) {
681
+ const r = params.reasoning;
682
+ if (!r || typeof r !== 'object' || Array.isArray(r)) return { error: invalidRequest("'reasoning' must be an object", 'reasoning') };
683
+ const effort = (r as { effort?: unknown }).effort;
684
+ if (effort !== undefined) {
685
+ if (typeof effort !== 'string' || !REASONING_EFFORTS.has(effort)) return { error: invalidRequest("'reasoning.effort' must be one of 'minimal', 'low', 'medium', 'high'", 'reasoning.effort') };
686
+ reasoningEffort = effort;
687
+ }
688
+ }
689
+ return {
690
+ args: {
691
+ model: params.model,
692
+ inputText,
693
+ messages,
694
+ stream: params.stream === true,
695
+ store: params.store !== false, // OpenAI defaults store=true (stored & retrievable)
696
+ background: params.background === true,
697
+ ...(typeof prev === 'string' ? { previousResponseId: prev } : {}),
698
+ ...(reasoningEffort !== undefined ? { reasoningEffort } : {}),
699
+ },
700
+ };
701
+ }
702
+
703
+ // A deterministic reasoning-token budget per effort level (more effort → more reasoning tokens).
704
+ const REASONING_BUDGET: Record<string, number> = { minimal: 8, low: 16, medium: 48, high: 128 };
705
+
706
+ function buildResponse(args: ResponsesArgs, occurredAt?: string, idSuffix?: string): OpenAIResponse {
707
+ const text = stubAssistantText(args.messages, args.model);
708
+ const inputTokens = countPromptTokens(args.messages);
709
+ const messageTokens = estimateTokens(text);
710
+ const suffix = idSuffix ?? String(nowEpoch(occurredAt));
711
+ const messageItem: ResponseMessageItem = {
712
+ type: 'message',
713
+ id: `msg-twin-${suffix}`,
714
+ status: 'completed',
715
+ role: 'assistant',
716
+ content: [{ type: 'output_text', text, annotations: [] }],
717
+ };
718
+ const output: ResponseOutputItem[] = [];
719
+ const usage: ResponseUsage = { input_tokens: inputTokens, output_tokens: messageTokens, total_tokens: inputTokens + messageTokens };
720
+ // reasoning.effort → a faithful reasoning item (labeled-stub summary) BEFORE the message item,
721
+ // plus output_tokens_details.reasoning_tokens in usage (counted into output_tokens, like the vendor).
722
+ if (args.reasoningEffort) {
723
+ const reasoningTokens = REASONING_BUDGET[args.reasoningEffort] ?? 16;
724
+ const reasoningItem: ResponseReasoningItem = {
725
+ type: 'reasoning',
726
+ id: `rs-twin-${suffix}`,
727
+ summary: [{ type: 'summary_text', text: `[twin-stub] reasoning summary (effort=${args.reasoningEffort}); the twin cannot run the model, so the chain-of-thought is not real.` }],
728
+ };
729
+ output.push(reasoningItem);
730
+ usage.output_tokens += reasoningTokens;
731
+ usage.total_tokens += reasoningTokens;
732
+ usage.output_tokens_details = { reasoning_tokens: reasoningTokens };
733
+ }
734
+ output.push(messageItem);
735
+ return {
736
+ id: `resp-twin-${suffix}`,
737
+ object: 'response',
738
+ created_at: nowEpoch(occurredAt),
739
+ status: 'completed',
740
+ model: args.model,
741
+ output,
742
+ output_text: text,
743
+ usage,
744
+ ...(args.reasoningEffort ? { reasoning: { effort: args.reasoningEffort, summary: null } } : {}),
745
+ ...(args.previousResponseId ? { previous_response_id: args.previousResponseId } : {}),
746
+ };
747
+ }
748
+
749
+ // A deterministic id suffix for a stored response (hash of model + input + chain) so retrieval
750
+ // is stable + assertable, like the twin's other deterministic outputs.
751
+ function responseSuffix(args: ResponsesArgs): string {
752
+ let h = 0x811c9dc5;
753
+ const s = args.model + '|' + args.inputText + '|' + (args.previousResponseId ?? '');
754
+ for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193); }
755
+ return (h >>> 0).toString(36);
756
+ }
757
+
758
+ // Persist a stored Response so it can be retrieved/deleted later (Responses server-side state).
759
+ async function storeResponse(resp: OpenAIResponse, args: ResponsesArgs, req: OpenAIRequest): Promise<void> {
760
+ const inputItems = args.messages.map((m, i) => ({ id: `msg-in-${i}`, type: 'message', role: m.role, content: [{ type: 'input_text', text: typeof m.content === 'string' ? m.content : '' }] }));
761
+ await applyTwinWrite(SERVICE, {
762
+ operation: 'response.create',
763
+ subjectType: 'response',
764
+ subjectId: resp.id,
765
+ fields: { ...resp, _stored: true, _input_items: inputItems },
766
+ ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}),
767
+ actor: { kind: 'agent' },
768
+ }, req.root);
769
+ }
770
+ function responseView(r: Record<string, unknown>): Record<string, unknown> {
771
+ return { id: r.id, ...strip(r) };
772
+ }
773
+
774
+ // ── background responses (background:true → queued → poll → completed | cancel) ───────────
775
+ // Persist a `queued` background response: the faithful queued envelope (no output yet) plus the
776
+ // stashed args so the first poll can compute the real stub output. background implies store.
777
+ async function storeQueuedResponse(args: ResponsesArgs, req: OpenAIRequest, suffix: string): Promise<Record<string, unknown>> {
778
+ const id = `resp-twin-${suffix}`;
779
+ const inputItems = args.messages.map((m, i) => ({ id: `msg-in-${i}`, type: 'message', role: m.role, content: [{ type: 'input_text', text: typeof m.content === 'string' ? m.content : '' }] }));
780
+ const fields = {
781
+ id,
782
+ object: 'response',
783
+ created_at: nowEpoch(req.occurredAt),
784
+ status: 'queued',
785
+ background: true,
786
+ model: args.model,
787
+ output: [],
788
+ output_text: null,
789
+ usage: null,
790
+ error: null,
791
+ incomplete_details: null,
792
+ ...(args.previousResponseId ? { previous_response_id: args.previousResponseId } : {}),
793
+ _stored: true,
794
+ _input_items: inputItems,
795
+ _bg_args: JSON.stringify(args),
796
+ _bg_suffix: suffix,
797
+ };
798
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'response.create', subjectType: 'response', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
799
+ return resource;
800
+ }
801
+ // First poll of a queued background response: compute the real stub output + usage, flip the
802
+ // persisted row to `completed`, and record the (now billable) usage exactly once.
803
+ async function completeQueuedResponse(r: Record<string, unknown>, req: OpenAIRequest): Promise<Record<string, unknown>> {
804
+ const args = JSON.parse(String(r._bg_args ?? '{}')) as ResponsesArgs;
805
+ const suffix = String(r._bg_suffix ?? responseSuffix(args));
806
+ const resp = buildResponse(args, req.occurredAt, suffix);
807
+ const fields = { ...resp, background: true, status: 'completed', _bg_args: undefined };
808
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'response.update', subjectType: 'response', subjectId: String(r.id), fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
809
+ await recordUsage(req, 'responses', resp.model, resp.usage.input_tokens, resp.usage.output_tokens);
810
+ return resource;
811
+ }
812
+
813
+ /** Emit the faithful Responses API streaming events (response.created → output_text.delta* →
814
+ * response.completed → [DONE]) into the injected sink. */
815
+ export function streamResponse(args: ResponsesArgs, sink: SseSink, occurredAt?: string, idSuffix?: string): OpenAIResponse {
816
+ const resp = buildResponse(args, occurredAt, idSuffix);
817
+ sink({ data: { type: 'response.created', response: { ...resp, output: [], output_text: '' } } });
818
+ // Emit each output item in order; the message item carries the text deltas at its own index.
819
+ const msgIndex = resp.output.findIndex((o) => o.type === 'message');
820
+ resp.output.forEach((item, i) => {
821
+ sink({ data: { type: 'response.output_item.added', output_index: i, item } });
822
+ if (item.type === 'reasoning') sink({ data: { type: 'response.output_item.done', output_index: i, item } });
823
+ });
824
+ const text = resp.output_text ?? '';
825
+ for (const piece of chunkText(text)) {
826
+ sink({ data: { type: 'response.output_text.delta', output_index: msgIndex, content_index: 0, delta: piece } });
827
+ }
828
+ sink({ data: { type: 'response.output_text.done', output_index: msgIndex, content_index: 0, text } });
829
+ sink({ data: { type: 'response.completed', response: resp } });
830
+ sink({ done: true });
831
+ return resp;
832
+ }
833
+
834
+ // ── Embeddings (deterministic pseudo-vectors) ───────────────────────────────────────────
835
+ function handleEmbeddings(params: Record<string, unknown>): OpenAIResponseEnvelope {
836
+ if (params.model === undefined || params.model === '') return invalidRequest("you must provide a model parameter", 'model');
837
+ if (params.input === undefined) return invalidRequest("you must provide an input parameter", 'input');
838
+ const model = String(params.model);
839
+ const inputs: string[] = typeof params.input === 'string'
840
+ ? [params.input]
841
+ : Array.isArray(params.input)
842
+ ? (params.input as unknown[]).map((x) => (typeof x === 'string' ? x : JSON.stringify(x)))
843
+ : [];
844
+ if (inputs.length === 0) return invalidRequest("'input' must be a non-empty string or array", 'input');
845
+ const dimensions = params.dimensions !== undefined ? Number(params.dimensions) : defaultDims(model);
846
+ if (!Number.isInteger(dimensions) || dimensions < 1) return invalidRequest("'dimensions' must be a positive integer", 'dimensions');
847
+ // The `openai` SDK defaults to encoding_format:'base64' and decodes a base64 Float32 buffer
848
+ // back into numbers client-side. Honor both formats faithfully.
849
+ const asBase64 = params.encoding_format === 'base64';
850
+ let promptTokens = 0;
851
+ const data: Embedding[] = inputs.map((text, index) => {
852
+ promptTokens += estimateTokens(text);
853
+ const vec = pseudoEmbedding(text, dimensions);
854
+ return { object: 'embedding', index, embedding: (asBase64 ? floatsToBase64(vec) : vec) as unknown as number[] };
855
+ });
856
+ const out: EmbeddingResponse = { object: 'list', data, model, usage: { prompt_tokens: promptTokens, total_tokens: promptTokens } };
857
+ return { status: 200, body: out };
858
+ }
859
+ function defaultDims(model: string): number {
860
+ if (model.includes('large')) return 3072;
861
+ return 1536; // small + ada-002
862
+ }
863
+ /** Encode a float vector as a base64 little-endian Float32 buffer (the SDK's base64 format). */
864
+ function floatsToBase64(vec: number[]): string {
865
+ const buf = new ArrayBuffer(vec.length * 4);
866
+ const view = new DataView(buf);
867
+ for (let i = 0; i < vec.length; i++) view.setFloat32(i * 4, vec[i]!, true);
868
+ // btoa over the raw bytes (available in Bun/browser).
869
+ let bin = '';
870
+ const bytes = new Uint8Array(buf);
871
+ for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);
872
+ return btoa(bin);
873
+ }
874
+
875
+ // ── Files (stateful) ────────────────────────────────────────────────────────────────────
876
+ async function createFile(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
877
+ // The SDK sends multipart/form-data; the twin's JSON contract accepts { purpose, filename,
878
+ // bytes } (the server adapts multipart → this shape). purpose is required like the vendor.
879
+ const purpose = params.purpose;
880
+ if (purpose === undefined || purpose === '') return invalidRequest("you must provide a purpose parameter", 'purpose');
881
+ const id = nextId('file', 'file', req.root);
882
+ const filename = typeof params.filename === 'string' && params.filename ? params.filename : 'upload.jsonl';
883
+ const bytes = Number(params.bytes ?? (typeof params.content === 'string' ? params.content.length : 0));
884
+ const fields = {
885
+ object: 'file',
886
+ bytes: Number.isFinite(bytes) ? bytes : 0,
887
+ created_at: nowEpoch(req.occurredAt),
888
+ filename,
889
+ purpose: String(purpose),
890
+ status: 'processed',
891
+ // The raw bytes are carved out (openai.files.real_bytes); we keep the supplied content for
892
+ // the /content endpoint when present so round-trips are faithful.
893
+ _content: typeof params.content === 'string' ? params.content : '',
894
+ };
895
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'file.create', subjectType: 'file', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
896
+ return { status: 200, body: fileView(resource) };
897
+ }
898
+ function fileView(r: Record<string, unknown>): Record<string, unknown> {
899
+ const { _content: _c, ...rest } = strip(r);
900
+ return { id: r.id, ...rest };
901
+ }
902
+
903
+ // ── Batches (stateful) ──────────────────────────────────────────────────────────────────
904
+ async function createBatch(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
905
+ if (params.input_file_id === undefined || params.input_file_id === '') return invalidRequest("you must provide an input_file_id parameter", 'input_file_id');
906
+ if (params.endpoint === undefined || params.endpoint === '') return invalidRequest("you must provide an endpoint parameter", 'endpoint');
907
+ if (params.completion_window === undefined) return invalidRequest("you must provide a completion_window parameter", 'completion_window');
908
+ const id = nextId('batch', 'batch', req.root);
909
+ const created = nowEpoch(req.occurredAt);
910
+ // The twin has nothing to process asynchronously, so a batch completes immediately. The
911
+ // results file id is a deterministic synthetic id.
912
+ const fields = {
913
+ object: 'batch',
914
+ endpoint: String(params.endpoint),
915
+ input_file_id: String(params.input_file_id),
916
+ completion_window: String(params.completion_window),
917
+ status: 'completed',
918
+ output_file_id: `file-twin-batchout-${id}`,
919
+ error_file_id: null,
920
+ created_at: created,
921
+ in_progress_at: created,
922
+ completed_at: created,
923
+ request_counts: { total: 0, completed: 0, failed: 0 },
924
+ metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : null,
925
+ };
926
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'batch.create', subjectType: 'batch', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
927
+ return { status: 200, body: batchView(resource) };
928
+ }
929
+ function batchView(r: Record<string, unknown>): Record<string, unknown> {
930
+ return { id: r.id, ...strip(r) };
931
+ }
932
+
933
+ // ── Moderations (deterministic) ─────────────────────────────────────────────────────────
934
+ function handleModerations(params: Record<string, unknown>, occurredAt?: string): OpenAIResponseEnvelope {
935
+ if (params.input === undefined) return invalidRequest("you must provide an input parameter", 'input');
936
+ const inputs: string[] = typeof params.input === 'string'
937
+ ? [params.input]
938
+ : Array.isArray(params.input)
939
+ ? (params.input as unknown[]).map((x) => (typeof x === 'string' ? x : JSON.stringify(x)))
940
+ : [];
941
+ if (inputs.length === 0) return invalidRequest("'input' must be a non-empty string or array", 'input');
942
+ const model = typeof params.model === 'string' && params.model ? params.model : 'omni-moderation-latest';
943
+ const results = inputs.map((t) => moderateText(t));
944
+ return { status: 200, body: { id: `modr-twin-${nowEpoch(occurredAt)}`, model, results } };
945
+ }
946
+
947
+ // ── Fine-tuning jobs (stateful) ─────────────────────────────────────────────────────────
948
+ async function createFineTune(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
949
+ if (params.model === undefined || params.model === '') return invalidRequest("you must provide a model parameter", 'model');
950
+ if (params.training_file === undefined || params.training_file === '') return invalidRequest("you must provide a training_file parameter", 'training_file');
951
+ const id = nextId('ftjob', 'ftjob', req.root);
952
+ const created = nowEpoch(req.occurredAt);
953
+ // The twin cannot train a model, so the job completes immediately with a synthetic
954
+ // fine-tuned model id (the lifecycle/shape is faithful; the trained model is a stub).
955
+ const fields = {
956
+ object: 'fine_tuning.job',
957
+ model: String(params.model),
958
+ created_at: created,
959
+ finished_at: created,
960
+ fine_tuned_model: `ft:${String(params.model)}:twin::${id}`,
961
+ organization_id: 'org-twin',
962
+ status: 'succeeded',
963
+ training_file: String(params.training_file),
964
+ validation_file: params.validation_file ?? null,
965
+ hyperparameters: (params.hyperparameters && typeof params.hyperparameters === 'object') ? params.hyperparameters : { n_epochs: 'auto' },
966
+ result_files: [],
967
+ trained_tokens: 0,
968
+ error: null,
969
+ seed: Number(params.seed ?? 0),
970
+ };
971
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'fine_tuning_job.create', subjectType: 'fine_tuning_job', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
972
+ return { status: 200, body: ftView(resource) };
973
+ }
974
+ function ftView(r: Record<string, unknown>): Record<string, unknown> {
975
+ return { id: r.id, ...strip(r) };
976
+ }
977
+ /** The fine-tuned model objects minted by succeeded fine-tuning jobs (deletable, owned by the
978
+ * user), minus any that have been deleted via DELETE /v1/models/:id. */
979
+ function fineTunedModels(root?: string): Array<{ id: string; object: 'model'; created: number; owned_by: string }> {
980
+ const deleted = new Set(rows('model', root).filter((r) => r._deleted).map((r) => String(r.id)));
981
+ const out: Array<{ id: string; object: 'model'; created: number; owned_by: string }> = [];
982
+ for (const j of rows('fine_tuning_job', root)) {
983
+ const ftm = (j as { fine_tuned_model?: unknown }).fine_tuned_model;
984
+ if (typeof ftm === 'string' && ftm && !deleted.has(ftm)) {
985
+ out.push({ id: ftm, object: 'model', created: Number(j.created_at ?? 0), owned_by: 'org-twin' });
986
+ }
987
+ }
988
+ return out;
989
+ }
990
+
991
+ // ── Vector stores (stateful) ────────────────────────────────────────────────────────────
992
+ async function createVectorStore(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
993
+ const id = nextId('vector_store', 'vs', req.root);
994
+ const created = nowEpoch(req.occurredAt);
995
+ const fileIds = Array.isArray(params.file_ids) ? (params.file_ids as unknown[]) : [];
996
+ const fields = {
997
+ object: 'vector_store',
998
+ created_at: created,
999
+ name: typeof params.name === 'string' ? params.name : null,
1000
+ usage_bytes: 0,
1001
+ status: 'completed',
1002
+ file_counts: { in_progress: 0, completed: fileIds.length, failed: 0, cancelled: 0, total: fileIds.length },
1003
+ metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : {},
1004
+ last_active_at: created,
1005
+ };
1006
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'vector_store.create', subjectType: 'vector_store', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1007
+ // Seed any provided file_ids as vector_store_file children.
1008
+ for (const fid of fileIds) {
1009
+ await addVectorStoreFile(id, String(fid), req);
1010
+ }
1011
+ return { status: 200, body: vsView(resource) };
1012
+ }
1013
+ function vsView(r: Record<string, unknown>): Record<string, unknown> {
1014
+ return { id: r.id, ...strip(r) };
1015
+ }
1016
+ async function addVectorStoreFile(storeId: string, fileId: string, req: OpenAIRequest, batchId?: string): Promise<Record<string, unknown>> {
1017
+ const created = nowEpoch(req.occurredAt);
1018
+ const childId = `${storeId}::${fileId}`;
1019
+ const fields = { object: 'vector_store.file', vector_store_id: storeId, file_id: fileId, created_at: created, status: 'completed', usage_bytes: 0, ...(batchId ? { _batch_id: batchId } : {}) };
1020
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'vector_store_file.create', subjectType: 'vector_store_file', subjectId: childId, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1021
+ return vsFileView(resource);
1022
+ }
1023
+ function vsFileView(r: Record<string, unknown>): Record<string, unknown> {
1024
+ const s = strip(r);
1025
+ return { id: String(r.id).split('::')[1] ?? r.id, object: 'vector_store.file', file_id: s.file_id, vector_store_id: s.vector_store_id, created_at: s.created_at, status: s.status, usage_bytes: s.usage_bytes ?? 0 };
1026
+ }
1027
+ function vsBatchView(r: Record<string, unknown>): Record<string, unknown> {
1028
+ const { _file_ids: _f, ...rest } = strip(r);
1029
+ return { id: r.id, ...rest };
1030
+ }
1031
+
1032
+ // ── Admin / org API (stateful) ──────────────────────────────────────────────────────────
1033
+ // The Admin API (under /v1/organization, authed with an admin key) manages organization
1034
+ // projects and their API keys. The twin models projects (create/list/retrieve/modify/archive)
1035
+ // and project API keys (create/list/retrieve/delete) statefully. Keys are SYNTHETIC twin keys —
1036
+ // never real OpenAI credentials.
1037
+ async function createProject(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1038
+ if (params.name === undefined || params.name === '') return invalidRequest("you must provide a name parameter", 'name');
1039
+ const id = nextId('project', 'proj', req.root);
1040
+ const created = nowEpoch(req.occurredAt);
1041
+ const fields = { object: 'organization.project', name: String(params.name), created_at: created, archived_at: null, status: 'active' };
1042
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'project.create', subjectType: 'project', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1043
+ return { status: 200, body: idView(resource) };
1044
+ }
1045
+ async function createProjectApiKey(projectId: string, params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1046
+ if (params.name === undefined || params.name === '') return invalidRequest("you must provide a name parameter", 'name');
1047
+ const seq = rows('api_key', req.root).filter((r) => r.project_id === projectId).length + 1;
1048
+ const id = `key_twin_${projectId}_${seq}`;
1049
+ const created = nowEpoch(req.occurredAt);
1050
+ // a SYNTHETIC twin secret — clearly not a real OpenAI key (only shown at creation, like the vendor).
1051
+ const secret = `sk-twin-proj-${projectId}-${seq}`;
1052
+ const fields = { object: 'organization.project.api_key', name: String(params.name), created_at: created, project_id: projectId, redacted_value: `sk-twin-...${seq}`, _secret: secret };
1053
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'api_key.create', subjectType: 'api_key', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1054
+ // creation echoes the one-time secret `value`; subsequent reads only show redacted_value.
1055
+ return { status: 200, body: { ...apiKeyView(resource), value: secret } };
1056
+ }
1057
+ function apiKeyView(r: Record<string, unknown>): Record<string, unknown> {
1058
+ const { _secret: _s, ...rest } = strip(r);
1059
+ return { id: r.id, ...rest };
1060
+ }
1061
+
1062
+ // ── Uploads API (multipart large-file parts, stateful) ──────────────────────────────────
1063
+ // The Uploads API lets a client stream a large file in parts: create an upload, POST each part,
1064
+ // then complete (which assembles the parts into a real File object). The twin models the full
1065
+ // lifecycle and assembles the concatenated part contents into a File (faithful, stateful).
1066
+ async function createUpload(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1067
+ for (const k of ['filename', 'purpose', 'bytes', 'mime_type']) {
1068
+ if (params[k] === undefined || params[k] === '') return invalidRequest(`you must provide a ${k} parameter`, k);
1069
+ }
1070
+ const id = nextId('upload', 'upload', req.root);
1071
+ const created = nowEpoch(req.occurredAt);
1072
+ const fields = {
1073
+ object: 'upload', created_at: created, filename: String(params.filename), bytes: Number(params.bytes),
1074
+ purpose: String(params.purpose), mime_type: String(params.mime_type), status: 'pending',
1075
+ expires_at: created + 3600, file: null,
1076
+ };
1077
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'upload.create', subjectType: 'upload', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1078
+ return { status: 200, body: uploadView(resource) };
1079
+ }
1080
+ function uploadView(r: Record<string, unknown>): Record<string, unknown> {
1081
+ const { _parts: _p, ...rest } = strip(r);
1082
+ return { id: r.id, ...rest };
1083
+ }
1084
+ async function addUploadPart(uploadId: string, params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1085
+ const up = getRow('upload', uploadId, req.root);
1086
+ if (!up || up.status !== 'pending') return notFound(`No such Upload object: ${uploadId}`);
1087
+ if (params.data === undefined) return invalidRequest("you must provide a data parameter", 'data');
1088
+ const parts = Array.isArray((up as { _parts?: unknown })._parts) ? [...((up as { _parts: unknown[] })._parts)] : [];
1089
+ const partId = `part-twin-${uploadId}-${parts.length + 1}`;
1090
+ parts.push({ id: partId, data: String(params.data) });
1091
+ await applyTwinWrite(SERVICE, { operation: 'upload.update', subjectType: 'upload', subjectId: uploadId, fields: { _parts: parts }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1092
+ return { status: 200, body: { id: partId, object: 'upload.part', created_at: nowEpoch(req.occurredAt), upload_id: uploadId } };
1093
+ }
1094
+ async function completeUpload(uploadId: string, params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1095
+ const up = getRow('upload', uploadId, req.root);
1096
+ if (!up || up.status !== 'pending') return notFound(`No such Upload object: ${uploadId}`);
1097
+ if (!Array.isArray(params.part_ids) || params.part_ids.length === 0) return invalidRequest("you must provide a part_ids array", 'part_ids');
1098
+ const parts = Array.isArray((up as { _parts?: unknown })._parts) ? (up as { _parts: Array<{ id: string; data: string }> })._parts : [];
1099
+ const byId = new Map(parts.map((p) => [p.id, p.data]));
1100
+ // assemble the file content from the parts in the caller-specified order.
1101
+ let content = '';
1102
+ for (const pid of params.part_ids as string[]) {
1103
+ if (!byId.has(String(pid))) return invalidRequest(`unknown part_id '${pid}'`, 'part_ids');
1104
+ content += byId.get(String(pid));
1105
+ }
1106
+ // mint a real File object from the assembled content.
1107
+ const fileId = nextId('file', 'file', req.root);
1108
+ const created = nowEpoch(req.occurredAt);
1109
+ const fileFields = {
1110
+ object: 'file', bytes: Number(up.bytes ?? content.length), created_at: created,
1111
+ filename: String(up.filename), purpose: String(up.purpose), status: 'processed', _content: content,
1112
+ };
1113
+ await applyTwinWrite(SERVICE, { operation: 'file.create', subjectType: 'file', subjectId: fileId, fields: fileFields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1114
+ const fileObj = fileView({ id: fileId, ...fileFields });
1115
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'upload.update', subjectType: 'upload', subjectId: uploadId, fields: { status: 'completed', file: fileObj }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1116
+ return { status: 200, body: uploadView(resource) };
1117
+ }
1118
+
1119
+ // ── Assistants / Threads / Runs (beta, stateful) ────────────────────────────────────────
1120
+ // The Assistants API is fully stateful CRUD over assistants, threads (+ messages), and runs
1121
+ // (+ run steps). The twin models the OBJECTS + lifecycle faithfully; a run cannot invoke a real
1122
+ // model, so a run completes immediately and any assistant reply message is a clearly-labeled stub.
1123
+ async function createAssistant(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1124
+ if (params.model === undefined || params.model === '') return invalidRequest("you must provide a model parameter", 'model');
1125
+ const id = nextId('assistant', 'asst', req.root);
1126
+ const created = nowEpoch(req.occurredAt);
1127
+ const fields = {
1128
+ object: 'assistant', created_at: created,
1129
+ name: params.name ?? null, description: params.description ?? null,
1130
+ model: String(params.model), instructions: params.instructions ?? null,
1131
+ tools: Array.isArray(params.tools) ? params.tools : [],
1132
+ metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : {},
1133
+ temperature: params.temperature ?? null, top_p: params.top_p ?? null,
1134
+ response_format: params.response_format ?? 'auto',
1135
+ };
1136
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'assistant.create', subjectType: 'assistant', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1137
+ return { status: 200, body: idView(resource) };
1138
+ }
1139
+ function idView(r: Record<string, unknown>): Record<string, unknown> {
1140
+ return { id: r.id, ...strip(r) };
1141
+ }
1142
+ /** Pick the updatable fields present in an update body (vendor PATCH-via-POST semantics). */
1143
+ function pickUpdate(params: Record<string, unknown>, keys: string[]): Record<string, unknown> {
1144
+ const out: Record<string, unknown> = {};
1145
+ for (const k of keys) if (params[k] !== undefined) out[k] = params[k];
1146
+ return out;
1147
+ }
1148
+ async function createThread(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1149
+ const id = nextId('thread', 'thread', req.root);
1150
+ const created = nowEpoch(req.occurredAt);
1151
+ const fields = { object: 'thread', created_at: created, metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : {}, tool_resources: params.tool_resources ?? null };
1152
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'thread.create', subjectType: 'thread', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1153
+ // Seed any inline messages provided at thread creation.
1154
+ if (Array.isArray(params.messages)) {
1155
+ for (const m of params.messages as Array<Record<string, unknown>>) await addThreadMessage(id, m, req);
1156
+ }
1157
+ return { status: 200, body: idView(resource) };
1158
+ }
1159
+ async function addThreadMessage(threadId: string, params: Record<string, unknown>, req: OpenAIRequest): Promise<Record<string, unknown>> {
1160
+ const seq = rows('message', req.root).filter((r) => r.thread_id === threadId).length + 1;
1161
+ const id = `msg-twin-${threadId}-${seq}`;
1162
+ const created = nowEpoch(req.occurredAt);
1163
+ const role = typeof params.role === 'string' ? params.role : 'user';
1164
+ const text = typeof params.content === 'string' ? params.content : Array.isArray(params.content) ? (params.content as Array<Record<string, unknown>>).map((p) => String((p as { text?: { value?: unknown } }).text?.value ?? (p as { text?: unknown }).text ?? '')).join('') : '';
1165
+ const fields = {
1166
+ object: 'thread.message', created_at: created, thread_id: threadId, role,
1167
+ content: [{ type: 'text', text: { value: text, annotations: [] } }],
1168
+ assistant_id: params.assistant_id ?? null, run_id: params.run_id ?? null,
1169
+ attachments: Array.isArray(params.attachments) ? params.attachments : [],
1170
+ metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : {},
1171
+ _seq: seq,
1172
+ };
1173
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'message.create', subjectType: 'message', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1174
+ return msgView(resource);
1175
+ }
1176
+ function msgView(r: Record<string, unknown>): Record<string, unknown> {
1177
+ const { _seq: _s, ...rest } = strip(r);
1178
+ return { id: r.id, ...rest };
1179
+ }
1180
+ // A run completes immediately (the twin can't invoke the model): it appends a clearly-labeled
1181
+ // stub assistant message to the thread and records two run steps (message creation lifecycle).
1182
+ async function createRun(threadId: string, params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1183
+ if (params.assistant_id === undefined || params.assistant_id === '') return invalidRequest("you must provide an assistant_id parameter", 'assistant_id');
1184
+ const assistantId = String(params.assistant_id);
1185
+ const assistant = getRow('assistant', assistantId, req.root);
1186
+ if (!assistant || assistant._deleted) return notFound(`No assistant found with id '${assistantId}'.`);
1187
+ const id = nextId('run', 'run', req.root);
1188
+ const created = nowEpoch(req.occurredAt);
1189
+ const model = typeof params.model === 'string' && params.model ? params.model : String(assistant.model);
1190
+ // append the stub assistant reply to the thread.
1191
+ const replyText = `[twin-stub:${model}] deterministic assistant run output (no model weights are run)`;
1192
+ const reply = await addThreadMessage(threadId, { role: 'assistant', content: replyText, assistant_id: assistantId, run_id: id }, req);
1193
+ const fields = {
1194
+ object: 'thread.run', created_at: created, thread_id: threadId, assistant_id: assistantId,
1195
+ status: 'completed', model, instructions: params.instructions ?? assistant.instructions ?? null,
1196
+ tools: Array.isArray(params.tools) ? params.tools : (assistant.tools ?? []),
1197
+ started_at: created, completed_at: created, expires_at: null, cancelled_at: null, failed_at: null,
1198
+ required_action: null, last_error: null,
1199
+ usage: { prompt_tokens: 0, completion_tokens: estimateTokens(replyText), total_tokens: estimateTokens(replyText) },
1200
+ metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : {},
1201
+ _reply_msg: reply.id,
1202
+ };
1203
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'run.create', subjectType: 'run', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1204
+ return { status: 200, body: runView(resource) };
1205
+ }
1206
+ function runView(r: Record<string, unknown>): Record<string, unknown> {
1207
+ const { _reply_msg: _m, ...rest } = strip(r);
1208
+ return { id: r.id, ...rest };
1209
+ }
1210
+ function runSteps(run: Record<string, unknown>): Array<Record<string, unknown>> {
1211
+ const created = Number(run.created_at ?? 0);
1212
+ const replyMsg = String((run as { _reply_msg?: unknown })._reply_msg ?? '');
1213
+ return [{
1214
+ id: `step-${run.id}-1`, object: 'thread.run.step', created_at: created,
1215
+ run_id: String(run.id), assistant_id: String(run.assistant_id), thread_id: String(run.thread_id),
1216
+ type: 'message_creation', status: 'completed', completed_at: created,
1217
+ step_details: { type: 'message_creation', message_creation: { message_id: replyMsg } },
1218
+ usage: run.usage ?? null,
1219
+ }];
1220
+ }
1221
+
1222
+ // ── Images (response shape; real pixels out of scope) ───────────────────────────────────
1223
+ function handleImages(params: Record<string, unknown>, occurredAt?: string): OpenAIResponseEnvelope {
1224
+ if (params.prompt === undefined || params.prompt === '') return invalidRequest("you must provide a prompt parameter", 'prompt');
1225
+ const n = Number(params.n ?? 1);
1226
+ const count = Number.isInteger(n) && n > 0 ? n : 1;
1227
+ const data = Array.from({ length: count }, (_, i) => ({
1228
+ url: `https://twin.invalid/openai-image-stub/${nowEpoch(occurredAt)}-${i}.png`,
1229
+ revised_prompt: `[twin-stub] ${String(params.prompt)}`,
1230
+ }));
1231
+ return { status: 200, body: { created: nowEpoch(occurredAt), data } };
1232
+ }
1233
+ // Image EDITS (require image + prompt) and VARIATIONS (require image, no prompt). Real pixels are
1234
+ // out of scope, so the twin returns the faithful response shape with a placeholder URL.
1235
+ function handleImageEdit(params: Record<string, unknown>, occurredAt?: string): OpenAIResponseEnvelope {
1236
+ if (params.image === undefined || params.image === '') return invalidRequest("you must provide an image to edit", 'image');
1237
+ if (params.prompt === undefined || params.prompt === '') return invalidRequest("you must provide a prompt parameter", 'prompt');
1238
+ const n = Number(params.n ?? 1);
1239
+ const count = Number.isInteger(n) && n > 0 ? n : 1;
1240
+ const data = Array.from({ length: count }, (_, i) => ({ url: `https://twin.invalid/openai-image-edit-stub/${nowEpoch(occurredAt)}-${i}.png` }));
1241
+ return { status: 200, body: { created: nowEpoch(occurredAt), data } };
1242
+ }
1243
+ function handleImageVariation(params: Record<string, unknown>, occurredAt?: string): OpenAIResponseEnvelope {
1244
+ if (params.image === undefined || params.image === '') return invalidRequest("you must provide an image", 'image');
1245
+ const n = Number(params.n ?? 1);
1246
+ const count = Number.isInteger(n) && n > 0 ? n : 1;
1247
+ const data = Array.from({ length: count }, (_, i) => ({ url: `https://twin.invalid/openai-image-variation-stub/${nowEpoch(occurredAt)}-${i}.png` }));
1248
+ return { status: 200, body: { created: nowEpoch(occurredAt), data } };
1249
+ }
1250
+
1251
+ // ── Audio (deterministic stubs; real model output is out of scope) ──────────────────────
1252
+ // Transcription/translation cannot run a real speech model, so the twin returns a clearly
1253
+ // labeled deterministic transcript derived from the supplied filename/text. The response SHAPE
1254
+ // (json / verbose_json / text) is vendor-faithful.
1255
+ function handleTranscription(params: Record<string, unknown>, translate: boolean): OpenAIResponseEnvelope {
1256
+ // The server adapts multipart → JSON { file (filename), model, response_format, language }.
1257
+ if (params.file === undefined || params.file === '') return invalidRequest("you must provide a file parameter", 'file');
1258
+ if (params.model === undefined || params.model === '') return invalidRequest("you must provide a model parameter", 'model');
1259
+ const filename = String(params.file);
1260
+ const verb = translate ? 'translation' : 'transcription';
1261
+ const text = `[twin-stub] deterministic ${verb} of ${filename} (no speech model is run)`;
1262
+ const format = typeof params.response_format === 'string' ? params.response_format : 'json';
1263
+ if (format === 'text') return { status: 200, body: text };
1264
+ if (format === 'verbose_json') {
1265
+ return { status: 200, body: { task: verb, language: translate ? 'english' : (typeof params.language === 'string' ? params.language : 'english'), duration: 1.0, text, segments: [{ id: 0, start: 0, end: 1, text }] } };
1266
+ }
1267
+ return { status: 200, body: { text } };
1268
+ }
1269
+ // Text-to-speech: cannot synthesize real audio (out of scope), but the endpoint returns audio
1270
+ // bytes deterministically derived from the input so the round-trip shape is faithful.
1271
+ function handleSpeech(params: Record<string, unknown>): OpenAIResponseEnvelope {
1272
+ if (params.model === undefined || params.model === '') return invalidRequest("you must provide a model parameter", 'model');
1273
+ if (params.input === undefined || params.input === '') return invalidRequest("you must provide an input parameter", 'input');
1274
+ if (params.voice === undefined || params.voice === '') return invalidRequest("you must provide a voice parameter", 'voice');
1275
+ // Deterministic placeholder "audio" payload (base64 of a labeled stub), never real audio.
1276
+ const marker = `[twin-stub-audio] voice=${String(params.voice)} format=${String(params.response_format ?? 'mp3')} text=${String(params.input)}`;
1277
+ return { status: 200, body: { object: 'audio.speech', audio_base64: btoa(marker), format: String(params.response_format ?? 'mp3') } };
1278
+ }
1279
+
1280
+ // ── Vector store search (deterministic ranking over attached files) ──────────────────────
1281
+ // The twin can't run a real similarity search (no embeddings of file content), so it returns
1282
+ // the attached files ranked by a deterministic pseudo-score seeded from (query, file_id). The
1283
+ // response SHAPE (object:vector_store.search_results.page, data[].score/file_id/content) is faithful.
1284
+ function searchScore(query: string, fileId: string): number {
1285
+ let h = 0x811c9dc5;
1286
+ const s = query + '|' + fileId;
1287
+ for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193); }
1288
+ return (h >>> 0) / 0xffffffff;
1289
+ }
1290
+
1291
+ // ── Evals API (stateful: eval config → runs → output items) ──────────────────────────────
1292
+ // The Evals API systematically evaluates model output. The twin cannot run a real grader, so a
1293
+ // run completes immediately with a deterministic synthetic result + one output item per datasource
1294
+ // item (passed, since the twin's stub output is graded by a deterministic stub grader). The eval/
1295
+ // run/output-item SHAPES are vendor-faithful; the grading values are clearly-deterministic stubs.
1296
+ async function createEval(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1297
+ if (params.data_source_config === undefined) return invalidRequest("you must provide a data_source_config parameter", 'data_source_config');
1298
+ if (params.testing_criteria === undefined || !Array.isArray(params.testing_criteria)) return invalidRequest("you must provide a testing_criteria parameter (array)", 'testing_criteria');
1299
+ const id = nextId('eval', 'eval', req.root);
1300
+ const fields = {
1301
+ object: 'eval',
1302
+ name: typeof params.name === 'string' ? params.name : `eval-${id}`,
1303
+ created_at: nowEpoch(req.occurredAt),
1304
+ data_source_config: params.data_source_config,
1305
+ testing_criteria: params.testing_criteria,
1306
+ metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : {},
1307
+ };
1308
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'eval.create', subjectType: 'eval', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1309
+ return { status: 200, body: idView(resource) };
1310
+ }
1311
+ function evalView(r: Record<string, unknown>): Record<string, unknown> {
1312
+ return { id: r.id, ...strip(r) };
1313
+ }
1314
+ // A run records a synthetic per-model usage + a result_counts of passed items. The twin grades a
1315
+ // fixed single datasource item (the twin cannot fetch a real dataset) → 1 passed item.
1316
+ async function createEvalRun(evalId: string, params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1317
+ if (params.data_source === undefined) return invalidRequest("you must provide a data_source parameter", 'data_source');
1318
+ const id = nextId('evalrun', 'evalrun', req.root);
1319
+ const created = nowEpoch(req.occurredAt);
1320
+ const model = typeof (params.data_source as { model?: unknown })?.model === 'string' ? String((params.data_source as { model?: unknown }).model) : 'gpt-4o';
1321
+ const fields = {
1322
+ object: 'eval.run',
1323
+ eval_id: evalId,
1324
+ name: typeof params.name === 'string' ? params.name : `run-${id}`,
1325
+ created_at: created,
1326
+ status: 'completed',
1327
+ model,
1328
+ data_source: params.data_source,
1329
+ result_counts: { total: 1, errored: 0, failed: 0, passed: 1 },
1330
+ per_model_usage: [{ model_name: model, invocation_count: 1, prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, cached_tokens: 0 }],
1331
+ per_testing_criteria_results: [{ testing_criteria: 'twin-stub-grader', passed: 1, failed: 0 }],
1332
+ report_url: `https://twin.invalid/evals/${evalId}/runs/${id}`,
1333
+ metadata: (params.metadata && typeof params.metadata === 'object') ? params.metadata : {},
1334
+ error: null,
1335
+ };
1336
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'eval_run.create', subjectType: 'eval_run', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1337
+ return { status: 200, body: evalRunView(resource) };
1338
+ }
1339
+ function evalRunView(r: Record<string, unknown>): Record<string, unknown> {
1340
+ return { id: r.id, ...strip(r) };
1341
+ }
1342
+ // The output items for a completed run: a single deterministic passed item (the twin's stub
1343
+ // output graded by a deterministic stub grader). Shape: object:'eval.run.output_item'.
1344
+ function evalRunOutputItems(run: Record<string, unknown>): Array<Record<string, unknown>> {
1345
+ const created = Number(run.created_at ?? 0);
1346
+ return [{
1347
+ id: `evalitem-${run.id}-1`,
1348
+ object: 'eval.run.output_item',
1349
+ created_at: created,
1350
+ run_id: String(run.id),
1351
+ eval_id: String(run.eval_id),
1352
+ status: 'pass',
1353
+ datasource_item_id: 0,
1354
+ datasource_item: { item: { input: '[twin-stub] datasource item' } },
1355
+ results: [{ name: 'twin-stub-grader', passed: true, score: 1.0 }],
1356
+ sample: { input: [], output: [{ role: 'assistant', content: '[twin-stub] eval sample output (no real model run)' }], finish_reason: 'stop', model: String(run.model ?? 'gpt-4o'), usage: { total_tokens: 0, completion_tokens: 0, prompt_tokens: 0, cached_tokens: 0 }, error: null },
1357
+ }];
1358
+ }
1359
+
1360
+ // ── Containers API (code-interpreter sandboxes: container → container files) ──────────────
1361
+ // A container is an isolated sandbox for the code_interpreter tool. The twin cannot run a real
1362
+ // sandbox, so a container is created `running` and its files are stored faithfully (metadata +
1363
+ // supplied text content, like the Files API). The SHAPES are vendor-faithful.
1364
+ async function createContainer(params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1365
+ if (params.name === undefined || params.name === '') return invalidRequest("you must provide a name parameter", 'name');
1366
+ const id = nextId('container', 'cntr', req.root);
1367
+ const created = nowEpoch(req.occurredAt);
1368
+ const fields = {
1369
+ object: 'container',
1370
+ name: String(params.name),
1371
+ created_at: created,
1372
+ status: 'running',
1373
+ last_active_at: created,
1374
+ expires_after: (params.expires_after && typeof params.expires_after === 'object') ? params.expires_after : { anchor: 'last_active_at', minutes: 20 },
1375
+ };
1376
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'container.create', subjectType: 'container', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1377
+ return { status: 200, body: containerView(resource) };
1378
+ }
1379
+ function containerView(r: Record<string, unknown>): Record<string, unknown> {
1380
+ return { id: r.id, ...strip(r) };
1381
+ }
1382
+ async function createContainerFile(containerId: string, params: Record<string, unknown>, req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1383
+ // The server adapts a multipart upload (or a JSON { file_id } reference) into JSON; the twin
1384
+ // accepts either an inline `content`+`path` (text) or a `file_id` referencing a stored File.
1385
+ const seq = rows('container_file', req.root).filter((r) => r.container_id === containerId).length + 1;
1386
+ const id = `cfile-twin-${containerId}-${seq}`;
1387
+ const created = nowEpoch(req.occurredAt);
1388
+ let content = '';
1389
+ let source = 'user';
1390
+ if (typeof params.file_id === 'string' && params.file_id) {
1391
+ const f = getRow('file', params.file_id, req.root);
1392
+ if (!f || f._deleted) return notFound(`No such File object: ${String(params.file_id)}`);
1393
+ content = String(f._content ?? '');
1394
+ source = 'file_id';
1395
+ } else if (typeof params.content === 'string') {
1396
+ content = params.content;
1397
+ }
1398
+ const path = typeof params.path === 'string' && params.path ? params.path : `/mnt/data/${id}`;
1399
+ const fields = {
1400
+ object: 'container.file',
1401
+ container_id: containerId,
1402
+ created_at: created,
1403
+ bytes: content.length,
1404
+ path,
1405
+ source,
1406
+ _content: content,
1407
+ };
1408
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'container_file.create', subjectType: 'container_file', subjectId: id, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1409
+ return { status: 200, body: containerFileView(resource) };
1410
+ }
1411
+ function containerFileView(r: Record<string, unknown>): Record<string, unknown> {
1412
+ return { id: r.id, ...strip(r) };
1413
+ }
1414
+
1415
+ // ── public entry: cross-cutting protocol (auth / rate-limit / idempotency) then route ─────
1416
+ // The HTTP server passes request headers, so on the live wire every call is auth/rate-limit/
1417
+ // idempotency-checked; in-process trusted calls (capability verify, connector) omit `headers`/
1418
+ // `apiKey` and are NOT gated. Idempotency-Key dedup wraps the WHOLE request so any successful
1419
+ // mutation is replayed verbatim on a re-issue with the same key.
1420
+ export async function handleOpenAITwinRequest(req: OpenAIRequest): Promise<OpenAIResponseEnvelope> {
1421
+ const method = req.method.toUpperCase();
1422
+
1423
+ // Modeled authentication (401). Gated only when the request carries an auth surface.
1424
+ if (req.headers !== undefined || req.apiKey !== undefined) {
1425
+ const authErr = checkAuth(req);
1426
+ if (authErr) return authErr;
1427
+ }
1428
+ // Modeled rate limiting (429) — deterministic opt-in trigger header.
1429
+ if (rateLimitTriggered(req)) return rateLimitError();
1430
+
1431
+ // Idempotency-Key: a re-issue with the same key replays the prior response byte-for-byte. Only
1432
+ // mutations (non-GET) are keyed (matching the vendor), and only 2xx results are cached.
1433
+ const idemKey = method !== 'GET' ? req.headers?.['idempotency-key'] : undefined;
1434
+ if (idemKey) {
1435
+ const cached = getIdempotentResult(idemKey, req.root);
1436
+ if (cached) return cached;
1437
+ const result = await routeOpenAI(req, method);
1438
+ if (result.status >= 200 && result.status < 300) await storeIdempotentResult(idemKey, result, req);
1439
+ return result;
1440
+ }
1441
+ return routeOpenAI(req, method);
1442
+ }
1443
+
1444
+ // ── router ──────────────────────────────────────────────────────────────────────────────
1445
+ async function routeOpenAI(req: OpenAIRequest, method: string): Promise<OpenAIResponseEnvelope> {
1446
+ const path = (req.path.split('?')[0] ?? '/').replace(/\/+$/, '') || '/';
1447
+ const seg = path.replace(/^\/+/, '').split('/'); // ["v1","chat","completions",...]
1448
+ const params = parseJson(req.body);
1449
+ const dec = (s: string) => decodeURIComponent(s);
1450
+
1451
+ // D3: a read-only twin rejects any mutation with a vendor-shaped error.
1452
+ if (req.readOnly && method !== 'GET') {
1453
+ return { status: 405, body: errBody('invalid_request_error', 'twin is read-only; omit readOnly to accept writes', 'method_not_allowed') };
1454
+ }
1455
+
1456
+ // ---- models (static catalog + fine-tuned models minted by fine-tuning jobs) ----
1457
+ if (path === '/v1/models' && method === 'GET') {
1458
+ // List the static catalog plus every (non-deleted) fine-tuned model produced by a job.
1459
+ return { status: 200, body: { object: 'list', data: [...OPENAI_MODELS, ...fineTunedModels(req.root)] } };
1460
+ }
1461
+ if (seg[1] === 'models' && seg.length === 3 && method === 'GET') {
1462
+ const mid = dec(seg[2]!);
1463
+ const m = findModel(mid) ?? fineTunedModels(req.root).find((fm) => fm.id === mid);
1464
+ return m ? { status: 200, body: m } : notFound(`The model '${mid}' does not exist`, 'model_not_found');
1465
+ }
1466
+ // Delete a fine-tuned model (only models you own — base catalog models can't be deleted).
1467
+ if (seg[1] === 'models' && seg.length === 3 && method === 'DELETE') {
1468
+ const mid = dec(seg[2]!);
1469
+ if (findModel(mid)) return invalidRequest(`The model '${mid}' cannot be deleted`, 'model', 'model_not_deletable');
1470
+ const ft = fineTunedModels(req.root).find((fm) => fm.id === mid);
1471
+ if (!ft) return notFound(`The model '${mid}' does not exist`, 'model_not_found');
1472
+ await applyTwinWrite(SERVICE, { operation: 'model.delete', subjectType: 'model', subjectId: mid, fields: { _deleted: true, object: 'model' }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1473
+ return { status: 200, body: { id: mid, object: 'model', deleted: true } };
1474
+ }
1475
+
1476
+ // ---- stored chat completions (store:true) — retrieve / list / messages / delete ----
1477
+ // (must precede the generic create route below; these are GET/DELETE on the same prefix.)
1478
+ if (path === '/v1/chat/completions' && method === 'GET') {
1479
+ const items = rows('chat_completion', req.root).filter((r) => !r._deleted).map(storedChatView).sort((a, b) => Number(b.created) - Number(a.created));
1480
+ return { status: 200, body: paginate(items, req.path) };
1481
+ }
1482
+ if (seg[1] === 'chat' && seg[2] === 'completions' && seg.length === 4 && method === 'GET') {
1483
+ const c = getRow('chat_completion', dec(seg[3]!), req.root);
1484
+ return c && !c._deleted ? { status: 200, body: storedChatView(c) } : notFound(`No chat completion found with id '${dec(seg[3]!)}'.`);
1485
+ }
1486
+ if (seg[1] === 'chat' && seg[2] === 'completions' && seg.length === 5 && seg[4] === 'messages' && method === 'GET') {
1487
+ const c = getRow('chat_completion', dec(seg[3]!), req.root);
1488
+ if (!c || c._deleted) return notFound(`No chat completion found with id '${dec(seg[3]!)}'.`);
1489
+ const msgs = ((c as { _input_messages?: unknown })._input_messages as unknown[]) ?? [];
1490
+ return { status: 200, body: paginate(msgs as Array<Record<string, unknown>>, req.path) };
1491
+ }
1492
+ if (seg[1] === 'chat' && seg[2] === 'completions' && seg.length === 4 && method === 'DELETE') {
1493
+ const cid = dec(seg[3]!);
1494
+ const c = getRow('chat_completion', cid, req.root);
1495
+ if (!c || c._deleted) return notFound(`No chat completion found with id '${cid}'.`);
1496
+ await applyTwinWrite(SERVICE, { operation: 'chat_completion.delete', subjectType: 'chat_completion', subjectId: cid, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1497
+ return { status: 200, body: { id: cid, object: 'chat.completion.deleted', deleted: true } };
1498
+ }
1499
+
1500
+ // ---- chat completions (the generative stub; envelope is faithful) ----
1501
+ if (path === '/v1/chat/completions' && method === 'POST') {
1502
+ const validated = validateChat(params);
1503
+ if ('error' in validated) return validated.error;
1504
+ const args = validated.args;
1505
+ let result: ChatCompletion;
1506
+ if (args.stream && req.sseSink) result = streamChat(args, req.sseSink, req.occurredAt, req.scenarioEngine);
1507
+ else result = buildChatCompletion(args, req.occurredAt, req.scenarioEngine);
1508
+ // store:true → persist the completion so it can be retrieved/listed later (stored completions).
1509
+ if (args.store) await storeChatCompletion(result, args, req);
1510
+ await recordUsage(req, 'completions', result.model, result.usage.prompt_tokens, result.usage.completion_tokens);
1511
+ return { status: 200, body: result };
1512
+ }
1513
+
1514
+ // ---- responses API ----
1515
+ if (path === '/v1/responses' && method === 'POST') {
1516
+ const validated = validateResponses(params);
1517
+ if ('error' in validated) return validated.error;
1518
+ const args = validated.args;
1519
+ // previous_response_id chaining: the referenced response must exist; its assistant output is
1520
+ // folded into the prompt context (so usage reflects the chained history — server-side state).
1521
+ if (args.previousResponseId) {
1522
+ const prior = getRow('response', args.previousResponseId, req.root);
1523
+ if (!prior || prior._deleted) return notFound(`Response with id '${args.previousResponseId}' not found.`);
1524
+ const priorText = String((prior as { output_text?: unknown }).output_text ?? '');
1525
+ args.messages = [{ role: 'assistant', content: priorText }, ...args.messages];
1526
+ }
1527
+ // Deterministic stored-response id (hash of model+input+chain) so retrieval is assertable.
1528
+ const suffix = responseSuffix(args);
1529
+ // background:true → the response is created `queued` and processed asynchronously. The twin has
1530
+ // nothing to run in the background, so it persists a queued response that the FIRST poll
1531
+ // (GET /v1/responses/:id) transitions to `completed` (computing the real stub output then). The
1532
+ // queued envelope carries no output yet; background implies store (so it can be polled/cancelled).
1533
+ if (args.background) {
1534
+ const queued = await storeQueuedResponse(args, req, suffix);
1535
+ return { status: 200, body: responseView(queued) };
1536
+ }
1537
+ if (args.stream) {
1538
+ const resp = req.sseSink ? streamResponse(args, req.sseSink, req.occurredAt, suffix) : buildResponse(args, req.occurredAt, suffix);
1539
+ if (args.store) await storeResponse(resp, args, req);
1540
+ await recordUsage(req, 'responses', resp.model, resp.usage.input_tokens, resp.usage.output_tokens);
1541
+ return { status: 200, body: resp };
1542
+ }
1543
+ const resp = buildResponse(args, req.occurredAt, suffix);
1544
+ if (args.store) await storeResponse(resp, args, req);
1545
+ await recordUsage(req, 'responses', resp.model, resp.usage.input_tokens, resp.usage.output_tokens);
1546
+ return { status: 200, body: resp };
1547
+ }
1548
+ // Responses: retrieve / delete a stored response by id.
1549
+ if (seg[1] === 'responses' && seg.length === 3 && method === 'GET') {
1550
+ const r = getRow('response', dec(seg[2]!), req.root);
1551
+ if (!r || r._deleted) return notFound(`Response with id '${dec(seg[2]!)}' not found.`);
1552
+ // A queued background response is processed on first poll: transition queued → completed,
1553
+ // computing the real stub output + usage at that point (then record billable usage once).
1554
+ if (r.status === 'queued') {
1555
+ const completed = await completeQueuedResponse(r, req);
1556
+ return { status: 200, body: responseView(completed) };
1557
+ }
1558
+ return { status: 200, body: responseView(r) };
1559
+ }
1560
+ // Responses: cancel a background response (only a queued/in_progress one can be cancelled).
1561
+ if (seg[1] === 'responses' && seg.length === 4 && seg[3] === 'cancel' && method === 'POST') {
1562
+ const rid = dec(seg[2]!);
1563
+ const r = getRow('response', rid, req.root);
1564
+ if (!r || r._deleted) return notFound(`Response with id '${rid}' not found.`);
1565
+ if (r.status !== 'queued' && r.status !== 'in_progress') {
1566
+ return invalidRequest(`Cannot cancel a response with status '${String(r.status)}'.`, null, 'invalid_status');
1567
+ }
1568
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'response.cancel', subjectType: 'response', subjectId: rid, fields: { status: 'cancelled' }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1569
+ return { status: 200, body: responseView(resource) };
1570
+ }
1571
+ if (seg[1] === 'responses' && seg.length === 3 && method === 'DELETE') {
1572
+ const rid = dec(seg[2]!);
1573
+ const r = getRow('response', rid, req.root);
1574
+ if (!r || r._deleted) return notFound(`Response with id '${rid}' not found.`);
1575
+ await applyTwinWrite(SERVICE, { operation: 'response.delete', subjectType: 'response', subjectId: rid, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1576
+ return { status: 200, body: { id: rid, object: 'response.deleted', deleted: true } };
1577
+ }
1578
+ // Responses: list the input items of a stored response (the user turns folded into it).
1579
+ if (seg[1] === 'responses' && seg.length === 4 && seg[3] === 'input_items' && method === 'GET') {
1580
+ const r = getRow('response', dec(seg[2]!), req.root);
1581
+ if (!r || r._deleted) return notFound(`Response with id '${dec(seg[2]!)}' not found.`);
1582
+ const items = ((r as { _input_items?: unknown })._input_items as unknown[]) ?? [];
1583
+ return { status: 200, body: { object: 'list', data: items, has_more: false } };
1584
+ }
1585
+
1586
+ // ---- embeddings ----
1587
+ if (path === '/v1/embeddings' && method === 'POST') {
1588
+ const res = handleEmbeddings(params);
1589
+ if (res.status === 200) {
1590
+ const b = res.body as EmbeddingResponse;
1591
+ await recordUsage(req, 'embeddings', b.model, b.usage.prompt_tokens, 0);
1592
+ }
1593
+ return res;
1594
+ }
1595
+
1596
+ // ---- moderations ----
1597
+ if (path === '/v1/moderations' && method === 'POST') {
1598
+ return handleModerations(params, req.occurredAt);
1599
+ }
1600
+
1601
+ // ---- images ----
1602
+ if (path === '/v1/images/generations' && method === 'POST') {
1603
+ return handleImages(params, req.occurredAt);
1604
+ }
1605
+ if (path === '/v1/images/edits' && method === 'POST') return handleImageEdit(params, req.occurredAt);
1606
+ if (path === '/v1/images/variations' && method === 'POST') return handleImageVariation(params, req.occurredAt);
1607
+
1608
+ // ---- audio ----
1609
+ if (path === '/v1/audio/transcriptions' && method === 'POST') return handleTranscription(params, false);
1610
+ if (path === '/v1/audio/translations' && method === 'POST') return handleTranscription(params, true);
1611
+ if (path === '/v1/audio/speech' && method === 'POST') return handleSpeech(params);
1612
+
1613
+ // ---- files (stateful) ----
1614
+ if (path === '/v1/files' && method === 'POST') return createFile(params, req);
1615
+ if (path === '/v1/files' && method === 'GET') {
1616
+ const items = rows('file', req.root).filter((r) => !r._deleted).map(fileView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
1617
+ return { status: 200, body: paginate(items, req.path) };
1618
+ }
1619
+ if (seg[1] === 'files' && seg.length === 3 && method === 'GET') {
1620
+ const f = getRow('file', dec(seg[2]!), req.root);
1621
+ return f && !f._deleted ? { status: 200, body: fileView(f) } : notFound(`No such File object: ${dec(seg[2]!)}`);
1622
+ }
1623
+ if (seg[1] === 'files' && seg.length === 4 && seg[3] === 'content' && method === 'GET') {
1624
+ const f = getRow('file', dec(seg[2]!), req.root);
1625
+ if (!f || f._deleted) return notFound(`No such File object: ${dec(seg[2]!)}`);
1626
+ return { status: 200, body: (f._content as string) ?? '' };
1627
+ }
1628
+ if (seg[1] === 'files' && seg.length === 3 && method === 'DELETE') {
1629
+ const id = dec(seg[2]!);
1630
+ const f = getRow('file', id, req.root);
1631
+ if (!f) return notFound(`No such File object: ${id}`);
1632
+ await applyTwinWrite(SERVICE, { operation: 'file.delete', subjectType: 'file', subjectId: id, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1633
+ return { status: 200, body: { id, object: 'file', deleted: true } };
1634
+ }
1635
+
1636
+ // ---- uploads (multipart large-file parts, stateful) ----
1637
+ if (path === '/v1/uploads' && method === 'POST') return createUpload(params, req);
1638
+ if (seg[1] === 'uploads' && seg.length === 4 && seg[3] === 'parts' && method === 'POST') return addUploadPart(dec(seg[2]!), params, req);
1639
+ if (seg[1] === 'uploads' && seg.length === 4 && seg[3] === 'complete' && method === 'POST') return completeUpload(dec(seg[2]!), params, req);
1640
+ if (seg[1] === 'uploads' && seg.length === 4 && seg[3] === 'cancel' && method === 'POST') {
1641
+ const uid = dec(seg[2]!);
1642
+ const up = getRow('upload', uid, req.root);
1643
+ if (!up || up.status !== 'pending') return notFound(`No such Upload object: ${uid}`);
1644
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'upload.cancel', subjectType: 'upload', subjectId: uid, fields: { status: 'cancelled' }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1645
+ return { status: 200, body: uploadView(resource) };
1646
+ }
1647
+
1648
+ // ---- batches (stateful) ----
1649
+ if (path === '/v1/batches' && method === 'POST') return createBatch(params, req);
1650
+ if (path === '/v1/batches' && method === 'GET') {
1651
+ const items = rows('batch', req.root).map(batchView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
1652
+ return { status: 200, body: paginate(items, req.path) };
1653
+ }
1654
+ if (seg[1] === 'batches' && seg.length === 3 && method === 'GET') {
1655
+ const b = getRow('batch', dec(seg[2]!), req.root);
1656
+ return b ? { status: 200, body: batchView(b) } : notFound(`No such Batch object: ${dec(seg[2]!)}`);
1657
+ }
1658
+ if (seg[1] === 'batches' && seg.length === 4 && seg[3] === 'cancel' && method === 'POST') {
1659
+ const id = dec(seg[2]!);
1660
+ const b = getRow('batch', id, req.root);
1661
+ if (!b) return notFound(`No such Batch object: ${id}`);
1662
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'batch.cancel', subjectType: 'batch', subjectId: id, fields: { status: 'cancelled', cancelled_at: nowEpoch(req.occurredAt) }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1663
+ return { status: 200, body: batchView(resource) };
1664
+ }
1665
+
1666
+ // ---- moderations handled above ----
1667
+
1668
+ // ---- fine-tuning jobs (stateful) ----
1669
+ if (path === '/v1/fine_tuning/jobs' && method === 'POST') return createFineTune(params, req);
1670
+ if (path === '/v1/fine_tuning/jobs' && method === 'GET') {
1671
+ const items = rows('fine_tuning_job', req.root).map(ftView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
1672
+ return { status: 200, body: paginate(items, req.path) };
1673
+ }
1674
+ if (seg[1] === 'fine_tuning' && seg[2] === 'jobs' && seg.length === 4 && method === 'GET') {
1675
+ const j = getRow('fine_tuning_job', dec(seg[3]!), req.root);
1676
+ return j ? { status: 200, body: ftView(j) } : notFound(`No such fine-tuning job: ${dec(seg[3]!)}`);
1677
+ }
1678
+ if (seg[1] === 'fine_tuning' && seg[2] === 'jobs' && seg.length === 5 && seg[4] === 'events' && method === 'GET') {
1679
+ const j = getRow('fine_tuning_job', dec(seg[3]!), req.root);
1680
+ if (!j) return notFound(`No such fine-tuning job: ${dec(seg[3]!)}`);
1681
+ // Deterministic synthetic event stream for a completed twin job.
1682
+ const created = Number(j.created_at ?? 0);
1683
+ const events = [
1684
+ { object: 'fine_tuning.job.event', id: `ftevent-${j.id}-1`, created_at: created, level: 'info', message: 'Created fine-tuning job', type: 'message' },
1685
+ { object: 'fine_tuning.job.event', id: `ftevent-${j.id}-2`, created_at: created, level: 'info', message: 'Fine-tuning job successfully completed (twin stub)', type: 'message' },
1686
+ ];
1687
+ return { status: 200, body: { object: 'list', data: events, has_more: false } };
1688
+ }
1689
+ // Fine-tuning checkpoints: a completed job exposes its training checkpoints (one per epoch). The
1690
+ // twin synthesizes a deterministic checkpoint per succeeded job (shape faithful; metrics are stubs).
1691
+ if (seg[1] === 'fine_tuning' && seg[2] === 'jobs' && seg.length === 5 && seg[4] === 'checkpoints' && method === 'GET') {
1692
+ const j = getRow('fine_tuning_job', dec(seg[3]!), req.root);
1693
+ if (!j) return notFound(`No such fine-tuning job: ${dec(seg[3]!)}`);
1694
+ const created = Number(j.created_at ?? 0);
1695
+ // succeeded jobs have a final checkpoint pointing at the fine-tuned model; cancelled jobs have none.
1696
+ const checkpoints = j.status === 'succeeded'
1697
+ ? [{
1698
+ object: 'fine_tuning.job.checkpoint',
1699
+ id: `ftckpt-${j.id}-1`,
1700
+ created_at: created,
1701
+ fine_tuned_model_checkpoint: String(j.fine_tuned_model ?? `ft:twin::${j.id}:ckpt-step-1`),
1702
+ fine_tuning_job_id: String(j.id),
1703
+ metrics: { step: 1, train_loss: 0, train_mean_token_accuracy: 1, full_valid_loss: 0, full_valid_mean_token_accuracy: 1 },
1704
+ step_number: 1,
1705
+ }]
1706
+ : [];
1707
+ return { status: 200, body: { object: 'list', data: checkpoints, has_more: false, first_id: checkpoints[0]?.id ?? null, last_id: checkpoints[0]?.id ?? null } };
1708
+ }
1709
+ if (seg[1] === 'fine_tuning' && seg[2] === 'jobs' && seg.length === 5 && seg[4] === 'cancel' && method === 'POST') {
1710
+ const id = dec(seg[3]!);
1711
+ const j = getRow('fine_tuning_job', id, req.root);
1712
+ if (!j) return notFound(`No such fine-tuning job: ${id}`);
1713
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'fine_tuning_job.cancel', subjectType: 'fine_tuning_job', subjectId: id, fields: { status: 'cancelled' }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1714
+ return { status: 200, body: ftView(resource) };
1715
+ }
1716
+ // Pause a fine-tuning job → status 'paused' (a deployable checkpoint is preserved). Only a
1717
+ // running/queued/succeeded job can be paused; a cancelled job can't (vendor 400).
1718
+ if (seg[1] === 'fine_tuning' && seg[2] === 'jobs' && seg.length === 5 && seg[4] === 'pause' && method === 'POST') {
1719
+ const id = dec(seg[3]!);
1720
+ const j = getRow('fine_tuning_job', id, req.root);
1721
+ if (!j) return notFound(`No such fine-tuning job: ${id}`);
1722
+ if (j.status === 'cancelled' || j.status === 'failed') return invalidRequest(`Cannot pause a job with status '${String(j.status)}'.`, null, 'invalid_status');
1723
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'fine_tuning_job.pause', subjectType: 'fine_tuning_job', subjectId: id, fields: { status: 'paused' }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1724
+ return { status: 200, body: ftView(resource) };
1725
+ }
1726
+ // Resume a paused fine-tuning job → status back to 'succeeded' (the twin's terminal state, since
1727
+ // it cannot actually train). Only a paused job can be resumed (vendor 400 otherwise).
1728
+ if (seg[1] === 'fine_tuning' && seg[2] === 'jobs' && seg.length === 5 && seg[4] === 'resume' && method === 'POST') {
1729
+ const id = dec(seg[3]!);
1730
+ const j = getRow('fine_tuning_job', id, req.root);
1731
+ if (!j) return notFound(`No such fine-tuning job: ${id}`);
1732
+ if (j.status !== 'paused') return invalidRequest(`Cannot resume a job with status '${String(j.status)}'.`, null, 'invalid_status');
1733
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'fine_tuning_job.resume', subjectType: 'fine_tuning_job', subjectId: id, fields: { status: 'succeeded' }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1734
+ return { status: 200, body: ftView(resource) };
1735
+ }
1736
+
1737
+ // ---- vector stores (stateful) ----
1738
+ if (path === '/v1/vector_stores' && method === 'POST') return createVectorStore(params, req);
1739
+ if (path === '/v1/vector_stores' && method === 'GET') {
1740
+ const items = rows('vector_store', req.root).filter((r) => !r._deleted).map(vsView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
1741
+ return { status: 200, body: paginate(items, req.path) };
1742
+ }
1743
+ if (seg[1] === 'vector_stores' && seg.length === 3 && method === 'GET') {
1744
+ const v = getRow('vector_store', dec(seg[2]!), req.root);
1745
+ return v && !v._deleted ? { status: 200, body: vsView(v) } : notFound(`No such vector store: ${dec(seg[2]!)}`);
1746
+ }
1747
+ if (seg[1] === 'vector_stores' && seg.length === 3 && method === 'DELETE') {
1748
+ const id = dec(seg[2]!);
1749
+ const v = getRow('vector_store', id, req.root);
1750
+ if (!v) return notFound(`No such vector store: ${id}`);
1751
+ await applyTwinWrite(SERVICE, { operation: 'vector_store.delete', subjectType: 'vector_store', subjectId: id, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1752
+ return { status: 200, body: { id, object: 'vector_store.deleted', deleted: true } };
1753
+ }
1754
+ if (seg[1] === 'vector_stores' && seg.length === 4 && seg[3] === 'files' && method === 'POST') {
1755
+ const storeId = dec(seg[2]!);
1756
+ if (!getRow('vector_store', storeId, req.root)) return notFound(`No such vector store: ${storeId}`);
1757
+ if (params.file_id === undefined || params.file_id === '') return invalidRequest("you must provide a file_id parameter", 'file_id');
1758
+ const view = await addVectorStoreFile(storeId, String(params.file_id), req);
1759
+ return { status: 200, body: view };
1760
+ }
1761
+ if (seg[1] === 'vector_stores' && seg.length === 4 && seg[3] === 'files' && method === 'GET') {
1762
+ const storeId = dec(seg[2]!);
1763
+ if (!getRow('vector_store', storeId, req.root)) return notFound(`No such vector store: ${storeId}`);
1764
+ const all = rows('vector_store_file', req.root).filter((r) => r.vector_store_id === storeId && !r._deleted).map(vsFileView);
1765
+ return { status: 200, body: paginate(all, req.path) };
1766
+ }
1767
+ // Vector store FILE BATCHES: bulk-attach a list of file_ids to a store in one call. The twin
1768
+ // attaches each file as a vector_store.file child, then returns a batch object (status completed,
1769
+ // since the twin has nothing to process async). Retrieve + list-files re-read the batch.
1770
+ if (seg[1] === 'vector_stores' && seg.length === 4 && seg[3] === 'file_batches' && method === 'POST') {
1771
+ const storeId = dec(seg[2]!);
1772
+ if (!getRow('vector_store', storeId, req.root)) return notFound(`No such vector store: ${storeId}`);
1773
+ if (!Array.isArray(params.file_ids) || params.file_ids.length === 0) return invalidRequest("you must provide a non-empty file_ids array", 'file_ids');
1774
+ const fileIds = (params.file_ids as unknown[]).map(String);
1775
+ const batchId = nextId('vector_store_file_batch', 'vsfb', req.root);
1776
+ for (const fid of fileIds) await addVectorStoreFile(storeId, fid, req, batchId);
1777
+ const created = nowEpoch(req.occurredAt);
1778
+ const fields = {
1779
+ object: 'vector_store.file_batch',
1780
+ vector_store_id: storeId,
1781
+ created_at: created,
1782
+ status: 'completed',
1783
+ file_counts: { in_progress: 0, completed: fileIds.length, failed: 0, cancelled: 0, total: fileIds.length },
1784
+ _file_ids: fileIds,
1785
+ };
1786
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'vector_store_file_batch.create', subjectType: 'vector_store_file_batch', subjectId: batchId, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1787
+ return { status: 200, body: vsBatchView(resource) };
1788
+ }
1789
+ if (seg[1] === 'vector_stores' && seg.length === 5 && seg[3] === 'file_batches' && method === 'GET') {
1790
+ const storeId = dec(seg[2]!);
1791
+ if (!getRow('vector_store', storeId, req.root)) return notFound(`No such vector store: ${storeId}`);
1792
+ const b = getRow('vector_store_file_batch', dec(seg[4]!), req.root);
1793
+ return b && b.vector_store_id === storeId ? { status: 200, body: vsBatchView(b) } : notFound(`No such file batch: ${dec(seg[4]!)}`);
1794
+ }
1795
+ if (seg[1] === 'vector_stores' && seg.length === 6 && seg[3] === 'file_batches' && seg[5] === 'files' && method === 'GET') {
1796
+ const storeId = dec(seg[2]!);
1797
+ if (!getRow('vector_store', storeId, req.root)) return notFound(`No such vector store: ${storeId}`);
1798
+ const batchId = dec(seg[4]!);
1799
+ if (!getRow('vector_store_file_batch', batchId, req.root)) return notFound(`No such file batch: ${batchId}`);
1800
+ const all = rows('vector_store_file', req.root).filter((r) => r.vector_store_id === storeId && r._batch_id === batchId && !r._deleted).map(vsFileView);
1801
+ return { status: 200, body: paginate(all, req.path) };
1802
+ }
1803
+
1804
+ // Vector store search: deterministic ranked results over the store's attached files.
1805
+ if (seg[1] === 'vector_stores' && seg.length === 4 && seg[3] === 'search' && method === 'POST') {
1806
+ const storeId = dec(seg[2]!);
1807
+ if (!getRow('vector_store', storeId, req.root)) return notFound(`No such vector store: ${storeId}`);
1808
+ if (params.query === undefined || params.query === '') return invalidRequest("you must provide a query parameter", 'query');
1809
+ const query = typeof params.query === 'string' ? params.query : JSON.stringify(params.query);
1810
+ const files = rows('vector_store_file', req.root).filter((r) => r.vector_store_id === storeId && !r._deleted);
1811
+ const maxResults = Number.isInteger(Number(params.max_num_results)) ? Number(params.max_num_results) : 10;
1812
+ const ranked = files
1813
+ .map((f) => ({ file_id: String((f as { file_id?: unknown }).file_id ?? ''), score: searchScore(query, String((f as { file_id?: unknown }).file_id ?? '')) }))
1814
+ .sort((a, b) => b.score - a.score)
1815
+ .slice(0, Math.max(1, maxResults))
1816
+ .map((r) => ({ file_id: r.file_id, filename: r.file_id, score: r.score, attributes: {}, content: [{ type: 'text', text: `[twin-stub] deterministic pseudo-match for "${query}" in ${r.file_id} (no real semantic search)` }] }));
1817
+ return { status: 200, body: { object: 'vector_store.search_results.page', search_query: query, data: ranked, has_more: false, next_page: null } };
1818
+ }
1819
+
1820
+ // ---- assistants (beta, stateful) ----
1821
+ if (path === '/v1/assistants' && method === 'POST') return createAssistant(params, req);
1822
+ if (path === '/v1/assistants' && method === 'GET') {
1823
+ const items = rows('assistant', req.root).filter((r) => !r._deleted).map(idView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
1824
+ return { status: 200, body: paginate(items, req.path) };
1825
+ }
1826
+ if (seg[1] === 'assistants' && seg.length === 3 && method === 'GET') {
1827
+ const a = getRow('assistant', dec(seg[2]!), req.root);
1828
+ return a && !a._deleted ? { status: 200, body: idView(a) } : notFound(`No assistant found with id '${dec(seg[2]!)}'.`);
1829
+ }
1830
+ if (seg[1] === 'assistants' && seg.length === 3 && method === 'POST') {
1831
+ const aid = dec(seg[2]!);
1832
+ const a = getRow('assistant', aid, req.root);
1833
+ if (!a || a._deleted) return notFound(`No assistant found with id '${aid}'.`);
1834
+ const fields = pickUpdate(params, ['name', 'description', 'model', 'instructions', 'tools', 'metadata', 'temperature', 'top_p', 'response_format']);
1835
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'assistant.update', subjectType: 'assistant', subjectId: aid, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1836
+ return { status: 200, body: idView(resource) };
1837
+ }
1838
+ if (seg[1] === 'assistants' && seg.length === 3 && method === 'DELETE') {
1839
+ const aid = dec(seg[2]!);
1840
+ const a = getRow('assistant', aid, req.root);
1841
+ if (!a || a._deleted) return notFound(`No assistant found with id '${aid}'.`);
1842
+ await applyTwinWrite(SERVICE, { operation: 'assistant.delete', subjectType: 'assistant', subjectId: aid, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1843
+ return { status: 200, body: { id: aid, object: 'assistant.deleted', deleted: true } };
1844
+ }
1845
+
1846
+ // ---- threads (+ messages) (beta, stateful) ----
1847
+ if (path === '/v1/threads' && method === 'POST') return createThread(params, req);
1848
+ if (seg[1] === 'threads' && seg.length === 3 && method === 'GET') {
1849
+ const t = getRow('thread', dec(seg[2]!), req.root);
1850
+ return t && !t._deleted ? { status: 200, body: idView(t) } : notFound(`No thread found with id '${dec(seg[2]!)}'.`);
1851
+ }
1852
+ if (seg[1] === 'threads' && seg.length === 3 && method === 'POST') {
1853
+ const tid = dec(seg[2]!);
1854
+ const t = getRow('thread', tid, req.root);
1855
+ if (!t || t._deleted) return notFound(`No thread found with id '${tid}'.`);
1856
+ const fields = pickUpdate(params, ['metadata', 'tool_resources']);
1857
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'thread.update', subjectType: 'thread', subjectId: tid, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1858
+ return { status: 200, body: idView(resource) };
1859
+ }
1860
+ if (seg[1] === 'threads' && seg.length === 3 && method === 'DELETE') {
1861
+ const tid = dec(seg[2]!);
1862
+ const t = getRow('thread', tid, req.root);
1863
+ if (!t || t._deleted) return notFound(`No thread found with id '${tid}'.`);
1864
+ await applyTwinWrite(SERVICE, { operation: 'thread.delete', subjectType: 'thread', subjectId: tid, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1865
+ return { status: 200, body: { id: tid, object: 'thread.deleted', deleted: true } };
1866
+ }
1867
+ if (seg[1] === 'threads' && seg.length === 4 && seg[3] === 'messages' && method === 'POST') {
1868
+ const tid = dec(seg[2]!);
1869
+ if (!getRow('thread', tid, req.root)) return notFound(`No thread found with id '${tid}'.`);
1870
+ if (params.content === undefined) return invalidRequest("you must provide a content parameter", 'content');
1871
+ const view = await addThreadMessage(tid, params, req);
1872
+ return { status: 200, body: view };
1873
+ }
1874
+ if (seg[1] === 'threads' && seg.length === 4 && seg[3] === 'messages' && method === 'GET') {
1875
+ const tid = dec(seg[2]!);
1876
+ if (!getRow('thread', tid, req.root)) return notFound(`No thread found with id '${tid}'.`);
1877
+ const all = rows('message', req.root).filter((r) => r.thread_id === tid && !r._deleted).map(msgView).sort((a, b) => Number(b.created_at) - Number(a.created_at) || String(b.id).localeCompare(String(a.id)));
1878
+ return { status: 200, body: paginate(all, req.path) };
1879
+ }
1880
+ if (seg[1] === 'threads' && seg.length === 5 && seg[3] === 'messages' && method === 'GET') {
1881
+ const tid = dec(seg[2]!);
1882
+ if (!getRow('thread', tid, req.root)) return notFound(`No thread found with id '${tid}'.`);
1883
+ const m = getRow('message', dec(seg[4]!), req.root);
1884
+ return m && m.thread_id === tid && !m._deleted ? { status: 200, body: msgView(m) } : notFound(`No message found with id '${dec(seg[4]!)}'.`);
1885
+ }
1886
+
1887
+ // ---- runs (+ run steps) (beta, stateful) ----
1888
+ if (seg[1] === 'threads' && seg.length === 4 && seg[3] === 'runs' && method === 'POST') {
1889
+ const tid = dec(seg[2]!);
1890
+ if (!getRow('thread', tid, req.root)) return notFound(`No thread found with id '${tid}'.`);
1891
+ return createRun(tid, params, req);
1892
+ }
1893
+ if (seg[1] === 'threads' && seg.length === 4 && seg[3] === 'runs' && method === 'GET') {
1894
+ const tid = dec(seg[2]!);
1895
+ if (!getRow('thread', tid, req.root)) return notFound(`No thread found with id '${tid}'.`);
1896
+ const all = rows('run', req.root).filter((r) => r.thread_id === tid && !r._deleted).map(runView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
1897
+ return { status: 200, body: paginate(all, req.path) };
1898
+ }
1899
+ if (seg[1] === 'threads' && seg.length === 6 && seg[3] === 'runs' && seg[5] === 'steps' && method === 'GET') {
1900
+ const tid = dec(seg[2]!);
1901
+ const r = getRow('run', dec(seg[4]!), req.root);
1902
+ if (!r || r.thread_id !== tid || r._deleted) return notFound(`No run found with id '${dec(seg[4]!)}'.`);
1903
+ return { status: 200, body: { object: 'list', data: runSteps(r), has_more: false, first_id: `step-${r.id}-1`, last_id: `step-${r.id}-1` } };
1904
+ }
1905
+ if (seg[1] === 'threads' && seg.length === 7 && seg[3] === 'runs' && seg[5] === 'steps' && method === 'GET') {
1906
+ const tid = dec(seg[2]!);
1907
+ const r = getRow('run', dec(seg[4]!), req.root);
1908
+ if (!r || r.thread_id !== tid || r._deleted) return notFound(`No run found with id '${dec(seg[4]!)}'.`);
1909
+ const step = runSteps(r).find((s) => s.id === dec(seg[6]!));
1910
+ return step ? { status: 200, body: step } : notFound(`No run step found with id '${dec(seg[6]!)}'.`);
1911
+ }
1912
+ if (seg[1] === 'threads' && seg.length === 5 && seg[3] === 'runs' && method === 'GET') {
1913
+ const tid = dec(seg[2]!);
1914
+ const r = getRow('run', dec(seg[4]!), req.root);
1915
+ return r && r.thread_id === tid && !r._deleted ? { status: 200, body: runView(r) } : notFound(`No run found with id '${dec(seg[4]!)}'.`);
1916
+ }
1917
+ if (seg[1] === 'threads' && seg.length === 6 && seg[3] === 'runs' && seg[5] === 'cancel' && method === 'POST') {
1918
+ const tid = dec(seg[2]!);
1919
+ const r = getRow('run', dec(seg[4]!), req.root);
1920
+ if (!r || r.thread_id !== tid || r._deleted) return notFound(`No run found with id '${dec(seg[4]!)}'.`);
1921
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'run.cancel', subjectType: 'run', subjectId: dec(seg[4]!), fields: { status: 'cancelled', cancelled_at: nowEpoch(req.occurredAt) }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1922
+ return { status: 200, body: runView(resource) };
1923
+ }
1924
+
1925
+ // ---- admin: usage + costs reporting (computed over REAL recorded usage rows) ----
1926
+ // The vendor's Usage/Costs API returns time-bucketed aggregates. The twin records a usage row per
1927
+ // billable inference call (recordUsage) and aggregates them here — nothing is hardcoded; with no
1928
+ // traffic the report is genuinely empty. A single bucket covers [0, now] (the twin is offline +
1929
+ // time-frozen), grouped by model, faithful to the vendor's bucket/result shape.
1930
+ if (seg[1] === 'organization' && seg[2] === 'usage' && (seg.length === 3 || seg.length === 4) && method === 'GET') {
1931
+ const kindFilter = seg.length === 4 ? dec(seg[3]!) : undefined; // 'completions' | 'embeddings' | undefined (all)
1932
+ const all = rows('usage_record', req.root).filter((r) => kindFilter === undefined || r.kind === kindFilter);
1933
+ // group by model → one result object per model
1934
+ const byModel = new Map<string, { input: number; output: number; requests: number }>();
1935
+ for (const r of all) {
1936
+ const m = String(r.model);
1937
+ const agg = byModel.get(m) ?? { input: 0, output: 0, requests: 0 };
1938
+ agg.input += Number(r.input_tokens ?? 0);
1939
+ agg.output += Number(r.output_tokens ?? 0);
1940
+ agg.requests += Number(r.num_model_requests ?? 0);
1941
+ byModel.set(m, agg);
1942
+ }
1943
+ const resultObject = kindFilter === 'embeddings' ? 'organization.usage.embeddings.result' : 'organization.usage.completions.result';
1944
+ const results = [...byModel.entries()].sort((a, b) => (a[0] < b[0] ? -1 : 1)).map(([model, agg]) => ({
1945
+ object: resultObject,
1946
+ input_tokens: agg.input,
1947
+ output_tokens: agg.output,
1948
+ num_model_requests: agg.requests,
1949
+ model,
1950
+ project_id: null,
1951
+ }));
1952
+ const buckets = results.length === 0 ? [] : [{ object: 'bucket', start_time: 0, end_time: nowEpoch(req.occurredAt), results }];
1953
+ return { status: 200, body: { object: 'page', data: buckets, has_more: false, next_page: null } };
1954
+ }
1955
+ if (path === '/v1/organization/costs' && method === 'GET') {
1956
+ const all = rows('usage_record', req.root);
1957
+ // group cost by line_item (the usage kind, vendor-style) → one result per line item
1958
+ const byLine = new Map<string, number>();
1959
+ for (const r of all) {
1960
+ const line = String(r.kind);
1961
+ byLine.set(line, (byLine.get(line) ?? 0) + Number(r.cost_usd ?? 0));
1962
+ }
1963
+ const results = [...byLine.entries()].sort((a, b) => (a[0] < b[0] ? -1 : 1)).map(([line, amount]) => ({
1964
+ object: 'organization.costs.result',
1965
+ amount: { value: amount, currency: 'usd' },
1966
+ line_item: line,
1967
+ project_id: null,
1968
+ }));
1969
+ const buckets = results.length === 0 ? [] : [{ object: 'bucket', start_time: 0, end_time: nowEpoch(req.occurredAt), results }];
1970
+ return { status: 200, body: { object: 'page', data: buckets, has_more: false, next_page: null } };
1971
+ }
1972
+
1973
+ // ---- admin: organization projects (stateful) ----
1974
+ if (path === '/v1/organization/projects' && method === 'POST') return createProject(params, req);
1975
+ if (path === '/v1/organization/projects' && method === 'GET') {
1976
+ const includeArchived = new URLSearchParams(req.path.split('?')[1] ?? '').get('include_archived') === 'true';
1977
+ const items = rows('project', req.root).filter((r) => includeArchived || r.status !== 'archived').map(idView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
1978
+ return { status: 200, body: paginate(items, req.path) };
1979
+ }
1980
+ if (seg[1] === 'organization' && seg[2] === 'projects' && seg.length === 4 && method === 'GET') {
1981
+ const p = getRow('project', dec(seg[3]!), req.root);
1982
+ return p ? { status: 200, body: idView(p) } : notFound(`Project ${dec(seg[3]!)} not found`);
1983
+ }
1984
+ if (seg[1] === 'organization' && seg[2] === 'projects' && seg.length === 4 && method === 'POST') {
1985
+ const pid = dec(seg[3]!);
1986
+ if (!getRow('project', pid, req.root)) return notFound(`Project ${pid} not found`);
1987
+ const fields = pickUpdate(params, ['name']);
1988
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'project.update', subjectType: 'project', subjectId: pid, fields, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1989
+ return { status: 200, body: idView(resource) };
1990
+ }
1991
+ if (seg[1] === 'organization' && seg[2] === 'projects' && seg.length === 5 && seg[4] === 'archive' && method === 'POST') {
1992
+ const pid = dec(seg[3]!);
1993
+ if (!getRow('project', pid, req.root)) return notFound(`Project ${pid} not found`);
1994
+ const { resource } = await applyTwinWrite(SERVICE, { operation: 'project.archive', subjectType: 'project', subjectId: pid, fields: { status: 'archived', archived_at: nowEpoch(req.occurredAt) }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
1995
+ return { status: 200, body: idView(resource) };
1996
+ }
1997
+ // ---- admin: project API keys (stateful; synthetic twin keys) ----
1998
+ if (seg[1] === 'organization' && seg[2] === 'projects' && seg.length === 5 && seg[4] === 'api_keys' && method === 'POST') {
1999
+ const pid = dec(seg[3]!);
2000
+ if (!getRow('project', pid, req.root)) return notFound(`Project ${pid} not found`);
2001
+ return createProjectApiKey(pid, params, req);
2002
+ }
2003
+ if (seg[1] === 'organization' && seg[2] === 'projects' && seg.length === 5 && seg[4] === 'api_keys' && method === 'GET') {
2004
+ const pid = dec(seg[3]!);
2005
+ if (!getRow('project', pid, req.root)) return notFound(`Project ${pid} not found`);
2006
+ const items = rows('api_key', req.root).filter((r) => r.project_id === pid && !r._deleted).map(apiKeyView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
2007
+ return { status: 200, body: paginate(items, req.path) };
2008
+ }
2009
+ if (seg[1] === 'organization' && seg[2] === 'projects' && seg.length === 6 && seg[4] === 'api_keys' && method === 'GET') {
2010
+ const k = getRow('api_key', dec(seg[5]!), req.root);
2011
+ return k && k.project_id === dec(seg[3]!) && !k._deleted ? { status: 200, body: apiKeyView(k) } : notFound(`API key ${dec(seg[5]!)} not found`);
2012
+ }
2013
+ if (seg[1] === 'organization' && seg[2] === 'projects' && seg.length === 6 && seg[4] === 'api_keys' && method === 'DELETE') {
2014
+ const kid = dec(seg[5]!);
2015
+ const k = getRow('api_key', kid, req.root);
2016
+ if (!k || k.project_id !== dec(seg[3]!) || k._deleted) return notFound(`API key ${kid} not found`);
2017
+ await applyTwinWrite(SERVICE, { operation: 'api_key.delete', subjectType: 'api_key', subjectId: kid, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
2018
+ return { status: 200, body: { id: kid, object: 'organization.project.api_key.deleted', deleted: true } };
2019
+ }
2020
+
2021
+ // ---- evals (stateful: eval config → runs → output items) ----
2022
+ if (path === '/v1/evals' && method === 'POST') return createEval(params, req);
2023
+ if (path === '/v1/evals' && method === 'GET') {
2024
+ const items = rows('eval', req.root).map(evalView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
2025
+ return { status: 200, body: paginate(items, req.path) };
2026
+ }
2027
+ if (seg[1] === 'evals' && seg.length === 3 && method === 'GET') {
2028
+ const e = getRow('eval', dec(seg[2]!), req.root);
2029
+ return e ? { status: 200, body: evalView(e) } : notFound(`Eval ${dec(seg[2]!)} not found`);
2030
+ }
2031
+ if (seg[1] === 'evals' && seg.length === 4 && seg[3] === 'runs' && method === 'POST') {
2032
+ const evalId = dec(seg[2]!);
2033
+ if (!getRow('eval', evalId, req.root)) return notFound(`Eval ${evalId} not found`);
2034
+ return createEvalRun(evalId, params, req);
2035
+ }
2036
+ if (seg[1] === 'evals' && seg.length === 4 && seg[3] === 'runs' && method === 'GET') {
2037
+ const evalId = dec(seg[2]!);
2038
+ if (!getRow('eval', evalId, req.root)) return notFound(`Eval ${evalId} not found`);
2039
+ const items = rows('eval_run', req.root).filter((r) => r.eval_id === evalId).map(evalRunView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
2040
+ return { status: 200, body: paginate(items, req.path) };
2041
+ }
2042
+ if (seg[1] === 'evals' && seg.length === 5 && seg[3] === 'runs' && method === 'GET') {
2043
+ const run = getRow('eval_run', dec(seg[4]!), req.root);
2044
+ return run && run.eval_id === dec(seg[2]!) ? { status: 200, body: evalRunView(run) } : notFound(`Eval run ${dec(seg[4]!)} not found`);
2045
+ }
2046
+ if (seg[1] === 'evals' && seg.length === 6 && seg[3] === 'runs' && seg[5] === 'output_items' && method === 'GET') {
2047
+ const run = getRow('eval_run', dec(seg[4]!), req.root);
2048
+ if (!run || run.eval_id !== dec(seg[2]!)) return notFound(`Eval run ${dec(seg[4]!)} not found`);
2049
+ return { status: 200, body: paginate(evalRunOutputItems(run), req.path) };
2050
+ }
2051
+
2052
+ // ---- containers (code-interpreter sandboxes) → container files ----
2053
+ if (path === '/v1/containers' && method === 'POST') return createContainer(params, req);
2054
+ if (path === '/v1/containers' && method === 'GET') {
2055
+ const items = rows('container', req.root).filter((r) => !r._deleted).map(containerView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
2056
+ return { status: 200, body: paginate(items, req.path) };
2057
+ }
2058
+ if (seg[1] === 'containers' && seg.length === 3 && method === 'GET') {
2059
+ const c = getRow('container', dec(seg[2]!), req.root);
2060
+ return c && !c._deleted ? { status: 200, body: containerView(c) } : notFound(`Container ${dec(seg[2]!)} not found`);
2061
+ }
2062
+ if (seg[1] === 'containers' && seg.length === 3 && method === 'DELETE') {
2063
+ const cid = dec(seg[2]!);
2064
+ const c = getRow('container', cid, req.root);
2065
+ if (!c || c._deleted) return notFound(`Container ${cid} not found`);
2066
+ await applyTwinWrite(SERVICE, { operation: 'container.delete', subjectType: 'container', subjectId: cid, fields: { _deleted: true, status: 'deleted' }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
2067
+ return { status: 200, body: { id: cid, object: 'container.deleted', deleted: true } };
2068
+ }
2069
+ if (seg[1] === 'containers' && seg.length === 4 && seg[3] === 'files' && method === 'POST') {
2070
+ const cid = dec(seg[2]!);
2071
+ const c = getRow('container', cid, req.root);
2072
+ if (!c || c._deleted) return notFound(`Container ${cid} not found`);
2073
+ return createContainerFile(cid, params, req);
2074
+ }
2075
+ if (seg[1] === 'containers' && seg.length === 4 && seg[3] === 'files' && method === 'GET') {
2076
+ const cid = dec(seg[2]!);
2077
+ if (!getRow('container', cid, req.root)) return notFound(`Container ${cid} not found`);
2078
+ const items = rows('container_file', req.root).filter((r) => r.container_id === cid && !r._deleted).map(containerFileView).sort((a, b) => Number(b.created_at) - Number(a.created_at));
2079
+ return { status: 200, body: paginate(items, req.path) };
2080
+ }
2081
+ if (seg[1] === 'containers' && seg.length === 5 && seg[3] === 'files' && method === 'GET') {
2082
+ const cf = getRow('container_file', dec(seg[4]!), req.root);
2083
+ return cf && cf.container_id === dec(seg[2]!) && !cf._deleted ? { status: 200, body: containerFileView(cf) } : notFound(`Container file ${dec(seg[4]!)} not found`);
2084
+ }
2085
+ if (seg[1] === 'containers' && seg.length === 6 && seg[3] === 'files' && seg[5] === 'content' && method === 'GET') {
2086
+ const cf = getRow('container_file', dec(seg[4]!), req.root);
2087
+ if (!cf || cf.container_id !== dec(seg[2]!) || cf._deleted) return notFound(`Container file ${dec(seg[4]!)} not found`);
2088
+ return { status: 200, body: (cf._content as string) ?? '' };
2089
+ }
2090
+ if (seg[1] === 'containers' && seg.length === 5 && seg[3] === 'files' && method === 'DELETE') {
2091
+ const fid = dec(seg[4]!);
2092
+ const cf = getRow('container_file', fid, req.root);
2093
+ if (!cf || cf.container_id !== dec(seg[2]!) || cf._deleted) return notFound(`Container file ${fid} not found`);
2094
+ await applyTwinWrite(SERVICE, { operation: 'container_file.delete', subjectType: 'container_file', subjectId: fid, fields: { _deleted: true }, ...(req.occurredAt ? { occurredAt: req.occurredAt } : {}), actor: { kind: 'agent' } }, req.root);
2095
+ return { status: 200, body: { id: fid, object: 'container.file.deleted', deleted: true } };
2096
+ }
2097
+
2098
+ // Unknown route → vendor-faithful 404 (never a fabricated success — D2).
2099
+ return notFound(`Unknown request URL: ${method} ${path}. Please check the URL for typos.`);
2100
+ }