@nick3/copilot-api 1.5.6 → 1.5.9

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.
Files changed (31) hide show
  1. package/README.md +8 -1
  2. package/dist/{account-AacnHem5.js → account-CbYMFuS4.js} +12 -4
  3. package/dist/account-CbYMFuS4.js.map +1 -0
  4. package/dist/accounts-manager-BKG9aZEL.js +2899 -0
  5. package/dist/accounts-manager-BKG9aZEL.js.map +1 -0
  6. package/dist/admin/assets/index-BFN8rXmt.css +1 -0
  7. package/dist/admin/assets/index-HnEqzcKv.js +101 -0
  8. package/dist/admin/index.html +2 -2
  9. package/dist/{auth-B7x3wjry.js → auth-Ckj1wD43.js} +3 -3
  10. package/dist/{auth-B7x3wjry.js.map → auth-Ckj1wD43.js.map} +1 -1
  11. package/dist/{check-usage-B1cbDEOI.js → check-usage-bIbj_1Q_.js} +3 -3
  12. package/dist/check-usage-bIbj_1Q_.js.map +1 -0
  13. package/dist/{get-copilot-token-cha9rQwA.js → get-copilot-token-MAZsr5Vu.js} +2 -2
  14. package/dist/{get-copilot-token-cha9rQwA.js.map → get-copilot-token-MAZsr5Vu.js.map} +1 -1
  15. package/dist/main.js +3 -3
  16. package/dist/{poll-access-token-DFooFWhY.js → poll-access-token-DiwBJNtK.js} +58 -33
  17. package/dist/poll-access-token-DiwBJNtK.js.map +1 -0
  18. package/dist/{server-DVpkQrk2.js → server-DAxpfPde.js} +866 -894
  19. package/dist/server-DAxpfPde.js.map +1 -0
  20. package/dist/{start-fPbCDj4c.js → start-8dkfsQqd.js} +7 -6
  21. package/dist/start-8dkfsQqd.js.map +1 -0
  22. package/package.json +1 -1
  23. package/dist/account-AacnHem5.js.map +0 -1
  24. package/dist/accounts-manager-BE-Dq5Wn.js +0 -1494
  25. package/dist/accounts-manager-BE-Dq5Wn.js.map +0 -1
  26. package/dist/admin/assets/index-CdoHTemy.css +0 -1
  27. package/dist/admin/assets/index-wcoGQpIM.js +0 -66
  28. package/dist/check-usage-B1cbDEOI.js.map +0 -1
  29. package/dist/poll-access-token-DFooFWhY.js.map +0 -1
  30. package/dist/server-DVpkQrk2.js.map +0 -1
  31. package/dist/start-fPbCDj4c.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { A as resolveTraceId, C as normalizeDomain, D as accountFromState, E as prepareMessageProxyHeaders, O as state, T as prepareInteractionHeaders, _ as HTTPError, b as copilotHeaders, c as getRootSessionId, d as parseUserIdMetadata, f as sleep, g as getCopilotUsage, h as getDeviceCode, k as requestContext, l as getUUID, m as getGitHubUser, r as cacheModels, s as generateRequestIdFromPayload, t as pollAccessToken, u as isNullish, v as forwardError, w as prepareForCompact, y as copilotBaseUrl } from "./poll-access-token-DFooFWhY.js";
2
- import { a as getAccountClientIdentityByLoginAndApp, c as listAccountsFromRegistry, f as removeAccountFromRegistry, g as DEFAULT_IDENTITY_ENTERPRISE_DOMAIN, h as saveRegistry, m as saveAccountToken, p as removeAccountToken, r as addAccountToRegistry, t as isAccountType, u as loadRegistry, y as getCurrentIdentityEnvironment } from "./account-AacnHem5.js";
1
+ import { A as requestContext, D as prepareMessageProxyHeaders, E as prepareInteractionHeaders, O as accountFromState, T as prepareForCompact, _ as HTTPError, b as copilotHeaders, c as getUUID, d as parseUserIdMetadata, f as resolveAffinityKey, g as getCopilotUsage, h as getDeviceCode, j as resolveTraceId, k as state, l as isNullish, m as getGitHubUser, o as generateRequestIdFromPayload, p as sleep, s as getRootSessionId, t as pollAccessToken, u as normalizeStableSessionId, v as forwardError, w as normalizeDomain, y as copilotBaseUrl } from "./poll-access-token-DiwBJNtK.js";
2
+ import { _ as DEFAULT_IDENTITY_ENTERPRISE_DOMAIN, a as getAccountClientIdentityByLoginAndApp, b as getCurrentIdentityEnvironment, d as loadRegistry, g as saveRegistry, h as saveAccountToken, l as listAccountsFromRegistry, m as removeAccountToken, p as removeAccountFromRegistry, r as addAccountToRegistry, t as isAccountType } from "./account-CbYMFuS4.js";
3
3
  import { r as ensurePaths, t as PATHS } from "./paths-DGlr310R.js";
4
- import "./get-copilot-token-cha9rQwA.js";
5
- import { _ as isMessagesApiEnabled, a as getClaudeTokenMultiplier, b as mergeConfigWithDefaults, c as getModelAliases, d as getProviderConfig, f as getReasoningEffortForModel, g as isMessageStartInputTokensFallbackEnabled, h as isForceAgentEnabled, i as getAnthropicApiKey, l as getModelAliasesInfo, m as isAccountAffinityEnabled, n as PROVIDER_TYPE_ANTHROPIC, o as getConfig, p as getSmallModel, r as getAliasTargetSet, s as getExtraPromptForModel, t as accountsManager, u as getModelRefreshIntervalMs, v as isResponsesApiContextManagementModel, x as shouldCompactUseSmallModel, y as isResponsesApiWebSearchEnabled } from "./accounts-manager-BE-Dq5Wn.js";
4
+ import "./get-copilot-token-MAZsr5Vu.js";
5
+ import { A as isResponsesApiWebSearchEnabled, C as getReasoningEffortForModel, D as isMessageStartInputTokensFallbackEnabled, E as isForceAgentEnabled, M as resolveModelAlias, N as shouldCompactUseSmallModel, O as isMessagesApiEnabled, S as getProviderConfig, T as isAccountAffinityEnabled, _ as getExtraPromptForModel, a as getClientIpInfo, b as getModelAliasesInfo, c as normalizeChatCompletionsUsage, d as toLocalDateString, f as PROVIDER_TYPE_ANTHROPIC, g as getConfig, h as getClaudeTokenMultiplier, i as extractResponsesUsageFromStreamEvent, j as mergeConfigWithDefaults, k as isResponsesApiContextManagementModel, l as normalizeEmbeddingsUsage, m as getAnthropicApiKey, n as applySharedSessionAffinityRetention, o as getRequestHistoryStore, p as getAliasTargetSet, r as extractResponsesUsageFromResult, s as getStatsStore, t as accountsManager, u as normalizeMessagesUsage, v as getLogLevel, w as getSmallModel, x as getModelRefreshIntervalMs, y as getModelAliases } from "./accounts-manager-BKG9aZEL.js";
6
6
  import consola from "consola";
7
7
  import fs, { readFile } from "node:fs/promises";
8
8
  import { randomUUID, timingSafeEqual } from "node:crypto";
@@ -12,7 +12,6 @@ import { Hono } from "hono";
12
12
  import { cors } from "hono/cors";
13
13
  import { logger } from "hono/logger";
14
14
  import fs$1, { existsSync } from "node:fs";
15
- import { Database } from "bun:sqlite";
16
15
  import { fileURLToPath } from "node:url";
17
16
  import { streamSSE } from "hono/streaming";
18
17
  import util from "node:util";
@@ -121,583 +120,6 @@ const traceIdMiddleware = async (c, next) => {
121
120
  });
122
121
  };
123
122
 
