@nick3/copilot-api 1.5.6 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +41 -31
  2. package/README.zh-CN.md +953 -0
  3. package/dist/{account-AacnHem5.js → account-B2tSWtVS.js} +12 -4
  4. package/dist/account-B2tSWtVS.js.map +1 -0
  5. package/dist/accounts-manager-CAyZJSn8.js +2932 -0
  6. package/dist/accounts-manager-CAyZJSn8.js.map +1 -0
  7. package/dist/admin/assets/index-BESw8Vvd.css +1 -0
  8. package/dist/admin/assets/index-Ddo9RHg-.js +101 -0
  9. package/dist/admin/index.html +2 -2
  10. package/dist/{auth-B7x3wjry.js → auth-BXCeDjRG.js} +3 -3
  11. package/dist/{auth-B7x3wjry.js.map → auth-BXCeDjRG.js.map} +1 -1
  12. package/dist/{check-usage-B1cbDEOI.js → check-usage-CQxXYfUx.js} +3 -3
  13. package/dist/check-usage-CQxXYfUx.js.map +1 -0
  14. package/dist/{get-copilot-token-cha9rQwA.js → get-copilot-token-p17sJyPU.js} +2 -2
  15. package/dist/{get-copilot-token-cha9rQwA.js.map → get-copilot-token-p17sJyPU.js.map} +1 -1
  16. package/dist/main.js +3 -3
  17. package/dist/{poll-access-token-DFooFWhY.js → poll-access-token-Bc6VwWab.js} +105 -40
  18. package/dist/poll-access-token-Bc6VwWab.js.map +1 -0
  19. package/dist/{server-DVpkQrk2.js → server-CFijvv3C.js} +969 -945
  20. package/dist/server-CFijvv3C.js.map +1 -0
  21. package/dist/{start-fPbCDj4c.js → start-DQlnH71A.js} +7 -6
  22. package/dist/start-DQlnH71A.js.map +1 -0
  23. package/package.json +1 -1
  24. package/dist/account-AacnHem5.js.map +0 -1
  25. package/dist/accounts-manager-BE-Dq5Wn.js +0 -1494
  26. package/dist/accounts-manager-BE-Dq5Wn.js.map +0 -1
  27. package/dist/admin/assets/index-CdoHTemy.css +0 -1
  28. package/dist/admin/assets/index-wcoGQpIM.js +0 -66
  29. package/dist/check-usage-B1cbDEOI.js.map +0 -1
  30. package/dist/poll-access-token-DFooFWhY.js.map +0 -1
  31. package/dist/server-DVpkQrk2.js.map +0 -1
  32. 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 captureOutboundHeadersSnapshot, D as prepareMessageProxyHeaders, E as prepareInteractionHeaders, M as requestContext, N as resolveTraceId, 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, 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-Bc6VwWab.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-B2tSWtVS.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-p17sJyPU.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-CAyZJSn8.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,81 @@ 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(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6).getTime());
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
+ /** DST-safe end-of-day: next local midnight minus 1 ms. */
1191
+ function localDateEndMs(dateStr) {
1192
+ const [y, m, d] = dateStr.split("-").map(Number);
1193
+ return new Date(y, m - 1, d + 1).getTime() - 1;
1194
+ }
1195
+ const statsStore = getStatsStore();
1196
+ if (!statsStore) return c.json({
1197
+ daily: [],
1198
+ by_account: [],
1199
+ range: {
1200
+ from: resolvedFrom,
1201
+ to: resolvedTo,
1202
+ granularity
1203
+ }
1204
+ });
1205
+ if (granularity === "hour") {
1206
+ const clientFromMs = parseFiniteNumber(p.get("from_ms"));
1207
+ const clientToMs = parseFiniteNumber(p.get("to_ms"));
1208
+ let fromMs;
1209
+ let toMs;
1210
+ if (clientFromMs !== void 0 && clientToMs !== void 0) {
1211
+ fromMs = clientFromMs;
1212
+ toMs = clientToMs;
1213
+ } else {
1214
+ fromMs = parseLocalDate(resolvedFrom).getTime();
1215
+ toMs = localDateEndMs(resolvedTo);
1216
+ }
1217
+ if (toMs - fromMs > 840 * 60 * 60 * 1e3) return jsonError(c, 400, {
1218
+ message: "Hourly granularity is only supported for ranges up to 35 days.",
1219
+ type: "bad_request"
1220
+ });
1221
+ const result$1 = statsStore.getHourlyPremiumStats({
1222
+ fromMs,
1223
+ toMs,
1224
+ accountId
1225
+ });
1226
+ return c.json({
1227
+ daily: result$1.daily,
1228
+ by_account: result$1.byAccount,
1229
+ range: {
1230
+ from: resolvedFrom,
1231
+ to: resolvedTo,
1232
+ granularity
1233
+ }
1234
+ });
1235
+ }
1236
+ const result = statsStore.getDailyPremiumStats({
1237
+ from: resolvedFrom,
1238
+ to: resolvedTo,
1239
+ accountId
1240
+ });
1241
+ return c.json({
1242
+ daily: result.daily,
1243
+ by_account: result.byAccount,
1244
+ range: {
1245
+ from: resolvedFrom,
1246
+ to: resolvedTo,
1247
+ granularity
1248
+ }
1249
+ });
1250
+ });
1699
1251
 
1700
1252
  //#endregion
1701
1253
  //#region src/routes/admin/route.ts
@@ -2365,39 +1917,122 @@ function toAccountContext(account) {
2365
1917
  clientSessionId: account.clientSessionId
2366
1918
  };
2367
1919
  }
1920
+ const OWNERSHIP_MISMATCH_PATTERN = /does not belong to this connection/i;
1921
+ 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;
1922
+ const OPAQUE_ID_PATTERN = /\b(?:msg|resp|chatcmpl|out|toolu|call|item)_[\w-]+\b/g;
1923
+ const UPSTREAM_ERROR_MAX_LENGTH = 1e3;
1924
+ const UPSTREAM_ERROR_READ_FAILED_SENTINEL = "[upstream body read failed]";
1925
+ function sanitizeUpstreamErrorMessage(value) {
1926
+ return truncate(value.trim().replaceAll(UUID_PATTERN, "<uuid>").replaceAll(OPAQUE_ID_PATTERN, "<opaque_id>"), UPSTREAM_ERROR_MAX_LENGTH);
1927
+ }
1928
+ function readErrorField(value) {
1929
+ return typeof value === "string" ? value : void 0;
1930
+ }
1931
+ function formatUpstreamErrorBody(payload) {
1932
+ if (typeof payload === "string") {
1933
+ const text = payload.trim();
1934
+ return text.length > 0 ? text : void 0;
1935
+ }
1936
+ if (!payload || typeof payload !== "object") return;
1937
+ const root = payload;
1938
+ const message = readErrorField(root.error?.message) ?? readErrorField(root.message);
1939
+ const code = readErrorField(root.error?.code) ?? readErrorField(root.code);
1940
+ if (message && code) return `${message} [code:${code}]`;
1941
+ return message ?? (code ? `[code:${code}]` : void 0);
1942
+ }
1943
+ async function extractUpstreamErrorMessageRaw(error) {
1944
+ if (!(error instanceof HTTPError)) return { upstreamErrorMessageReadFailed: false };
1945
+ try {
1946
+ const bodyText = await error.response.clone().text();
1947
+ if (!bodyText) return { upstreamErrorMessageReadFailed: false };
1948
+ let candidate;
1949
+ try {
1950
+ candidate = formatUpstreamErrorBody(JSON.parse(bodyText));
1951
+ } catch {
1952
+ candidate = bodyText;
1953
+ }
1954
+ if (!candidate) return { upstreamErrorMessageReadFailed: false };
1955
+ const sanitized = sanitizeUpstreamErrorMessage(candidate);
1956
+ return {
1957
+ upstreamErrorMessageRaw: sanitized.length > 0 ? sanitized : void 0,
1958
+ upstreamErrorMessageReadFailed: false
1959
+ };
1960
+ } catch (readError) {
1961
+ consola.warn("Failed to read upstream HTTP error response body:", {
1962
+ status: error.response.status,
1963
+ readError
1964
+ });
1965
+ return { upstreamErrorMessageReadFailed: true };
1966
+ }
1967
+ }
2368
1968
  function extractErrorDetails(error) {
2369
1969
  const errorName = error instanceof Error ? error.name : "Error";
2370
1970
  const errorMessage = error instanceof Error ? truncate(error.message) : truncate(String(error));
2371
1971
  const errorStatus = error instanceof HTTPError ? error.response.status : void 0;
1972
+ const httpStatus = errorStatus ?? 500;
1973
+ const unauthorized = errorStatus === 401;
2372
1974
  return {
2373
- httpStatus: errorStatus ?? 500,
1975
+ httpStatus,
2374
1976
  errorName,
2375
1977
  errorStatus,
2376
1978
  errorMessage,
2377
- unauthorized: errorStatus === 401
1979
+ unauthorized,
1980
+ ownershipMismatch: unauthorized && OWNERSHIP_MISMATCH_PATTERN.test(errorMessage)
1981
+ };
1982
+ }
1983
+ async function extractErrorObservability(error) {
1984
+ const details = extractErrorDetails(error);
1985
+ const { upstreamErrorMessageRaw, upstreamErrorMessageReadFailed } = await extractUpstreamErrorMessageRaw(error);
1986
+ const observableUpstreamErrorMessageRaw = upstreamErrorMessageRaw ?? (upstreamErrorMessageReadFailed ? UPSTREAM_ERROR_READ_FAILED_SENTINEL : void 0);
1987
+ return {
1988
+ ...details,
1989
+ ownershipMismatch: details.ownershipMismatch || details.unauthorized && OWNERSHIP_MISMATCH_PATTERN.test(upstreamErrorMessageRaw ?? ""),
1990
+ upstreamErrorMessageRaw: observableUpstreamErrorMessageRaw,
1991
+ upstreamErrorMessageReadFailed
2378
1992
  };
2379
1993
  }
1994
+ function getUserVisibleErrorMessage(details) {
1995
+ if (details.upstreamErrorMessageReadFailed) return details.errorMessage;
1996
+ return details.upstreamErrorMessageRaw ?? details.errorMessage;
1997
+ }
1998
+ /**
1999
+ * Returns true when the 401 indicates a genuine auth/token failure
2000
+ * (not an ownership mismatch) and the account should be marked failed.
2001
+ */
2002
+ function shouldMarkAccountFailed(details) {
2003
+ return details.unauthorized && !details.ownershipMismatch && details.upstreamErrorMessageReadFailed !== true;
2004
+ }
2380
2005
 
2381
2006
  //#endregion
2382
2007
  //#region src/lib/logger.ts
2383
2008
  const LOG_RETENTION_MS = 10080 * 60 * 1e3;
2384
2009
  const CLEANUP_INTERVAL_MS = 1440 * 60 * 1e3;
2385
- const LOG_DIR = path.join(PATHS.APP_DIR, "logs");
2386
2010
  const FLUSH_INTERVAL_MS = 1e3;
2387
2011
  const MAX_BUFFER_SIZE = 100;
2012
+ const FILE_LOG_LEVEL_PRIORITY = {
2013
+ error: 0,
2014
+ warn: 1,
2015
+ info: 2,
2016
+ debug: 3
2017
+ };
2018
+ let logDir = path.join(PATHS.APP_DIR, "logs");
2019
+ let testLogLevelOverride;
2388
2020
  const logStreams = /* @__PURE__ */ new Map();
2389
2021
  const logBuffers = /* @__PURE__ */ new Map();
2390
2022
  let runtimeInitialized = false;
2391
2023
  let flushInterval;
2392
2024
  let cleanupInterval;
2025
+ let exitHandler;
2026
+ let sigintHandler;
2027
+ let sigtermHandler;
2393
2028
  const ensureLogDirectory = () => {
2394
- if (!fs$1.existsSync(LOG_DIR)) fs$1.mkdirSync(LOG_DIR, { recursive: true });
2029
+ if (!fs$1.existsSync(logDir)) fs$1.mkdirSync(logDir, { recursive: true });
2395
2030
  };