124
- //#endregion
125
- //#region src/lib/admin-db.ts
126
- const DEFAULT_DB_PATH = path.join(PATHS.APP_DIR, "admin.sqlite");
127
- let sharedDb = null;
128
- let initialized = false;
129
- const INIT_WARN_THROTTLE_MS = 3e4;
130
- let lastInitWarnAtMs = 0;
131
- let suppressedInitWarnCount = 0;
132
- function warnAdminDbInitFailure(error) {
133
- const now = Date.now();
134
- if (now - lastInitWarnAtMs < INIT_WARN_THROTTLE_MS) {
135
- suppressedInitWarnCount++;
136
- return;
137
- }
138
- const suppressed = suppressedInitWarnCount;
139
- suppressedInitWarnCount = 0;
140
- lastInitWarnAtMs = now;
141
- const suffix = suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : "";
142
- consola.warn(`Failed to initialize admin DB; admin features disabled${suffix}`, error);
143
- }
144
- function getAdminDbPath() {
145
- return DEFAULT_DB_PATH;
146
- }
147
- function openAdminDb(filePath = DEFAULT_DB_PATH) {
148
- return new Database(filePath);
149
- }
150
- function initAdminDb(db) {
151
- db.run("PRAGMA journal_mode = WAL;");
152
- db.run("PRAGMA synchronous = NORMAL;");
153
- db.run("PRAGMA busy_timeout = 3000;");
154
- db.run("PRAGMA foreign_keys = ON;");
155
- migrateAdminDb(db);
156
- }
157
- function getAdminDb() {
158
- if (!sharedDb) sharedDb = openAdminDb();
159
- if (!initialized) try {
160
- initAdminDb(sharedDb);
161
- initialized = true;
162
- } catch (error) {
163
- warnAdminDbInitFailure(error);
164
- }
165
- return sharedDb;
166
- }
167
- function getAdminDbUserVersion(db = getAdminDb()) {
168
- try {
169
- return db.query("PRAGMA user_version;").get()?.user_version ?? 0;
170
- } catch {
171
- return 0;
172
- }
173
- }
174
- function migrateV1(db) {
175
- db.run(`
176
- CREATE TABLE IF NOT EXISTS request_log (
177
- id INTEGER PRIMARY KEY AUTOINCREMENT,
178
- request_id TEXT NOT NULL UNIQUE,
179
-
180
- started_at_ms INTEGER NOT NULL,
181
- finished_at_ms INTEGER,
182
- duration_ms INTEGER,
183
- ttfb_ms INTEGER,
184
-
185
- method TEXT NOT NULL,
186
- path TEXT NOT NULL,
187
- upstream_endpoint TEXT,
188
- stream INTEGER NOT NULL DEFAULT 0,
189
-
190
- account_id TEXT,
191
- account_type TEXT,
192
- cost_units REAL,
193
- client_model TEXT,
194
- upstream_model TEXT,
195
-
196
- client_ip TEXT,
197
- client_ip_source TEXT,
198
- user_agent TEXT,
199
-
200
- tokens_input INTEGER,
201
- tokens_output INTEGER,
202
- tokens_total INTEGER,
203
- tokens_cached_input INTEGER,
204
- usage_json TEXT,
205
-
206
- premium_remaining_before REAL,
207
- premium_remaining_after REAL,
208
- premium_remaining_diff REAL,
209
- premium_unlimited_before INTEGER,
210
- premium_unlimited_after INTEGER,
211
-
212
- http_status INTEGER,
213
- error_name TEXT,
214
- error_status INTEGER,
215
- error_message TEXT,
216
- selection_failure_reason TEXT
217
- );
218
-
219
- CREATE INDEX IF NOT EXISTS idx_request_log_started_at
220
- ON request_log(started_at_ms DESC);
221
- CREATE INDEX IF NOT EXISTS idx_request_log_account_started_at
222
- ON request_log(account_id, started_at_ms DESC);
223
- CREATE INDEX IF NOT EXISTS idx_request_log_model_started_at
224
- ON request_log(upstream_model, started_at_ms DESC);
225
- CREATE INDEX IF NOT EXISTS idx_request_log_endpoint_started_at
226
- ON request_log(upstream_endpoint, started_at_ms DESC);
227
- CREATE INDEX IF NOT EXISTS idx_request_log_status_started_at
228
- ON request_log(http_status, started_at_ms DESC);
229
-
230
- PRAGMA user_version = 1;
231
- `);
232
- }
233
- function migrateAdminDb(db) {
234
- const current = db.query("PRAGMA user_version;").get()?.user_version ?? 0;
235
- if (current >= 6) return;
236
- if (current < 1) migrateV1(db);
237
- if (current < 2) db.run(`
238
- ALTER TABLE request_log ADD COLUMN user_id TEXT;
239
- ALTER TABLE request_log ADD COLUMN safety_identifier TEXT;
240
- ALTER TABLE request_log ADD COLUMN prompt_cache_key TEXT;
241
- ALTER TABLE request_log ADD COLUMN initiator TEXT;
242
- ALTER TABLE request_log ADD COLUMN upstream_request_id TEXT;
243
-
244
- PRAGMA user_version = 2;
245
- `);
246
- if (current < 3) db.run(`
247
- CREATE INDEX IF NOT EXISTS idx_request_log_session_finished
248
- ON request_log(
249
- prompt_cache_key,
250
- safety_identifier,
251
- finished_at_ms DESC
252
- )
253
- WHERE finished_at_ms IS NOT NULL
254
- AND tokens_input IS NOT NULL;
255
-
256
- PRAGMA user_version = 3;
257
- `);
258
- if (current < 4) db.run(`
259
- CREATE INDEX IF NOT EXISTS idx_request_log_session_finished_by_client_model
260
- ON request_log(
261
- prompt_cache_key,
262
- safety_identifier,
263
- client_model,
264
- finished_at_ms DESC
265
- )
266
- WHERE finished_at_ms IS NOT NULL
267
- AND tokens_input IS NOT NULL;
268
-
269
- PRAGMA user_version = 4;
270
- `);
271
- if (current < 5) db.run(`
272
- ALTER TABLE request_log ADD COLUMN affinity_hit INTEGER;
273
- ALTER TABLE request_log ADD COLUMN affinity_cache_key TEXT;
274
-
275
- PRAGMA user_version = 5;
276
- `);
277
- if (current < 6) {
278
- if (!db.query("PRAGMA table_info(request_log);").all().some((row) => row.name === "is_subagent")) db.run("ALTER TABLE request_log ADD COLUMN is_subagent INTEGER;");
279
- db.run("PRAGMA user_version = 6;");
280
- }
281
- }
282
-
283
- //#endregion
284
- //#region src/lib/request-history.ts
285
- const DEFAULT_RETENTION_DAYS = 14;
286
- const DEFAULT_MAX_ROWS = 2e5;
287
- const INSERT_WARN_THROTTLE_MS = 3e4;
288
- let lastInsertWarnAtMs = 0;
289
- let suppressedInsertWarnCount = 0;
290
- function warnInsertFailure(error) {
291
- const now = Date.now();
292
- if (now - lastInsertWarnAtMs < INSERT_WARN_THROTTLE_MS) {
293
- suppressedInsertWarnCount++;
294
- return;
295
- }
296
- const suppressed = suppressedInsertWarnCount;
297
- suppressedInsertWarnCount = 0;
298
- lastInsertWarnAtMs = now;
299
- const suffix = suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : "";
300
- consola.warn(`Failed to insert request log${suffix}`, error);
301
- }
302
- function toDbNull(value) {
303
- return value === void 0 ? null : value;
304
- }
305
- function toDbBool(value) {
306
- if (value === true) return 1;
307
- if (value === false) return 0;
308
- return null;
309
- }
310
- function getClientIpInfo(c) {
311
- const cf = c.req.header("cf-connecting-ip");
312
- if (cf) return {
313
- ip: cf.trim(),
314
- source: "cf-connecting-ip"
315
- };
316
- const xff = c.req.header("x-forwarded-for");
317
- if (xff) {
318
- const first = xff.split(",")[0]?.trim();
319
- if (first) return {
320
- ip: first,
321
- source: "x-forwarded-for"
322
- };
323
- }
324
- const xri = c.req.header("x-real-ip");
325
- if (xri) return {
326
- ip: xri.trim(),
327
- source: "x-real-ip"
328
- };
329
- return {};
330
- }
331
- function normalizeChatCompletionsUsage(usage) {
332
- if (!usage) return {};
333
- const cached = usage.prompt_tokens_details?.cached_tokens ?? 0;
334
- const prompt = usage.prompt_tokens;
335
- const completion = usage.completion_tokens;
336
- const total = usage.total_tokens;
337
- return {
338
- tokensCachedInput: cached,
339
- tokensInput: Math.max(0, prompt - cached),
340
- tokensOutput: completion,
341
- tokensTotal: total,
342
- usageJson: JSON.stringify(usage)
343
- };
344
- }
345
- function normalizeResponsesUsage(usage) {
346
- if (!usage) return {};
347
- const cached = usage.input_tokens_details?.cached_tokens ?? 0;
348
- const input = usage.input_tokens;
349
- const output = usage.output_tokens ?? 0;
350
- const total = usage.total_tokens;
351
- return {
352
- tokensCachedInput: cached,
353
- tokensInput: Math.max(0, input - cached),
354
- tokensOutput: output,
355
- tokensTotal: total,
356
- usageJson: JSON.stringify(usage)
357
- };
358
- }
359
- function normalizeMessagesUsage(usage) {
360
- if (!usage) return {};
361
- const cached = usage.cache_read_input_tokens ?? 0;
362
- const input = usage.input_tokens;
363
- const output = usage.output_tokens;
364
- const hasInput = typeof input === "number";
365
- const hasOutput = typeof output === "number";
366
- return {
367
- tokensCachedInput: cached,
368
- tokensInput: hasInput ? Math.max(0, input - cached) : void 0,
369
- tokensOutput: hasOutput ? output : void 0,
370
- tokensTotal: hasInput || hasOutput ? (input ?? 0) + (output ?? 0) : void 0,
371
- usageJson: JSON.stringify(usage)
372
- };
373
- }
374
- function normalizeEmbeddingsUsage(usage) {
375
- if (!usage) return {};
376
- return {
377
- tokensCachedInput: 0,
378
- tokensInput: usage.prompt_tokens,
379
- tokensOutput: 0,
380
- tokensTotal: usage.total_tokens,
381
- usageJson: JSON.stringify(usage)
382
- };
383
- }
384
- var RequestHistoryStore = class {
385
- db;
386
- insertStmt;
387
- getByRequestIdStmt;
388
- getLastCompletedUsageBySessionStmt;
389
- constructor(db) {
390
- this.db = db;
391
- this.insertStmt = db.query(`
392
- INSERT INTO request_log (
393
- request_id,
394
- started_at_ms,
395
- finished_at_ms,
396
- duration_ms,
397
- ttfb_ms,
398
- method,
399
- path,
400
- upstream_endpoint,
401
- stream,
402
- account_id,
403
- account_type,
404
- cost_units,
405
- client_model,
406
- upstream_model,
407
- client_ip,
408
- client_ip_source,
409
- user_agent,
410
- user_id,
411
- safety_identifier,
412
- prompt_cache_key,
413
- initiator,
414
- is_subagent,
415
- upstream_request_id,
416
- tokens_input,
417
- tokens_output,
418
- tokens_total,
419
- tokens_cached_input,
420
- usage_json,
421
- premium_remaining_before,
422
- premium_remaining_after,
423
- premium_remaining_diff,
424
- premium_unlimited_before,
425
- premium_unlimited_after,
426
- http_status,
427
- error_name,
428
- error_status,
429
- error_message,
430
- selection_failure_reason,
431
- affinity_hit,
432
- affinity_cache_key
433
- ) VALUES (
434
- ?,?,?,?,?,?,?,?,
435
- ?,?,?,?,?,?,?,?,
436
- ?,?,?,?,?,?,?,?,
437
- ?,?,?,?,?,?,?,?,
438
- ?,?,?,?,?,?,?,?
439
- );
440
- `);
441
- this.getByRequestIdStmt = db.query("SELECT * FROM request_log WHERE request_id = ? LIMIT 1;");
442
- this.getLastCompletedUsageBySessionStmt = db.query(`
443
- SELECT
444
- tokens_input,
445
- tokens_output,
446
- tokens_total,
447
- tokens_cached_input
448
- FROM request_log
449
- WHERE prompt_cache_key = ?
450
- AND safety_identifier = ?
451
- AND client_model = ?
452
- AND finished_at_ms IS NOT NULL
453
- AND tokens_input IS NOT NULL
454
- ORDER BY finished_at_ms DESC
455
- LIMIT 1;
456
- `);
457
- }
458
- insert(record) {
459
- try {
460
- const args = [
461
- record.requestId,
462
- record.startedAtMs,
463
- toDbNull(record.finishedAtMs),
464
- toDbNull(record.durationMs),
465
- toDbNull(record.ttfbMs),
466
- record.method,
467
- record.path,
468
- toDbNull(record.upstreamEndpoint),
469
- record.stream ? 1 : 0,
470
- toDbNull(record.accountId),
471
- toDbNull(record.accountType),
472
- toDbNull(record.costUnits),
473
- toDbNull(record.clientModel),
474
- toDbNull(record.upstreamModel),
475
- toDbNull(record.clientIp),
476
- toDbNull(record.clientIpSource),
477
- toDbNull(record.userAgent),
478
- toDbNull(record.userId),
479
- toDbNull(record.safetyIdentifier),
480
- toDbNull(record.promptCacheKey),
481
- toDbNull(record.initiator),
482
- toDbBool(record.isSubagent),
483
- toDbNull(record.upstreamRequestId),
484
- toDbNull(record.tokensInput),
485
- toDbNull(record.tokensOutput),
486
- toDbNull(record.tokensTotal),
487
- toDbNull(record.tokensCachedInput),
488
- toDbNull(record.usageJson),
489
- toDbNull(record.premiumRemainingBefore),
490
- toDbNull(record.premiumRemainingAfter),
491
- toDbNull(record.premiumRemainingDiff),
492
- toDbBool(record.premiumUnlimitedBefore),
493
- toDbBool(record.premiumUnlimitedAfter),
494
- toDbNull(record.httpStatus),
495
- toDbNull(record.errorName),
496
- toDbNull(record.errorStatus),
497
- toDbNull(record.errorMessage),
498
- toDbNull(record.selectionFailureReason),
499
- toDbBool(record.affinityHit),
500
- toDbNull(record.affinityCacheKey)
501
- ];
502
- this.insertStmt.run(...args);
503
- } catch (error) {
504
- warnInsertFailure(error);
505
- }
506
- }
507
- getByRequestId(requestId) {
508
- try {
509
- return this.getByRequestIdStmt.get(requestId) ?? null;
510
- } catch (error) {
511
- consola.debug("Failed to fetch request log by request_id", error);
512
- return null;
513
- }
514
- }
515
- getLastCompletedUsageBySession(session) {
516
- if (!session.promptCacheKey || !session.safetyIdentifier || !session.clientModel) return null;
517
- try {
518
- const row = this.getLastCompletedUsageBySessionStmt.get(session.promptCacheKey, session.safetyIdentifier, session.clientModel);
519
- if (!row || row.tokens_input === null) return null;
520
- const tokensOutput = row.tokens_output === null ? void 0 : row.tokens_output;
521
- const tokensTotal = row.tokens_total === null ? void 0 : row.tokens_total;
522
- const tokensCachedInput = row.tokens_cached_input === null ? void 0 : row.tokens_cached_input;
523
- return {
524
- tokensInput: row.tokens_input,
525
- tokensOutput,
526
- tokensTotal,
527
- tokensCachedInput
528
- };
529
- } catch (error) {
530
- consola.debug("Failed to fetch last completed usage by session", error);
531
- return null;
532
- }
533
- }
534
- query(params) {
535
- const limit = Math.max(1, Math.min(params.limit, 200));
536
- const where = [];
537
- const values = [];
538
- if (params.cursorId !== void 0) {
539
- where.push("id < ?");
540
- values.push(params.cursorId);
541
- }
542
- if (params.accountId) {
543
- where.push("account_id = ?");
544
- values.push(params.accountId);
545
- }
546
- if (params.upstreamModel) {
547
- where.push("upstream_model = ?");
548
- values.push(params.upstreamModel);
549
- }
550
- if (params.clientModel) {
551
- where.push("client_model = ?");
552
- values.push(params.clientModel);
553
- }
554
- if (params.upstreamEndpoint) {
555
- where.push("upstream_endpoint = ?");
556
- values.push(params.upstreamEndpoint);
557
- }
558
- if (params.path) {
559
- where.push("path = ?");
560
- values.push(params.path);
561
- }
562
- if (params.status !== void 0) {
563
- where.push("http_status = ?");
564
- values.push(params.status);
565
- }
566
- if (params.hasError === true) where.push("http_status >= 400");
567
- if (params.hasError === false) where.push("http_status < 400");
568
- if (params.fromMs !== void 0) {
569
- where.push("started_at_ms >= ?");
570
- values.push(params.fromMs);
571
- }
572
- if (params.toMs !== void 0) {
573
- where.push("started_at_ms <= ?");
574
- values.push(params.toMs);
575
- }
576
- const sql = `
577
- SELECT *
578
- FROM request_log
579
- ${where.length > 0 ? `WHERE ${where.join(" AND ")}` : ""}
580
- ORDER BY id DESC
581
- LIMIT ?;
582
- `;
583
- const rows = this.db.query(sql).all(...values, limit + 1);
584
- const items = rows.slice(0, limit);
585
- const hasMore = rows.length > limit;
586
- return {
587
- items,
588
- nextCursorId: hasMore ? items.at(-1)?.id : void 0,
589
- hasMore
590
- };
591
- }
592
- getAccountStatsSince(sinceMs) {
593
- try {
594
- const rows = this.db.query(`
595
- SELECT
596
- account_id,
597
- COUNT(*) AS request_count,
598
- SUM(CASE WHEN http_status >= 400 THEN 1 ELSE 0 END) AS error_count,
599
- COALESCE(SUM(tokens_total), 0) AS tokens_total,
600
- COALESCE(AVG(duration_ms), 0) AS avg_duration_ms,
601
- COALESCE(MAX(started_at_ms), 0) AS last_request_at_ms
602
- FROM request_log
603
- WHERE started_at_ms >= ? AND account_id IS NOT NULL
604
- GROUP BY account_id;
605
- `).all(sinceMs);
606
- const map = {};
607
- for (const row of rows) map[row.account_id] = row;
608
- return map;
609
- } catch (error) {
610
- consola.debug("Failed to fetch account stats", error);
611
- return {};
612
- }
613
- }
614
- cleanupRetention(retentionDays = DEFAULT_RETENTION_DAYS, maxRows = DEFAULT_MAX_ROWS) {
615
- try {
616
- const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
617
- this.db.query("DELETE FROM request_log WHERE started_at_ms < ?;").run(cutoffMs);
618
- if (this.db.query("SELECT COUNT(*) AS c FROM request_log;").get().c <= maxRows) return;
619
- const offset = maxRows - 1;
620
- const thresholdId = this.db.query("SELECT id FROM request_log ORDER BY id DESC LIMIT 1 OFFSET ?;").get(offset)?.id;
621
- if (!thresholdId) return;
622
- this.db.query("DELETE FROM request_log WHERE id < ?;").run(thresholdId);
623
- } catch (error) {
624
- consola.debug("Failed to cleanup request_log retention", error);
625
- }
626
- }
627
- meta() {
628
- return {
629
- dbPath: getAdminDbPath(),
630
- userVersion: getAdminDbUserVersion(this.db),
631
- retentionDays: DEFAULT_RETENTION_DAYS,
632
- maxRows: DEFAULT_MAX_ROWS
633
- };
634
- }
635
- };
636
- const STORE_INIT_WARN_THROTTLE_MS = 3e4;
637
- const STORE_INIT_RETRY_DELAY_MS = 3e4;
638
- let lastStoreInitWarnAtMs = 0;
639
- let suppressedStoreInitWarnCount = 0;
640
- let nextStoreRetryAtMs = 0;
641
- function warnStoreInitFailure(error) {
642
- const now = Date.now();
643
- if (now - lastStoreInitWarnAtMs < STORE_INIT_WARN_THROTTLE_MS) {
644
- suppressedStoreInitWarnCount++;
645
- return;
646
- }
647
- const suppressed = suppressedStoreInitWarnCount;
648
- suppressedStoreInitWarnCount = 0;
649
- lastStoreInitWarnAtMs = now;
650
- const suffix = suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : "";
651
- consola.warn(`Request history store is disabled${suffix}`, error);
652
- }
653
- const disabledStore = {
654
- insert: () => {},
655
- getByRequestId: () => null,
656
- getLastCompletedUsageBySession: () => null,
657
- query: () => ({
658
- items: [],
659
- hasMore: false
660
- }),
661
- getAccountStatsSince: () => ({}),
662
- cleanupRetention: () => {},
663
- meta: () => ({
664
- dbPath: getAdminDbPath(),
665
- userVersion: 0,
666
- retentionDays: DEFAULT_RETENTION_DAYS,
667
- maxRows: DEFAULT_MAX_ROWS
668
- })
669
- };
670
- let sharedStore = null;
671
- let maintenanceStarted = false;
672
- function getRequestHistoryStore() {
673
- if (sharedStore) return sharedStore;
674
- const now = Date.now();
675
- if (now < nextStoreRetryAtMs) return disabledStore;
676
- try {
677
- sharedStore = new RequestHistoryStore(getAdminDb());
678
- if (!maintenanceStarted) {
679
- maintenanceStarted = true;
680
- sharedStore.cleanupRetention();
681
- setInterval(() => {
682
- sharedStore?.cleanupRetention();
683
- }, 1440 * 60 * 1e3);
684
- }
685
- return sharedStore;
686
- } catch (error) {
687
- nextStoreRetryAtMs = now + STORE_INIT_RETRY_DELAY_MS;
688
- warnStoreInitFailure(error);
689
- return disabledStore;
690
- }
691
- }
692
- function extractResponsesUsageFromStreamEvent(event) {
693
- if (event.type === "response.completed" || event.type === "response.incomplete") return normalizeResponsesUsage(event.response.usage);
694
- if (event.type === "response.failed") return normalizeResponsesUsage(event.response.usage);
695
- return {};
696
- }
697
- function extractResponsesUsageFromResult(result) {
698
- return normalizeResponsesUsage(result.usage);
699
- }
700
-
701
123
  //#endregion
702
124
  //#region src/routes/admin-api/auth-sessions.ts
703
125
  function buildOauthUrls(enterpriseDomain) {
@@ -917,6 +339,7 @@ const CONFIG_KEYS = new Set([
917
339
  "auth",
918
340
  "extraPrompts",
919
341
  "smallModel",
342
+ "logLevel",
920
343
  "accountAffinity",
921
344
  "apiKey",
922
345
  "anthropicApiKey",
@@ -930,6 +353,7 @@ const CONFIG_KEYS = new Set([
930
353
  "compactUseSmallModel",
931
354
  "messageStartInputTokensFallback",
932
355
  "modelRefreshIntervalHours",
356
+ "sessionAffinityRetentionDays",
933
357
  "useMessagesApi",
934
358
  "useResponsesApiWebSearch"
935
359
  ]);
@@ -941,6 +365,12 @@ const REASONING_EFFORTS = new Set([
941
365
  "high",
942
366
  "xhigh"
943
367
  ]);
368
+ const LOG_LEVELS = new Set([
369
+ "error",
370
+ "warn",
371
+ "info",
372
+ "debug"
373
+ ]);
944
374
  const BLOCKED_KEYS = new Set([
945
375
  "__proto__",
946
376
  "constructor",
@@ -959,6 +389,13 @@ function parseOptionalString(value, field) {
959
389
  if (!trimmed) return { clear: true };
960
390
  return { value: trimmed };
961
391
  }
392
+ function parseOptionalLogLevel(value) {
393
+ const parsed = parseOptionalString(value, "logLevel");
394
+ if ("error" in parsed) return parsed;
395
+ if ("clear" in parsed) return parsed;
396
+ if (!LOG_LEVELS.has(parsed.value)) return { error: `logLevel must be one of: ${[...LOG_LEVELS].join(", ")}` };
397
+ return { value: parsed.value };
398
+ }
962
399
  function parseOptionalBoolean(value, field) {
963
400
  if (value === null || value === void 0) return { clear: true };
964
401
  if (typeof value !== "boolean") return { error: `${field} must be a boolean` };
@@ -1238,6 +675,15 @@ function applyAuthConfig(next, value) {
1238
675
  }
1239
676
  next.auth = parsed.value;
1240
677
  }
678
+ function applyLogLevel(next, value) {
679
+ const parsed = parseOptionalLogLevel(value);
680
+ if ("error" in parsed) return parsed.error;
681
+ if ("clear" in parsed) {
682
+ next.logLevel = void 0;
683
+ return;
684
+ }
685
+ next.logLevel = parsed.value;
686
+ }
1241
687
  function applyOptionalBoolean(next, key, value) {
1242
688
  const parsed = parseOptionalBoolean(value, key);
1243
689
  if ("error" in parsed) return parsed.error;
@@ -1305,6 +751,7 @@ const CONFIG_PATCH_HANDLERS = {
1305
751
  auth: applyAuthConfig,
1306
752
  extraPrompts: applyExtraPrompts,
1307
753
  smallModel: (next, value) => applyOptionalString(next, "smallModel", value),
754
+ logLevel: applyLogLevel,
1308
755
  accountAffinity: (next, value) => applyOptionalBoolean(next, "accountAffinity", value),
1309
756
  apiKey: (next, value) => applyOptionalString(next, "apiKey", value),
1310
757
  anthropicApiKey: (next, value) => applyOptionalString(next, "anthropicApiKey", value),
@@ -1318,6 +765,7 @@ const CONFIG_PATCH_HANDLERS = {
1318
765
  compactUseSmallModel: (next, value) => applyOptionalBoolean(next, "compactUseSmallModel", value),
1319
766
  messageStartInputTokensFallback: (next, value) => applyOptionalBoolean(next, "messageStartInputTokensFallback", value),
1320
767
  modelRefreshIntervalHours: (next, value) => applyOptionalNumber(next, "modelRefreshIntervalHours", value),
768
+ sessionAffinityRetentionDays: (next, value) => applyOptionalNumber(next, "sessionAffinityRetentionDays", value),
1321
769
  useMessagesApi: (next, value) => applyOptionalBoolean(next, "useMessagesApi", value),
1322
770
  useResponsesApiWebSearch: (next, value) => applyOptionalBoolean(next, "useResponsesApiWebSearch", value)
1323
771
  };
@@ -1400,6 +848,7 @@ adminApiRoutes.post("/config", async (c) => {
1400
848
  const merged = mergeConfigWithDefaults();
1401
849
  accountsManager.setAccountAffinityEnabled(isAccountAffinityEnabled());
1402
850
  accountsManager.setModelsRefreshIntervalMs(getModelRefreshIntervalMs());
851
+ applySharedSessionAffinityRetention();
1403
852
  return c.json({
1404
853
  ...merged,
1405
854
  _configPath: PATHS.CONFIG_PATH
@@ -1548,7 +997,8 @@ adminApiRoutes.get("/accounts", async (c) => {
1548
997
  remaining: s.remaining,
1549
998
  unlimited: s.unlimited,
1550
999
  failed: s.failed,
1551
- failureReason: s.failureReason
1000
+ failureReason: s.failureReason,
1001
+ enabled: s.enabled
1552
1002
  },
1553
1003
  stats
1554
1004
  };
@@ -1608,11 +1058,7 @@ adminApiRoutes.post("/accounts/auth/start", async (c) => {
1608
1058
  });
1609
1059
  const enterpriseDomainRaw = payload.enterpriseDomain;
1610
1060
  let enterpriseDomain;
1611
- if (accountType === "enterprise" && typeof enterpriseDomainRaw === "string") enterpriseDomain = enterpriseDomainRaw.trim();
1612
- if (accountType === "enterprise" && !enterpriseDomain) return jsonError(c, 400, {
1613
- message: "enterpriseDomain is required for enterprise accounts.",
1614
- type: "bad_request"
1615
- });
1061
+ if (accountType === "enterprise" && typeof enterpriseDomainRaw === "string") enterpriseDomain = enterpriseDomainRaw.trim() || void 0;
1616
1062
  try {
1617
1063
  const result = await authSessionManager.startAuth({
1618
1064
  accountType,
@@ -1643,6 +1089,40 @@ adminApiRoutes.post("/accounts/auth/cancel/:sessionId", (c) => {
1643
1089
  });
1644
1090
  return c.json({ cancelled: true });
1645
1091
  });
1092
+ adminApiRoutes.patch("/accounts/:id", async (c) => {
1093
+ const accountId = c.req.param("id");
1094
+ let payload;
1095
+ try {
1096
+ payload = await c.req.json();
1097
+ } catch {
1098
+ return jsonError(c, 400, {
1099
+ message: "Invalid JSON body.",
1100
+ type: "bad_request"
1101
+ });
1102
+ }
1103
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload) || typeof payload.enabled !== "boolean") return jsonError(c, 400, {
1104
+ message: "Request body must contain { enabled: boolean }.",
1105
+ type: "bad_request"
1106
+ });
1107
+ const enabled = payload.enabled;
1108
+ try {
1109
+ const registry = await loadRegistry();
1110
+ const entry = registry.accounts.find((a) => a.id === accountId);
1111
+ if (!entry) return jsonError(c, 404, {
1112
+ message: "Account not found.",
1113
+ type: "not_found"
1114
+ });
1115
+ entry.enabled = enabled;
1116
+ await saveRegistry(registry);
1117
+ await accountsManager.reloadRegistryNow();
1118
+ return c.json({ success: true });
1119
+ } catch (error) {
1120
+ return jsonError(c, 500, {
1121
+ message: `Failed to update account: ${error instanceof Error ? error.message : String(error)}`,
1122
+ type: "internal_error"
1123
+ });
1124
+ }
1125
+ });
1646
1126
  adminApiRoutes.delete("/accounts/:id", async (c) => {
1647
1127
  const accountId = c.req.param("id");
1648
1128
  try {
@@ -1656,6 +1136,7 @@ adminApiRoutes.delete("/accounts/:id", async (c) => {
1656
1136
  } catch (error) {
1657
1137
  console.error(`Account ${accountId} deleted but token cleanup failed.`, error);
1658
1138
  }
1139
+ await accountsManager.reloadRegistryNow();
1659
1140
  return c.json({
1660
1141
  deleted: true,
1661
1142
  accountId
@@ -1679,10 +1160,6 @@ adminApiRoutes.post("/accounts/:id/reauth", async (c) => {
1679
1160
  const resolvedEnterpriseDomain = (await getAccountClientIdentityByLoginAndApp(accountId, oauthApp))?.enterpriseDomain;
1680
1161
  let enterpriseDomain;
1681
1162
  if (resolvedEnterpriseDomain && resolvedEnterpriseDomain !== DEFAULT_IDENTITY_ENTERPRISE_DOMAIN) enterpriseDomain = resolvedEnterpriseDomain;
1682
- if (account.accountType === "enterprise" && !enterpriseDomain) return jsonError(c, 400, {
1683
- message: "Cannot re-authenticate enterprise account: enterprise domain could not be resolved from stored identity.",
1684
- type: "bad_request"
1685
- });
1686
1163
  const result = await authSessionManager.startAuth({
1687
1164
  accountType: account.accountType,
1688
1165
  enterpriseDomain,
@@ -1696,6 +1173,70 @@ adminApiRoutes.post("/accounts/:id/reauth", async (c) => {
1696
1173
  });
1697
1174
  }
1698
1175
  });
1176
+ adminApiRoutes.get("/stats/premium-daily", (c) => {
1177
+ const p = new URL(c.req.url, "http://local").searchParams;
1178
+ const from = p.get("from") || void 0;
1179
+ const to = p.get("to") || void 0;
1180
+ const accountId = p.get("account_id") || void 0;
1181
+ const granularity = p.get("granularity") === "hour" ? "hour" : "day";
1182
+ const now = /* @__PURE__ */ new Date();
1183
+ const todayStr = toLocalDateString(now.getTime());
1184
+ const resolvedFrom = from || toLocalDateString(now.getTime() - 10080 * 60 * 1e3);
1185
+ const resolvedTo = to || todayStr;
1186
+ function parseLocalDate(dateStr) {
1187
+ const [y, m, d] = dateStr.split("-").map(Number);
1188
+ return new Date(y, m - 1, d);
1189
+ }
1190
+ if (granularity === "hour") {
1191
+ const fromDate = parseLocalDate(resolvedFrom);
1192
+ if (parseLocalDate(resolvedTo).getTime() - fromDate.getTime() + 1440 * 60 * 1e3 > 2880 * 60 * 1e3) return jsonError(c, 400, {
1193
+ message: "Hourly granularity is only supported for ranges up to 48 hours.",
1194
+ type: "bad_request"
1195
+ });
1196
+ }
1197
+ const statsStore = getStatsStore();
1198
+ if (!statsStore) return c.json({
1199
+ daily: [],
1200
+ by_account: [],
1201
+ range: {
1202
+ from: resolvedFrom,
1203
+ to: resolvedTo,
1204
+ granularity
1205
+ }
1206
+ });
1207
+ if (granularity === "hour") {
1208
+ const fromMs = parseLocalDate(resolvedFrom).getTime();
1209
+ const toMs = parseLocalDate(resolvedTo).getTime() + 86399999;
1210
+ const result$1 = statsStore.getHourlyPremiumStats({
1211
+ fromMs,
1212
+ toMs,
1213
+ accountId
1214
+ });
1215
+ return c.json({
1216
+ daily: result$1.daily,
1217
+ by_account: result$1.byAccount,
1218
+ range: {
1219
+ from: resolvedFrom,
1220
+ to: resolvedTo,
1221
+ granularity
1222
+ }
1223
+ });
1224
+ }
1225
+ const result = statsStore.getDailyPremiumStats({
1226
+ from: resolvedFrom,
1227
+ to: resolvedTo,
1228
+ accountId
1229
+ });
1230
+ return c.json({
1231
+ daily: result.daily,
1232
+ by_account: result.byAccount,
1233
+ range: {
1234
+ from: resolvedFrom,
1235
+ to: resolvedTo,
1236
+ granularity
1237
+ }
1238
+ });
1239
+ });
1699
1240
 
1700
1241
  //#endregion
1701
1242
  //#region src/routes/admin/route.ts
@@ -2365,39 +1906,122 @@ function toAccountContext(account) {
2365
1906
  clientSessionId: account.clientSessionId
2366
1907
  };
2367
1908
  }
1909
+ const OWNERSHIP_MISMATCH_PATTERN = /does not belong to this connection/i;
1910
+ const UUID_PATTERN = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi;
1911
+ const OPAQUE_ID_PATTERN = /\b(?:msg|resp|chatcmpl|out|toolu|call|item)_[\w-]+\b/g;
1912
+ const UPSTREAM_ERROR_MAX_LENGTH = 1e3;
1913
+ const UPSTREAM_ERROR_READ_FAILED_SENTINEL = "[upstream body read failed]";
1914
+ function sanitizeUpstreamErrorMessage(value) {
1915
+ return truncate(value.trim().replaceAll(UUID_PATTERN, "<uuid>").replaceAll(OPAQUE_ID_PATTERN, "<opaque_id>"), UPSTREAM_ERROR_MAX_LENGTH);
1916
+ }
1917
+ function readErrorField(value) {
1918
+ return typeof value === "string" ? value : void 0;
1919
+ }
1920
+ function formatUpstreamErrorBody(payload) {
1921
+ if (typeof payload === "string") {
1922
+ const text = payload.trim();
1923
+ return text.length > 0 ? text : void 0;
1924
+ }
1925
+ if (!payload || typeof payload !== "object") return;
1926
+ const root = payload;
1927
+ const message = readErrorField(root.error?.message) ?? readErrorField(root.message);
1928
+ const code = readErrorField(root.error?.code) ?? readErrorField(root.code);
1929
+ if (message && code) return `${message} [code:${code}]`;
1930
+ return message ?? (code ? `[code:${code}]` : void 0);
1931
+ }
1932
+ async function extractUpstreamErrorMessageRaw(error) {
1933
+ if (!(error instanceof HTTPError)) return { upstreamErrorMessageReadFailed: false };
1934
+ try {
1935
+ const bodyText = await error.response.clone().text();
1936
+ if (!bodyText) return { upstreamErrorMessageReadFailed: false };
1937
+ let candidate;
1938
+ try {
1939
+ candidate = formatUpstreamErrorBody(JSON.parse(bodyText));
1940
+ } catch {
1941
+ candidate = bodyText;
1942
+ }
1943
+ if (!candidate) return { upstreamErrorMessageReadFailed: false };
1944
+ const sanitized = sanitizeUpstreamErrorMessage(candidate);
1945
+ return {
1946
+ upstreamErrorMessageRaw: sanitized.length > 0 ? sanitized : void 0,
1947
+ upstreamErrorMessageReadFailed: false
1948
+ };
1949
+ } catch (readError) {
1950
+ consola.warn("Failed to read upstream HTTP error response body:", {
1951
+ status: error.response.status,
1952
+ readError
1953
+ });
1954
+ return { upstreamErrorMessageReadFailed: true };
1955
+ }
1956
+ }
2368
1957
  function extractErrorDetails(error) {
2369
1958
  const errorName = error instanceof Error ? error.name : "Error";
2370
1959
  const errorMessage = error instanceof Error ? truncate(error.message) : truncate(String(error));
2371
1960
  const errorStatus = error instanceof HTTPError ? error.response.status : void 0;
1961
+ const httpStatus = errorStatus ?? 500;
1962
+ const unauthorized = errorStatus === 401;
2372
1963
  return {
2373
- httpStatus: errorStatus ?? 500,
1964
+ httpStatus,
2374
1965
  errorName,
2375
1966
  errorStatus,
2376
1967
  errorMessage,
2377
- unauthorized: errorStatus === 401
1968
+ unauthorized,
1969
+ ownershipMismatch: unauthorized && OWNERSHIP_MISMATCH_PATTERN.test(errorMessage)
1970
+ };
1971
+ }
1972
+ async function extractErrorObservability(error) {
1973
+ const details = extractErrorDetails(error);
1974
+ const { upstreamErrorMessageRaw, upstreamErrorMessageReadFailed } = await extractUpstreamErrorMessageRaw(error);
1975
+ const observableUpstreamErrorMessageRaw = upstreamErrorMessageRaw ?? (upstreamErrorMessageReadFailed ? UPSTREAM_ERROR_READ_FAILED_SENTINEL : void 0);
1976
+ return {
1977
+ ...details,
1978
+ ownershipMismatch: details.ownershipMismatch || details.unauthorized && OWNERSHIP_MISMATCH_PATTERN.test(upstreamErrorMessageRaw ?? ""),
1979
+ upstreamErrorMessageRaw: observableUpstreamErrorMessageRaw,
1980
+ upstreamErrorMessageReadFailed
2378
1981
  };
2379
1982
  }
1983
+ function getUserVisibleErrorMessage(details) {
1984
+ if (details.upstreamErrorMessageReadFailed) return details.errorMessage;
1985
+ return details.upstreamErrorMessageRaw ?? details.errorMessage;
1986
+ }
1987
+ /**
1988
+ * Returns true when the 401 indicates a genuine auth/token failure
1989
+ * (not an ownership mismatch) and the account should be marked failed.
1990
+ */
1991
+ function shouldMarkAccountFailed(details) {
1992
+ return details.unauthorized && !details.ownershipMismatch && details.upstreamErrorMessageReadFailed !== true;
1993
+ }
2380
1994
 
2381
1995
  //#endregion
2382
1996
  //#region src/lib/logger.ts
2383
1997
  const LOG_RETENTION_MS = 10080 * 60 * 1e3;
2384
1998
  const CLEANUP_INTERVAL_MS = 1440 * 60 * 1e3;
2385
- const LOG_DIR = path.join(PATHS.APP_DIR, "logs");
2386
1999
  const FLUSH_INTERVAL_MS = 1e3;
2387
2000
  const MAX_BUFFER_SIZE = 100;
2001
+ const FILE_LOG_LEVEL_PRIORITY = {
2002
+ error: 0,
2003
+ warn: 1,
2004
+ info: 2,
2005
+ debug: 3
2006
+ };
2007
+ let logDir = path.join(PATHS.APP_DIR, "logs");
2008
+ let testLogLevelOverride;
2388
2009
  const logStreams = /* @__PURE__ */ new Map();
2389
2010
  const logBuffers = /* @__PURE__ */ new Map();
2390
2011
  let runtimeInitialized = false;
2391
2012
  let flushInterval;
2392
2013
  let cleanupInterval;
2014
+ let exitHandler;
2015
+ let sigintHandler;
2016
+ let sigtermHandler;
2393
2017
  const ensureLogDirectory = () => {
2394
- if (!fs$1.existsSync(LOG_DIR)) fs$1.mkdirSync(LOG_DIR, { recursive: true });
2018
+ if (!fs$1.existsSync(logDir)) fs$1.mkdirSync(logDir, { recursive: true });
2395
2019
  };
2396
2020
  const cleanupOldLogs = () => {
2397
- if (!fs$1.existsSync(LOG_DIR)) return;
2021
+ if (!fs$1.existsSync(logDir)) return;
2398
2022
  const now = Date.now();
2399
- for (const entry of fs$1.readdirSync(LOG_DIR)) {
2400
- const filePath = path.join(LOG_DIR, entry);
2023
+ for (const entry of fs$1.readdirSync(logDir)) {
2024
+ const filePath = path.join(logDir, entry);
2401
2025
  let stats;
2402
2026
  try {
2403
2027
  stats = fs$1.statSync(filePath);
@@ -2420,6 +2044,14 @@ const sanitizeName = (name) => {
2420
2044
  const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
2421
2045
  return normalized === "" ? "handler" : normalized;
2422
2046
  };
2047
+ const getHandlerLogFilePath = (name, date) => {
2048
+ const dateKey = [
2049
+ date.getFullYear(),
2050
+ String(date.getMonth() + 1).padStart(2, "0"),
2051
+ String(date.getDate()).padStart(2, "0")
2052
+ ].join("-");
2053
+ return path.join(logDir, `${sanitizeName(name)}-${dateKey}.log`);
2054
+ };
2423
2055
  const maybeUnref = (timer) => {
2424
2056
  timer.unref();
2425
2057
  };
@@ -2459,15 +2091,18 @@ const initializeLoggerRuntime = () => {
2459
2091
  maybeUnref(flushInterval);
2460
2092
  cleanupInterval = setInterval(cleanupOldLogs, CLEANUP_INTERVAL_MS);
2461
2093
  maybeUnref(cleanupInterval);
2462
- process.once("exit", cleanup);
2463
- process.once("SIGINT", () => {
2094
+ exitHandler = cleanup;
2095
+ sigintHandler = () => {
2464
2096
  cleanup();
2465
2097
  process.exit(0);
2466
- });
2467
- process.once("SIGTERM", () => {
2098
+ };
2099
+ sigtermHandler = () => {
2468
2100
  cleanup();
2469
2101
  process.exit(0);
2470
- });
2102
+ };
2103
+ process.once("exit", exitHandler);
2104
+ process.once("SIGINT", sigintHandler);
2105
+ process.once("SIGTERM", sigtermHandler);
2471
2106
  };
2472
2107
  const getLogStream = (filePath) => {
2473
2108
  initializeLoggerRuntime();
@@ -2491,8 +2126,21 @@ const appendLine = (filePath, line) => {
2491
2126
  buffer.push(line);
2492
2127
  if (buffer.length >= MAX_BUFFER_SIZE) flushBuffer(filePath);
2493
2128
  };
2129
+ const normalizeLogTypeToLevel = (type) => {
2130
+ switch (type) {
2131
+ case "error": return "error";
2132
+ case "warn": return "warn";
2133
+ case "debug": return "debug";
2134
+ default: return "info";
2135
+ }
2136
+ };
2137
+ const shouldWriteFileLog = (type, logLevel = resolveLogLevel()) => {
2138
+ return FILE_LOG_LEVEL_PRIORITY[normalizeLogTypeToLevel(type)] <= FILE_LOG_LEVEL_PRIORITY[logLevel];
2139
+ };
2140
+ const resolveLogLevel = () => testLogLevelOverride ?? getLogLevel();
2141
+ const isDebugFileLoggingEnabled = () => resolveLogLevel() === "debug";
2494
2142
  const debugLazy = (logger$7, factory) => {
2495
- if (!state.verbose) return;
2143
+ if (!isDebugFileLoggingEnabled()) return;
2496
2144
  logger$7.debug(...factory());
2497
2145
  };
2498
2146
  const debugJson = (logger$7, label, value) => {
@@ -2501,18 +2149,31 @@ const debugJson = (logger$7, label, value) => {
2501
2149
  const debugJsonTail = (logger$7, label, { value, tailLength = 400 }) => {
2502
2150
  debugLazy(logger$7, () => [label, JSON.stringify(value).slice(-tailLength)]);
2503
2151
  };
2152
+ const getConsolaLevel = () => {
2153
+ const logLevel = resolveLogLevel();
2154
+ switch (logLevel) {
2155
+ case "error": return 0;
2156
+ case "warn": return 1;
2157
+ case "info": return 2;
2158
+ case "debug": return 4;
2159
+ default: return logLevel;
2160
+ }
2161
+ };
2504
2162
  const createHandlerLogger = (name) => {
2505
- const sanitizedName = sanitizeName(name);
2506
2163
  const instance = consola.withTag(name);
2507
- if (state.verbose) instance.level = 5;
2164
+ Object.defineProperty(instance, "level", {
2165
+ get: getConsolaLevel,
2166
+ configurable: true
2167
+ });
2508
2168
  instance.setReporters([]);
2509
2169
  instance.addReporter({ log(logObj) {
2170
+ const fileLogLevel = resolveLogLevel();
2171
+ if (!shouldWriteFileLog(logObj.type, fileLogLevel)) return;
2510
2172
  initializeLoggerRuntime();
2511
2173
  const traceId = requestContext.getStore()?.traceId;
2512
2174
  const date = logObj.date;
2513
- const dateKey = date.toLocaleDateString("sv-SE");
2514
2175
  const timestamp = date.toLocaleString("sv-SE", { hour12: false });
2515
- const filePath = path.join(LOG_DIR, `${sanitizedName}-${dateKey}.log`);
2176
+ const filePath = getHandlerLogFilePath(name, date);
2516
2177
  const message = formatArgs(logObj.args);
2517
2178
  const traceIdStr = traceId ? ` [${traceId}]` : "";
2518
2179
  appendLine(filePath, `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${traceIdStr}${message ? ` ${message}` : ""}`);
@@ -2761,6 +2422,13 @@ const getTokenCount = async (payload, model) => {
2761
2422
  };
2762
2423
  };
2763
2424
 
2425
+ //#endregion
2426
+ //#region src/lib/request-initiator.ts
2427
+ function resolveEffectiveInitiator(baseInitiator, options) {
2428
+ if (options.isCompact || options.isSubagent) return "agent";
2429
+ return baseInitiator;
2430
+ }
2431
+
2764
2432
  //#endregion
2765
2433
  //#region src/services/copilot/create-chat-completions.ts
2766
2434
  function isGpt5MiniFamily(modelId) {
@@ -2784,10 +2452,13 @@ const createChatCompletions = async (payload, account, options) => {
2784
2452
  const ctx = account ?? accountFromState();
2785
2453
  if (!ctx.copilotToken) throw new Error("Copilot token not found");
2786
2454
  const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x$1) => x$1.type === "image_url"));
2787
- const initiator = options?.initiator ?? getChatInitiator(payload.messages);
2455
+ const effectiveInitiator = resolveEffectiveInitiator(options?.initiator ?? getChatInitiator(payload.messages), {
2456
+ isCompact: options?.isCompact,
2457
+ isSubagent: Boolean(options?.subagentMarker)
2458
+ });
2788
2459
  const headers = {
2789
2460
  ...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
2790
- "x-initiator": options?.subagentMarker ? "agent" : initiator
2461
+ "x-initiator": effectiveInitiator
2791
2462
  };
2792
2463
  prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
2793
2464
  const upstreamPayload = applyDefaultReasoningEffort(payload);
@@ -2805,26 +2476,136 @@ const createChatCompletions = async (payload, account, options) => {
2805
2476
  return await response.json();
2806
2477
  };
2807
2478
 
2479
+ //#endregion
2480
+ //#region src/routes/chat-completions/support.ts
2481
+ const CHAT_COMPLETIONS_ENDPOINT$1 = "/chat/completions";
2482
+ const GPT_5_4_MODEL_ID = "gpt-5.4";
2483
+ const GPT_5_4_CHAT_COMPLETIONS_MESSAGE = "Please use `/v1/responses` or `/v1/messages` API";
2484
+ function buildRequestContext$1(c) {
2485
+ const requestId = randomUUID();
2486
+ const startedAtMs = Date.now();
2487
+ const method = c.req.raw.method;
2488
+ const path$2 = new URL(c.req.url, "http://local").pathname;
2489
+ const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
2490
+ return {
2491
+ requestId,
2492
+ startedAtMs,
2493
+ method,
2494
+ path: path$2,
2495
+ clientIp,
2496
+ clientIpSource,
2497
+ userAgent: c.req.header("user-agent") ?? void 0
2498
+ };
2499
+ }
2500
+ function insertRequestLog$2(store, request, record) {
2501
+ store.insert({
2502
+ requestId: request.requestId,
2503
+ startedAtMs: request.startedAtMs,
2504
+ method: request.method,
2505
+ path: request.path,
2506
+ clientIp: request.clientIp,
2507
+ clientIpSource: request.clientIpSource,
2508
+ userAgent: request.userAgent,
2509
+ userId: request.userId,
2510
+ safetyIdentifier: request.safetyIdentifier,
2511
+ promptCacheKey: request.promptCacheKey,
2512
+ initiator: request.initiator,
2513
+ upstreamRequestId: request.upstreamRequestId,
2514
+ affinityKeyUsed: request.affinityKeyUsed,
2515
+ affinityKeySource: request.affinityKeySource,
2516
+ selectionReason: request.selectionReason,
2517
+ affinityHit: request.affinityHit,
2518
+ affinityCacheKey: request.affinityCacheKey,
2519
+ ...record
2520
+ });
2521
+ }
2522
+ function recordUnsupportedChatCompletionsModel(store, params) {
2523
+ const { request, stream, clientModel, upstreamModel, accountId, accountType, costUnits, premiumRemainingBefore, premiumRemainingAfter, premiumUnlimitedBefore, premiumUnlimitedAfter } = params;
2524
+ const finishedAtMs = Date.now();
2525
+ let premiumRemainingDiff;
2526
+ if (premiumRemainingBefore !== void 0 && premiumRemainingAfter !== void 0) premiumRemainingDiff = premiumRemainingAfter - premiumRemainingBefore;
2527
+ insertRequestLog$2(store, request, {
2528
+ finishedAtMs,
2529
+ durationMs: finishedAtMs - request.startedAtMs,
2530
+ upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
2531
+ stream,
2532
+ accountId,
2533
+ accountType,
2534
+ costUnits,
2535
+ clientModel,
2536
+ upstreamModel,
2537
+ premiumRemainingBefore,
2538
+ premiumRemainingAfter,
2539
+ premiumRemainingDiff,
2540
+ premiumUnlimitedBefore,
2541
+ premiumUnlimitedAfter,
2542
+ httpStatus: 400,
2543
+ errorName: "invalid_request_error",
2544
+ errorMessage: GPT_5_4_CHAT_COMPLETIONS_MESSAGE
2545
+ });
2546
+ }
2547
+ function unsupportedChatCompletionsModelResponse(c) {
2548
+ return c.json({ error: {
2549
+ message: GPT_5_4_CHAT_COMPLETIONS_MESSAGE,
2550
+ type: "invalid_request_error"
2551
+ } }, 400);
2552
+ }
2553
+ function getSelectionFailureStatus(reason) {
2554
+ if (reason === "MODEL_NOT_SUPPORTED") return 400;
2555
+ if (reason === "NO_ACCOUNTS") return 503;
2556
+ return 429;
2557
+ }
2558
+ function recordSelectionFailure$2(store, params) {
2559
+ const { request, stream, clientModel, reason } = params;
2560
+ const finishedAtMs = Date.now();
2561
+ insertRequestLog$2(store, request, {
2562
+ finishedAtMs,
2563
+ durationMs: finishedAtMs - request.startedAtMs,
2564
+ upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
2565
+ stream,
2566
+ clientModel,
2567
+ httpStatus: getSelectionFailureStatus(reason),
2568
+ selectionFailureReason: reason
2569
+ });
2570
+ }
2571
+ function selectionFailureResponse$2(c, params) {
2572
+ const { clientModel, reason } = params;
2573
+ if (reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
2574
+ message: `Model "${clientModel}" is not available for any configured account.`,
2575
+ type: "invalid_request_error"
2576
+ } }, 400);
2577
+ if (reason === "NO_ACCOUNTS") return c.json({ error: {
2578
+ message: "No enabled Copilot accounts are configured. Add or re-enable an account.",
2579
+ type: "service_unavailable_error"
2580
+ } }, 503);
2581
+ return c.json({ error: {
2582
+ message: "All accounts have exhausted their quota. Please wait for quota refresh or add additional accounts.",
2583
+ type: "rate_limit_error"
2584
+ } }, 429);
2585
+ }
2586
+
2808
2587
  //#endregion
2809
2588
  //#region src/routes/chat-completions/handler.ts
2810
2589
  const logger$6 = createHandlerLogger("chat-completions-handler");
2811
- const CHAT_COMPLETIONS_ENDPOINT$1 = "/chat/completions";
2812
- async function handleCompletion$1(c) {
2813
- await checkRateLimit(state);
2814
- const store = getRequestHistoryStore();
2815
- const request = buildRequestContext$1(c);
2816
- const payload = await c.req.json();
2817
- const clientModel = payload.model;
2818
- const streamRequested = Boolean(payload.stream);
2819
- const initiator = getChatInitiator(payload.messages);
2820
- const userId = payload.user ?? void 0;
2821
- const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
2822
- const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
2823
- const normalizedPromptCacheKey = promptCacheKey ?? void 0;
2824
- request.userId = userId;
2825
- request.safetyIdentifier = normalizedSafetyIdentifier;
2826
- request.promptCacheKey = normalizedPromptCacheKey;
2827
- request.initiator = initiator;
2590
+ function buildChatCompletionCandidates(clientModel) {
2591
+ return [{
2592
+ modelId: clientModel,
2593
+ endpoint: CHAT_COMPLETIONS_ENDPOINT$1
2594
+ }];
2595
+ }
2596
+ function isUnsupportedChatCompletionsModel(modelId) {
2597
+ return resolveModelAlias(modelId).toLowerCase() === GPT_5_4_MODEL_ID;
2598
+ }
2599
+ function maybeRejectChatCompletionsClientModel(c, store, params) {
2600
+ const { request, clientModel, streamRequested } = params;
2601
+ if (isUnsupportedChatCompletionsModel(clientModel)) {
2602
+ recordUnsupportedChatCompletionsModel(store, {
2603
+ request,
2604
+ clientModel,
2605
+ stream: streamRequested
2606
+ });
2607
+ return unsupportedChatCompletionsModelResponse(c);
2608
+ }
2828
2609
  if (getAliasTargetSet().has(clientModel.toLowerCase())) {
2829
2610
  recordSelectionFailure$2(store, {
2830
2611
  request,
@@ -2837,36 +2618,60 @@ async function handleCompletion$1(c) {
2837
2618
  reason: "MODEL_NOT_SUPPORTED"
2838
2619
  });
2839
2620
  }
2840
- debugJsonTail(logger$6, "Request payload:", {
2841
- value: payload,
2842
- tailLength: 400
2621
+ return null;
2622
+ }
2623
+ async function handleCompletion$1(c) {
2624
+ await checkRateLimit(state);
2625
+ const store = getRequestHistoryStore();
2626
+ const request = buildRequestContext$1(c);
2627
+ const payload = await c.req.json();
2628
+ const clientModel = payload.model;
2629
+ const streamRequested = Boolean(payload.stream);
2630
+ const normalizedPromptCacheKey = applyChatRequestMetadata(request, payload, getChatInitiator(payload.messages));
2631
+ const blockedResponse = maybeRejectChatCompletionsClientModel(c, store, {
2632
+ request,
2633
+ clientModel,
2634
+ streamRequested
2843
2635
  });
2844
- const upstreamRequestId = generateRequestIdFromPayload(payload, normalizedPromptCacheKey);
2845
- const selection = await accountsManager.selectAccountForRequest([{
2846
- modelId: clientModel,
2847
- endpoint: CHAT_COMPLETIONS_ENDPOINT$1
2848
- }], { requestId: upstreamRequestId });
2849
- if (!selection.ok) {
2850
- recordSelectionFailure$2(store, {
2636
+ if (blockedResponse) return blockedResponse;
2637
+ const selectionResult = await selectChatCompletionAccount({
2638
+ c,
2639
+ store,
2640
+ request,
2641
+ payload,
2642
+ clientModel,
2643
+ streamRequested,
2644
+ normalizedPromptCacheKey
2645
+ });
2646
+ if (selectionResult instanceof Response) return selectionResult;
2647
+ const { headerSessionId, selection, upstreamRequestId } = selectionResult;
2648
+ const { account, reservation, selectedModel } = selection;
2649
+ request.affinityHit = selection.affinityHit;
2650
+ request.affinityCacheKey = selection.affinityCacheKey;
2651
+ request.selectionReason = selection.selectionReason;
2652
+ const premiumRemainingBefore = account.premiumRemaining;
2653
+ const premiumUnlimitedBefore = account.unlimited;
2654
+ if (selectedModel.id === GPT_5_4_MODEL_ID) {
2655
+ await accountsManager.finalizeQuota(account, reservation);
2656
+ recordUnsupportedChatCompletionsModel(store, {
2851
2657
  request,
2852
2658
  clientModel,
2853
2659
  stream: streamRequested,
2854
- reason: selection.reason
2855
- });
2856
- return selectionFailureResponse$2(c, {
2857
- clientModel,
2858
- reason: selection.reason
2660
+ upstreamModel: selectedModel.id,
2661
+ accountId: account.id,
2662
+ accountType: account.accountType,
2663
+ costUnits: selection.costUnits,
2664
+ premiumRemainingBefore,
2665
+ premiumRemainingAfter: account.premiumRemaining,
2666
+ premiumUnlimitedBefore,
2667
+ premiumUnlimitedAfter: account.unlimited
2859
2668
  });
2669
+ return unsupportedChatCompletionsModelResponse(c);
2860
2670
  }
2861
- const { account, selectedModel } = selection;
2862
- request.affinityHit = selection.affinityHit;
2863
- request.affinityCacheKey = selection.affinityCacheKey;
2864
2671
  const upstreamPayload = {
2865
2672
  ...payload,
2866
2673
  model: selectedModel.id
2867
2674
  };
2868
- const premiumRemainingBefore = account.premiumRemaining;
2869
- const premiumUnlimitedBefore = account.unlimited;
2870
2675
  await logTokenCountForRequest({
2871
2676
  payload: upstreamPayload,
2872
2677
  selectedModel
@@ -2874,7 +2679,7 @@ async function handleCompletion$1(c) {
2874
2679
  if (state.manualApprove) await awaitApproval();
2875
2680
  const payloadWithMaxTokens = applyDefaultMaxTokens(upstreamPayload, selectedModel);
2876
2681
  const accountCtx = toAccountContext(account);
2877
- const upstreamSessionId = getUUID(upstreamRequestId);
2682
+ const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
2878
2683
  request.upstreamRequestId = upstreamRequestId;
2879
2684
  request.upstreamSessionId = upstreamSessionId;
2880
2685
  if (streamRequested) return handleStreamingRequest({
@@ -2900,64 +2705,60 @@ async function handleCompletion$1(c) {
2900
2705
  premiumUnlimitedBefore
2901
2706
  });
2902
2707
  }
2903
- function buildRequestContext$1(c) {
2904
- const requestId = randomUUID();
2905
- const startedAtMs = Date.now();
2906
- const method = c.req.raw.method;
2907
- const path$2 = new URL(c.req.url, "http://local").pathname;
2908
- const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
2909
- return {
2910
- requestId,
2911
- startedAtMs,
2912
- method,
2913
- path: path$2,
2914
- clientIp,
2915
- clientIpSource,
2916
- userAgent: c.req.header("user-agent") ?? void 0
2917
- };
2708
+ function applyChatRequestMetadata(request, payload, initiator) {
2709
+ const userId = payload.user ?? void 0;
2710
+ const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
2711
+ const normalizedPromptCacheKey = promptCacheKey ?? void 0;
2712
+ request.userId = userId;
2713
+ request.safetyIdentifier = safetyIdentifier ?? void 0;
2714
+ request.promptCacheKey = normalizedPromptCacheKey;
2715
+ request.initiator = initiator;
2716
+ return normalizedPromptCacheKey;
2918
2717
  }
2919
- function insertRequestLog$2(store, request, record) {
2920
- store.insert({
2921
- requestId: request.requestId,
2922
- startedAtMs: request.startedAtMs,
2923
- method: request.method,
2924
- path: request.path,
2925
- clientIp: request.clientIp,
2926
- clientIpSource: request.clientIpSource,
2927
- userAgent: request.userAgent,
2928
- userId: request.userId,
2929
- safetyIdentifier: request.safetyIdentifier,
2930
- promptCacheKey: request.promptCacheKey,
2931
- initiator: request.initiator,
2932
- upstreamRequestId: request.upstreamRequestId,
2933
- affinityHit: request.affinityHit,
2934
- affinityCacheKey: request.affinityCacheKey,
2935
- ...record
2936
- });
2718
+ async function writeChatCompletionsStreamError(stream, message) {
2719
+ try {
2720
+ await stream.writeSSE({ data: JSON.stringify({ error: {
2721
+ message,
2722
+ type: "error"
2723
+ } }) });
2724
+ await stream.writeSSE({ data: "[DONE]" });
2725
+ } catch (streamError) {
2726
+ logger$6.warn("Failed to write chat completions stream error event:", streamError);
2727
+ }
2937
2728
  }
2938
- function recordSelectionFailure$2(store, params) {
2939
- const { request, stream, clientModel, reason } = params;
2940
- const finishedAtMs = Date.now();
2941
- insertRequestLog$2(store, request, {
2942
- finishedAtMs,
2943
- durationMs: finishedAtMs - request.startedAtMs,
2944
- upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
2945
- stream,
2946
- clientModel,
2947
- httpStatus: reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
2948
- selectionFailureReason: reason
2729
+ async function selectChatCompletionAccount(params) {
2730
+ const { c, store, request, payload, clientModel, streamRequested, normalizedPromptCacheKey } = params;
2731
+ debugJsonTail(logger$6, "Request payload:", {
2732
+ value: payload,
2733
+ tailLength: 400
2949
2734
  });
2950
- }
2951
- function selectionFailureResponse$2(c, params) {
2952
- const { clientModel, reason } = params;
2953
- if (reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
2954
- message: `Model "${clientModel}" is not available for any configured account.`,
2955
- type: "invalid_request_error"
2956
- } }, 400);
2957
- return c.json({ error: {
2958
- message: "All accounts have exhausted their quota. Please wait for quota refresh or add additional accounts.",
2959
- type: "rate_limit_error"
2960
- } }, 429);
2735
+ const upstreamRequestId = generateRequestIdFromPayload(payload, normalizedPromptCacheKey);
2736
+ const headerSessionId = c.req.header("x-session-id") ?? null;
2737
+ const affinityKey = resolveAffinityKey({
2738
+ metadataSessionId: normalizedPromptCacheKey,
2739
+ headerSessionId,
2740
+ upstreamRequestId
2741
+ });
2742
+ request.affinityKeyUsed = affinityKey.affinityKeyUsed;
2743
+ request.affinityKeySource = affinityKey.affinityKeySource;
2744
+ const selection = await accountsManager.selectAccountForRequest(buildChatCompletionCandidates(clientModel), { requestId: affinityKey.requestId });
2745
+ if (!selection.ok) {
2746
+ recordSelectionFailure$2(store, {
2747
+ request,
2748
+ clientModel,
2749
+ stream: streamRequested,
2750
+ reason: selection.reason
2751
+ });
2752
+ return selectionFailureResponse$2(c, {
2753
+ clientModel,
2754
+ reason: selection.reason
2755
+ });
2756
+ }
2757
+ return {
2758
+ headerSessionId,
2759
+ selection,
2760
+ upstreamRequestId
2761
+ };
2961
2762
  }
2962
2763
  async function logTokenCountForRequest(params) {
2963
2764
  try {
@@ -3022,8 +2823,8 @@ async function handleUpstreamCreateError$1(params) {
3022
2823
  const { store, request, selection, clientModel, premiumRemainingBefore, premiumUnlimitedBefore, error } = params;
3023
2824
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
3024
2825
  const finishedAtMs = Date.now();
3025
- const details = extractErrorDetails(error);
3026
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
2826
+ const details = await extractErrorObservability(error);
2827
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3027
2828
  await accountsManager.finalizeQuota(account, reservation);
3028
2829
  const premiumRemainingAfter = account.premiumRemaining;
3029
2830
  const premiumUnlimitedAfter = account.unlimited;
@@ -3045,7 +2846,8 @@ async function handleUpstreamCreateError$1(params) {
3045
2846
  httpStatus: details.httpStatus,
3046
2847
  errorName: details.errorName,
3047
2848
  errorStatus: details.errorStatus,
3048
- errorMessage: details.errorMessage
2849
+ errorMessage: details.errorMessage,
2850
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
3049
2851
  });
3050
2852
  throw error;
3051
2853
  }
@@ -3057,16 +2859,18 @@ async function handleNonStreamingUpstreamResponse(params) {
3057
2859
  let errorName;
3058
2860
  let errorStatus;
3059
2861
  let errorMessage;
2862
+ let upstreamErrorMessageRaw;
3060
2863
  const finishedAtMs = Date.now();
3061
2864
  try {
3062
2865
  debugJson(logger$6, "Non-streaming response:", response);
3063
2866
  return c.json(response);
3064
2867
  } catch (error) {
3065
- const details = extractErrorDetails(error);
2868
+ const details = await extractErrorObservability(error);
3066
2869
  httpStatus = details.httpStatus;
3067
2870
  errorName = details.errorName;
3068
2871
  errorStatus = details.errorStatus;
3069
2872
  errorMessage = details.errorMessage;
2873
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3070
2874
  throw error;
3071
2875
  } finally {
3072
2876
  await accountsManager.finalizeQuota(account, reservation);
@@ -3091,7 +2895,8 @@ async function handleNonStreamingUpstreamResponse(params) {
3091
2895
  httpStatus,
3092
2896
  errorName,
3093
2897
  errorStatus,
3094
- errorMessage
2898
+ errorMessage,
2899
+ upstreamErrorMessageRaw
3095
2900
  });
3096
2901
  }
3097
2902
  }
@@ -3103,6 +2908,7 @@ async function streamChatCompletionsAndLog$1(params) {
3103
2908
  let errorName;
3104
2909
  let errorStatus;
3105
2910
  let errorMessage;
2911
+ let upstreamErrorMessageRaw;
3106
2912
  try {
3107
2913
  for await (const rawChunk of response) {
3108
2914
  const chunk = rawChunk;
@@ -3113,11 +2919,14 @@ async function streamChatCompletionsAndLog$1(params) {
3113
2919
  await stream.writeSSE(chunk);
3114
2920
  }
3115
2921
  } catch (error) {
3116
- const details = extractErrorDetails(error);
2922
+ const details = await extractErrorObservability(error);
3117
2923
  errorName = details.errorName;
3118
2924
  errorStatus = details.errorStatus;
3119
2925
  errorMessage = details.errorMessage;
2926
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3120
2927
  logger$6.warn("Streaming error:", error);
2928
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
2929
+ await writeChatCompletionsStreamError(stream, getUserVisibleErrorMessage(details));
3121
2930
  } finally {
3122
2931
  const finishedAtMs = Date.now();
3123
2932
  await accountsManager.finalizeQuota(account, reservation);
@@ -3143,18 +2952,29 @@ async function streamChatCompletionsAndLog$1(params) {
3143
2952
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
3144
2953
  errorName,
3145
2954
  errorStatus,
3146
- errorMessage
2955
+ errorMessage,
2956
+ upstreamErrorMessageRaw
3147
2957
  });
3148
2958
  }
3149
2959
  }
3150
2960
  async function extractUsageFromChunk(chunk) {
3151
- const data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
2961
+ let data;
2962
+ try {
2963
+ data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
2964
+ } catch (error) {
2965
+ logger$6.warn("Failed to read chat completions usage chunk:", error);
2966
+ return;
2967
+ }
3152
2968
  if (!data || data === "[DONE]") return;
3153
2969
  try {
3154
2970
  const parsed = JSON.parse(data);
3155
2971
  if (!parsed.usage) return void 0;
3156
2972
  return normalizeChatCompletionsUsage(parsed.usage);
3157
- } catch {
2973
+ } catch (error) {
2974
+ logger$6.warn("Failed to parse chat completions usage chunk:", {
2975
+ error,
2976
+ data
2977
+ });
3158
2978
  return;
3159
2979
  }
3160
2980
  }
@@ -3166,31 +2986,28 @@ async function handleNonStreamingRequest(params) {
3166
2986
  let errorName;
3167
2987
  let errorStatus;
3168
2988
  let errorMessage;
2989
+ let upstreamErrorMessageRaw;
3169
2990
  let finishedAtMs;
3170
2991
  try {
3171
2992
  const response = await createChatCompletions(payload, accountCtx, {
3172
2993
  upstreamRequestId: request.upstreamRequestId,
3173
2994
  sessionId: request.upstreamSessionId
3174
2995
  });
2996
+ if (!isNonStreaming$1(response)) throw new Error("Upstream returned a stream unexpectedly");
3175
2997
  selection.confirmAffinity?.();
3176
2998
  finishedAtMs = Date.now();
3177
- if (!isNonStreaming$1(response)) {
3178
- logger$6.debug("Unexpected streaming response");
3179
- return streamSSE(c, async (stream) => {
3180
- for await (const chunk of response) await stream.writeSSE(chunk);
3181
- });
3182
- }
3183
2999
  usage = normalizeChatCompletionsUsage(response.usage);
3184
3000
  debugJson(logger$6, "Non-streaming response:", response);
3185
3001
  return c.json(response);
3186
3002
  } catch (error) {
3187
3003
  finishedAtMs = Date.now();
3188
- const details = extractErrorDetails(error);
3004
+ const details = await extractErrorObservability(error);
3189
3005
  httpStatus = details.httpStatus;
3190
3006
  errorName = details.errorName;
3191
3007
  errorStatus = details.errorStatus;
3192
3008
  errorMessage = details.errorMessage;
3193
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3009
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3010
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3194
3011
  throw error;
3195
3012
  } finally {
3196
3013
  const finishedAtMsFinal = finishedAtMs ?? Date.now();
@@ -3216,7 +3033,8 @@ async function handleNonStreamingRequest(params) {
3216
3033
  httpStatus,
3217
3034
  errorName,
3218
3035
  errorStatus,
3219
- errorMessage
3036
+ errorMessage,
3037
+ upstreamErrorMessageRaw
3220
3038
  });
3221
3039
  }
3222
3040
  }
@@ -3344,6 +3162,7 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
3344
3162
  let errorName;
3345
3163
  let errorStatus;
3346
3164
  let errorMessage;
3165
+ let upstreamErrorMessageRaw;
3347
3166
  let finishedAtMs;
3348
3167
  try {
3349
3168
  const response = await createEmbeddings(payload, toAccountContext(account));
@@ -3352,12 +3171,13 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
3352
3171
  return c.json(response);
3353
3172
  } catch (error) {
3354
3173
  finishedAtMs = Date.now();
3355
- const details = extractErrorDetails(error);
3174
+ const details = await extractErrorObservability(error);
3356
3175
  httpStatus = details.httpStatus;
3357
3176
  errorName = details.errorName;
3358
3177
  errorStatus = details.errorStatus;
3359
3178
  errorMessage = details.errorMessage;
3360
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3179
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3180
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3361
3181
  throw error;
3362
3182
  } finally {
3363
3183
  const finishedAtMsFinal = finishedAtMs ?? Date.now();
@@ -3390,22 +3210,23 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
3390
3210
  httpStatus,
3391
3211
  errorName,
3392
3212
  errorStatus,
3393
- errorMessage
3213
+ errorMessage,
3214
+ upstreamErrorMessageRaw
3394
3215
  });
3395
3216
  }
3396
3217
  }
3397
3218
 
3398
3219
  //#endregion
3399
3220
  //#region src/lib/models.ts
3221
+ const getAvailableModels = () => (accountsManager.getFirstAccountModels()?.data ?? []).filter((model) => model.model_picker_enabled || model.capabilities.type === "embeddings");
3400
3222
  const findEndpointModel = (sdkModelId) => {
3401
- const models = state.models?.data ?? [];
3223
+ const models = getAvailableModels();
3402
3224
  const exactMatch = models.find((m) => m.id === sdkModelId);
3403
3225
  if (exactMatch) return exactMatch;
3404
3226
  const normalized = _normalizeSdkModelId(sdkModelId);
3405
3227
  if (!normalized) return;
3406
3228
  const modelName = `claude-${normalized.family}-${normalized.version}`;
3407
- const model = models.find((m) => m.id === modelName);
3408
- if (model) return model;
3229
+ return models.find((m) => m.id === modelName);
3409
3230
  };
3410
3231
  /**
3411
3232
  * Normalizes an SDK model ID to extract the model family and version.
@@ -3611,7 +3432,7 @@ const isWarmupProbeRequest = (payload) => {
3611
3432
  return false;
3612
3433
  };
3613
3434
  const handleSelectionFailure = (context) => {
3614
- const { c, store, requestId, startedAtMs, method, path: path$2, streamRequested, clientModel, clientIp, clientIpSource, userAgent, userId, safetyIdentifier, promptCacheKey, initiator, isSubagent, selection } = context;
3435
+ const { c, store, requestId, startedAtMs, method, path: path$2, streamRequested, clientModel, clientIp, clientIpSource, userAgent, userId, safetyIdentifier, promptCacheKey, initiator, isSubagent, affinityKeyUsed, affinityKeySource, selectionReason, selection } = context;
3615
3436
  const finishedAtMs = Date.now();
3616
3437
  store.insert({
3617
3438
  requestId,
@@ -3630,7 +3451,10 @@ const handleSelectionFailure = (context) => {
3630
3451
  promptCacheKey,
3631
3452
  initiator,
3632
3453
  isSubagent,
3454
+ affinityKeyUsed,
3455
+ affinityKeySource,
3633
3456
  httpStatus: selection.reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
3457
+ selectionReason: selectionReason ?? selection.selectionReason,
3634
3458
  selectionFailureReason: selection.reason
3635
3459
  });
3636
3460
  if (selection.reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
@@ -3658,8 +3482,7 @@ const maybeBlockOriginalModelName = (context) => {
3658
3482
  const THINKING_TEXT = "Thinking...";
3659
3483
  function translateToOpenAI(payload) {
3660
3484
  const modelId = payload.model;
3661
- const model = state.models?.data.find((m) => m.id === modelId);
3662
- const thinkingBudget = getThinkingBudget(payload, model);
3485
+ const thinkingBudget = getThinkingBudget(payload, getAvailableModels().find((m) => m.id === modelId));
3663
3486
  return {
3664
3487
  model: modelId,
3665
3488
  messages: translateAnthropicMessagesToOpenAI(payload, modelId, thinkingBudget),
@@ -3955,9 +3778,13 @@ async function handleCountTokens(c) {
3955
3778
  const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, isCompact }, account) => {
3956
3779
  const ctx = account ?? accountFromState();
3957
3780
  if (!ctx.copilotToken) throw new Error("Copilot token not found");
3781
+ const effectiveInitiator = resolveEffectiveInitiator(initiator, {
3782
+ isCompact,
3783
+ isSubagent: Boolean(subagentMarker)
3784
+ });
3958
3785
  const headers = {
3959
3786
  ...copilotHeaders(ctx, vision, upstreamRequestId),
3960
- "x-initiator": initiator
3787
+ "x-initiator": effectiveInitiator
3961
3788
  };
3962
3789
  prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
3963
3790
  prepareForCompact(headers, isCompact);
@@ -4882,6 +4709,44 @@ const getPayloadItems = (payload) => {
4882
4709
  if (Array.isArray(input)) result.push(...input);
4883
4710
  return result;
4884
4711
  };
4712
+ const useFunctionApplyPatch = (payload) => {
4713
+ if (!(getConfig().useFunctionApplyPatch ?? true)) return;
4714
+ if (Array.isArray(payload.tools)) {
4715
+ const toolsArr = payload.tools;
4716
+ for (let i = 0; i < toolsArr.length; i++) {
4717
+ const t = toolsArr[i];
4718
+ if (t.type === "custom" && t.name === "apply_patch") toolsArr[i] = {
4719
+ type: "function",
4720
+ name: t.name,
4721
+ description: "Use the `apply_patch` tool to edit files",
4722
+ parameters: {
4723
+ type: "object",
4724
+ properties: { input: {
4725
+ type: "string",
4726
+ description: "The entire contents of the apply_patch command"
4727
+ } },
4728
+ required: ["input"]
4729
+ },
4730
+ strict: false
4731
+ };
4732
+ }
4733
+ }
4734
+ };
4735
+ const removeWebSearchTool = (payload) => {
4736
+ if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
4737
+ payload.tools = payload.tools.filter((t) => {
4738
+ return t.type !== "web_search";
4739
+ });
4740
+ };
4741
+ function getStreamChunkFields(chunk) {
4742
+ const c = chunk;
4743
+ return {
4744
+ id: c.id,
4745
+ event: c.event,
4746
+ data: c.data
4747
+ };
4748
+ }
4749
+ const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
4885
4750
  const containsVisionContent = (value) => {
4886
4751
  if (!value) return false;
4887
4752
  if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
@@ -4931,9 +4796,13 @@ const shouldUseMessageProxyHeaders = (payload) => {
4931
4796
  return Boolean(safetyIdentifier && sessionId);
4932
4797
  };
4933
4798
  const buildMessagesHeaders = ({ ctx, enableVision, initiator, options, payload }) => {
4799
+ const effectiveInitiator = resolveEffectiveInitiator(initiator, {
4800
+ isCompact: options?.isCompact,
4801
+ isSubagent: Boolean(options?.subagentMarker)
4802
+ });
4934
4803
  const headers = {
4935
4804
  ...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
4936
- "x-initiator": options?.subagentMarker ? "agent" : initiator
4805
+ "x-initiator": effectiveInitiator
4937
4806
  };
4938
4807
  prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
4939
4808
  prepareForCompact(headers, options?.isCompact);
@@ -5225,34 +5094,73 @@ function closeThinkingBlockIfOpen(state$1, events$1) {
5225
5094
  //#endregion
5226
5095
  //#region src/routes/messages/subagent-marker.ts
5227
5096
  const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
5097
+ const subagentStartContextPrefix = "SubagentStart hook additional context:";
5228
5098
  const REMINDER_RE = /<system-reminder>([\s\S]*?)<\/system-reminder>/g;
5229
- const parseSubagentMarkerFromFirstUser = (payload) => {
5099
+ const NONE_INSPECTION = {
5100
+ kind: "none",
5101
+ marker: null
5102
+ };
5103
+ const INVALID_INSPECTION = {
5104
+ kind: "invalid",
5105
+ marker: null
5106
+ };
5107
+ const isSubagentMarker = (value) => {
5108
+ if (!value || typeof value !== "object") return false;
5109
+ const candidate = value;
5110
+ return typeof candidate.session_id === "string" && typeof candidate.agent_id === "string" && typeof candidate.agent_type === "string" && candidate.session_id.trim().length > 0 && candidate.agent_id.trim().length > 0 && candidate.agent_type.trim().length > 0;
5111
+ };
5112
+ const inspectSubagentMarkerFromFirstUser = (payload) => {
5230
5113
  const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
5231
- if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
5114
+ if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return NONE_INSPECTION;
5115
+ let sawInvalidMarker = false;
5232
5116
  for (const block of firstUserMessage.content) {
5233
5117
  if (block.type !== "text") continue;
5234
- const marker = parseSubagentMarkerFromSystemReminder(block.text);
5235
- if (marker) return marker;
5236
- }
5237
- return null;
5238
- };
5239
- const parseSubagentMarkerFromSystemReminder = (text) => {
5118
+ const inspection = inspectSubagentMarkerFromSystemReminder(block.text);
5119
+ if (inspection.kind === "valid") return inspection;
5120
+ if (inspection.kind === "invalid") sawInvalidMarker = true;
5121
+ }
5122
+ return sawInvalidMarker ? INVALID_INSPECTION : NONE_INSPECTION;
5123
+ };
5124
+ const extractMarkerPayloadFromReminderLine = (line) => {
5125
+ const trimmedLine = line.trim();
5126
+ if (!trimmedLine) return null;
5127
+ let markerLine = trimmedLine;
5128
+ if (markerLine.startsWith(subagentStartContextPrefix)) markerLine = markerLine.slice(38).trimStart();
5129
+ if (!markerLine.startsWith(subagentMarkerPrefix)) return null;
5130
+ return markerLine.slice(19).trimStart();
5131
+ };
5132
+ const inspectSubagentMarkerFromSystemReminder = (text) => {
5133
+ let sawInvalidMarker = false;
5240
5134
  for (const [, content] of text.matchAll(REMINDER_RE)) {
5241
- const markerIndex = content.indexOf(subagentMarkerPrefix);
5242
- if (markerIndex === -1) continue;
5243
- const afterPrefix = content.slice(markerIndex + 19).trimStart();
5244
- if (!afterPrefix.startsWith("{")) continue;
5245
- const json = extractBalancedJson(afterPrefix);
5246
- if (!json) continue;
5247
- try {
5248
- const parsed = JSON.parse(json);
5249
- if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) continue;
5250
- return parsed;
5251
- } catch {
5252
- continue;
5135
+ const lines = content.split(/\r?\n/);
5136
+ for (const line of lines) {
5137
+ const markerPayload = extractMarkerPayloadFromReminderLine(line);
5138
+ if (markerPayload === null) continue;
5139
+ if (!markerPayload.startsWith("{")) {
5140
+ sawInvalidMarker = true;
5141
+ continue;
5142
+ }
5143
+ const json = extractBalancedJson(markerPayload);
5144
+ if (!json) {
5145
+ sawInvalidMarker = true;
5146
+ continue;
5147
+ }
5148
+ try {
5149
+ const parsed = JSON.parse(json);
5150
+ if (!isSubagentMarker(parsed)) {
5151
+ sawInvalidMarker = true;
5152
+ continue;
5153
+ }
5154
+ return {
5155
+ kind: "valid",
5156
+ marker: parsed
5157
+ };
5158
+ } catch {
5159
+ sawInvalidMarker = true;
5160
+ }
5253
5161
  }
5254
5162
  }
5255
- return null;
5163
+ return sawInvalidMarker ? INVALID_INSPECTION : NONE_INSPECTION;
5256
5164
  };
5257
5165
  /** Extract the first balanced `{...}` object from text that starts with `{`. */
5258
5166
  const extractBalancedJson = (text) => {
@@ -5306,13 +5214,18 @@ async function handleCompletion(c) {
5306
5214
  const userAgent = c.req.header("user-agent") ?? void 0;
5307
5215
  const anthropicPayload = await c.req.json();
5308
5216
  debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
5309
- const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
5310
- const initiatorOverride = subagentMarker ? "agent" : void 0;
5217
+ const markerInspection = inspectSubagentMarkerFromFirstUser(anthropicPayload);
5218
+ const subagentMarker = markerInspection.kind === "valid" ? markerInspection.marker : null;
5219
+ const isSubagentRequest = subagentMarker !== null;
5220
+ const invalidSubagentMarkerSelectionReason = markerInspection.kind === "invalid" ? "subagent_marker_invalid_fallback" : void 0;
5311
5221
  if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
5312
5222
  const sessionId = getRootSessionId(anthropicPayload, c);
5313
5223
  logger$5.debug("Extracted session ID:", sessionId);
5224
+ const ownershipLookupSessionId = markerInspection.kind === "valid" ? normalizeStableSessionId(markerInspection.marker.session_id) : void 0;
5225
+ const ownershipWriteSessionId = markerInspection.kind === "none" ? sessionId : void 0;
5314
5226
  const anthropicBeta = c.req.header("anthropic-beta");
5315
5227
  const isCompact = isCompactRequest(anthropicPayload);
5228
+ const originalRequestModel = anthropicPayload.model;
5316
5229
  if (anthropicBeta && isWarmupProbeRequest(anthropicPayload)) anthropicPayload.model = getSmallModel();
5317
5230
  if (isCompact) {
5318
5231
  logger$5.debug("Is compact request:", isCompact);
@@ -5330,6 +5243,11 @@ async function handleCompletion(c) {
5330
5243
  const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
5331
5244
  const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
5332
5245
  const normalizedPromptCacheKey = promptCacheKey ?? void 0;
5246
+ const openAIPayload = translateToOpenAI(anthropicPayload);
5247
+ const fallbackInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
5248
+ isCompact,
5249
+ isSubagent: isSubagentRequest
5250
+ });
5333
5251
  const blockedResponse = maybeBlockOriginalModelName({
5334
5252
  c,
5335
5253
  store,
@@ -5345,14 +5263,14 @@ async function handleCompletion(c) {
5345
5263
  userId,
5346
5264
  safetyIdentifier: normalizedSafetyIdentifier,
5347
5265
  promptCacheKey: normalizedPromptCacheKey,
5348
- initiator: initiatorOverride,
5349
- isSubagent: Boolean(subagentMarker)
5266
+ initiator: fallbackInitiator,
5267
+ isSubagent: isSubagentRequest,
5268
+ selectionReason: invalidSubagentMarkerSelectionReason
5350
5269
  });
5351
5270
  if (blockedResponse) return blockedResponse;
5352
- const openAIPayload = translateToOpenAI(anthropicPayload);
5353
- const fallbackInitiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
5354
5271
  const endpointModel = findEndpointModel(clientModel);
5355
5272
  const resolvedClientModel = endpointModel?.id ?? clientModel;
5273
+ const affinityModelId = clientModel !== originalRequestModel ? findEndpointModel(originalRequestModel)?.id ?? originalRequestModel : void 0;
5356
5274
  const useMessagesApi = isMessagesApiEnabled();
5357
5275
  const candidates = [];
5358
5276
  if (useMessagesApi) candidates.push({
@@ -5366,7 +5284,18 @@ async function handleCompletion(c) {
5366
5284
  modelId: endpointModel?.id ?? openAIPayload.model,
5367
5285
  endpoint: CHAT_COMPLETIONS_ENDPOINT
5368
5286
  });
5369
- const selection = await accountsManager.selectAccountForRequest(candidates, { requestId: upstreamRequestId });
5287
+ const affinityKey = resolveAffinityKey({
5288
+ metadataSessionId: promptCacheKey,
5289
+ headerSessionId: c.req.header("x-session-id") ?? null,
5290
+ upstreamRequestId
5291
+ });
5292
+ const selection = await accountsManager.selectAccountForRequest(candidates, {
5293
+ requestId: affinityKey.requestId,
5294
+ affinityModelId,
5295
+ ownershipLookupSessionId,
5296
+ ownershipWriteSessionId
5297
+ });
5298
+ const selectionReason = invalidSubagentMarkerSelectionReason ?? selection.selectionReason;
5370
5299
  if (!selection.ok) return handleSelectionFailure({
5371
5300
  c,
5372
5301
  store,
@@ -5383,7 +5312,10 @@ async function handleCompletion(c) {
5383
5312
  safetyIdentifier: normalizedSafetyIdentifier,
5384
5313
  promptCacheKey: normalizedPromptCacheKey,
5385
5314
  initiator: fallbackInitiator,
5386
- isSubagent: Boolean(subagentMarker),
5315
+ isSubagent: isSubagentRequest,
5316
+ affinityKeyUsed: affinityKey.affinityKeyUsed,
5317
+ affinityKeySource: affinityKey.affinityKeySource,
5318
+ selectionReason,
5387
5319
  selection
5388
5320
  });
5389
5321
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
@@ -5404,7 +5336,7 @@ async function handleCompletion(c) {
5404
5336
  userId,
5405
5337
  safetyIdentifier: normalizedSafetyIdentifier,
5406
5338
  promptCacheKey: normalizedPromptCacheKey,
5407
- isSubagent: Boolean(subagentMarker),
5339
+ isSubagent: isSubagentRequest,
5408
5340
  clientModel,
5409
5341
  account,
5410
5342
  reservation,
@@ -5415,14 +5347,17 @@ async function handleCompletion(c) {
5415
5347
  premiumRemainingBefore,
5416
5348
  premiumUnlimitedBefore,
5417
5349
  confirmAffinity: selection.confirmAffinity,
5350
+ confirmOwnership: selection.confirmOwnership,
5418
5351
  affinityHit: selection.affinityHit,
5419
- affinityCacheKey: selection.affinityCacheKey
5352
+ affinityCacheKey: selection.affinityCacheKey,
5353
+ affinityKeyUsed: affinityKey.affinityKeyUsed,
5354
+ affinityKeySource: affinityKey.affinityKeySource,
5355
+ selectionReason
5420
5356
  };
5421
5357
  if (endpoint === MESSAGES_ENDPOINT) return await handleWithMessagesApi({
5422
5358
  c,
5423
5359
  anthropicPayload,
5424
5360
  anthropicBetaHeader: anthropicBeta ?? void 0,
5425
- initiatorOverride,
5426
5361
  subagentMarker,
5427
5362
  sessionId,
5428
5363
  instr,
@@ -5433,7 +5368,6 @@ async function handleCompletion(c) {
5433
5368
  c,
5434
5369
  anthropicPayload,
5435
5370
  openAIPayload,
5436
- initiatorOverride,
5437
5371
  subagentMarker,
5438
5372
  sessionId,
5439
5373
  selectedModel,
@@ -5443,7 +5377,6 @@ async function handleCompletion(c) {
5443
5377
  return await handleWithChatCompletions({
5444
5378
  c,
5445
5379
  openAIPayload,
5446
- initiatorOverride,
5447
5380
  subagentMarker,
5448
5381
  sessionId,
5449
5382
  selectedModel,
@@ -5452,21 +5385,25 @@ async function handleCompletion(c) {
5452
5385
  });
5453
5386
  }
5454
5387
  const handleWithChatCompletions = async (params) => {
5455
- const { c, openAIPayload, initiatorOverride, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
5388
+ const { c, openAIPayload, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
5456
5389
  debugJson(logger$5, "Translated OpenAI request payload:", openAIPayload);
5457
5390
  const ctx = toAccountContext(instr.account);
5458
- const initiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
5459
- instr.initiator = initiator;
5391
+ const effectiveInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
5392
+ isCompact,
5393
+ isSubagent: Boolean(subagentMarker)
5394
+ });
5395
+ instr.initiator = effectiveInitiator;
5460
5396
  let response;
5461
5397
  try {
5462
5398
  response = await createChatCompletions(openAIPayload, ctx, {
5463
5399
  upstreamRequestId: instr.upstreamRequestId,
5464
- initiator,
5400
+ initiator: effectiveInitiator,
5465
5401
  subagentMarker,
5466
5402
  sessionId,
5467
5403
  isCompact
5468
5404
  });
5469
5405
  instr.confirmAffinity?.();
5406
+ instr.confirmOwnership?.();
5470
5407
  } catch (error) {
5471
5408
  return await handleChatCompletionsCreateError({
5472
5409
  error,
@@ -5496,26 +5433,30 @@ const handleWithChatCompletions = async (params) => {
5496
5433
  }));
5497
5434
  };
5498
5435
  const handleWithResponsesApi = async (params) => {
5499
- const { c, anthropicPayload, openAIPayload, initiatorOverride, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
5436
+ const { c, anthropicPayload, openAIPayload, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
5500
5437
  const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, selectedModel.id);
5501
5438
  applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
5502
5439
  compactInputByLatestCompaction(responsesPayload);
5503
5440
  debugJson(logger$5, "Translated Responses payload:", responsesPayload);
5504
5441
  const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
5505
- const resolvedInitiator = initiatorOverride ?? initiator;
5442
+ const effectiveInitiator = resolveEffectiveInitiator(initiator, {
5443
+ isCompact,
5444
+ isSubagent: Boolean(subagentMarker)
5445
+ });
5506
5446
  const ctx = toAccountContext(instr.account);
5507
- instr.initiator = resolvedInitiator;
5447
+ instr.initiator = effectiveInitiator;
5508
5448
  let response;
5509
5449
  try {
5510
5450
  response = await createResponses(responsesPayload, {
5511
5451
  vision,
5512
- initiator: resolvedInitiator,
5452
+ initiator: effectiveInitiator,
5513
5453
  upstreamRequestId: instr.upstreamRequestId,
5514
5454
  subagentMarker,
5515
5455
  sessionId,
5516
5456
  isCompact
5517
5457
  }, ctx);
5518
5458
  instr.confirmAffinity?.();
5459
+ instr.confirmOwnership?.();
5519
5460
  } catch (error) {
5520
5461
  return await handleResponsesCreateError({
5521
5462
  error,
@@ -5564,6 +5505,9 @@ function insertRequestLog$1(instr, record) {
5564
5505
  upstreamRequestId: instr.upstreamRequestId,
5565
5506
  affinityHit: instr.affinityHit,
5566
5507
  affinityCacheKey: instr.affinityCacheKey,
5508
+ affinityKeyUsed: instr.affinityKeyUsed,
5509
+ affinityKeySource: instr.affinityKeySource,
5510
+ selectionReason: instr.selectionReason,
5567
5511
  clientModel,
5568
5512
  upstreamEndpoint,
5569
5513
  accountId: account.id,
@@ -5587,8 +5531,8 @@ async function finalizeQuotaAndGetPremiumSnapshot(instr) {
5587
5531
  async function handleChatCompletionsCreateError(params) {
5588
5532
  const { error, instr, stream } = params;
5589
5533
  const finishedAtMs = Date.now();
5590
- const details = extractErrorDetails(error);
5591
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5534
+ const details = await extractErrorObservability(error);
5535
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5592
5536
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
5593
5537
  insertRequestLog$1(instr, {
5594
5538
  finishedAtMs,
@@ -5600,7 +5544,8 @@ async function handleChatCompletionsCreateError(params) {
5600
5544
  httpStatus: details.httpStatus,
5601
5545
  errorName: details.errorName,
5602
5546
  errorStatus: details.errorStatus,
5603
- errorMessage: details.errorMessage
5547
+ errorMessage: details.errorMessage,
5548
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
5604
5549
  });
5605
5550
  throw error;
5606
5551
  }
@@ -5611,6 +5556,7 @@ async function handleChatCompletionsNonStreaming(params) {
5611
5556
  let errorName;
5612
5557
  let errorStatus;
5613
5558
  let errorMessage;
5559
+ let upstreamErrorMessageRaw;
5614
5560
  const finishedAtMs = Date.now();
5615
5561
  try {
5616
5562
  logger$5.debug("Non-streaming response from Copilot:", JSON.stringify(response));
@@ -5618,12 +5564,13 @@ async function handleChatCompletionsNonStreaming(params) {
5618
5564
  debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
5619
5565
  return c.json(anthropicResponse);
5620
5566
  } catch (error) {
5621
- const details = extractErrorDetails(error);
5567
+ const details = await extractErrorObservability(error);
5622
5568
  httpStatus = details.httpStatus;
5623
5569
  errorName = details.errorName;
5624
5570
  errorStatus = details.errorStatus;
5625
5571
  errorMessage = details.errorMessage;
5626
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5572
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5573
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5627
5574
  throw error;
5628
5575
  } finally {
5629
5576
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5638,7 +5585,8 @@ async function handleChatCompletionsNonStreaming(params) {
5638
5585
  httpStatus,
5639
5586
  errorName,
5640
5587
  errorStatus,
5641
- errorMessage
5588
+ errorMessage,
5589
+ upstreamErrorMessageRaw
5642
5590
  });
5643
5591
  }
5644
5592
  }
@@ -5649,6 +5597,7 @@ async function streamChatCompletionsAndLog(params) {
5649
5597
  let errorName;
5650
5598
  let errorStatus;
5651
5599
  let errorMessage;
5600
+ let upstreamErrorMessageRaw;
5652
5601
  const streamState = {
5653
5602
  messageStartSent: false,
5654
5603
  contentBlockIndex: 0,
@@ -5680,12 +5629,14 @@ async function streamChatCompletionsAndLog(params) {
5680
5629
  }
5681
5630
  }
5682
5631
  } catch (error) {
5683
- const details = extractErrorDetails(error);
5632
+ const details = await extractErrorObservability(error);
5684
5633
  errorName = details.errorName;
5685
5634
  errorStatus = details.errorStatus;
5686
5635
  errorMessage = details.errorMessage;
5636
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5687
5637
  logger$5.warn("Streaming error:", error);
5688
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5638
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5639
+ await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
5689
5640
  } finally {
5690
5641
  const finishedAtMs = Date.now();
5691
5642
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5701,15 +5652,16 @@ async function streamChatCompletionsAndLog(params) {
5701
5652
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
5702
5653
  errorName,
5703
5654
  errorStatus,
5704
- errorMessage
5655
+ errorMessage,
5656
+ upstreamErrorMessageRaw
5705
5657
  });
5706
5658
  }
5707
5659
  }
5708
5660
  async function handleResponsesCreateError(params) {
5709
5661
  const { error, instr, stream } = params;
5710
5662
  const finishedAtMs = Date.now();
5711
- const details = extractErrorDetails(error);
5712
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5663
+ const details = await extractErrorObservability(error);
5664
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5713
5665
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
5714
5666
  insertRequestLog$1(instr, {
5715
5667
  finishedAtMs,
@@ -5721,7 +5673,8 @@ async function handleResponsesCreateError(params) {
5721
5673
  httpStatus: details.httpStatus,
5722
5674
  errorName: details.errorName,
5723
5675
  errorStatus: details.errorStatus,
5724
- errorMessage: details.errorMessage
5676
+ errorMessage: details.errorMessage,
5677
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
5725
5678
  });
5726
5679
  throw error;
5727
5680
  }
@@ -5732,6 +5685,7 @@ async function handleResponsesNonStreaming(params) {
5732
5685
  let errorName;
5733
5686
  let errorStatus;
5734
5687
  let errorMessage;
5688
+ let upstreamErrorMessageRaw;
5735
5689
  const finishedAtMs = Date.now();
5736
5690
  try {
5737
5691
  usage = extractResponsesUsageFromResult(result);
@@ -5740,12 +5694,13 @@ async function handleResponsesNonStreaming(params) {
5740
5694
  debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
5741
5695
  return c.json(anthropicResponse);
5742
5696
  } catch (error) {
5743
- const details = extractErrorDetails(error);
5697
+ const details = await extractErrorObservability(error);
5744
5698
  httpStatus = details.httpStatus;
5745
5699
  errorName = details.errorName;
5746
5700
  errorStatus = details.errorStatus;
5747
5701
  errorMessage = details.errorMessage;
5748
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5702
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5703
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5749
5704
  throw error;
5750
5705
  } finally {
5751
5706
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5760,7 +5715,8 @@ async function handleResponsesNonStreaming(params) {
5760
5715
  httpStatus,
5761
5716
  errorName,
5762
5717
  errorStatus,
5763
- errorMessage
5718
+ errorMessage,
5719
+ upstreamErrorMessageRaw
5764
5720
  });
5765
5721
  }
5766
5722
  }
@@ -5776,6 +5732,17 @@ async function ensureResponsesStreamCompleted(params) {
5776
5732
  data: JSON.stringify(errorEvent)
5777
5733
  });
5778
5734
  }
5735
+ async function writeAnthropicStreamError(stream, message) {
5736
+ try {
5737
+ const errorEvent = buildErrorEvent(message);
5738
+ await stream.writeSSE({
5739
+ event: errorEvent.type,
5740
+ data: JSON.stringify(errorEvent)
5741
+ });
5742
+ } catch (streamError) {
5743
+ logger$5.warn("Failed to write Anthropic stream error event:", streamError);
5744
+ }
5745
+ }
5779
5746
  async function streamResponsesAndLog$1(params) {
5780
5747
  const { stream, response, instr, estimatedInputTokens, historicalUsage } = params;
5781
5748
  let ttfbMs;
@@ -5783,6 +5750,7 @@ async function streamResponsesAndLog$1(params) {
5783
5750
  let errorName;
5784
5751
  let errorStatus;
5785
5752
  let errorMessage;
5753
+ let upstreamErrorMessageRaw;
5786
5754
  const streamState = createResponsesStreamState();
5787
5755
  streamState.estimatedInputTokens = estimatedInputTokens;
5788
5756
  streamState.historicalInputTokens = historicalUsage?.tokensInput;
@@ -5827,12 +5795,14 @@ async function streamResponsesAndLog$1(params) {
5827
5795
  }
5828
5796
  });
5829
5797
  } catch (error) {
5830
- const details = extractErrorDetails(error);
5798
+ const details = await extractErrorObservability(error);
5831
5799
  errorName = details.errorName;
5832
5800
  errorStatus = details.errorStatus;
5833
5801
  errorMessage = details.errorMessage;
5802
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5834
5803
  logger$5.warn("Streaming error:", error);
5835
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5804
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5805
+ await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
5836
5806
  } finally {
5837
5807
  const finishedAtMs = Date.now();
5838
5808
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5848,15 +5818,16 @@ async function streamResponsesAndLog$1(params) {
5848
5818
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
5849
5819
  errorName,
5850
5820
  errorStatus,
5851
- errorMessage
5821
+ errorMessage,
5822
+ upstreamErrorMessageRaw
5852
5823
  });
5853
5824
  }
5854
5825
  }
5855
5826
  async function handleMessagesCreateError(params) {
5856
5827
  const { error, instr, stream } = params;
5857
5828
  const finishedAtMs = Date.now();
5858
- const details = extractErrorDetails(error);
5859
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5829
+ const details = await extractErrorObservability(error);
5830
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5860
5831
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
5861
5832
  insertRequestLog$1(instr, {
5862
5833
  finishedAtMs,
@@ -5868,7 +5839,8 @@ async function handleMessagesCreateError(params) {
5868
5839
  httpStatus: details.httpStatus,
5869
5840
  errorName: details.errorName,
5870
5841
  errorStatus: details.errorStatus,
5871
- errorMessage: details.errorMessage
5842
+ errorMessage: details.errorMessage,
5843
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
5872
5844
  });
5873
5845
  throw error;
5874
5846
  }
@@ -5879,17 +5851,19 @@ async function handleMessagesNonStreaming(params) {
5879
5851
  let errorName;
5880
5852
  let errorStatus;
5881
5853
  let errorMessage;
5854
+ let upstreamErrorMessageRaw;
5882
5855
  const finishedAtMs = Date.now();
5883
5856
  try {
5884
5857
  logger$5.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
5885
5858
  return c.json(response);
5886
5859
  } catch (error) {
5887
- const details = extractErrorDetails(error);
5860
+ const details = await extractErrorObservability(error);
5888
5861
  httpStatus = details.httpStatus;
5889
5862
  errorName = details.errorName;
5890
5863
  errorStatus = details.errorStatus;
5891
5864
  errorMessage = details.errorMessage;
5892
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5865
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5866
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5893
5867
  throw error;
5894
5868
  } finally {
5895
5869
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5904,7 +5878,8 @@ async function handleMessagesNonStreaming(params) {
5904
5878
  httpStatus,
5905
5879
  errorName,
5906
5880
  errorStatus,
5907
- errorMessage
5881
+ errorMessage,
5882
+ upstreamErrorMessageRaw
5908
5883
  });
5909
5884
  }
5910
5885
  }
@@ -5926,6 +5901,7 @@ async function streamMessagesAndLog(params) {
5926
5901
  let errorName;
5927
5902
  let errorStatus;
5928
5903
  let errorMessage;
5904
+ let upstreamErrorMessageRaw;
5929
5905
  try {
5930
5906
  for await (const rawEvent of response) {
5931
5907
  if (ttfbMs === void 0) ttfbMs = Date.now() - instr.startedAtMs;
@@ -5941,12 +5917,14 @@ async function streamMessagesAndLog(params) {
5941
5917
  });
5942
5918
  }
5943
5919
  } catch (error) {
5944
- const details = extractErrorDetails(error);
5920
+ const details = await extractErrorObservability(error);
5945
5921
  errorName = details.errorName;
5946
5922
  errorStatus = details.errorStatus;
5947
5923
  errorMessage = details.errorMessage;
5924
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5948
5925
  logger$5.warn("Streaming error:", error);
5949
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5926
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5927
+ await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
5950
5928
  } finally {
5951
5929
  const finishedAtMs = Date.now();
5952
5930
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5962,28 +5940,33 @@ async function streamMessagesAndLog(params) {
5962
5940
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
5963
5941
  errorName,
5964
5942
  errorStatus,
5965
- errorMessage
5943
+ errorMessage,
5944
+ upstreamErrorMessageRaw
5966
5945
  });
5967
5946
  }
5968
5947
  }
5969
5948
  const handleWithMessagesApi = async (params) => {
5970
- const { c, anthropicPayload, anthropicBetaHeader, initiatorOverride, subagentMarker, sessionId, instr, selectedModel, isCompact } = params;
5949
+ const { c, anthropicPayload, anthropicBetaHeader, subagentMarker, sessionId, instr, selectedModel, isCompact } = params;
5971
5950
  prepareMessagesApiPayload(anthropicPayload, selectedModel);
5972
5951
  debugJson(logger$5, "Translated Messages payload:", anthropicPayload);
5973
5952
  const ctx = toAccountContext(instr.account);
5974
- const initiator = initiatorOverride ?? getMessagesInitiator(anthropicPayload);
5975
- instr.initiator = initiator;
5953
+ const effectiveInitiator = resolveEffectiveInitiator(getMessagesInitiator(anthropicPayload), {
5954
+ isCompact,
5955
+ isSubagent: Boolean(subagentMarker)
5956
+ });
5957
+ instr.initiator = effectiveInitiator;
5976
5958
  let response;
5977
5959
  try {
5978
5960
  response = await createMessages(anthropicPayload, ctx, {
5979
5961
  anthropicBetaHeader,
5980
5962
  upstreamRequestId: instr.upstreamRequestId,
5981
- initiator,
5963
+ initiator: effectiveInitiator,
5982
5964
  subagentMarker,
5983
5965
  sessionId,
5984
5966
  isCompact
5985
5967
  });
5986
5968
  instr.confirmAffinity?.();
5969
+ instr.confirmOwnership?.();
5987
5970
  } catch (error) {
5988
5971
  return await handleMessagesCreateError({
5989
5972
  error,
@@ -6031,9 +6014,8 @@ messageRoutes.post("/count_tokens", async (c) => {
6031
6014
  const modelRoutes = new Hono();
6032
6015
  modelRoutes.get("/", async (c) => {
6033
6016
  try {
6034
- if (!state.models) await cacheModels();
6035
6017
  const blockedTargets = getAliasTargetSet();
6036
- const models = state.models?.data.filter((model) => !blockedTargets.has(model.id.toLowerCase())).map((model) => ({
6018
+ const models = getAvailableModels().filter((model) => !blockedTargets.has(model.id.toLowerCase())).map((model) => ({
6037
6019
  id: model.id,
6038
6020
  object: "model",
6039
6021
  type: "model",
@@ -6041,7 +6023,7 @@ modelRoutes.get("/", async (c) => {
6041
6023
  created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
6042
6024
  owned_by: model.vendor,
6043
6025
  display_name: model.name
6044
- })) ?? [];
6026
+ }));
6045
6027
  const aliasModels = Object.keys(getModelAliases()).map((alias) => ({
6046
6028
  id: alias,
6047
6029
  object: "model",
@@ -6090,7 +6072,7 @@ async function handleProviderCountTokens(c) {
6090
6072
  const anthropicPayload = await c.req.json();
6091
6073
  const openAIPayload = translateToOpenAI(anthropicPayload);
6092
6074
  const modelId = anthropicPayload.model.trim();
6093
- let selectedModel = state.models?.data.find((model) => model.id === modelId);
6075
+ let selectedModel = getAvailableModels().find((model) => model.id === modelId);
6094
6076
  if (!selectedModel && modelId) selectedModel = createFallbackModel(modelId);
6095
6077
  if (!selectedModel) {
6096
6078
  logger$4.warn("provider.count_tokens.model_not_found", {
@@ -6350,9 +6332,10 @@ const handleResponses = async (c) => {
6350
6332
  const streamRequested = Boolean(payload.stream);
6351
6333
  const { initiator: initialInitiator } = getResponsesRequestOptions(payload);
6352
6334
  const userId = payload.metadata?.user_id;
6353
- const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
6335
+ const requestBodyPromptCacheKey = typeof payload.prompt_cache_key === "string" ? payload.prompt_cache_key : null;
6336
+ const { safetyIdentifier, sessionId: metadataSessionId } = parseUserIdMetadata(userId);
6354
6337
  const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
6355
- const normalizedPromptCacheKey = promptCacheKey ?? void 0;
6338
+ const normalizedPromptCacheKey = requestBodyPromptCacheKey ?? metadataSessionId ?? void 0;
6356
6339
  request.userId = userId;
6357
6340
  request.safetyIdentifier = normalizedSafetyIdentifier;
6358
6341
  request.promptCacheKey = normalizedPromptCacheKey;
@@ -6370,10 +6353,19 @@ const handleResponses = async (c) => {
6370
6353
  });
6371
6354
  }
6372
6355
  const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, normalizedPromptCacheKey);
6356
+ const headerSessionId = c.req.header("x-session-id") ?? null;
6357
+ const affinityKey = resolveAffinityKey({
6358
+ promptCacheKey: requestBodyPromptCacheKey,
6359
+ metadataSessionId,
6360
+ headerSessionId,
6361
+ upstreamRequestId
6362
+ });
6363
+ request.affinityKeyUsed = affinityKey.affinityKeyUsed;
6364
+ request.affinityKeySource = affinityKey.affinityKeySource;
6373
6365
  const selection = await accountsManager.selectAccountForRequest([{
6374
6366
  modelId: clientModel,
6375
6367
  endpoint: RESPONSES_ENDPOINT
6376
- }], { requestId: upstreamRequestId });
6368
+ }], { requestId: affinityKey.requestId });
6377
6369
  if (!selection.ok) {
6378
6370
  recordSelectionFailure(store, {
6379
6371
  request,
@@ -6386,6 +6378,7 @@ const handleResponses = async (c) => {
6386
6378
  const { account, selectedModel } = selection;
6387
6379
  request.affinityHit = selection.affinityHit;
6388
6380
  request.affinityCacheKey = selection.affinityCacheKey;
6381
+ request.selectionReason = selection.selectionReason;
6389
6382
  const upstreamPayload = {
6390
6383
  ...payload,
6391
6384
  model: selectedModel.id
@@ -6399,7 +6392,7 @@ const handleResponses = async (c) => {
6399
6392
  request.initiator = initiator;
6400
6393
  if (state.manualApprove) await awaitApproval();
6401
6394
  const accountCtx = toAccountContext(account);
6402
- const upstreamSessionId = getUUID(upstreamRequestId);
6395
+ const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
6403
6396
  request.upstreamRequestId = upstreamRequestId;
6404
6397
  request.upstreamSessionId = upstreamSessionId;
6405
6398
  if (streamRequested) return handleStreamingResponses({
@@ -6429,6 +6422,37 @@ const handleResponses = async (c) => {
6429
6422
  premiumUnlimitedBefore
6430
6423
  });
6431
6424
  };
6425
+ async function observeRequestError(accountId, error) {
6426
+ const details = await extractErrorObservability(error);
6427
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(accountId, "Unauthorized (401)");
6428
+ return {
6429
+ httpStatus: details.httpStatus,
6430
+ errorName: details.errorName,
6431
+ errorStatus: details.errorStatus,
6432
+ errorMessage: details.errorMessage,
6433
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
6434
+ };
6435
+ }
6436
+ function buildResponsesStreamError(message) {
6437
+ return {
6438
+ type: "error",
6439
+ code: null,
6440
+ message,
6441
+ param: null,
6442
+ sequence_number: 0
6443
+ };
6444
+ }
6445
+ async function writeResponsesStreamError(stream, message) {
6446
+ try {
6447
+ const errorEvent = buildResponsesStreamError(message);
6448
+ await stream.writeSSE({
6449
+ event: errorEvent.type,
6450
+ data: JSON.stringify(errorEvent)
6451
+ });
6452
+ } catch (streamError) {
6453
+ logger$1.warn("Failed to write Responses stream error event:", streamError);
6454
+ }
6455
+ }
6432
6456
  function buildRequestContext(c) {
6433
6457
  const requestId = randomUUID();
6434
6458
  const startedAtMs = Date.now();
@@ -6459,6 +6483,9 @@ function insertRequestLog(store, request, record) {
6459
6483
  promptCacheKey: request.promptCacheKey,
6460
6484
  initiator: request.initiator,
6461
6485
  upstreamRequestId: request.upstreamRequestId,
6486
+ affinityKeyUsed: request.affinityKeyUsed,
6487
+ affinityKeySource: request.affinityKeySource,
6488
+ selectionReason: request.selectionReason,
6462
6489
  affinityHit: request.affinityHit,
6463
6490
  affinityCacheKey: request.affinityCacheKey,
6464
6491
  ...record
@@ -6497,14 +6524,6 @@ function extractUsageFromChunkData(data) {
6497
6524
  return;
6498
6525
  }
6499
6526
  }
6500
- function getStreamChunkFields(chunk) {
6501
- const c = chunk;
6502
- return {
6503
- id: c.id,
6504
- event: c.event,
6505
- data: c.data
6506
- };
6507
- }
6508
6527
  async function handleStreamingResponses(params) {
6509
6528
  const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore } = params;
6510
6529
  let response;
@@ -6555,8 +6574,8 @@ async function handleUpstreamCreateError(params) {
6555
6574
  const { store, request, selection, clientModel, premiumRemainingBefore, premiumUnlimitedBefore, error } = params;
6556
6575
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
6557
6576
  const finishedAtMs = Date.now();
6558
- const details = extractErrorDetails(error);
6559
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
6577
+ const details = await extractErrorObservability(error);
6578
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
6560
6579
  await accountsManager.finalizeQuota(account, reservation);
6561
6580
  const premiumRemainingAfter = account.premiumRemaining;
6562
6581
  const premiumUnlimitedAfter = account.unlimited;
@@ -6578,7 +6597,8 @@ async function handleUpstreamCreateError(params) {
6578
6597
  httpStatus: details.httpStatus,
6579
6598
  errorName: details.errorName,
6580
6599
  errorStatus: details.errorStatus,
6581
- errorMessage: details.errorMessage
6600
+ errorMessage: details.errorMessage,
6601
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
6582
6602
  });
6583
6603
  throw error;
6584
6604
  }
@@ -6590,6 +6610,7 @@ async function handleNonStreamingUpstreamResult(params) {
6590
6610
  let errorName;
6591
6611
  let errorStatus;
6592
6612
  let errorMessage;
6613
+ let upstreamErrorMessageRaw;
6593
6614
  const finishedAtMs = Date.now();
6594
6615
  try {
6595
6616
  debugJsonTail(logger$1, "Forwarding native Responses result:", {
@@ -6598,11 +6619,12 @@ async function handleNonStreamingUpstreamResult(params) {
6598
6619
  });
6599
6620
  return c.json(result);
6600
6621
  } catch (error) {
6601
- const details = extractErrorDetails(error);
6622
+ const details = await extractErrorObservability(error);
6602
6623
  httpStatus = details.httpStatus;
6603
6624
  errorName = details.errorName;
6604
6625
  errorStatus = details.errorStatus;
6605
6626
  errorMessage = details.errorMessage;
6627
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
6606
6628
  throw error;
6607
6629
  } finally {
6608
6630
  await accountsManager.finalizeQuota(account, reservation);
@@ -6627,7 +6649,8 @@ async function handleNonStreamingUpstreamResult(params) {
6627
6649
  httpStatus,
6628
6650
  errorName,
6629
6651
  errorStatus,
6630
- errorMessage
6652
+ errorMessage,
6653
+ upstreamErrorMessageRaw
6631
6654
  });
6632
6655
  }
6633
6656
  }
@@ -6640,6 +6663,7 @@ async function streamResponsesAndLog(params) {
6640
6663
  let errorName;
6641
6664
  let errorStatus;
6642
6665
  let errorMessage;
6666
+ let upstreamErrorMessageRaw;
6643
6667
  try {
6644
6668
  for await (const chunk of response) {
6645
6669
  if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
@@ -6655,11 +6679,14 @@ async function streamResponsesAndLog(params) {
6655
6679
  });
6656
6680
  }
6657
6681
  } catch (error) {
6658
- const details = extractErrorDetails(error);
6682
+ const details = await extractErrorObservability(error);
6659
6683
  errorName = details.errorName;
6660
6684
  errorStatus = details.errorStatus;
6661
6685
  errorMessage = details.errorMessage;
6686
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
6662
6687
  logger$1.warn("Responses streaming error:", error);
6688
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
6689
+ await writeResponsesStreamError(stream, getUserVisibleErrorMessage(details));
6663
6690
  } finally {
6664
6691
  const finishedAtMs = Date.now();
6665
6692
  await accountsManager.finalizeQuota(account, reservation);
@@ -6685,18 +6712,16 @@ async function streamResponsesAndLog(params) {
6685
6712
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
6686
6713
  errorName,
6687
6714
  errorStatus,
6688
- errorMessage
6715
+ errorMessage,
6716
+ upstreamErrorMessageRaw
6689
6717
  });
6690
6718
  }
6691
6719
  }
6692
6720
  async function handleNonStreamingResponses(params) {
6693
6721
  const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore } = params;
6694
6722
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
6695
- let httpStatus = 200;
6696
6723
  let usage = {};
6697
- let errorName;
6698
- let errorStatus;
6699
- let errorMessage;
6724
+ let errorState = { httpStatus: 200 };
6700
6725
  let finishedAtMs;
6701
6726
  try {
6702
6727
  const response = await createResponses(payload, {
@@ -6705,10 +6730,9 @@ async function handleNonStreamingResponses(params) {
6705
6730
  upstreamRequestId: request.upstreamRequestId,
6706
6731
  sessionId: request.upstreamSessionId
6707
6732
  }, accountCtx);
6733
+ if (isAsyncIterable(response)) throw new Error("Upstream returned a stream unexpectedly");
6708
6734
  selection.confirmAffinity?.();
6709
6735
  finishedAtMs = Date.now();
6710
- const streamResponse = handleUnexpectedResponsesStream(c, response);
6711
- if (streamResponse) return streamResponse;
6712
6736
  const result = response;
6713
6737
  usage = extractResponsesUsageFromResult(result);
6714
6738
  debugJsonTail(logger$1, "Forwarding native Responses result:", {
@@ -6718,12 +6742,7 @@ async function handleNonStreamingResponses(params) {
6718
6742
  return c.json(result);
6719
6743
  } catch (error) {
6720
6744
  finishedAtMs = Date.now();
6721
- const details = extractErrorDetails(error);
6722
- httpStatus = details.httpStatus;
6723
- errorName = details.errorName;
6724
- errorStatus = details.errorStatus;
6725
- errorMessage = details.errorMessage;
6726
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
6745
+ errorState = await observeRequestError(account.id, error);
6727
6746
  throw error;
6728
6747
  } finally {
6729
6748
  const finishedAtMsFinal = finishedAtMs ?? Date.now();
@@ -6746,61 +6765,14 @@ async function handleNonStreamingResponses(params) {
6746
6765
  premiumRemainingDiff: computeDiff(premiumRemainingBefore, premiumRemainingAfter),
6747
6766
  premiumUnlimitedBefore,
6748
6767
  premiumUnlimitedAfter,
6749
- httpStatus,
6750
- errorName,
6751
- errorStatus,
6752
- errorMessage
6768
+ httpStatus: errorState.httpStatus,
6769
+ errorName: errorState.errorName,
6770
+ errorStatus: errorState.errorStatus,
6771
+ errorMessage: errorState.errorMessage,
6772
+ upstreamErrorMessageRaw: errorState.upstreamErrorMessageRaw
6753
6773
  });
6754
6774
  }
6755
6775
  }
6756
- function handleUnexpectedResponsesStream(c, response) {
6757
- if (!isAsyncIterable(response)) return null;
6758
- logger$1.debug("Forwarding native Responses stream (unexpected)");
6759
- return streamSSE(c, async (stream) => {
6760
- const idTracker = createStreamIdTracker();
6761
- for await (const chunk of response) {
6762
- const { id, event, data } = getStreamChunkFields(chunk);
6763
- const processedData = fixStreamIds(data ?? "", event, idTracker);
6764
- debugJson(logger$1, "Responses stream chunk:", chunk);
6765
- await stream.writeSSE({
6766
- id,
6767
- event,
6768
- data: processedData
6769
- });
6770
- }
6771
- });
6772
- }
6773
- const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
6774
- const useFunctionApplyPatch = (payload) => {
6775
- if (!(getConfig().useFunctionApplyPatch ?? true)) return;
6776
- logger$1.debug("Using function tool apply_patch for responses");
6777
- if (Array.isArray(payload.tools)) {
6778
- const toolsArr = payload.tools;
6779
- for (let i = 0; i < toolsArr.length; i++) {
6780
- const t = toolsArr[i];
6781
- if (t.type === "custom" && t.name === "apply_patch") toolsArr[i] = {
6782
- type: "function",
6783
- name: t.name,
6784
- description: "Use the `apply_patch` tool to edit files",
6785
- parameters: {
6786
- type: "object",
6787
- properties: { input: {
6788
- type: "string",
6789
- description: "The entire contents of the apply_patch command"
6790
- } },
6791
- required: ["input"]
6792
- },
6793
- strict: false
6794
- };
6795
- }
6796
- }
6797
- };
6798
- const removeWebSearchTool = (payload) => {
6799
- if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
6800
- payload.tools = payload.tools.filter((t) => {
6801
- return t.type !== "web_search";
6802
- });
6803
- };
6804
6776
 
6805
6777
  //#endregion
6806
6778
  //#region src/routes/responses/route.ts
@@ -6894,4 +6866,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
6894
6866
 
6895
6867
  //#endregion
6896
6868
  export { server };
6897
- //# sourceMappingURL=server-DVpkQrk2.js.map
6869
+ //# sourceMappingURL=server-DAxpfPde.js.map