2396
2031
  const cleanupOldLogs = () => {
2397
- if (!fs$1.existsSync(LOG_DIR)) return;
2032
+ if (!fs$1.existsSync(logDir)) return;
2398
2033
  const now = Date.now();
2399
- for (const entry of fs$1.readdirSync(LOG_DIR)) {
2400
- const filePath = path.join(LOG_DIR, entry);
2034
+ for (const entry of fs$1.readdirSync(logDir)) {
2035
+ const filePath = path.join(logDir, entry);
2401
2036
  let stats;
2402
2037
  try {
2403
2038
  stats = fs$1.statSync(filePath);
@@ -2420,6 +2055,14 @@ const sanitizeName = (name) => {
2420
2055
  const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
2421
2056
  return normalized === "" ? "handler" : normalized;
2422
2057
  };
2058
+ const getHandlerLogFilePath = (name, date) => {
2059
+ const dateKey = [
2060
+ date.getFullYear(),
2061
+ String(date.getMonth() + 1).padStart(2, "0"),
2062
+ String(date.getDate()).padStart(2, "0")
2063
+ ].join("-");
2064
+ return path.join(logDir, `${sanitizeName(name)}-${dateKey}.log`);
2065
+ };
2423
2066
  const maybeUnref = (timer) => {
2424
2067
  timer.unref();
2425
2068
  };
@@ -2459,15 +2102,18 @@ const initializeLoggerRuntime = () => {
2459
2102
  maybeUnref(flushInterval);
2460
2103
  cleanupInterval = setInterval(cleanupOldLogs, CLEANUP_INTERVAL_MS);
2461
2104
  maybeUnref(cleanupInterval);
2462
- process.once("exit", cleanup);
2463
- process.once("SIGINT", () => {
2105
+ exitHandler = cleanup;
2106
+ sigintHandler = () => {
2464
2107
  cleanup();
2465
2108
  process.exit(0);
2466
- });
2467
- process.once("SIGTERM", () => {
2109
+ };
2110
+ sigtermHandler = () => {
2468
2111
  cleanup();
2469
2112
  process.exit(0);
2470
- });
2113
+ };
2114
+ process.once("exit", exitHandler);
2115
+ process.once("SIGINT", sigintHandler);
2116
+ process.once("SIGTERM", sigtermHandler);
2471
2117
  };
2472
2118
  const getLogStream = (filePath) => {
2473
2119
  initializeLoggerRuntime();
@@ -2491,8 +2137,21 @@ const appendLine = (filePath, line) => {
2491
2137
  buffer.push(line);
2492
2138
  if (buffer.length >= MAX_BUFFER_SIZE) flushBuffer(filePath);
2493
2139
  };
2140
+ const normalizeLogTypeToLevel = (type) => {
2141
+ switch (type) {
2142
+ case "error": return "error";
2143
+ case "warn": return "warn";
2144
+ case "debug": return "debug";
2145
+ default: return "info";
2146
+ }
2147
+ };
2148
+ const shouldWriteFileLog = (type, logLevel = resolveLogLevel()) => {
2149
+ return FILE_LOG_LEVEL_PRIORITY[normalizeLogTypeToLevel(type)] <= FILE_LOG_LEVEL_PRIORITY[logLevel];
2150
+ };
2151
+ const resolveLogLevel = () => testLogLevelOverride ?? getLogLevel();
2152
+ const isDebugFileLoggingEnabled = () => resolveLogLevel() === "debug";
2494
2153
  const debugLazy = (logger$7, factory) => {
2495
- if (!state.verbose) return;
2154
+ if (!isDebugFileLoggingEnabled()) return;
2496
2155
  logger$7.debug(...factory());
2497
2156
  };
2498
2157
  const debugJson = (logger$7, label, value) => {
@@ -2501,18 +2160,31 @@ const debugJson = (logger$7, label, value) => {
2501
2160
  const debugJsonTail = (logger$7, label, { value, tailLength = 400 }) => {
2502
2161
  debugLazy(logger$7, () => [label, JSON.stringify(value).slice(-tailLength)]);
2503
2162
  };
2163
+ const getConsolaLevel = () => {
2164
+ const logLevel = resolveLogLevel();
2165
+ switch (logLevel) {
2166
+ case "error": return 0;
2167
+ case "warn": return 1;
2168
+ case "info": return 2;
2169
+ case "debug": return 4;
2170
+ default: return logLevel;
2171
+ }
2172
+ };
2504
2173
  const createHandlerLogger = (name) => {
2505
- const sanitizedName = sanitizeName(name);
2506
2174
  const instance = consola.withTag(name);
2507
- if (state.verbose) instance.level = 5;
2175
+ Object.defineProperty(instance, "level", {
2176
+ get: getConsolaLevel,
2177
+ configurable: true
2178
+ });
2508
2179
  instance.setReporters([]);
2509
2180
  instance.addReporter({ log(logObj) {
2181
+ const fileLogLevel = resolveLogLevel();
2182
+ if (!shouldWriteFileLog(logObj.type, fileLogLevel)) return;
2510
2183
  initializeLoggerRuntime();
2511
2184
  const traceId = requestContext.getStore()?.traceId;
2512
2185
  const date = logObj.date;
2513
- const dateKey = date.toLocaleDateString("sv-SE");
2514
2186
  const timestamp = date.toLocaleString("sv-SE", { hour12: false });
2515
- const filePath = path.join(LOG_DIR, `${sanitizedName}-${dateKey}.log`);
2187
+ const filePath = getHandlerLogFilePath(name, date);
2516
2188
  const message = formatArgs(logObj.args);
2517
2189
  const traceIdStr = traceId ? ` [${traceId}]` : "";
2518
2190
  appendLine(filePath, `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${traceIdStr}${message ? ` ${message}` : ""}`);
@@ -2761,6 +2433,13 @@ const getTokenCount = async (payload, model) => {
2761
2433
  };
2762
2434
  };
2763
2435
 
2436
+ //#endregion
2437
+ //#region src/lib/request-initiator.ts
2438
+ function resolveEffectiveInitiator(baseInitiator, options) {
2439
+ if (options.isCompact || options.isSubagent) return "agent";
2440
+ return baseInitiator;
2441
+ }
2442
+
2764
2443
  //#endregion
2765
2444
  //#region src/services/copilot/create-chat-completions.ts
2766
2445
  function isGpt5MiniFamily(modelId) {
@@ -2784,14 +2463,18 @@ const createChatCompletions = async (payload, account, options) => {
2784
2463
  const ctx = account ?? accountFromState();
2785
2464
  if (!ctx.copilotToken) throw new Error("Copilot token not found");
2786
2465
  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);
2466
+ const effectiveInitiator = resolveEffectiveInitiator(options?.initiator ?? getChatInitiator(payload.messages), {
2467
+ isCompact: Boolean(options?.compactType),
2468
+ isSubagent: Boolean(options?.subagentMarker)
2469
+ });
2788
2470
  const headers = {
2789
2471
  ...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
2790
- "x-initiator": options?.subagentMarker ? "agent" : initiator
2472
+ "x-initiator": effectiveInitiator
2791
2473
  };
2792
2474
  prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
2793
2475
  const upstreamPayload = applyDefaultReasoningEffort(payload);
2794
- prepareForCompact(headers, options?.isCompact);
2476
+ prepareForCompact(headers, options?.compactType);
2477
+ captureOutboundHeadersSnapshot(headers);
2795
2478
  const response = await fetch(`${copilotBaseUrl(ctx)}/chat/completions`, {
2796
2479
  method: "POST",
2797
2480
  headers,
@@ -2806,100 +2489,10 @@ const createChatCompletions = async (payload, account, options) => {
2806
2489
  };
2807
2490
 
2808
2491
  //#endregion
2809
- //#region src/routes/chat-completions/handler.ts
2810
- const logger$6 = createHandlerLogger("chat-completions-handler");
2492
+ //#region src/routes/chat-completions/support.ts
2811
2493
  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;
2828
- if (getAliasTargetSet().has(clientModel.toLowerCase())) {
2829
- recordSelectionFailure$2(store, {
2830
- request,
2831
- clientModel,
2832
- stream: streamRequested,
2833
- reason: "MODEL_NOT_SUPPORTED"
2834
- });
2835
- return selectionFailureResponse$2(c, {
2836
- clientModel,
2837
- reason: "MODEL_NOT_SUPPORTED"
2838
- });
2839
- }
2840
- debugJsonTail(logger$6, "Request payload:", {
2841
- value: payload,
2842
- tailLength: 400
2843
- });
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, {
2851
- request,
2852
- clientModel,
2853
- stream: streamRequested,
2854
- reason: selection.reason
2855
- });
2856
- return selectionFailureResponse$2(c, {
2857
- clientModel,
2858
- reason: selection.reason
2859
- });
2860
- }
2861
- const { account, selectedModel } = selection;
2862
- request.affinityHit = selection.affinityHit;
2863
- request.affinityCacheKey = selection.affinityCacheKey;
2864
- const upstreamPayload = {
2865
- ...payload,
2866
- model: selectedModel.id
2867
- };
2868
- const premiumRemainingBefore = account.premiumRemaining;
2869
- const premiumUnlimitedBefore = account.unlimited;
2870
- await logTokenCountForRequest({
2871
- payload: upstreamPayload,
2872
- selectedModel
2873
- });
2874
- if (state.manualApprove) await awaitApproval();
2875
- const payloadWithMaxTokens = applyDefaultMaxTokens(upstreamPayload, selectedModel);
2876
- const accountCtx = toAccountContext(account);
2877
- const upstreamSessionId = getUUID(upstreamRequestId);
2878
- request.upstreamRequestId = upstreamRequestId;
2879
- request.upstreamSessionId = upstreamSessionId;
2880
- if (streamRequested) return handleStreamingRequest({
2881
- c,
2882
- store,
2883
- request,
2884
- payload: payloadWithMaxTokens,
2885
- selection,
2886
- accountCtx,
2887
- clientModel,
2888
- premiumRemainingBefore,
2889
- premiumUnlimitedBefore
2890
- });
2891
- return handleNonStreamingRequest({
2892
- c,
2893
- store,
2894
- request,
2895
- payload: payloadWithMaxTokens,
2896
- selection,
2897
- accountCtx,
2898
- clientModel,
2899
- premiumRemainingBefore,
2900
- premiumUnlimitedBefore
2901
- });
2902
- }
2494
+ const GPT_5_4_MODEL_ID = "gpt-5.4";
2495
+ const GPT_5_4_CHAT_COMPLETIONS_MESSAGE = "Please use `/v1/responses` or `/v1/messages` API";
2903
2496
  function buildRequestContext$1(c) {
2904
2497
  const requestId = randomUUID();
2905
2498
  const startedAtMs = Date.now();
@@ -2930,34 +2523,254 @@ function insertRequestLog$2(store, request, record) {
2930
2523
  promptCacheKey: request.promptCacheKey,
2931
2524
  initiator: request.initiator,
2932
2525
  upstreamRequestId: request.upstreamRequestId,
2526
+ affinityKeyUsed: request.affinityKeyUsed,
2527
+ affinityKeySource: request.affinityKeySource,
2528
+ selectionReason: request.selectionReason,
2933
2529
  affinityHit: request.affinityHit,
2934
2530
  affinityCacheKey: request.affinityCacheKey,
2935
2531
  ...record
2936
2532
  });
2937
- }
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,
2533
+ }
2534
+ function recordUnsupportedChatCompletionsModel(store, params) {
2535
+ const { request, stream, clientModel, upstreamModel, accountId, accountType, costUnits, premiumRemainingBefore, premiumRemainingAfter, premiumUnlimitedBefore, premiumUnlimitedAfter } = params;
2536
+ const finishedAtMs = Date.now();
2537
+ let premiumRemainingDiff;
2538
+ if (premiumRemainingBefore !== void 0 && premiumRemainingAfter !== void 0) premiumRemainingDiff = premiumRemainingAfter - premiumRemainingBefore;
2539
+ insertRequestLog$2(store, request, {
2540
+ finishedAtMs,
2541
+ durationMs: finishedAtMs - request.startedAtMs,
2542
+ upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
2543
+ stream,
2544
+ accountId,
2545
+ accountType,
2546
+ costUnits,
2547
+ clientModel,
2548
+ upstreamModel,
2549
+ premiumRemainingBefore,
2550
+ premiumRemainingAfter,
2551
+ premiumRemainingDiff,
2552
+ premiumUnlimitedBefore,
2553
+ premiumUnlimitedAfter,
2554
+ httpStatus: 400,
2555
+ errorName: "invalid_request_error",
2556
+ errorMessage: GPT_5_4_CHAT_COMPLETIONS_MESSAGE
2557
+ });
2558
+ }
2559
+ function unsupportedChatCompletionsModelResponse(c) {
2560
+ return c.json({ error: {
2561
+ message: GPT_5_4_CHAT_COMPLETIONS_MESSAGE,
2562
+ type: "invalid_request_error"
2563
+ } }, 400);
2564
+ }
2565
+ function getSelectionFailureStatus(reason) {
2566
+ if (reason === "MODEL_NOT_SUPPORTED") return 400;
2567
+ if (reason === "NO_ACCOUNTS") return 503;
2568
+ return 429;
2569
+ }
2570
+ function recordSelectionFailure$2(store, params) {
2571
+ const { request, stream, clientModel, reason } = params;
2572
+ const finishedAtMs = Date.now();
2573
+ insertRequestLog$2(store, request, {
2574
+ finishedAtMs,
2575
+ durationMs: finishedAtMs - request.startedAtMs,
2576
+ upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
2577
+ stream,
2578
+ clientModel,
2579
+ httpStatus: getSelectionFailureStatus(reason),
2580
+ selectionFailureReason: reason
2581
+ });
2582
+ }
2583
+ function selectionFailureResponse$2(c, params) {
2584
+ const { clientModel, reason } = params;
2585
+ if (reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
2586
+ message: `Model "${clientModel}" is not available for any configured account.`,
2587
+ type: "invalid_request_error"
2588
+ } }, 400);
2589
+ if (reason === "NO_ACCOUNTS") return c.json({ error: {
2590
+ message: "No enabled Copilot accounts are configured. Add or re-enable an account.",
2591
+ type: "service_unavailable_error"
2592
+ } }, 503);
2593
+ return c.json({ error: {
2594
+ message: "All accounts have exhausted their quota. Please wait for quota refresh or add additional accounts.",
2595
+ type: "rate_limit_error"
2596
+ } }, 429);
2597
+ }
2598
+
2599
+ //#endregion
2600
+ //#region src/routes/chat-completions/handler.ts
2601
+ const logger$6 = createHandlerLogger("chat-completions-handler");
2602
+ function buildChatCompletionCandidates(clientModel) {
2603
+ return [{
2604
+ modelId: clientModel,
2605
+ endpoint: CHAT_COMPLETIONS_ENDPOINT$1
2606
+ }];
2607
+ }
2608
+ function isUnsupportedChatCompletionsModel(modelId) {
2609
+ return resolveModelAlias(modelId).toLowerCase() === GPT_5_4_MODEL_ID;
2610
+ }
2611
+ function maybeRejectChatCompletionsClientModel(c, store, params) {
2612
+ const { request, clientModel, streamRequested } = params;
2613
+ if (isUnsupportedChatCompletionsModel(clientModel)) {
2614
+ recordUnsupportedChatCompletionsModel(store, {
2615
+ request,
2616
+ clientModel,
2617
+ stream: streamRequested
2618
+ });
2619
+ return unsupportedChatCompletionsModelResponse(c);
2620
+ }
2621
+ if (getAliasTargetSet().has(clientModel.toLowerCase())) {
2622
+ recordSelectionFailure$2(store, {
2623
+ request,
2624
+ clientModel,
2625
+ stream: streamRequested,
2626
+ reason: "MODEL_NOT_SUPPORTED"
2627
+ });
2628
+ return selectionFailureResponse$2(c, {
2629
+ clientModel,
2630
+ reason: "MODEL_NOT_SUPPORTED"
2631
+ });
2632
+ }
2633
+ return null;
2634
+ }
2635
+ async function handleCompletion$1(c) {
2636
+ await checkRateLimit(state);
2637
+ const store = getRequestHistoryStore();
2638
+ const request = buildRequestContext$1(c);
2639
+ const payload = await c.req.json();
2640
+ const clientModel = payload.model;
2641
+ const streamRequested = Boolean(payload.stream);
2642
+ const normalizedPromptCacheKey = applyChatRequestMetadata(request, payload, getChatInitiator(payload.messages));
2643
+ const blockedResponse = maybeRejectChatCompletionsClientModel(c, store, {
2644
+ request,
2645
+ clientModel,
2646
+ streamRequested
2647
+ });
2648
+ if (blockedResponse) return blockedResponse;
2649
+ const selectionResult = await selectChatCompletionAccount({
2650
+ c,
2651
+ store,
2652
+ request,
2653
+ payload,
2654
+ clientModel,
2655
+ streamRequested,
2656
+ normalizedPromptCacheKey
2657
+ });
2658
+ if (selectionResult instanceof Response) return selectionResult;
2659
+ const { headerSessionId, selection, upstreamRequestId } = selectionResult;
2660
+ const { account, reservation, selectedModel } = selection;
2661
+ request.affinityHit = selection.affinityHit;
2662
+ request.affinityCacheKey = selection.affinityCacheKey;
2663
+ request.selectionReason = selection.selectionReason;
2664
+ const premiumRemainingBefore = account.premiumRemaining;
2665
+ const premiumUnlimitedBefore = account.unlimited;
2666
+ if (selectedModel.id === GPT_5_4_MODEL_ID) {
2667
+ await accountsManager.finalizeQuota(account, reservation);
2668
+ recordUnsupportedChatCompletionsModel(store, {
2669
+ request,
2670
+ clientModel,
2671
+ stream: streamRequested,
2672
+ upstreamModel: selectedModel.id,
2673
+ accountId: account.id,
2674
+ accountType: account.accountType,
2675
+ costUnits: selection.costUnits,
2676
+ premiumRemainingBefore,
2677
+ premiumRemainingAfter: account.premiumRemaining,
2678
+ premiumUnlimitedBefore,
2679
+ premiumUnlimitedAfter: account.unlimited
2680
+ });
2681
+ return unsupportedChatCompletionsModelResponse(c);
2682
+ }
2683
+ const upstreamPayload = {
2684
+ ...payload,
2685
+ model: selectedModel.id
2686
+ };
2687
+ await logTokenCountForRequest({
2688
+ payload: upstreamPayload,
2689
+ selectedModel
2690
+ });
2691
+ if (state.manualApprove) await awaitApproval();
2692
+ const payloadWithMaxTokens = applyDefaultMaxTokens(upstreamPayload, selectedModel);
2693
+ const accountCtx = toAccountContext(account);
2694
+ const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
2695
+ request.upstreamRequestId = upstreamRequestId;
2696
+ request.upstreamSessionId = upstreamSessionId;
2697
+ if (streamRequested) return handleStreamingRequest({
2698
+ c,
2699
+ store,
2700
+ request,
2701
+ payload: payloadWithMaxTokens,
2702
+ selection,
2703
+ accountCtx,
2704
+ clientModel,
2705
+ premiumRemainingBefore,
2706
+ premiumUnlimitedBefore
2707
+ });
2708
+ return handleNonStreamingRequest({
2709
+ c,
2710
+ store,
2711
+ request,
2712
+ payload: payloadWithMaxTokens,
2713
+ selection,
2714
+ accountCtx,
2946
2715
  clientModel,
2947
- httpStatus: reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
2948
- selectionFailureReason: reason
2716
+ premiumRemainingBefore,
2717
+ premiumUnlimitedBefore
2949
2718
  });
2950
2719
  }
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);
2720
+ function applyChatRequestMetadata(request, payload, initiator) {
2721
+ const userId = payload.user ?? void 0;
2722
+ const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
2723
+ const normalizedPromptCacheKey = promptCacheKey ?? void 0;
2724
+ request.userId = userId;
2725
+ request.safetyIdentifier = safetyIdentifier ?? void 0;
2726
+ request.promptCacheKey = normalizedPromptCacheKey;
2727
+ request.initiator = initiator;
2728
+ return normalizedPromptCacheKey;
2729
+ }
2730
+ async function writeChatCompletionsStreamError(stream, message) {
2731
+ try {
2732
+ await stream.writeSSE({ data: JSON.stringify({ error: {
2733
+ message,
2734
+ type: "error"
2735
+ } }) });
2736
+ await stream.writeSSE({ data: "[DONE]" });
2737
+ } catch (streamError) {
2738
+ logger$6.warn("Failed to write chat completions stream error event:", streamError);
2739
+ }
2740
+ }
2741
+ async function selectChatCompletionAccount(params) {
2742
+ const { c, store, request, payload, clientModel, streamRequested, normalizedPromptCacheKey } = params;
2743
+ debugJsonTail(logger$6, "Request payload:", {
2744
+ value: payload,
2745
+ tailLength: 400
2746
+ });
2747
+ const upstreamRequestId = generateRequestIdFromPayload(payload, normalizedPromptCacheKey);
2748
+ const headerSessionId = c.req.header("x-session-id") ?? null;
2749
+ const affinityKey = resolveAffinityKey({
2750
+ metadataSessionId: normalizedPromptCacheKey,
2751
+ headerSessionId,
2752
+ upstreamRequestId
2753
+ });
2754
+ request.affinityKeyUsed = affinityKey.affinityKeyUsed;
2755
+ request.affinityKeySource = affinityKey.affinityKeySource;
2756
+ const selection = await accountsManager.selectAccountForRequest(buildChatCompletionCandidates(clientModel), { requestId: affinityKey.requestId });
2757
+ if (!selection.ok) {
2758
+ recordSelectionFailure$2(store, {
2759
+ request,
2760
+ clientModel,
2761
+ stream: streamRequested,
2762
+ reason: selection.reason
2763
+ });
2764
+ return selectionFailureResponse$2(c, {
2765
+ clientModel,
2766
+ reason: selection.reason
2767
+ });
2768
+ }
2769
+ return {
2770
+ headerSessionId,
2771
+ selection,
2772
+ upstreamRequestId
2773
+ };
2961
2774
  }
2962
2775
  async function logTokenCountForRequest(params) {
2963
2776
  try {
@@ -3022,8 +2835,8 @@ async function handleUpstreamCreateError$1(params) {
3022
2835
  const { store, request, selection, clientModel, premiumRemainingBefore, premiumUnlimitedBefore, error } = params;
3023
2836
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
3024
2837
  const finishedAtMs = Date.now();
3025
- const details = extractErrorDetails(error);
3026
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
2838
+ const details = await extractErrorObservability(error);
2839
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3027
2840
  await accountsManager.finalizeQuota(account, reservation);
3028
2841
  const premiumRemainingAfter = account.premiumRemaining;
3029
2842
  const premiumUnlimitedAfter = account.unlimited;
@@ -3045,7 +2858,8 @@ async function handleUpstreamCreateError$1(params) {
3045
2858
  httpStatus: details.httpStatus,
3046
2859
  errorName: details.errorName,
3047
2860
  errorStatus: details.errorStatus,
3048
- errorMessage: details.errorMessage
2861
+ errorMessage: details.errorMessage,
2862
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
3049
2863
  });
3050
2864
  throw error;
3051
2865
  }
@@ -3057,16 +2871,18 @@ async function handleNonStreamingUpstreamResponse(params) {
3057
2871
  let errorName;
3058
2872
  let errorStatus;
3059
2873
  let errorMessage;
2874
+ let upstreamErrorMessageRaw;
3060
2875
  const finishedAtMs = Date.now();
3061
2876
  try {
3062
2877
  debugJson(logger$6, "Non-streaming response:", response);
3063
2878
  return c.json(response);
3064
2879
  } catch (error) {
3065
- const details = extractErrorDetails(error);
2880
+ const details = await extractErrorObservability(error);
3066
2881
  httpStatus = details.httpStatus;
3067
2882
  errorName = details.errorName;
3068
2883
  errorStatus = details.errorStatus;
3069
2884
  errorMessage = details.errorMessage;
2885
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3070
2886
  throw error;
3071
2887
  } finally {
3072
2888
  await accountsManager.finalizeQuota(account, reservation);
@@ -3091,7 +2907,8 @@ async function handleNonStreamingUpstreamResponse(params) {
3091
2907
  httpStatus,
3092
2908
  errorName,
3093
2909
  errorStatus,
3094
- errorMessage
2910
+ errorMessage,
2911
+ upstreamErrorMessageRaw
3095
2912
  });
3096
2913
  }
3097
2914
  }
@@ -3103,6 +2920,7 @@ async function streamChatCompletionsAndLog$1(params) {
3103
2920
  let errorName;
3104
2921
  let errorStatus;
3105
2922
  let errorMessage;
2923
+ let upstreamErrorMessageRaw;
3106
2924
  try {
3107
2925
  for await (const rawChunk of response) {
3108
2926
  const chunk = rawChunk;
@@ -3113,11 +2931,14 @@ async function streamChatCompletionsAndLog$1(params) {
3113
2931
  await stream.writeSSE(chunk);
3114
2932
  }
3115
2933
  } catch (error) {
3116
- const details = extractErrorDetails(error);
2934
+ const details = await extractErrorObservability(error);
3117
2935
  errorName = details.errorName;
3118
2936
  errorStatus = details.errorStatus;
3119
2937
  errorMessage = details.errorMessage;
2938
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3120
2939
  logger$6.warn("Streaming error:", error);
2940
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
2941
+ await writeChatCompletionsStreamError(stream, getUserVisibleErrorMessage(details));
3121
2942
  } finally {
3122
2943
  const finishedAtMs = Date.now();
3123
2944
  await accountsManager.finalizeQuota(account, reservation);
@@ -3143,18 +2964,29 @@ async function streamChatCompletionsAndLog$1(params) {
3143
2964
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
3144
2965
  errorName,
3145
2966
  errorStatus,
3146
- errorMessage
2967
+ errorMessage,
2968
+ upstreamErrorMessageRaw
3147
2969
  });
3148
2970
  }
3149
2971
  }
3150
2972
  async function extractUsageFromChunk(chunk) {
3151
- const data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
2973
+ let data;
2974
+ try {
2975
+ data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
2976
+ } catch (error) {
2977
+ logger$6.warn("Failed to read chat completions usage chunk:", error);
2978
+ return;
2979
+ }
3152
2980
  if (!data || data === "[DONE]") return;
3153
2981
  try {
3154
2982
  const parsed = JSON.parse(data);
3155
2983
  if (!parsed.usage) return void 0;
3156
2984
  return normalizeChatCompletionsUsage(parsed.usage);
3157
- } catch {
2985
+ } catch (error) {
2986
+ logger$6.warn("Failed to parse chat completions usage chunk:", {
2987
+ error,
2988
+ data
2989
+ });
3158
2990
  return;
3159
2991
  }
3160
2992
  }
@@ -3166,31 +2998,28 @@ async function handleNonStreamingRequest(params) {
3166
2998
  let errorName;
3167
2999
  let errorStatus;
3168
3000
  let errorMessage;
3001
+ let upstreamErrorMessageRaw;
3169
3002
  let finishedAtMs;
3170
3003
  try {
3171
3004
  const response = await createChatCompletions(payload, accountCtx, {
3172
3005
  upstreamRequestId: request.upstreamRequestId,
3173
3006
  sessionId: request.upstreamSessionId
3174
3007
  });
3008
+ if (!isNonStreaming$1(response)) throw new Error("Upstream returned a stream unexpectedly");
3175
3009
  selection.confirmAffinity?.();
3176
3010
  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
3011
  usage = normalizeChatCompletionsUsage(response.usage);
3184
3012
  debugJson(logger$6, "Non-streaming response:", response);
3185
3013
  return c.json(response);
3186
3014
  } catch (error) {
3187
3015
  finishedAtMs = Date.now();
3188
- const details = extractErrorDetails(error);
3016
+ const details = await extractErrorObservability(error);
3189
3017
  httpStatus = details.httpStatus;
3190
3018
  errorName = details.errorName;
3191
3019
  errorStatus = details.errorStatus;
3192
3020
  errorMessage = details.errorMessage;
3193
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3021
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3022
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3194
3023
  throw error;
3195
3024
  } finally {
3196
3025
  const finishedAtMsFinal = finishedAtMs ?? Date.now();
@@ -3216,7 +3045,8 @@ async function handleNonStreamingRequest(params) {
3216
3045
  httpStatus,
3217
3046
  errorName,
3218
3047
  errorStatus,
3219
- errorMessage
3048
+ errorMessage,
3049
+ upstreamErrorMessageRaw
3220
3050
  });
3221
3051
  }
3222
3052
  }
@@ -3238,9 +3068,11 @@ completionRoutes.post("/", async (c) => {
3238
3068
  const createEmbeddings = async (payload, account) => {
3239
3069
  const ctx = account ?? accountFromState();
3240
3070
  if (!ctx.copilotToken) throw new Error("Copilot token not found");
3071
+ const headers = copilotHeaders(ctx);
3072
+ captureOutboundHeadersSnapshot(headers);
3241
3073
  const response = await fetch(`${copilotBaseUrl(ctx)}/embeddings`, {
3242
3074
  method: "POST",
3243
- headers: copilotHeaders(ctx),
3075
+ headers,
3244
3076
  body: JSON.stringify(payload)
3245
3077
  });
3246
3078
  if (!response.ok) throw new HTTPError("Failed to create embeddings", response);
@@ -3344,6 +3176,7 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
3344
3176
  let errorName;
3345
3177
  let errorStatus;
3346
3178
  let errorMessage;
3179
+ let upstreamErrorMessageRaw;
3347
3180
  let finishedAtMs;
3348
3181
  try {
3349
3182
  const response = await createEmbeddings(payload, toAccountContext(account));
@@ -3352,12 +3185,13 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
3352
3185
  return c.json(response);
3353
3186
  } catch (error) {
3354
3187
  finishedAtMs = Date.now();
3355
- const details = extractErrorDetails(error);
3188
+ const details = await extractErrorObservability(error);
3356
3189
  httpStatus = details.httpStatus;
3357
3190
  errorName = details.errorName;
3358
3191
  errorStatus = details.errorStatus;
3359
3192
  errorMessage = details.errorMessage;
3360
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3193
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
3194
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
3361
3195
  throw error;
3362
3196
  } finally {
3363
3197
  const finishedAtMsFinal = finishedAtMs ?? Date.now();
@@ -3390,22 +3224,23 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
3390
3224
  httpStatus,
3391
3225
  errorName,
3392
3226
  errorStatus,
3393
- errorMessage
3227
+ errorMessage,
3228
+ upstreamErrorMessageRaw
3394
3229
  });
3395
3230
  }
3396
3231
  }
3397
3232
 
3398
3233
  //#endregion
3399
3234
  //#region src/lib/models.ts
3235
+ const getAvailableModels = () => (accountsManager.getFirstAccountModels()?.data ?? []).filter((model) => model.model_picker_enabled || model.capabilities.type === "embeddings");
3400
3236
  const findEndpointModel = (sdkModelId) => {
3401
- const models = state.models?.data ?? [];
3237
+ const models = getAvailableModels();
3402
3238
  const exactMatch = models.find((m) => m.id === sdkModelId);
3403
3239
  if (exactMatch) return exactMatch;
3404
3240
  const normalized = _normalizeSdkModelId(sdkModelId);
3405
3241
  if (!normalized) return;
3406
3242
  const modelName = `claude-${normalized.family}-${normalized.version}`;
3407
- const model = models.find((m) => m.id === modelName);
3408
- if (model) return model;
3243
+ return models.find((m) => m.id === modelName);
3409
3244
  };
3410
3245
  /**
3411
3246
  * Normalizes an SDK model ID to extract the model family and version.
@@ -3447,12 +3282,23 @@ const _normalizeSdkModelId = (sdkModelId) => {
3447
3282
  };
3448
3283
 
3449
3284
  //#endregion
3450
- //#region src/routes/messages/preprocess.ts
3285
+ //#region src/lib/compact.ts
3286
+ const COMPACT_REQUEST = 1;
3287
+ const COMPACT_AUTO_CONTINUE = 2;
3451
3288
  const compactSystemPromptStart = "You are a helpful AI assistant tasked with summarizing conversations";
3452
3289
  const compactTextOnlyGuard = "CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.";
3453
3290
  const compactSummaryPromptStart = "Your task is to create a detailed summary of the conversation so far";
3291
+ const compactAutoContinueClaudeCodePromptStart = "This session is being continued from a previous conversation that ran out of context. The summary below covers the earlier portion of the conversation.";
3292
+ const compactAutoContinueOpenCodePromptStart = "Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed.";
3293
+ const compactAutoContinuePromptStarts = [compactAutoContinueClaudeCodePromptStart, compactAutoContinueOpenCodePromptStart];
3454
3294
  const compactMessageSections = ["Pending Tasks:", "Current Work:"];
3295
+
3296
+ //#endregion
3297
+ //#region src/routes/messages/preprocess.ts
3455
3298
  const TOOL_REFERENCE_TURN_BOUNDARY = "Tool loaded.";
3299
+ const IDE_EXECUTE_CODE_TOOL = "mcp__ide__executeCode";
3300
+ const IDE_GET_DIAGNOSTICS_TOOL = "mcp__ide__getDiagnostics";
3301
+ const IDE_GET_DIAGNOSTICS_DESCRIPTION = "Get language diagnostics from VS Code. Returns errors, warnings, information, and hints for files in the workspace.";
3456
3302
  const getAnthropicEffortForModel = (model) => {
3457
3303
  const reasoningEffort = getReasoningEffortForModel(model);
3458
3304
  if (reasoningEffort === "xhigh") return "max";
@@ -3469,13 +3315,19 @@ const isCompactMessage = (lastMessage) => {
3469
3315
  if (!text) return false;
3470
3316
  return text.includes(compactTextOnlyGuard) && text.includes(compactSummaryPromptStart) && compactMessageSections.some((section) => text.includes(section));
3471
3317
  };
3472
- const isCompactRequest = (anthropicPayload) => {
3318
+ const isCompactAutoContinueMessage = (lastMessage) => {
3319
+ const text = getCompactCandidateText(lastMessage);
3320
+ return Boolean(text) && compactAutoContinuePromptStarts.some((promptStart) => text.startsWith(promptStart));
3321
+ };
3322
+ const getCompactType = (anthropicPayload) => {
3473
3323
  const lastMessage = anthropicPayload.messages.at(-1);
3474
- if (lastMessage && isCompactMessage(lastMessage)) return true;
3324
+ if (lastMessage && isCompactMessage(lastMessage)) return COMPACT_REQUEST;
3325
+ if (lastMessage && isCompactAutoContinueMessage(lastMessage)) return COMPACT_AUTO_CONTINUE;
3475
3326
  const system = anthropicPayload.system;
3476
- if (typeof system === "string") return system.startsWith(compactSystemPromptStart);
3477
- if (!Array.isArray(system)) return false;
3478
- return system.some((msg) => typeof msg.text === "string" && msg.text.startsWith(compactSystemPromptStart));
3327
+ if (typeof system === "string") return system.startsWith(compactSystemPromptStart) ? COMPACT_REQUEST : 0;
3328
+ if (!Array.isArray(system)) return 0;
3329
+ if (system.some((msg) => typeof msg.text === "string" && msg.text.startsWith(compactSystemPromptStart))) return COMPACT_REQUEST;
3330
+ return 0;
3479
3331
  };
3480
3332
  const mergeContentWithText = (tr, textBlock) => {
3481
3333
  if (typeof tr.content === "string") return {
@@ -3514,8 +3366,10 @@ const stripToolReferenceTurnBoundary = (anthropicPayload) => {
3514
3366
  msg.content = msg.content.filter((block) => block.type !== "text" || block.text.trim() !== TOOL_REFERENCE_TURN_BOUNDARY);
3515
3367
  }
3516
3368
  };
3517
- const mergeToolResultForClaude = (anthropicPayload) => {
3518
- for (const msg of anthropicPayload.messages) {
3369
+ const mergeToolResultForClaude = (anthropicPayload, options) => {
3370
+ const lastMessageIndex = anthropicPayload.messages.length - 1;
3371
+ for (const [index, msg] of anthropicPayload.messages.entries()) {
3372
+ if (options?.skipLastMessage && index === lastMessageIndex) continue;
3519
3373
  if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
3520
3374
  const toolResults = [];
3521
3375
  const textBlocks = [];
@@ -3530,6 +3384,17 @@ const mergeToolResultForClaude = (anthropicPayload) => {
3530
3384
  msg.content = mergeToolResult(toolResults, textBlocks);
3531
3385
  }
3532
3386
  };
3387
+ const sanitizeIdeTools = (payload) => {
3388
+ if (!payload.tools || payload.tools.length === 0) return;
3389
+ payload.tools = payload.tools.flatMap((tool) => {
3390
+ if (tool.name === IDE_EXECUTE_CODE_TOOL) return [];
3391
+ if (tool.name === IDE_GET_DIAGNOSTICS_TOOL) return [{
3392
+ ...tool,
3393
+ description: IDE_GET_DIAGNOSTICS_DESCRIPTION
3394
+ }];
3395
+ return [tool];
3396
+ });
3397
+ };
3533
3398
  const hasToolRef = (block) => {
3534
3399
  return Array.isArray(block.content) && block.content.some((c) => c.type === "tool_reference");
3535
3400
  };
@@ -3568,10 +3433,12 @@ const filterAssistantThinkingBlocks = (payload) => {
3568
3433
  const prepareMessagesApiPayload = (payload, selectedModel) => {
3569
3434
  stripCacheControl(payload);
3570
3435
  filterAssistantThinkingBlocks(payload);
3436
+ const hasThinking = Boolean(payload.thinking);
3571
3437
  const toolChoice = payload.tool_choice;
3572
3438
  const disableThink = toolChoice?.type === "any" || toolChoice?.type === "tool";
3573
3439
  if (selectedModel?.capabilities.supports.adaptive_thinking && !disableThink) {
3574
3440
  payload.thinking = { type: "adaptive" };
3441
+ if (!hasThinking) payload.thinking.display = "summarized";
3575
3442
  payload.output_config = { effort: getAnthropicEffortForModel(payload.model) };
3576
3443
  }
3577
3444
  };
@@ -3611,7 +3478,7 @@ const isWarmupProbeRequest = (payload) => {
3611
3478
  return false;
3612
3479
  };
3613
3480
  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;
3481
+ 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
3482
  const finishedAtMs = Date.now();
3616
3483
  store.insert({
3617
3484
  requestId,
@@ -3630,7 +3497,10 @@ const handleSelectionFailure = (context) => {
3630
3497
  promptCacheKey,
3631
3498
  initiator,
3632
3499
  isSubagent,
3500
+ affinityKeyUsed,
3501
+ affinityKeySource,
3633
3502
  httpStatus: selection.reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
3503
+ selectionReason: selectionReason ?? selection.selectionReason,
3634
3504
  selectionFailureReason: selection.reason
3635
3505
  });
3636
3506
  if (selection.reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
@@ -3658,8 +3528,7 @@ const maybeBlockOriginalModelName = (context) => {
3658
3528
  const THINKING_TEXT = "Thinking...";
3659
3529
  function translateToOpenAI(payload) {
3660
3530
  const modelId = payload.model;
3661
- const model = state.models?.data.find((m) => m.id === modelId);
3662
- const thinkingBudget = getThinkingBudget(payload, model);
3531
+ const thinkingBudget = getThinkingBudget(payload, getAvailableModels().find((m) => m.id === modelId));
3663
3532
  return {
3664
3533
  model: modelId,
3665
3534
  messages: translateAnthropicMessagesToOpenAI(payload, modelId, thinkingBudget),
@@ -3952,16 +3821,21 @@ async function handleCountTokens(c) {
3952
3821
 
3953
3822
  //#endregion
3954
3823
  //#region src/services/copilot/create-responses.ts
3955
- const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, isCompact }, account) => {
3824
+ const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, compactType }, account) => {
3956
3825
  const ctx = account ?? accountFromState();
3957
3826
  if (!ctx.copilotToken) throw new Error("Copilot token not found");
3827
+ const effectiveInitiator = resolveEffectiveInitiator(initiator, {
3828
+ isCompact: Boolean(compactType),
3829
+ isSubagent: Boolean(subagentMarker)
3830
+ });
3958
3831
  const headers = {
3959
3832
  ...copilotHeaders(ctx, vision, upstreamRequestId),
3960
- "x-initiator": initiator
3833
+ "x-initiator": effectiveInitiator
3961
3834
  };
3962
3835
  prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
3963
- prepareForCompact(headers, isCompact);
3836
+ prepareForCompact(headers, compactType);
3964
3837
  payload.service_tier = null;
3838
+ captureOutboundHeadersSnapshot(headers);
3965
3839
  const response = await fetch(`${copilotBaseUrl(ctx)}/responses`, {
3966
3840
  method: "POST",
3967
3841
  headers,
@@ -4882,6 +4756,44 @@ const getPayloadItems = (payload) => {
4882
4756
  if (Array.isArray(input)) result.push(...input);
4883
4757
  return result;
4884
4758
  };
4759
+ const useFunctionApplyPatch = (payload) => {
4760
+ if (!(getConfig().useFunctionApplyPatch ?? true)) return;
4761
+ if (Array.isArray(payload.tools)) {
4762
+ const toolsArr = payload.tools;
4763
+ for (let i = 0; i < toolsArr.length; i++) {
4764
+ const t = toolsArr[i];
4765
+ if (t.type === "custom" && t.name === "apply_patch") toolsArr[i] = {
4766
+ type: "function",
4767
+ name: t.name,
4768
+ description: "Use the `apply_patch` tool to edit files",
4769
+ parameters: {
4770
+ type: "object",
4771
+ properties: { input: {
4772
+ type: "string",
4773
+ description: "The entire contents of the apply_patch command"
4774
+ } },
4775
+ required: ["input"]
4776
+ },
4777
+ strict: false
4778
+ };
4779
+ }
4780
+ }
4781
+ };
4782
+ const removeWebSearchTool = (payload) => {
4783
+ if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
4784
+ payload.tools = payload.tools.filter((t) => {
4785
+ return t.type !== "web_search";
4786
+ });
4787
+ };
4788
+ function getStreamChunkFields(chunk) {
4789
+ const c = chunk;
4790
+ return {
4791
+ id: c.id,
4792
+ event: c.event,
4793
+ data: c.data
4794
+ };
4795
+ }
4796
+ const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
4885
4797
  const containsVisionContent = (value) => {
4886
4798
  if (!value) return false;
4887
4799
  if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
@@ -4909,18 +4821,18 @@ const getMessagesInitiator = (payload) => {
4909
4821
  return lastMessage.content.some((block) => block.type !== "tool_result") ? "user" : "agent";
4910
4822
  };
4911
4823
  const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
4824
+ const ADVANCED_TOOL_USE_BETA = "advanced-tool-use-2025-11-20";
4912
4825
  const allowedAnthropicBetas = new Set([
4913
4826
  INTERLEAVED_THINKING_BETA,
4914
4827
  "context-management-2025-06-27",
4915
- "advanced-tool-use-2025-11-20"
4828
+ ADVANCED_TOOL_USE_BETA
4916
4829
  ]);
4917
4830
  const buildAnthropicBetaHeader = (anthropicBetaHeader, thinking) => {
4918
4831
  const isAdaptiveThinking = thinking?.type === "adaptive";
4919
4832
  if (anthropicBetaHeader) {
4920
- const filteredBeta = anthropicBetaHeader.split(",").map((item) => item.trim()).filter((item) => item.length > 0).filter((item) => allowedAnthropicBetas.has(item));
4921
- const uniqueFilteredBetas = [...new Set(filteredBeta)];
4922
- const finalFilteredBetas = isAdaptiveThinking ? uniqueFilteredBetas.filter((item) => item !== INTERLEAVED_THINKING_BETA) : uniqueFilteredBetas;
4923
- if (finalFilteredBetas.length > 0) return finalFilteredBetas.join(",");
4833
+ const filteredBeta = anthropicBetaHeader.split(",").map((item) => item.trim()).filter((item) => item.length > 0).filter((item) => allowedAnthropicBetas.has(item)).filter((item) => !isAdaptiveThinking || item !== INTERLEAVED_THINKING_BETA);
4834
+ const uniqueFilteredBetas = [...new Set([ADVANCED_TOOL_USE_BETA, ...filteredBeta])];
4835
+ if (uniqueFilteredBetas.length > 0) return uniqueFilteredBetas.join(",");
4924
4836
  return;
4925
4837
  }
4926
4838
  if (thinking?.budget_tokens && !isAdaptiveThinking) return INTERLEAVED_THINKING_BETA;
@@ -4931,12 +4843,16 @@ const shouldUseMessageProxyHeaders = (payload) => {
4931
4843
  return Boolean(safetyIdentifier && sessionId);
4932
4844
  };
4933
4845
  const buildMessagesHeaders = ({ ctx, enableVision, initiator, options, payload }) => {
4846
+ const effectiveInitiator = resolveEffectiveInitiator(initiator, {
4847
+ isCompact: Boolean(options?.compactType),
4848
+ isSubagent: Boolean(options?.subagentMarker)
4849
+ });
4934
4850
  const headers = {
4935
4851
  ...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
4936
- "x-initiator": options?.subagentMarker ? "agent" : initiator
4852
+ "x-initiator": effectiveInitiator
4937
4853
  };
4938
4854
  prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
4939
- prepareForCompact(headers, options?.isCompact);
4855
+ prepareForCompact(headers, options?.compactType);
4940
4856
  if (shouldUseMessageProxyHeaders(payload)) prepareMessageProxyHeaders(headers);
4941
4857
  const anthropicBeta = buildAnthropicBetaHeader(options?.anthropicBetaHeader, payload.thinking);
4942
4858
  if (anthropicBeta) headers["anthropic-beta"] = anthropicBeta;
@@ -4952,6 +4868,7 @@ const createMessages = async (payload, account, options) => {
4952
4868
  options,
4953
4869
  payload
4954
4870
  });
4871
+ captureOutboundHeadersSnapshot(headers);
4955
4872
  const response = await fetch(`${copilotBaseUrl(ctx)}/v1/messages`, {
4956
4873
  method: "POST",
4957
4874
  headers,
@@ -5223,36 +5140,78 @@ function closeThinkingBlockIfOpen(state$1, events$1) {
5223
5140
  }
5224
5141
 
5225
5142
  //#endregion
5226
- //#region src/routes/messages/subagent-marker.ts
5143
+ //#region src/lib/subagent.ts
5227
5144
  const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
5145
+
5146
+ //#endregion
5147
+ //#region src/routes/messages/subagent-marker.ts
5148
+ const subagentStartContextPrefix = "SubagentStart hook additional context:";
5228
5149
  const REMINDER_RE = /<system-reminder>([\s\S]*?)<\/system-reminder>/g;
5229
- const parseSubagentMarkerFromFirstUser = (payload) => {
5150
+ const NONE_INSPECTION = {
5151
+ kind: "none",
5152
+ marker: null
5153
+ };
5154
+ const INVALID_INSPECTION = {
5155
+ kind: "invalid",
5156
+ marker: null
5157
+ };
5158
+ const isSubagentMarker = (value) => {
5159
+ if (!value || typeof value !== "object") return false;
5160
+ const candidate = value;
5161
+ 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;
5162
+ };
5163
+ const inspectSubagentMarkerFromFirstUser = (payload) => {
5230
5164
  const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
5231
- if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
5165
+ if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return NONE_INSPECTION;
5166
+ let sawInvalidMarker = false;
5232
5167
  for (const block of firstUserMessage.content) {
5233
5168
  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) => {
5169
+ const inspection = inspectSubagentMarkerFromSystemReminder(block.text);
5170
+ if (inspection.kind === "valid") return inspection;
5171
+ if (inspection.kind === "invalid") sawInvalidMarker = true;
5172
+ }
5173
+ return sawInvalidMarker ? INVALID_INSPECTION : NONE_INSPECTION;
5174
+ };
5175
+ const extractMarkerPayloadFromReminderLine = (line) => {
5176
+ const trimmedLine = line.trim();
5177
+ if (!trimmedLine) return null;
5178
+ let markerLine = trimmedLine;
5179
+ if (markerLine.startsWith(subagentStartContextPrefix)) markerLine = markerLine.slice(38).trimStart();
5180
+ if (!markerLine.startsWith(subagentMarkerPrefix)) return null;
5181
+ return markerLine.slice(subagentMarkerPrefix.length).trimStart();
5182
+ };
5183
+ const inspectSubagentMarkerFromSystemReminder = (text) => {
5184
+ let sawInvalidMarker = false;
5240
5185
  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;
5186
+ const lines = content.split(/\r?\n/);
5187
+ for (const line of lines) {
5188
+ const markerPayload = extractMarkerPayloadFromReminderLine(line);
5189
+ if (markerPayload === null) continue;
5190
+ if (!markerPayload.startsWith("{")) {
5191
+ sawInvalidMarker = true;
5192
+ continue;
5193
+ }
5194
+ const json = extractBalancedJson(markerPayload);
5195
+ if (!json) {
5196
+ sawInvalidMarker = true;
5197
+ continue;
5198
+ }
5199
+ try {
5200
+ const parsed = JSON.parse(json);
5201
+ if (!isSubagentMarker(parsed)) {
5202
+ sawInvalidMarker = true;
5203
+ continue;
5204
+ }
5205
+ return {
5206
+ kind: "valid",
5207
+ marker: parsed
5208
+ };
5209
+ } catch {
5210
+ sawInvalidMarker = true;
5211
+ }
5253
5212
  }
5254
5213
  }
5255
- return null;
5214
+ return sawInvalidMarker ? INVALID_INSPECTION : NONE_INSPECTION;
5256
5215
  };
5257
5216
  /** Extract the first balanced `{...}` object from text that starts with `{`. */
5258
5217
  const extractBalancedJson = (text) => {
@@ -5305,19 +5264,25 @@ async function handleCompletion(c) {
5305
5264
  const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
5306
5265
  const userAgent = c.req.header("user-agent") ?? void 0;
5307
5266
  const anthropicPayload = await c.req.json();
5267
+ sanitizeIdeTools(anthropicPayload);
5308
5268
  debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
5309
- const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
5310
- const initiatorOverride = subagentMarker ? "agent" : void 0;
5269
+ const markerInspection = inspectSubagentMarkerFromFirstUser(anthropicPayload);
5270
+ const subagentMarker = markerInspection.kind === "valid" ? markerInspection.marker : null;
5271
+ const isSubagentRequest = subagentMarker !== null;
5272
+ const invalidSubagentMarkerSelectionReason = markerInspection.kind === "invalid" ? "subagent_marker_invalid_fallback" : void 0;
5311
5273
  if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
5312
5274
  const sessionId = getRootSessionId(anthropicPayload, c);
5313
5275
  logger$5.debug("Extracted session ID:", sessionId);
5276
+ const ownershipLookupSessionId = markerInspection.kind === "valid" ? normalizeStableSessionId(markerInspection.marker.session_id) : void 0;
5277
+ const ownershipWriteSessionId = markerInspection.kind === "none" ? sessionId : void 0;
5314
5278
  const anthropicBeta = c.req.header("anthropic-beta");
5315
- const isCompact = isCompactRequest(anthropicPayload);
5279
+ const compactType = getCompactType(anthropicPayload);
5280
+ const isCompact = compactType !== 0;
5281
+ const originalRequestModel = anthropicPayload.model;
5316
5282
  if (anthropicBeta && isWarmupProbeRequest(anthropicPayload)) anthropicPayload.model = getSmallModel();
5317
- if (isCompact) {
5318
- logger$5.debug("Is compact request:", isCompact);
5319
- if (shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
5320
- } else {
5283
+ if (compactType !== 0) logger$5.debug("Compact request type:", compactType);
5284
+ if (compactType === COMPACT_REQUEST && shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
5285
+ if (compactType === 0) {
5321
5286
  stripToolReferenceTurnBoundary(anthropicPayload);
5322
5287
  mergeToolResultForClaude(anthropicPayload);
5323
5288
  }
@@ -5330,6 +5295,11 @@ async function handleCompletion(c) {
5330
5295
  const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
5331
5296
  const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
5332
5297
  const normalizedPromptCacheKey = promptCacheKey ?? void 0;
5298
+ const openAIPayload = translateToOpenAI(anthropicPayload);
5299
+ const fallbackInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
5300
+ isCompact,
5301
+ isSubagent: isSubagentRequest
5302
+ });
5333
5303
  const blockedResponse = maybeBlockOriginalModelName({
5334
5304
  c,
5335
5305
  store,
@@ -5345,14 +5315,14 @@ async function handleCompletion(c) {
5345
5315
  userId,
5346
5316
  safetyIdentifier: normalizedSafetyIdentifier,
5347
5317
  promptCacheKey: normalizedPromptCacheKey,
5348
- initiator: initiatorOverride,
5349
- isSubagent: Boolean(subagentMarker)
5318
+ initiator: fallbackInitiator,
5319
+ isSubagent: isSubagentRequest,
5320
+ selectionReason: invalidSubagentMarkerSelectionReason
5350
5321
  });
5351
5322
  if (blockedResponse) return blockedResponse;
5352
- const openAIPayload = translateToOpenAI(anthropicPayload);
5353
- const fallbackInitiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
5354
5323
  const endpointModel = findEndpointModel(clientModel);
5355
5324
  const resolvedClientModel = endpointModel?.id ?? clientModel;
5325
+ const affinityModelId = clientModel !== originalRequestModel ? findEndpointModel(originalRequestModel)?.id ?? originalRequestModel : void 0;
5356
5326
  const useMessagesApi = isMessagesApiEnabled();
5357
5327
  const candidates = [];
5358
5328
  if (useMessagesApi) candidates.push({
@@ -5366,7 +5336,18 @@ async function handleCompletion(c) {
5366
5336
  modelId: endpointModel?.id ?? openAIPayload.model,
5367
5337
  endpoint: CHAT_COMPLETIONS_ENDPOINT
5368
5338
  });
5369
- const selection = await accountsManager.selectAccountForRequest(candidates, { requestId: upstreamRequestId });
5339
+ const affinityKey = resolveAffinityKey({
5340
+ metadataSessionId: promptCacheKey,
5341
+ headerSessionId: c.req.header("x-session-id") ?? null,
5342
+ upstreamRequestId
5343
+ });
5344
+ const selection = await accountsManager.selectAccountForRequest(candidates, {
5345
+ requestId: affinityKey.requestId,
5346
+ affinityModelId,
5347
+ ownershipLookupSessionId,
5348
+ ownershipWriteSessionId
5349
+ });
5350
+ const selectionReason = invalidSubagentMarkerSelectionReason ?? selection.selectionReason;
5370
5351
  if (!selection.ok) return handleSelectionFailure({
5371
5352
  c,
5372
5353
  store,
@@ -5383,7 +5364,10 @@ async function handleCompletion(c) {
5383
5364
  safetyIdentifier: normalizedSafetyIdentifier,
5384
5365
  promptCacheKey: normalizedPromptCacheKey,
5385
5366
  initiator: fallbackInitiator,
5386
- isSubagent: Boolean(subagentMarker),
5367
+ isSubagent: isSubagentRequest,
5368
+ affinityKeyUsed: affinityKey.affinityKeyUsed,
5369
+ affinityKeySource: affinityKey.affinityKeySource,
5370
+ selectionReason,
5387
5371
  selection
5388
5372
  });
5389
5373
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
@@ -5404,7 +5388,7 @@ async function handleCompletion(c) {
5404
5388
  userId,
5405
5389
  safetyIdentifier: normalizedSafetyIdentifier,
5406
5390
  promptCacheKey: normalizedPromptCacheKey,
5407
- isSubagent: Boolean(subagentMarker),
5391
+ isSubagent: isSubagentRequest,
5408
5392
  clientModel,
5409
5393
  account,
5410
5394
  reservation,
@@ -5415,58 +5399,63 @@ async function handleCompletion(c) {
5415
5399
  premiumRemainingBefore,
5416
5400
  premiumUnlimitedBefore,
5417
5401
  confirmAffinity: selection.confirmAffinity,
5402
+ confirmOwnership: selection.confirmOwnership,
5418
5403
  affinityHit: selection.affinityHit,
5419
- affinityCacheKey: selection.affinityCacheKey
5404
+ affinityCacheKey: selection.affinityCacheKey,
5405
+ affinityKeyUsed: affinityKey.affinityKeyUsed,
5406
+ affinityKeySource: affinityKey.affinityKeySource,
5407
+ selectionReason
5420
5408
  };
5421
5409
  if (endpoint === MESSAGES_ENDPOINT) return await handleWithMessagesApi({
5422
5410
  c,
5423
5411
  anthropicPayload,
5424
5412
  anthropicBetaHeader: anthropicBeta ?? void 0,
5425
- initiatorOverride,
5426
5413
  subagentMarker,
5427
5414
  sessionId,
5428
5415
  instr,
5429
5416
  selectedModel,
5430
- isCompact
5417
+ compactType
5431
5418
  });
5432
5419
  if (endpoint === RESPONSES_ENDPOINT$1) return await handleWithResponsesApi({
5433
5420
  c,
5434
5421
  anthropicPayload,
5435
5422
  openAIPayload,
5436
- initiatorOverride,
5437
5423
  subagentMarker,
5438
5424
  sessionId,
5439
5425
  selectedModel,
5440
5426
  instr,
5441
- isCompact
5427
+ compactType
5442
5428
  });
5443
5429
  return await handleWithChatCompletions({
5444
5430
  c,
5445
5431
  openAIPayload,
5446
- initiatorOverride,
5447
5432
  subagentMarker,
5448
5433
  sessionId,
5449
5434
  selectedModel,
5450
5435
  instr,
5451
- isCompact
5436
+ compactType
5452
5437
  });
5453
5438
  }
5454
5439
  const handleWithChatCompletions = async (params) => {
5455
- const { c, openAIPayload, initiatorOverride, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
5440
+ const { c, openAIPayload, subagentMarker, sessionId, selectedModel, instr, compactType } = params;
5456
5441
  debugJson(logger$5, "Translated OpenAI request payload:", openAIPayload);
5457
5442
  const ctx = toAccountContext(instr.account);
5458
- const initiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
5459
- instr.initiator = initiator;
5443
+ const effectiveInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
5444
+ isCompact: compactType !== 0,
5445
+ isSubagent: Boolean(subagentMarker)
5446
+ });
5447
+ instr.initiator = effectiveInitiator;
5460
5448
  let response;
5461
5449
  try {
5462
5450
  response = await createChatCompletions(openAIPayload, ctx, {
5463
5451
  upstreamRequestId: instr.upstreamRequestId,
5464
- initiator,
5452
+ initiator: effectiveInitiator,
5465
5453
  subagentMarker,
5466
5454
  sessionId,
5467
- isCompact
5455
+ compactType
5468
5456
  });
5469
5457
  instr.confirmAffinity?.();
5458
+ instr.confirmOwnership?.();
5470
5459
  } catch (error) {
5471
5460
  return await handleChatCompletionsCreateError({
5472
5461
  error,
@@ -5496,26 +5485,30 @@ const handleWithChatCompletions = async (params) => {
5496
5485
  }));
5497
5486
  };
5498
5487
  const handleWithResponsesApi = async (params) => {
5499
- const { c, anthropicPayload, openAIPayload, initiatorOverride, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
5488
+ const { c, anthropicPayload, openAIPayload, subagentMarker, sessionId, selectedModel, instr, compactType } = params;
5500
5489
  const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, selectedModel.id);
5501
5490
  applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
5502
5491
  compactInputByLatestCompaction(responsesPayload);
5503
5492
  debugJson(logger$5, "Translated Responses payload:", responsesPayload);
5504
5493
  const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
5505
- const resolvedInitiator = initiatorOverride ?? initiator;
5494
+ const effectiveInitiator = resolveEffectiveInitiator(initiator, {
5495
+ isCompact: compactType !== 0,
5496
+ isSubagent: Boolean(subagentMarker)
5497
+ });
5506
5498
  const ctx = toAccountContext(instr.account);
5507
- instr.initiator = resolvedInitiator;
5499
+ instr.initiator = effectiveInitiator;
5508
5500
  let response;
5509
5501
  try {
5510
5502
  response = await createResponses(responsesPayload, {
5511
5503
  vision,
5512
- initiator: resolvedInitiator,
5504
+ initiator: effectiveInitiator,
5513
5505
  upstreamRequestId: instr.upstreamRequestId,
5514
5506
  subagentMarker,
5515
5507
  sessionId,
5516
- isCompact
5508
+ compactType
5517
5509
  }, ctx);
5518
5510
  instr.confirmAffinity?.();
5511
+ instr.confirmOwnership?.();
5519
5512
  } catch (error) {
5520
5513
  return await handleResponsesCreateError({
5521
5514
  error,
@@ -5564,6 +5557,9 @@ function insertRequestLog$1(instr, record) {
5564
5557
  upstreamRequestId: instr.upstreamRequestId,
5565
5558
  affinityHit: instr.affinityHit,
5566
5559
  affinityCacheKey: instr.affinityCacheKey,
5560
+ affinityKeyUsed: instr.affinityKeyUsed,
5561
+ affinityKeySource: instr.affinityKeySource,
5562
+ selectionReason: instr.selectionReason,
5567
5563
  clientModel,
5568
5564
  upstreamEndpoint,
5569
5565
  accountId: account.id,
@@ -5587,8 +5583,8 @@ async function finalizeQuotaAndGetPremiumSnapshot(instr) {
5587
5583
  async function handleChatCompletionsCreateError(params) {
5588
5584
  const { error, instr, stream } = params;
5589
5585
  const finishedAtMs = Date.now();
5590
- const details = extractErrorDetails(error);
5591
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5586
+ const details = await extractErrorObservability(error);
5587
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5592
5588
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
5593
5589
  insertRequestLog$1(instr, {
5594
5590
  finishedAtMs,
@@ -5600,7 +5596,8 @@ async function handleChatCompletionsCreateError(params) {
5600
5596
  httpStatus: details.httpStatus,
5601
5597
  errorName: details.errorName,
5602
5598
  errorStatus: details.errorStatus,
5603
- errorMessage: details.errorMessage
5599
+ errorMessage: details.errorMessage,
5600
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
5604
5601
  });
5605
5602
  throw error;
5606
5603
  }
@@ -5611,6 +5608,7 @@ async function handleChatCompletionsNonStreaming(params) {
5611
5608
  let errorName;
5612
5609
  let errorStatus;
5613
5610
  let errorMessage;
5611
+ let upstreamErrorMessageRaw;
5614
5612
  const finishedAtMs = Date.now();
5615
5613
  try {
5616
5614
  logger$5.debug("Non-streaming response from Copilot:", JSON.stringify(response));
@@ -5618,12 +5616,13 @@ async function handleChatCompletionsNonStreaming(params) {
5618
5616
  debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
5619
5617
  return c.json(anthropicResponse);
5620
5618
  } catch (error) {
5621
- const details = extractErrorDetails(error);
5619
+ const details = await extractErrorObservability(error);
5622
5620
  httpStatus = details.httpStatus;
5623
5621
  errorName = details.errorName;
5624
5622
  errorStatus = details.errorStatus;
5625
5623
  errorMessage = details.errorMessage;
5626
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5624
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5625
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5627
5626
  throw error;
5628
5627
  } finally {
5629
5628
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5638,7 +5637,8 @@ async function handleChatCompletionsNonStreaming(params) {
5638
5637
  httpStatus,
5639
5638
  errorName,
5640
5639
  errorStatus,
5641
- errorMessage
5640
+ errorMessage,
5641
+ upstreamErrorMessageRaw
5642
5642
  });
5643
5643
  }
5644
5644
  }
@@ -5649,6 +5649,7 @@ async function streamChatCompletionsAndLog(params) {
5649
5649
  let errorName;
5650
5650
  let errorStatus;
5651
5651
  let errorMessage;
5652
+ let upstreamErrorMessageRaw;
5652
5653
  const streamState = {
5653
5654
  messageStartSent: false,
5654
5655
  contentBlockIndex: 0,
@@ -5680,12 +5681,14 @@ async function streamChatCompletionsAndLog(params) {
5680
5681
  }
5681
5682
  }
5682
5683
  } catch (error) {
5683
- const details = extractErrorDetails(error);
5684
+ const details = await extractErrorObservability(error);
5684
5685
  errorName = details.errorName;
5685
5686
  errorStatus = details.errorStatus;
5686
5687
  errorMessage = details.errorMessage;
5688
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5687
5689
  logger$5.warn("Streaming error:", error);
5688
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5690
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5691
+ await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
5689
5692
  } finally {
5690
5693
  const finishedAtMs = Date.now();
5691
5694
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5701,15 +5704,16 @@ async function streamChatCompletionsAndLog(params) {
5701
5704
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
5702
5705
  errorName,
5703
5706
  errorStatus,
5704
- errorMessage
5707
+ errorMessage,
5708
+ upstreamErrorMessageRaw
5705
5709
  });
5706
5710
  }
5707
5711
  }
5708
5712
  async function handleResponsesCreateError(params) {
5709
5713
  const { error, instr, stream } = params;
5710
5714
  const finishedAtMs = Date.now();
5711
- const details = extractErrorDetails(error);
5712
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5715
+ const details = await extractErrorObservability(error);
5716
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5713
5717
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
5714
5718
  insertRequestLog$1(instr, {
5715
5719
  finishedAtMs,
@@ -5721,7 +5725,8 @@ async function handleResponsesCreateError(params) {
5721
5725
  httpStatus: details.httpStatus,
5722
5726
  errorName: details.errorName,
5723
5727
  errorStatus: details.errorStatus,
5724
- errorMessage: details.errorMessage
5728
+ errorMessage: details.errorMessage,
5729
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
5725
5730
  });
5726
5731
  throw error;
5727
5732
  }
@@ -5732,6 +5737,7 @@ async function handleResponsesNonStreaming(params) {
5732
5737
  let errorName;
5733
5738
  let errorStatus;
5734
5739
  let errorMessage;
5740
+ let upstreamErrorMessageRaw;
5735
5741
  const finishedAtMs = Date.now();
5736
5742
  try {
5737
5743
  usage = extractResponsesUsageFromResult(result);
@@ -5740,12 +5746,13 @@ async function handleResponsesNonStreaming(params) {
5740
5746
  debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
5741
5747
  return c.json(anthropicResponse);
5742
5748
  } catch (error) {
5743
- const details = extractErrorDetails(error);
5749
+ const details = await extractErrorObservability(error);
5744
5750
  httpStatus = details.httpStatus;
5745
5751
  errorName = details.errorName;
5746
5752
  errorStatus = details.errorStatus;
5747
5753
  errorMessage = details.errorMessage;
5748
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5754
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5755
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5749
5756
  throw error;
5750
5757
  } finally {
5751
5758
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5760,7 +5767,8 @@ async function handleResponsesNonStreaming(params) {
5760
5767
  httpStatus,
5761
5768
  errorName,
5762
5769
  errorStatus,
5763
- errorMessage
5770
+ errorMessage,
5771
+ upstreamErrorMessageRaw
5764
5772
  });
5765
5773
  }
5766
5774
  }
@@ -5776,6 +5784,17 @@ async function ensureResponsesStreamCompleted(params) {
5776
5784
  data: JSON.stringify(errorEvent)
5777
5785
  });
5778
5786
  }
5787
+ async function writeAnthropicStreamError(stream, message) {
5788
+ try {
5789
+ const errorEvent = buildErrorEvent(message);
5790
+ await stream.writeSSE({
5791
+ event: errorEvent.type,
5792
+ data: JSON.stringify(errorEvent)
5793
+ });
5794
+ } catch (streamError) {
5795
+ logger$5.warn("Failed to write Anthropic stream error event:", streamError);
5796
+ }
5797
+ }
5779
5798
  async function streamResponsesAndLog$1(params) {
5780
5799
  const { stream, response, instr, estimatedInputTokens, historicalUsage } = params;
5781
5800
  let ttfbMs;
@@ -5783,6 +5802,7 @@ async function streamResponsesAndLog$1(params) {
5783
5802
  let errorName;
5784
5803
  let errorStatus;
5785
5804
  let errorMessage;
5805
+ let upstreamErrorMessageRaw;
5786
5806
  const streamState = createResponsesStreamState();
5787
5807
  streamState.estimatedInputTokens = estimatedInputTokens;
5788
5808
  streamState.historicalInputTokens = historicalUsage?.tokensInput;
@@ -5827,12 +5847,14 @@ async function streamResponsesAndLog$1(params) {
5827
5847
  }
5828
5848
  });
5829
5849
  } catch (error) {
5830
- const details = extractErrorDetails(error);
5850
+ const details = await extractErrorObservability(error);
5831
5851
  errorName = details.errorName;
5832
5852
  errorStatus = details.errorStatus;
5833
5853
  errorMessage = details.errorMessage;
5854
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5834
5855
  logger$5.warn("Streaming error:", error);
5835
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5856
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5857
+ await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
5836
5858
  } finally {
5837
5859
  const finishedAtMs = Date.now();
5838
5860
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5848,15 +5870,16 @@ async function streamResponsesAndLog$1(params) {
5848
5870
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
5849
5871
  errorName,
5850
5872
  errorStatus,
5851
- errorMessage
5873
+ errorMessage,
5874
+ upstreamErrorMessageRaw
5852
5875
  });
5853
5876
  }
5854
5877
  }
5855
5878
  async function handleMessagesCreateError(params) {
5856
5879
  const { error, instr, stream } = params;
5857
5880
  const finishedAtMs = Date.now();
5858
- const details = extractErrorDetails(error);
5859
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5881
+ const details = await extractErrorObservability(error);
5882
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5860
5883
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
5861
5884
  insertRequestLog$1(instr, {
5862
5885
  finishedAtMs,
@@ -5868,7 +5891,8 @@ async function handleMessagesCreateError(params) {
5868
5891
  httpStatus: details.httpStatus,
5869
5892
  errorName: details.errorName,
5870
5893
  errorStatus: details.errorStatus,
5871
- errorMessage: details.errorMessage
5894
+ errorMessage: details.errorMessage,
5895
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
5872
5896
  });
5873
5897
  throw error;
5874
5898
  }
@@ -5879,17 +5903,19 @@ async function handleMessagesNonStreaming(params) {
5879
5903
  let errorName;
5880
5904
  let errorStatus;
5881
5905
  let errorMessage;
5906
+ let upstreamErrorMessageRaw;
5882
5907
  const finishedAtMs = Date.now();
5883
5908
  try {
5884
5909
  logger$5.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
5885
5910
  return c.json(response);
5886
5911
  } catch (error) {
5887
- const details = extractErrorDetails(error);
5912
+ const details = await extractErrorObservability(error);
5888
5913
  httpStatus = details.httpStatus;
5889
5914
  errorName = details.errorName;
5890
5915
  errorStatus = details.errorStatus;
5891
5916
  errorMessage = details.errorMessage;
5892
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5917
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5918
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5893
5919
  throw error;
5894
5920
  } finally {
5895
5921
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5904,7 +5930,8 @@ async function handleMessagesNonStreaming(params) {
5904
5930
  httpStatus,
5905
5931
  errorName,
5906
5932
  errorStatus,
5907
- errorMessage
5933
+ errorMessage,
5934
+ upstreamErrorMessageRaw
5908
5935
  });
5909
5936
  }
5910
5937
  }
@@ -5926,6 +5953,7 @@ async function streamMessagesAndLog(params) {
5926
5953
  let errorName;
5927
5954
  let errorStatus;
5928
5955
  let errorMessage;
5956
+ let upstreamErrorMessageRaw;
5929
5957
  try {
5930
5958
  for await (const rawEvent of response) {
5931
5959
  if (ttfbMs === void 0) ttfbMs = Date.now() - instr.startedAtMs;
@@ -5941,12 +5969,14 @@ async function streamMessagesAndLog(params) {
5941
5969
  });
5942
5970
  }
5943
5971
  } catch (error) {
5944
- const details = extractErrorDetails(error);
5972
+ const details = await extractErrorObservability(error);
5945
5973
  errorName = details.errorName;
5946
5974
  errorStatus = details.errorStatus;
5947
5975
  errorMessage = details.errorMessage;
5976
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
5948
5977
  logger$5.warn("Streaming error:", error);
5949
- if (details.unauthorized) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5978
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
5979
+ await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
5950
5980
  } finally {
5951
5981
  const finishedAtMs = Date.now();
5952
5982
  const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
@@ -5962,28 +5992,33 @@ async function streamMessagesAndLog(params) {
5962
5992
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
5963
5993
  errorName,
5964
5994
  errorStatus,
5965
- errorMessage
5995
+ errorMessage,
5996
+ upstreamErrorMessageRaw
5966
5997
  });
5967
5998
  }
5968
5999
  }
5969
6000
  const handleWithMessagesApi = async (params) => {
5970
- const { c, anthropicPayload, anthropicBetaHeader, initiatorOverride, subagentMarker, sessionId, instr, selectedModel, isCompact } = params;
6001
+ const { c, anthropicPayload, anthropicBetaHeader, subagentMarker, sessionId, instr, selectedModel, compactType } = params;
5971
6002
  prepareMessagesApiPayload(anthropicPayload, selectedModel);
5972
6003
  debugJson(logger$5, "Translated Messages payload:", anthropicPayload);
5973
6004
  const ctx = toAccountContext(instr.account);
5974
- const initiator = initiatorOverride ?? getMessagesInitiator(anthropicPayload);
5975
- instr.initiator = initiator;
6005
+ const effectiveInitiator = resolveEffectiveInitiator(getMessagesInitiator(anthropicPayload), {
6006
+ isCompact: compactType !== 0,
6007
+ isSubagent: Boolean(subagentMarker)
6008
+ });
6009
+ instr.initiator = effectiveInitiator;
5976
6010
  let response;
5977
6011
  try {
5978
6012
  response = await createMessages(anthropicPayload, ctx, {
5979
6013
  anthropicBetaHeader,
5980
6014
  upstreamRequestId: instr.upstreamRequestId,
5981
- initiator,
6015
+ initiator: effectiveInitiator,
5982
6016
  subagentMarker,
5983
6017
  sessionId,
5984
- isCompact
6018
+ compactType
5985
6019
  });
5986
6020
  instr.confirmAffinity?.();
6021
+ instr.confirmOwnership?.();
5987
6022
  } catch (error) {
5988
6023
  return await handleMessagesCreateError({
5989
6024
  error,
@@ -6031,9 +6066,8 @@ messageRoutes.post("/count_tokens", async (c) => {
6031
6066
  const modelRoutes = new Hono();
6032
6067
  modelRoutes.get("/", async (c) => {
6033
6068
  try {
6034
- if (!state.models) await cacheModels();
6035
6069
  const blockedTargets = getAliasTargetSet();
6036
- const models = state.models?.data.filter((model) => !blockedTargets.has(model.id.toLowerCase())).map((model) => ({
6070
+ const models = getAvailableModels().filter((model) => !blockedTargets.has(model.id.toLowerCase())).map((model) => ({
6037
6071
  id: model.id,
6038
6072
  object: "model",
6039
6073
  type: "model",
@@ -6041,7 +6075,7 @@ modelRoutes.get("/", async (c) => {
6041
6075
  created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
6042
6076
  owned_by: model.vendor,
6043
6077
  display_name: model.name
6044
- })) ?? [];
6078
+ }));
6045
6079
  const aliasModels = Object.keys(getModelAliases()).map((alias) => ({
6046
6080
  id: alias,
6047
6081
  object: "model",
@@ -6090,7 +6124,7 @@ async function handleProviderCountTokens(c) {
6090
6124
  const anthropicPayload = await c.req.json();
6091
6125
  const openAIPayload = translateToOpenAI(anthropicPayload);
6092
6126
  const modelId = anthropicPayload.model.trim();
6093
- let selectedModel = state.models?.data.find((model) => model.id === modelId);
6127
+ let selectedModel = getAvailableModels().find((model) => model.id === modelId);
6094
6128
  if (!selectedModel && modelId) selectedModel = createFallbackModel(modelId);
6095
6129
  if (!selectedModel) {
6096
6130
  logger$4.warn("provider.count_tokens.model_not_found", {
@@ -6350,9 +6384,10 @@ const handleResponses = async (c) => {
6350
6384
  const streamRequested = Boolean(payload.stream);
6351
6385
  const { initiator: initialInitiator } = getResponsesRequestOptions(payload);
6352
6386
  const userId = payload.metadata?.user_id;
6353
- const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
6387
+ const requestBodyPromptCacheKey = typeof payload.prompt_cache_key === "string" ? payload.prompt_cache_key : null;
6388
+ const { safetyIdentifier, sessionId: metadataSessionId } = parseUserIdMetadata(userId);
6354
6389
  const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
6355
- const normalizedPromptCacheKey = promptCacheKey ?? void 0;
6390
+ const normalizedPromptCacheKey = requestBodyPromptCacheKey ?? metadataSessionId ?? void 0;
6356
6391
  request.userId = userId;
6357
6392
  request.safetyIdentifier = normalizedSafetyIdentifier;
6358
6393
  request.promptCacheKey = normalizedPromptCacheKey;
@@ -6370,10 +6405,19 @@ const handleResponses = async (c) => {
6370
6405
  });
6371
6406
  }
6372
6407
  const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, normalizedPromptCacheKey);
6408
+ const headerSessionId = c.req.header("x-session-id") ?? null;
6409
+ const affinityKey = resolveAffinityKey({
6410
+ promptCacheKey: requestBodyPromptCacheKey,
6411
+ metadataSessionId,
6412
+ headerSessionId,
6413
+ upstreamRequestId
6414
+ });
6415
+ request.affinityKeyUsed = affinityKey.affinityKeyUsed;
6416
+ request.affinityKeySource = affinityKey.affinityKeySource;
6373
6417
  const selection = await accountsManager.selectAccountForRequest([{
6374
6418
  modelId: clientModel,
6375
6419
  endpoint: RESPONSES_ENDPOINT
6376
- }], { requestId: upstreamRequestId });
6420
+ }], { requestId: affinityKey.requestId });
6377
6421
  if (!selection.ok) {
6378
6422
  recordSelectionFailure(store, {
6379
6423
  request,
@@ -6386,6 +6430,7 @@ const handleResponses = async (c) => {
6386
6430
  const { account, selectedModel } = selection;
6387
6431
  request.affinityHit = selection.affinityHit;
6388
6432
  request.affinityCacheKey = selection.affinityCacheKey;
6433
+ request.selectionReason = selection.selectionReason;
6389
6434
  const upstreamPayload = {
6390
6435
  ...payload,
6391
6436
  model: selectedModel.id
@@ -6399,7 +6444,7 @@ const handleResponses = async (c) => {
6399
6444
  request.initiator = initiator;
6400
6445
  if (state.manualApprove) await awaitApproval();
6401
6446
  const accountCtx = toAccountContext(account);
6402
- const upstreamSessionId = getUUID(upstreamRequestId);
6447
+ const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
6403
6448
  request.upstreamRequestId = upstreamRequestId;
6404
6449
  request.upstreamSessionId = upstreamSessionId;
6405
6450
  if (streamRequested) return handleStreamingResponses({
@@ -6429,6 +6474,37 @@ const handleResponses = async (c) => {
6429
6474
  premiumUnlimitedBefore
6430
6475
  });
6431
6476
  };
6477
+ async function observeRequestError(accountId, error) {
6478
+ const details = await extractErrorObservability(error);
6479
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(accountId, "Unauthorized (401)");
6480
+ return {
6481
+ httpStatus: details.httpStatus,
6482
+ errorName: details.errorName,
6483
+ errorStatus: details.errorStatus,
6484
+ errorMessage: details.errorMessage,
6485
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
6486
+ };
6487
+ }
6488
+ function buildResponsesStreamError(message) {
6489
+ return {
6490
+ type: "error",
6491
+ code: null,
6492
+ message,
6493
+ param: null,
6494
+ sequence_number: 0
6495
+ };
6496
+ }
6497
+ async function writeResponsesStreamError(stream, message) {
6498
+ try {
6499
+ const errorEvent = buildResponsesStreamError(message);
6500
+ await stream.writeSSE({
6501
+ event: errorEvent.type,
6502
+ data: JSON.stringify(errorEvent)
6503
+ });
6504
+ } catch (streamError) {
6505
+ logger$1.warn("Failed to write Responses stream error event:", streamError);
6506
+ }
6507
+ }
6432
6508
  function buildRequestContext(c) {
6433
6509
  const requestId = randomUUID();
6434
6510
  const startedAtMs = Date.now();
@@ -6459,6 +6535,9 @@ function insertRequestLog(store, request, record) {
6459
6535
  promptCacheKey: request.promptCacheKey,
6460
6536
  initiator: request.initiator,
6461
6537
  upstreamRequestId: request.upstreamRequestId,
6538
+ affinityKeyUsed: request.affinityKeyUsed,
6539
+ affinityKeySource: request.affinityKeySource,
6540
+ selectionReason: request.selectionReason,
6462
6541
  affinityHit: request.affinityHit,
6463
6542
  affinityCacheKey: request.affinityCacheKey,
6464
6543
  ...record
@@ -6497,14 +6576,6 @@ function extractUsageFromChunkData(data) {
6497
6576
  return;
6498
6577
  }
6499
6578
  }
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
6579
  async function handleStreamingResponses(params) {
6509
6580
  const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore } = params;
6510
6581
  let response;
@@ -6555,8 +6626,8 @@ async function handleUpstreamCreateError(params) {
6555
6626
  const { store, request, selection, clientModel, premiumRemainingBefore, premiumUnlimitedBefore, error } = params;
6556
6627
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
6557
6628
  const finishedAtMs = Date.now();
6558
- const details = extractErrorDetails(error);
6559
- if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
6629
+ const details = await extractErrorObservability(error);
6630
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
6560
6631
  await accountsManager.finalizeQuota(account, reservation);
6561
6632
  const premiumRemainingAfter = account.premiumRemaining;
6562
6633
  const premiumUnlimitedAfter = account.unlimited;
@@ -6578,7 +6649,8 @@ async function handleUpstreamCreateError(params) {
6578
6649
  httpStatus: details.httpStatus,
6579
6650
  errorName: details.errorName,
6580
6651
  errorStatus: details.errorStatus,
6581
- errorMessage: details.errorMessage
6652
+ errorMessage: details.errorMessage,
6653
+ upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
6582
6654
  });
6583
6655
  throw error;
6584
6656
  }
@@ -6590,6 +6662,7 @@ async function handleNonStreamingUpstreamResult(params) {
6590
6662
  let errorName;
6591
6663
  let errorStatus;
6592
6664
  let errorMessage;
6665
+ let upstreamErrorMessageRaw;
6593
6666
  const finishedAtMs = Date.now();
6594
6667
  try {
6595
6668
  debugJsonTail(logger$1, "Forwarding native Responses result:", {
@@ -6598,11 +6671,12 @@ async function handleNonStreamingUpstreamResult(params) {
6598
6671
  });
6599
6672
  return c.json(result);
6600
6673
  } catch (error) {
6601
- const details = extractErrorDetails(error);
6674
+ const details = await extractErrorObservability(error);
6602
6675
  httpStatus = details.httpStatus;
6603
6676
  errorName = details.errorName;
6604
6677
  errorStatus = details.errorStatus;
6605
6678
  errorMessage = details.errorMessage;
6679
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
6606
6680
  throw error;
6607
6681
  } finally {
6608
6682
  await accountsManager.finalizeQuota(account, reservation);
@@ -6627,7 +6701,8 @@ async function handleNonStreamingUpstreamResult(params) {
6627
6701
  httpStatus,
6628
6702
  errorName,
6629
6703
  errorStatus,
6630
- errorMessage
6704
+ errorMessage,
6705
+ upstreamErrorMessageRaw
6631
6706
  });
6632
6707
  }
6633
6708
  }
@@ -6640,6 +6715,7 @@ async function streamResponsesAndLog(params) {
6640
6715
  let errorName;
6641
6716
  let errorStatus;
6642
6717
  let errorMessage;
6718
+ let upstreamErrorMessageRaw;
6643
6719
  try {
6644
6720
  for await (const chunk of response) {
6645
6721
  if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
@@ -6655,11 +6731,14 @@ async function streamResponsesAndLog(params) {
6655
6731
  });
6656
6732
  }
6657
6733
  } catch (error) {
6658
- const details = extractErrorDetails(error);
6734
+ const details = await extractErrorObservability(error);
6659
6735
  errorName = details.errorName;
6660
6736
  errorStatus = details.errorStatus;
6661
6737
  errorMessage = details.errorMessage;
6738
+ upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
6662
6739
  logger$1.warn("Responses streaming error:", error);
6740
+ if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
6741
+ await writeResponsesStreamError(stream, getUserVisibleErrorMessage(details));
6663
6742
  } finally {
6664
6743
  const finishedAtMs = Date.now();
6665
6744
  await accountsManager.finalizeQuota(account, reservation);
@@ -6685,18 +6764,16 @@ async function streamResponsesAndLog(params) {
6685
6764
  httpStatus: errorStatus ?? (errorName ? 500 : 200),
6686
6765
  errorName,
6687
6766
  errorStatus,
6688
- errorMessage
6767
+ errorMessage,
6768
+ upstreamErrorMessageRaw
6689
6769
  });
6690
6770
  }
6691
6771
  }
6692
6772
  async function handleNonStreamingResponses(params) {
6693
6773
  const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore } = params;
6694
6774
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
6695
- let httpStatus = 200;
6696
6775
  let usage = {};
6697
- let errorName;
6698
- let errorStatus;
6699
- let errorMessage;
6776
+ let errorState = { httpStatus: 200 };
6700
6777
  let finishedAtMs;
6701
6778
  try {
6702
6779
  const response = await createResponses(payload, {
@@ -6705,10 +6782,9 @@ async function handleNonStreamingResponses(params) {
6705
6782
  upstreamRequestId: request.upstreamRequestId,
6706
6783
  sessionId: request.upstreamSessionId
6707
6784
  }, accountCtx);
6785
+ if (isAsyncIterable(response)) throw new Error("Upstream returned a stream unexpectedly");
6708
6786
  selection.confirmAffinity?.();
6709
6787
  finishedAtMs = Date.now();
6710
- const streamResponse = handleUnexpectedResponsesStream(c, response);
6711
- if (streamResponse) return streamResponse;
6712
6788
  const result = response;
6713
6789
  usage = extractResponsesUsageFromResult(result);
6714
6790
  debugJsonTail(logger$1, "Forwarding native Responses result:", {
@@ -6718,12 +6794,7 @@ async function handleNonStreamingResponses(params) {
6718
6794
  return c.json(result);
6719
6795
  } catch (error) {
6720
6796
  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)");
6797
+ errorState = await observeRequestError(account.id, error);
6727
6798
  throw error;
6728
6799
  } finally {
6729
6800
  const finishedAtMsFinal = finishedAtMs ?? Date.now();
@@ -6746,61 +6817,14 @@ async function handleNonStreamingResponses(params) {
6746
6817
  premiumRemainingDiff: computeDiff(premiumRemainingBefore, premiumRemainingAfter),
6747
6818
  premiumUnlimitedBefore,
6748
6819
  premiumUnlimitedAfter,
6749
- httpStatus,
6750
- errorName,
6751
- errorStatus,
6752
- errorMessage
6820
+ httpStatus: errorState.httpStatus,
6821
+ errorName: errorState.errorName,
6822
+ errorStatus: errorState.errorStatus,
6823
+ errorMessage: errorState.errorMessage,
6824
+ upstreamErrorMessageRaw: errorState.upstreamErrorMessageRaw
6753
6825
  });
6754
6826
  }
6755
6827
  }
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
6828
 
6805
6829
  //#endregion
6806
6830
  //#region src/routes/responses/route.ts
@@ -6894,4 +6918,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
6894
6918
 
6895
6919
  //#endregion
6896
6920
  export { server };
6897
- //# sourceMappingURL=server-DVpkQrk2.js.map
6921
+ //# sourceMappingURL=server-CFijvv3C.js.map