omnigateway 0.1.6 → 0.1.8

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 (37) hide show
  1. package/README.md +24 -5
  2. package/bin/omni.js +805 -352
  3. package/gateway.js +622 -236
  4. package/package.json +1 -1
  5. package/public/assets/{Chip-Ccqsb_Mq.js → Chip-CM9ZMdr9.js} +2 -2
  6. package/public/assets/{CopyValue-cAwgC1sU.js → CopyValue-WrOTcHwq.js} +4 -4
  7. package/public/assets/{Field-Devm4K46.js → Field-Br6jKPz8.js} +8 -8
  8. package/public/assets/{Lamp-BXSUIAtG.js → Lamp-DO-sRDg2.js} +7 -7
  9. package/public/assets/{Meter-Fn8X7t60.js → Meter-BzMaum0C.js} +2 -2
  10. package/public/assets/{Modal-Dqir9wBz.js → Modal-lhjr949H.js} +8 -8
  11. package/public/assets/Rack-BFWC50ex.js +147 -0
  12. package/public/assets/{Readout-BKpOZIGX.js → Readout-DMJs4gYD.js} +5 -5
  13. package/public/assets/{States-DBzsiHjQ.js → States-BNeCLhZn.js} +10 -10
  14. package/public/assets/{Table-DPDJeeVc.js → Table-nOg_lgEJ.js} +1 -1
  15. package/public/assets/{Toggle-BhViDMm9.js → Toggle-DYoCVD37.js} +3 -3
  16. package/public/assets/_app-lXr06Xli.js +1 -0
  17. package/public/assets/_app.accounts-BTb5vHI_.js +51 -0
  18. package/public/assets/_app.console-DHUClMsI.js +25 -0
  19. package/public/assets/_app.index-DZ--9mKU.js +61 -0
  20. package/public/assets/_app.keys-B-cy-2DS.js +39 -0
  21. package/public/assets/_app.logs-BQjAIT2P.js +33 -0
  22. package/public/assets/{_app.models-CuD-JtGS.js → _app.models-BAmXHlf2.js} +27 -27
  23. package/public/assets/_app.settings-CG8bv_rm.js +34 -0
  24. package/public/assets/{_app.usage-DAyYUfIi.js → _app.usage-BLrQ2hB8.js} +28 -28
  25. package/public/assets/index-UAZ0O5y0.js +170 -0
  26. package/public/assets/{login-DXHM-Noj.js → login-BpUl6C5b.js} +8 -8
  27. package/public/assets/{queries-BMwc6EvM.js → queries-D_-Jj9vt.js} +12 -12
  28. package/public/assets/{trash-2-JF_uX2gE.js → trash-2-DjWP2u19.js} +1 -1
  29. package/public/index.html +2 -2
  30. package/public/assets/Rack-XtfiZ6GC.js +0 -147
  31. package/public/assets/_app-B8lqq_ks.js +0 -1
  32. package/public/assets/_app.accounts-DSMHSYBF.js +0 -51
  33. package/public/assets/_app.index-DXH5sH41.js +0 -61
  34. package/public/assets/_app.keys-DdEyVy_b.js +0 -39
  35. package/public/assets/_app.logs-Camxditb.js +0 -33
  36. package/public/assets/_app.settings-DA1aYcR4.js +0 -16
  37. package/public/assets/index-BtjyVE1i.js +0 -170
package/gateway.js CHANGED
@@ -535,7 +535,7 @@ function flushChunk(parts, chunk) {
535
535
  }
536
536
  function pushCodeUnit(parts, chunk, codeUnit) {
537
537
  chunk.push(codeUnit);
538
- if (chunk.length >= CHUNK)
538
+ if (chunk.length >= CHUNK2)
539
539
  flushChunk(parts, chunk);
540
540
  }
541
541
  function pushCodePoint(parts, chunk, cp) {
@@ -660,8 +660,8 @@ function decodeUTF16LE(bytes) {
660
660
  }
661
661
  function decodeASCII(bytes) {
662
662
  const parts = [];
663
- for (let i = 0;i < bytes.length; i += CHUNK) {
664
- const end = Math.min(bytes.length, i + CHUNK);
663
+ for (let i = 0;i < bytes.length; i += CHUNK2) {
664
+ const end = Math.min(bytes.length, i + CHUNK2);
665
665
  const codes = new Array(end - i);
666
666
  for (let j = i, k2 = 0;j < end; j++, k2++) {
667
667
  codes[k2] = bytes[j] & 127;
@@ -672,8 +672,8 @@ function decodeASCII(bytes) {
672
672
  }
673
673
  function decodeLatin1(bytes) {
674
674
  const parts = [];
675
- for (let i = 0;i < bytes.length; i += CHUNK) {
676
- const end = Math.min(bytes.length, i + CHUNK);
675
+ for (let i = 0;i < bytes.length; i += CHUNK2) {
676
+ const end = Math.min(bytes.length, i + CHUNK2);
677
677
  const codes = new Array(end - i);
678
678
  for (let j = i, k2 = 0;j < end; j++, k2++) {
679
679
  codes[k2] = bytes[j];
@@ -689,7 +689,7 @@ function decodeWindows1252(bytes) {
689
689
  const b = bytes[i];
690
690
  const extra = b >= 128 && b <= 159 ? WINDOWS_1252_EXTRA[b] : undefined;
691
691
  out += extra !== null && extra !== undefined ? extra : String.fromCharCode(b);
692
- if (out.length >= CHUNK) {
692
+ if (out.length >= CHUNK2) {
693
693
  parts.push(out);
694
694
  out = "";
695
695
  }
@@ -698,7 +698,7 @@ function decodeWindows1252(bytes) {
698
698
  parts.push(out);
699
699
  return parts.join("");
700
700
  }
701
- var WINDOWS_1252_EXTRA, WINDOWS_1252_REVERSE, _utf8Decoder, CHUNK, REPLACEMENT = 65533;
701
+ var WINDOWS_1252_EXTRA, WINDOWS_1252_REVERSE, _utf8Decoder, CHUNK2, REPLACEMENT = 65533;
702
702
  var init_lib = __esm(() => {
703
703
  WINDOWS_1252_EXTRA = {
704
704
  128: "\u20AC",
@@ -733,7 +733,7 @@ var init_lib = __esm(() => {
733
733
  for (const [code, char] of Object.entries(WINDOWS_1252_EXTRA)) {
734
734
  WINDOWS_1252_REVERSE[char] = Number.parseInt(code, 10);
735
735
  }
736
- CHUNK = 32 * 1024;
736
+ CHUNK2 = 32 * 1024;
737
737
  });
738
738
 
739
739
  // node_modules/.bun/token-types@6.1.2/node_modules/token-types/lib/index.js
@@ -5414,6 +5414,23 @@ function formatLine(level, at, msg, fields, color) {
5414
5414
  }
5415
5415
  return parts.length === 0 ? head : `${head} ${parts.join(" ")}`;
5416
5416
  }
5417
+ var ANSI = /\u001B\[[0-9;]*m/g;
5418
+ var HEAD = /^(\S+) (DEBUG|INFO|WARN|ERROR) {1,2}(.*)$/;
5419
+ var FIELDS = / {2}(?=[a-zA-Z]+=)/;
5420
+ function parseLine(raw) {
5421
+ const absent = { raw, at: null, level: null, msg: null };
5422
+ const match = HEAD.exec(raw.replace(ANSI, ""));
5423
+ if (match === null)
5424
+ return absent;
5425
+ const [, instant, label, tail] = match;
5426
+ if (instant === undefined || label === undefined || tail === undefined)
5427
+ return absent;
5428
+ const at = Date.parse(instant);
5429
+ if (Number.isNaN(at))
5430
+ return absent;
5431
+ const msg = tail.split(FIELDS, 1)[0] ?? tail;
5432
+ return { raw, at, level: label.toLowerCase(), msg };
5433
+ }
5417
5434
  function createLogger(opts) {
5418
5435
  const threshold = LEVELS[opts.level];
5419
5436
  const now = opts.now ?? (() => Date.now());
@@ -5491,7 +5508,7 @@ function collect(events) {
5491
5508
  else if (ev.delta.type === "thinking" && acc.kind === "thinking")
5492
5509
  acc.text += ev.delta.text;
5493
5510
  else if (ev.delta.type === "thinkingSignature" && acc.kind === "thinking")
5494
- acc.signature = ev.delta.signature;
5511
+ acc.signature = (acc.signature ?? "") + ev.delta.signature;
5495
5512
  else if (ev.delta.type === "toolJson" && acc.kind === "toolUse")
5496
5513
  acc.json += ev.delta.partial;
5497
5514
  break;
@@ -5526,6 +5543,54 @@ function parseJson(raw) {
5526
5543
  return {};
5527
5544
  }
5528
5545
  }
5546
+ // packages/ir/src/tokens.ts
5547
+ var CHARS_PER_TOKEN = 4;
5548
+ var IMAGE_TOKENS = 1600;
5549
+ var BLOCK_OVERHEAD = 4;
5550
+ var MESSAGE_OVERHEAD = 4;
5551
+ function fromText(text) {
5552
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
5553
+ }
5554
+ function blockTokens(block) {
5555
+ switch (block.type) {
5556
+ case "text":
5557
+ return BLOCK_OVERHEAD + fromText(block.text);
5558
+ case "image":
5559
+ return BLOCK_OVERHEAD + IMAGE_TOKENS;
5560
+ case "thinking":
5561
+ return BLOCK_OVERHEAD + fromText(block.text);
5562
+ case "toolUse":
5563
+ return BLOCK_OVERHEAD + fromText(block.name) + fromText(safeJson(block.input));
5564
+ case "toolResult":
5565
+ return BLOCK_OVERHEAD + fromText(block.toolUseId) + fromText(block.content);
5566
+ }
5567
+ }
5568
+ function messageTokens(message) {
5569
+ let total = MESSAGE_OVERHEAD;
5570
+ for (const block of message.content)
5571
+ total += blockTokens(block);
5572
+ return total;
5573
+ }
5574
+ function toolTokens(tool) {
5575
+ return BLOCK_OVERHEAD + fromText(tool.name) + fromText(tool.description ?? "") + fromText(safeJson(tool.inputSchema));
5576
+ }
5577
+ function safeJson(value) {
5578
+ try {
5579
+ return JSON.stringify(value) ?? "";
5580
+ } catch {
5581
+ return "";
5582
+ }
5583
+ }
5584
+ function estimateInputTokens(request) {
5585
+ let total = 0;
5586
+ for (const block of request.system ?? [])
5587
+ total += blockTokens(block);
5588
+ for (const message of request.messages)
5589
+ total += messageTokens(message);
5590
+ for (const tool of request.tools ?? [])
5591
+ total += toolTokens(tool);
5592
+ return total;
5593
+ }
5529
5594
  // packages/ir/src/validate.ts
5530
5595
  function validateRequest(req) {
5531
5596
  const seenToolUseIds = new Set;
@@ -5556,6 +5621,7 @@ function validateRequest(req) {
5556
5621
  }
5557
5622
  // packages/control/src/config.ts
5558
5623
  var MIN_KEY_LENGTH = 16;
5624
+ var TRUTHY = new Set(["1", "true", "yes", "on"]);
5559
5625
  var DECIMAL_INTEGER = /^\d+$/;
5560
5626
  function optionalText(value, fallback) {
5561
5627
  return value?.trim() || fallback;
@@ -5574,6 +5640,8 @@ function loadConfig(env) {
5574
5640
  const derivedBaseUrl = `http://${host}:${port}`;
5575
5641
  const baseUrl = optionalText(env.OMNI_BASE_URL, derivedBaseUrl).replace(/\/+$/, "") || derivedBaseUrl;
5576
5642
  const staticDir = env.OMNI_STATIC_DIR?.trim();
5643
+ const logFile = env.OMNI_LOG_FILE?.trim();
5644
+ const exposeClaudeCodeAliases = TRUTHY.has((env.OMNI_EXPOSE_CLAUDE_CODE_ALIASES ?? "").trim().toLowerCase());
5577
5645
  const rawLogLevel = env.OMNI_LOG_LEVEL?.trim();
5578
5646
  const logLevel = parseLogLevel(rawLogLevel);
5579
5647
  return {
@@ -5584,9 +5652,15 @@ function loadConfig(env) {
5584
5652
  databasePath: optionalText(env.OMNI_DB_PATH, "./omnigateway.db"),
5585
5653
  encryptionKey,
5586
5654
  baseUrl,
5587
- staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir
5655
+ staticDir: staticDir === undefined || staticDir.length === 0 ? null : staticDir,
5656
+ exposeClaudeCodeAliases,
5657
+ logFile: logFile === undefined || logFile.length === 0 ? null : logFile
5588
5658
  };
5589
5659
  }
5660
+ // packages/providers/src/betas.ts
5661
+ var CONTEXT_1M_BETA = "context-1m-2025-08-07";
5662
+ var CONTEXT_1M_TOKENS = 1e6;
5663
+
5590
5664
  // packages/providers/src/body.ts
5591
5665
  var BODY_ORDER = {
5592
5666
  anthropic: [
@@ -5694,6 +5768,119 @@ function signAnthropicBody(json) {
5694
5768
  return json.replace(needle, `cch=${computeCch(json)};`);
5695
5769
  }
5696
5770
 
5771
+ // packages/providers/src/anthropic/models.ts
5772
+ var ANTHROPIC_MODELS = {
5773
+ defaultModel: "claude-opus-5",
5774
+ models: [
5775
+ {
5776
+ id: "claude-fable-5",
5777
+ label: "Claude Fable 5",
5778
+ pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 },
5779
+ limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
5780
+ },
5781
+ {
5782
+ id: "claude-opus-5",
5783
+ label: "Claude Opus 5",
5784
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 },
5785
+ limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
5786
+ },
5787
+ {
5788
+ id: "claude-sonnet-5",
5789
+ label: "Claude Sonnet 5",
5790
+ pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 },
5791
+ limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
5792
+ },
5793
+ {
5794
+ id: "claude-haiku-4-5",
5795
+ label: "Claude Haiku 4.5",
5796
+ pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 },
5797
+ limits: { contextWindow: 200000, maxOutputTokens: 64000 }
5798
+ }
5799
+ ]
5800
+ };
5801
+
5802
+ // packages/providers/src/kimi/models.ts
5803
+ var KIMI_MODELS = {
5804
+ defaultModel: "k3-256k",
5805
+ models: [
5806
+ {
5807
+ id: "k3-256k",
5808
+ label: "Kimi K3 \u2014 256K",
5809
+ pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
5810
+ limits: { contextWindow: 262144, maxOutputTokens: 131072 }
5811
+ },
5812
+ {
5813
+ id: "k3",
5814
+ label: "Kimi K3 \u2014 up to 1M",
5815
+ pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
5816
+ limits: { contextWindow: 1048576, maxOutputTokens: 131072 }
5817
+ },
5818
+ {
5819
+ id: "kimi-for-coding",
5820
+ label: "Kimi K2.7 Code",
5821
+ pricing: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
5822
+ limits: { contextWindow: 262144, maxOutputTokens: 131072 }
5823
+ },
5824
+ {
5825
+ id: "kimi-for-coding-highspeed",
5826
+ label: "Kimi K2.7 Code \u2014 High Speed",
5827
+ pricing: { input: 0.95, output: 8, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
5828
+ limits: { contextWindow: 262144, maxOutputTokens: 131072 }
5829
+ }
5830
+ ]
5831
+ };
5832
+
5833
+ // packages/providers/src/openai/models.ts
5834
+ var OPENAI_MODELS = {
5835
+ defaultModel: "gpt-5.6",
5836
+ models: [
5837
+ {
5838
+ id: "gpt-5.6",
5839
+ label: "GPT-5.6 \u2014 routes to Sol",
5840
+ pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
5841
+ limits: { contextWindow: 922000, maxOutputTokens: 128000 },
5842
+ oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
5843
+ },
5844
+ {
5845
+ id: "gpt-5.6-sol",
5846
+ label: "GPT-5.6 Sol \u2014 deepest reasoning",
5847
+ pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
5848
+ limits: { contextWindow: 922000, maxOutputTokens: 128000 },
5849
+ oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
5850
+ },
5851
+ {
5852
+ id: "gpt-5.6-terra",
5853
+ label: "GPT-5.6 Terra \u2014 balanced",
5854
+ pricing: { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 },
5855
+ limits: { contextWindow: 922000, maxOutputTokens: 128000 },
5856
+ oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
5857
+ },
5858
+ {
5859
+ id: "gpt-5.6-luna",
5860
+ label: "GPT-5.6 Luna \u2014 fastest",
5861
+ pricing: { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 },
5862
+ limits: { contextWindow: 922000, maxOutputTokens: 128000 },
5863
+ oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
5864
+ }
5865
+ ]
5866
+ };
5867
+
5868
+ // packages/providers/src/catalog.ts
5869
+ var PROVIDER_MODEL_CATALOG = {
5870
+ anthropic: ANTHROPIC_MODELS,
5871
+ openai: OPENAI_MODELS,
5872
+ kimi: KIMI_MODELS
5873
+ };
5874
+ function catalogPricing(provider, model) {
5875
+ return PROVIDER_MODEL_CATALOG[provider]?.models.find((entry) => entry.id === model)?.pricing ?? null;
5876
+ }
5877
+ function catalogLimits(provider, model, auth = "apiKey") {
5878
+ const entry = PROVIDER_MODEL_CATALOG[provider]?.models.find((choice) => choice.id === model);
5879
+ if (entry === undefined)
5880
+ return null;
5881
+ return (auth === "oauth" ? entry.oauthLimits : undefined) ?? entry.limits;
5882
+ }
5883
+
5697
5884
  // packages/providers/src/http.ts
5698
5885
  function codeForStatus(status) {
5699
5886
  if (status === 401 || status === 403)
@@ -6009,7 +6196,7 @@ async function* decodeAnthropic(messages) {
6009
6196
  if (cb.type === "text")
6010
6197
  yield { type: "blockStart", index, block: { type: "text" } };
6011
6198
  else if (cb.type === "thinking")
6012
- yield { type: "blockStart", index, block: { type: "thinking" } };
6199
+ yield { type: "blockStart", index, block: { type: "thinking", signed: true } };
6013
6200
  else if (cb.type === "tool_use")
6014
6201
  yield {
6015
6202
  type: "blockStart",
@@ -6029,7 +6216,7 @@ async function* decodeAnthropic(messages) {
6029
6216
  index,
6030
6217
  delta: { type: "thinking", text: delta.thinking ?? "" }
6031
6218
  };
6032
- else if (delta.type === "signature_delta")
6219
+ else if (delta.type === "signature_delta" && (delta.signature ?? "") !== "")
6033
6220
  yield {
6034
6221
  type: "blockDelta",
6035
6222
  index,
@@ -6101,6 +6288,9 @@ function wireCacheControl(c) {
6101
6288
  return {};
6102
6289
  return { cache_control: { type: c.type, ...c.ttl === undefined ? {} : { ttl: c.ttl } } };
6103
6290
  }
6291
+ function isReplayableThinking(b) {
6292
+ return b.type !== "thinking" || b.signature !== undefined && b.signature !== "";
6293
+ }
6104
6294
  function encodeBlock(b) {
6105
6295
  const cache = wireCacheControl(cacheControlOf(b));
6106
6296
  switch (b.type) {
@@ -6130,6 +6320,16 @@ function encodeSystemTurn(content) {
6130
6320
  return content.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
6131
6321
  `);
6132
6322
  }
6323
+ function systemCacheControl(req) {
6324
+ const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
6325
+ const markedSystemBlocks = cacheable.filter(({ role, block }) => role === "system" && cacheControlOf(block) !== undefined);
6326
+ const final = markedSystemBlocks.at(-1);
6327
+ const promoted = final !== undefined && final.block.type === "text" && cacheable.at(-1) === final ? cacheControlOf(final.block) : undefined;
6328
+ return {
6329
+ ...promoted === undefined ? {} : { promoted },
6330
+ lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
6331
+ };
6332
+ }
6133
6333
  function encodeToolChoice(c) {
6134
6334
  switch (c.type) {
6135
6335
  case "auto":
@@ -6153,18 +6353,30 @@ function toWire(req, model, opts) {
6153
6353
  system = [{ type: "text", text: OAUTH_IDENTITY }, ...system ?? []];
6154
6354
  note("anthropic:oauth-system-prefix");
6155
6355
  }
6356
+ const systemCache = systemCacheControl(req);
6357
+ if (systemCache.lost)
6358
+ note("anthropic:system-turn-cache-control-dropped");
6156
6359
  const body = {
6157
6360
  model,
6158
- messages: req.messages.map((m) => {
6159
- if (m.role !== "system")
6160
- return { role: m.role, content: m.content.map(encodeBlock) };
6161
- if (m.content.some((b) => cacheControlOf(b) !== undefined)) {
6162
- note("anthropic:system-turn-cache-control-dropped");
6163
- }
6164
- return { role: m.role, content: encodeSystemTurn(m.content) };
6361
+ messages: req.messages.flatMap((m) => {
6362
+ if (m.role !== "system") {
6363
+ const replayable = m.content.filter(isReplayableThinking);
6364
+ if (replayable.length !== m.content.length)
6365
+ note("anthropic:unsigned-thinking-dropped");
6366
+ if (replayable.length === 0)
6367
+ return [];
6368
+ return [{ role: m.role, content: replayable.map(encodeBlock) }];
6369
+ }
6370
+ return [{ role: m.role, content: encodeSystemTurn(m.content) }];
6165
6371
  }),
6166
6372
  max_tokens: req.maxTokens ?? 4096,
6167
- stream: req.stream
6373
+ stream: req.stream,
6374
+ ...systemCache.promoted === undefined ? {} : {
6375
+ cache_control: {
6376
+ type: systemCache.promoted.type,
6377
+ ...systemCache.promoted.ttl === undefined ? {} : { ttl: systemCache.promoted.ttl }
6378
+ }
6379
+ }
6168
6380
  };
6169
6381
  if (system !== undefined && system.length > 0)
6170
6382
  body.system = system;
@@ -6226,6 +6438,15 @@ var anthropicAdapter = {
6226
6438
  ["Accept", "text/event-stream"]
6227
6439
  ];
6228
6440
  const betas = new Set(req.request.betas ?? []);
6441
+ const notes = [...degradations];
6442
+ if (betas.has(CONTEXT_1M_BETA)) {
6443
+ const limits = catalogLimits("anthropic", req.model, oauth ? "oauth" : "apiKey");
6444
+ const window2 = limits?.contextWindow;
6445
+ if (window2 !== undefined && window2 < CONTEXT_1M_TOKENS) {
6446
+ betas.delete(CONTEXT_1M_BETA);
6447
+ notes.push("anthropic:context-1m-dropped");
6448
+ }
6449
+ }
6229
6450
  if (oauth) {
6230
6451
  protocol.push(["Authorization", `Bearer ${req.credentials.accessToken}`]);
6231
6452
  betas.add(OAUTH_BETA);
@@ -6253,121 +6474,9 @@ var anthropicAdapter = {
6253
6474
  throw await httpError(res, "anthropic");
6254
6475
  if (res.body === null)
6255
6476
  throw new GatewayError("UPSTREAM", "empty response body", { provider: "anthropic" });
6256
- return { events: decodeAnthropic(parseSse(res.body)), degradations };
6477
+ return { events: decodeAnthropic(parseSse(res.body)), degradations: notes };
6257
6478
  }
6258
6479
  };
6259
- // packages/providers/src/anthropic/models.ts
6260
- var ANTHROPIC_MODELS = {
6261
- defaultModel: "claude-opus-5",
6262
- models: [
6263
- {
6264
- id: "claude-fable-5",
6265
- label: "Claude Fable 5",
6266
- pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 },
6267
- limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
6268
- },
6269
- {
6270
- id: "claude-opus-5",
6271
- label: "Claude Opus 5",
6272
- pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 },
6273
- limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
6274
- },
6275
- {
6276
- id: "claude-sonnet-5",
6277
- label: "Claude Sonnet 5",
6278
- pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 },
6279
- limits: { contextWindow: 1e6, maxOutputTokens: 128000 }
6280
- },
6281
- {
6282
- id: "claude-haiku-4-5",
6283
- label: "Claude Haiku 4.5",
6284
- pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 },
6285
- limits: { contextWindow: 200000, maxOutputTokens: 64000 }
6286
- }
6287
- ]
6288
- };
6289
-
6290
- // packages/providers/src/kimi/models.ts
6291
- var KIMI_MODELS = {
6292
- defaultModel: "k3-256k",
6293
- models: [
6294
- {
6295
- id: "k3-256k",
6296
- label: "Kimi K3 \u2014 256K",
6297
- pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
6298
- limits: { contextWindow: 262144, maxOutputTokens: 131072 }
6299
- },
6300
- {
6301
- id: "k3",
6302
- label: "Kimi K3 \u2014 up to 1M",
6303
- pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 0, cacheWrite1h: 0 },
6304
- limits: { contextWindow: 1048576, maxOutputTokens: 131072 }
6305
- },
6306
- {
6307
- id: "kimi-for-coding",
6308
- label: "Kimi K2.7 Code",
6309
- pricing: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
6310
- limits: { contextWindow: 262144, maxOutputTokens: 131072 }
6311
- },
6312
- {
6313
- id: "kimi-for-coding-highspeed",
6314
- label: "Kimi K2.7 Code \u2014 High Speed",
6315
- pricing: { input: 0.95, output: 8, cacheRead: 0.19, cacheWrite5m: 0, cacheWrite1h: 0 },
6316
- limits: { contextWindow: 262144, maxOutputTokens: 131072 }
6317
- }
6318
- ]
6319
- };
6320
-
6321
- // packages/providers/src/openai/models.ts
6322
- var OPENAI_MODELS = {
6323
- defaultModel: "gpt-5.6",
6324
- models: [
6325
- {
6326
- id: "gpt-5.6",
6327
- label: "GPT-5.6 \u2014 routes to Sol",
6328
- pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
6329
- limits: { contextWindow: 922000, maxOutputTokens: 128000 },
6330
- oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
6331
- },
6332
- {
6333
- id: "gpt-5.6-sol",
6334
- label: "GPT-5.6 Sol \u2014 deepest reasoning",
6335
- pricing: { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 },
6336
- limits: { contextWindow: 922000, maxOutputTokens: 128000 },
6337
- oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
6338
- },
6339
- {
6340
- id: "gpt-5.6-terra",
6341
- label: "GPT-5.6 Terra \u2014 balanced",
6342
- pricing: { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 },
6343
- limits: { contextWindow: 922000, maxOutputTokens: 128000 },
6344
- oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
6345
- },
6346
- {
6347
- id: "gpt-5.6-luna",
6348
- label: "GPT-5.6 Luna \u2014 fastest",
6349
- pricing: { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 },
6350
- limits: { contextWindow: 922000, maxOutputTokens: 128000 },
6351
- oauthLimits: { contextWindow: 272000, maxOutputTokens: 128000 }
6352
- }
6353
- ]
6354
- };
6355
-
6356
- // packages/providers/src/catalog.ts
6357
- var PROVIDER_MODEL_CATALOG = {
6358
- anthropic: ANTHROPIC_MODELS,
6359
- openai: OPENAI_MODELS,
6360
- kimi: KIMI_MODELS
6361
- };
6362
- function catalogPricing(provider, model) {
6363
- return PROVIDER_MODEL_CATALOG[provider]?.models.find((entry) => entry.id === model)?.pricing ?? null;
6364
- }
6365
- function catalogLimits(provider, model, auth = "apiKey") {
6366
- const entry = PROVIDER_MODEL_CATALOG[provider]?.models.find((choice) => choice.id === model);
6367
- if (entry === undefined)
6368
- return null;
6369
- return (auth === "oauth" ? entry.oauthLimits : undefined) ?? entry.limits;
6370
- }
6371
6480
  // packages/providers/src/http-client.ts
6372
6481
  import { request as httpRequest } from "http";
6373
6482
  import { request as httpsRequest } from "https";
@@ -6593,6 +6702,8 @@ function toChatWire(req, model) {
6593
6702
  if (!degradations.includes(d))
6594
6703
  degradations.push(d);
6595
6704
  };
6705
+ if (req.betas?.includes(CONTEXT_1M_BETA))
6706
+ note("kimi:context-1m-dropped");
6596
6707
  const messages = [];
6597
6708
  const system = req.system?.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
6598
6709
 
@@ -6869,6 +6980,8 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
6869
6980
  if (!degradations.includes(d))
6870
6981
  degradations.push(d);
6871
6982
  };
6983
+ if (req.betas?.includes(CONTEXT_1M_BETA))
6984
+ note("openai:context-1m-dropped");
6872
6985
  for (const message of req.messages) {
6873
6986
  const parts = [];
6874
6987
  const inlined = message.role === "system";
@@ -7527,6 +7640,60 @@ function createConnectFlows(deps) {
7527
7640
  }
7528
7641
  };
7529
7642
  }
7643
+ // packages/control/src/console.ts
7644
+ var UNIT_NAME = "omnigateway.service";
7645
+ var MAX_CONSOLE_LINES = 500;
7646
+ var DEFAULT_CONSOLE_LINES = 200;
7647
+ function resolveConsoleSource(input) {
7648
+ const path = input.logFile?.trim();
7649
+ if (path !== undefined && path.length > 0)
7650
+ return { kind: "file", path };
7651
+ if (input.unitInstalled)
7652
+ return { kind: "journal", unit: UNIT_NAME, scope: input.scope };
7653
+ return { kind: "none" };
7654
+ }
7655
+ function consoleLimit(requested) {
7656
+ const value = requested === undefined ? DEFAULT_CONSOLE_LINES : Number(requested);
7657
+ if (!Number.isFinite(value))
7658
+ return DEFAULT_CONSOLE_LINES;
7659
+ return Math.floor(Math.min(Math.max(1, value), MAX_CONSOLE_LINES));
7660
+ }
7661
+ function keep(line, query) {
7662
+ if (query.since !== undefined && (line.at === null || line.at <= query.since))
7663
+ return false;
7664
+ if (query.level === undefined || line.level === null)
7665
+ return true;
7666
+ return LEVELS[line.level] >= LEVELS[query.level];
7667
+ }
7668
+ var SCAN_FACTOR = 20;
7669
+ var SCAN_LIMIT = 5000;
7670
+ function scanWidth(query) {
7671
+ const filtered = query.level !== undefined || query.since !== undefined;
7672
+ return filtered ? Math.min(query.lines * SCAN_FACTOR, SCAN_LIMIT) : query.lines;
7673
+ }
7674
+ async function readSource(deps, source, lines) {
7675
+ if (source.kind === "none")
7676
+ return "";
7677
+ if (source.kind === "file")
7678
+ return deps.readFile(source.path, lines) ?? "";
7679
+ const unit = source.scope === "system" ? ["-u", source.unit] : [`--user-unit=${source.unit}`];
7680
+ const result = await deps.run([
7681
+ "journalctl",
7682
+ ...unit,
7683
+ "-n",
7684
+ String(lines),
7685
+ "--no-pager",
7686
+ "--output=cat"
7687
+ ]);
7688
+ return result.code === 0 ? result.stdout : "";
7689
+ }
7690
+ async function readConsole(deps, source, query) {
7691
+ const limited = { ...query, lines: consoleLimit(query.lines) };
7692
+ const text = await readSource(deps, source, scanWidth(limited));
7693
+ const lines = text.split(`
7694
+ `).filter((raw) => raw.trim().length > 0).map(parseLine).filter((line) => keep(line, limited)).slice(-limited.lines);
7695
+ return source.kind === "file" ? { source: "file", path: source.path, lines } : { source: source.kind, lines };
7696
+ }
7530
7697
  // node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
7531
7698
  var exports_external = {};
7532
7699
  __export(exports_external, {
@@ -21819,7 +21986,9 @@ var dryRunSchema = exports_external.object({
21819
21986
  reasoning: exports_external.boolean().default(false)
21820
21987
  }).strict();
21821
21988
  var modelSchema = exports_external.object({
21822
- id: exports_external.string().min(1),
21989
+ id: exports_external.string().min(1).refine((value) => !value.toLowerCase().startsWith("claude/"), {
21990
+ message: 'model id must not start with "claude/": that prefix is reserved for discovery mirrors'
21991
+ }),
21823
21992
  strategy: exports_external.enum(["score", "priority", "roundRobin", "weighted"]),
21824
21993
  isAlias: exports_external.boolean(),
21825
21994
  targets: exports_external.array(exports_external.object({
@@ -23273,6 +23442,60 @@ async function createKey(store, input) {
23273
23442
  async function revokeKey(store, id) {
23274
23443
  await store.keys.revoke(id);
23275
23444
  }
23445
+ // packages/control/src/modelLimits.ts
23446
+ function narrower(a, b) {
23447
+ const context = [a.contextWindow, b.contextWindow].filter((n) => n !== undefined);
23448
+ const output = [a.maxOutputTokens, b.maxOutputTokens].filter((n) => n !== undefined);
23449
+ return {
23450
+ ...context.length === 0 ? {} : { contextWindow: Math.min(...context) },
23451
+ ...output.length === 0 ? {} : { maxOutputTokens: Math.min(...output) }
23452
+ };
23453
+ }
23454
+ function targetLimits(target, auths) {
23455
+ const ways = auths.size === 0 ? ["apiKey"] : [...auths];
23456
+ let listed = {};
23457
+ for (const auth of ways) {
23458
+ const entry = catalogLimits(target.provider, target.model, auth);
23459
+ if (entry === null)
23460
+ continue;
23461
+ listed = narrower(listed, {
23462
+ contextWindow: entry.contextWindow,
23463
+ maxOutputTokens: entry.maxOutputTokens
23464
+ });
23465
+ }
23466
+ const contextWindow = target.contextWindow ?? listed.contextWindow;
23467
+ const maxOutputTokens = target.maxOutputTokens ?? listed.maxOutputTokens;
23468
+ return {
23469
+ ...contextWindow === undefined ? {} : { contextWindow },
23470
+ ...maxOutputTokens === undefined ? {} : { maxOutputTokens }
23471
+ };
23472
+ }
23473
+ function servingAuths(credentials) {
23474
+ const byProvider = new Map;
23475
+ for (const credential of credentials) {
23476
+ if (!credential.enabled)
23477
+ continue;
23478
+ const ways = byProvider.get(credential.provider) ?? new Set;
23479
+ ways.add(credential.authType);
23480
+ byProvider.set(credential.provider, ways);
23481
+ }
23482
+ return byProvider;
23483
+ }
23484
+ function resolveModelLimits(model, credentials) {
23485
+ const auths = servingAuths(credentials);
23486
+ let limits = {};
23487
+ for (const target of model.targets) {
23488
+ limits = narrower(limits, targetLimits(target, auths.get(target.provider) ?? new Set));
23489
+ }
23490
+ return limits;
23491
+ }
23492
+ function modelDisplayName(model) {
23493
+ const only = model.targets.length === 1 ? model.targets[0] : undefined;
23494
+ if (only === undefined)
23495
+ return model.id;
23496
+ const labelled = PROVIDER_MODEL_CATALOG[only.provider]?.models.find((choice) => choice.id === only.model);
23497
+ return labelled?.label ?? model.id;
23498
+ }
23276
23499
  // packages/control/src/models.ts
23277
23500
  async function listModels(store) {
23278
23501
  return store.config.listModels();
@@ -23689,6 +23912,112 @@ async function putSettings(store, input) {
23689
23912
  await store.config.putSettings(settings);
23690
23913
  return settings;
23691
23914
  }
23915
+ // packages/control/src/setup.ts
23916
+ var KEY_PLACEHOLDER = "<your OmniGateway key>";
23917
+ async function describeModelsForSetup(store) {
23918
+ const models = await listModels(store);
23919
+ const credentials = (await listCredentials(store)).map((credential) => ({
23920
+ provider: credential.provider,
23921
+ authType: credential.authType,
23922
+ enabled: credential.enabled
23923
+ }));
23924
+ return models.map((model) => ({
23925
+ model,
23926
+ limits: resolveModelLimits(model, credentials),
23927
+ label: modelDisplayName(model)
23928
+ }));
23929
+ }
23930
+ function slug(id) {
23931
+ return encodeURIComponent(id).replace(/\./g, "%2E");
23932
+ }
23933
+ function claudeProfiles(described, input) {
23934
+ const apiKey = input.apiKey ?? KEY_PLACEHOLDER;
23935
+ return described.map(({ model, limits }) => {
23936
+ const useMirror = input.discoveryMirrors === true && !/^(?:claude|anthropic)/i.test(model.id);
23937
+ const modelId = useMirror ? `claude/${model.id}` : model.id;
23938
+ const env2 = {
23939
+ ANTHROPIC_BASE_URL: input.baseUrl,
23940
+ ANTHROPIC_AUTH_TOKEN: apiKey,
23941
+ ANTHROPIC_MODEL: modelId,
23942
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1"
23943
+ };
23944
+ if (limits.contextWindow !== undefined && !/^claude-/i.test(modelId)) {
23945
+ env2.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(limits.contextWindow);
23946
+ }
23947
+ return {
23948
+ path: `${slug(model.id)}/settings.json`,
23949
+ contents: `${JSON.stringify({ env: env2 }, null, 2)}
23950
+ `
23951
+ };
23952
+ });
23953
+ }
23954
+ function opencodeConfig(described, input) {
23955
+ const models = {};
23956
+ for (const { model, limits, label: label2 } of described) {
23957
+ const limit = limits.contextWindow === undefined ? undefined : {
23958
+ context: limits.contextWindow,
23959
+ ...limits.maxOutputTokens === undefined ? {} : { output: limits.maxOutputTokens }
23960
+ };
23961
+ models[model.id] = { name: label2, ...limit === undefined ? {} : { limit } };
23962
+ }
23963
+ const contents = `${JSON.stringify({
23964
+ $schema: "https://opencode.ai/config.json",
23965
+ provider: {
23966
+ omnigateway: {
23967
+ npm: "@ai-sdk/openai-compatible",
23968
+ name: "OmniGateway",
23969
+ options: {
23970
+ baseURL: `${input.baseUrl.replace(/\/+$/, "").replace(/(?:\/v1)+$/i, "")}/v1`,
23971
+ apiKey: input.apiKey ?? KEY_PLACEHOLDER
23972
+ },
23973
+ models
23974
+ }
23975
+ }
23976
+ }, null, 2)}
23977
+ `;
23978
+ return { path: "opencode.json", contents };
23979
+ }
23980
+ async function setupFiles(store, client, input) {
23981
+ const described = await describeModelsForSetup(store);
23982
+ return client === "claude" ? claudeProfiles(described, input) : [opencodeConfig(described, input)];
23983
+ }
23984
+ // packages/control/src/tail.ts
23985
+ import { closeSync, fstatSync, openSync, readSync, statSync } from "fs";
23986
+ var CHUNK = 64 * 1024;
23987
+ var MAX_BYTES = 8 * 1024 * 1024;
23988
+ function tailFile(path, lines) {
23989
+ let fd;
23990
+ try {
23991
+ fd = openSync(path, "r");
23992
+ } catch {
23993
+ return null;
23994
+ }
23995
+ try {
23996
+ const size = fstatSync(fd).size;
23997
+ if (size === 0)
23998
+ return "";
23999
+ const budget = Math.min(size, MAX_BYTES);
24000
+ let read = 0;
24001
+ let newlines = 0;
24002
+ const chunks = [];
24003
+ while (read < budget && newlines <= lines) {
24004
+ const span = Math.min(CHUNK, budget - read);
24005
+ const buffer = Buffer.allocUnsafe(span);
24006
+ const position = size - read - span;
24007
+ readSync(fd, buffer, 0, span, position);
24008
+ chunks.unshift(buffer);
24009
+ read += span;
24010
+ for (const byte of buffer)
24011
+ if (byte === 10)
24012
+ newlines++;
24013
+ }
24014
+ return Buffer.concat(chunks).toString("utf8");
24015
+ } catch {
24016
+ return null;
24017
+ } finally {
24018
+ closeSync(fd);
24019
+ }
24020
+ }
23692
24021
  // packages/control/src/usage.ts
23693
24022
  var MAX_LOG_LIMIT = 500;
23694
24023
  var DEFAULT_LOG_LIMIT = 100;
@@ -38299,7 +38628,9 @@ function adminRoutes(deps) {
38299
38628
  }).post("/api/setup", async ({ request: request2, set: set2 }) => {
38300
38629
  if (await deps.admin.isConfigured()) {
38301
38630
  set2.status = 409;
38302
- return { error: { code: "CONFLICT", message: "an admin password is already configured" } };
38631
+ return {
38632
+ error: { code: "CONFLICT", message: "an admin password is already configured" }
38633
+ };
38303
38634
  }
38304
38635
  const body2 = await readJsonRecord(request2);
38305
38636
  if (typeof body2?.password !== "string") {
@@ -38313,7 +38644,9 @@ function adminRoutes(deps) {
38313
38644
  }
38314
38645
  if (!created) {
38315
38646
  set2.status = 409;
38316
- return { error: { code: "CONFLICT", message: "an admin password is already configured" } };
38647
+ return {
38648
+ error: { code: "CONFLICT", message: "an admin password is already configured" }
38649
+ };
38317
38650
  }
38318
38651
  const token = await deps.admin.login(body2.password);
38319
38652
  if (token === null)
@@ -38362,6 +38695,16 @@ function adminRoutes(deps) {
38362
38695
  await requireAdmin(request2, deps.admin);
38363
38696
  await removeModel(deps.store, params.id);
38364
38697
  return { ok: true };
38698
+ }).get("/api/agent-setup", async ({ request: request2, query }) => {
38699
+ await requireAdmin(request2, deps.admin);
38700
+ const client = query.client === "opencode" ? "opencode" : "claude";
38701
+ return {
38702
+ client,
38703
+ files: await setupFiles(deps.store, client, {
38704
+ baseUrl: deps.baseUrl,
38705
+ discoveryMirrors: deps.discoveryMirrors === true
38706
+ })
38707
+ };
38365
38708
  }).get("/api/keys", async ({ request: request2 }) => {
38366
38709
  await requireAdmin(request2, deps.admin);
38367
38710
  return { keys: await listKeys(deps.store) };
@@ -38396,6 +38739,17 @@ function adminRoutes(deps) {
38396
38739
  }).get("/api/logs", async ({ request: request2, query }) => {
38397
38740
  await requireAdmin(request2, deps.admin);
38398
38741
  return { logs: await recentLogs(deps.store, query.limit) };
38742
+ }).get("/api/console", async ({ request: request2, query }) => {
38743
+ await requireAdmin(request2, deps.admin);
38744
+ if (deps.console === undefined)
38745
+ return { source: "none", lines: [] };
38746
+ const level = parseLogLevel(query.level);
38747
+ const since = Number(query.since);
38748
+ return readConsole(deps.console.deps, deps.console.source, {
38749
+ lines: consoleLimit(query.lines),
38750
+ ...level === null ? {} : { level },
38751
+ ...Number.isFinite(since) && since > 0 ? { since } : {}
38752
+ });
38399
38753
  });
38400
38754
  }
38401
38755
 
@@ -38941,6 +39295,9 @@ var frame = (event, data) => ({
38941
39295
  });
38942
39296
  async function* anthropicStream(events, requestId) {
38943
39297
  let model = "";
39298
+ const suppressed = new Set;
39299
+ const outIndex = new Map;
39300
+ let nextOutIndex = 0;
38944
39301
  for await (const event of events) {
38945
39302
  switch (event.type) {
38946
39303
  case "start":
@@ -38966,26 +39323,39 @@ async function* anthropicStream(events, requestId) {
38966
39323
  break;
38967
39324
  case "blockStart": {
38968
39325
  const b = event.block;
39326
+ if (b.type === "thinking" && b.signed !== true) {
39327
+ suppressed.add(event.index);
39328
+ break;
39329
+ }
38969
39330
  const content_block = b.type === "text" ? { type: "text", text: "" } : b.type === "thinking" ? { type: "thinking", thinking: "" } : { type: "tool_use", id: b.id, name: b.name, input: {} };
39331
+ const index = nextOutIndex++;
39332
+ outIndex.set(event.index, index);
38970
39333
  yield frame("content_block_start", {
38971
39334
  type: "content_block_start",
38972
- index: event.index,
39335
+ index,
38973
39336
  content_block
38974
39337
  });
38975
39338
  break;
38976
39339
  }
38977
39340
  case "blockDelta": {
39341
+ if (suppressed.has(event.index))
39342
+ break;
38978
39343
  const d = event.delta;
38979
39344
  const delta2 = d.type === "text" ? { type: "text_delta", text: d.text } : d.type === "thinking" ? { type: "thinking_delta", thinking: d.text } : d.type === "thinkingSignature" ? { type: "signature_delta", signature: d.signature } : { type: "input_json_delta", partial_json: d.partial };
38980
39345
  yield frame("content_block_delta", {
38981
39346
  type: "content_block_delta",
38982
- index: event.index,
39347
+ index: outIndex.get(event.index) ?? event.index,
38983
39348
  delta: delta2
38984
39349
  });
38985
39350
  break;
38986
39351
  }
38987
39352
  case "blockEnd":
38988
- yield frame("content_block_stop", { type: "content_block_stop", index: event.index });
39353
+ if (suppressed.has(event.index))
39354
+ break;
39355
+ yield frame("content_block_stop", {
39356
+ type: "content_block_stop",
39357
+ index: outIndex.get(event.index) ?? event.index
39358
+ });
38989
39359
  break;
38990
39360
  case "end":
38991
39361
  yield frame("message_delta", {
@@ -39015,7 +39385,7 @@ function anthropicResponse(collected, requestId) {
39015
39385
  type: "message",
39016
39386
  role: "assistant",
39017
39387
  model: collected.model,
39018
- content: collected.content.map((b) => {
39388
+ content: collected.content.filter((b) => b.type !== "thinking" || b.signature !== undefined && b.signature !== "").map((b) => {
39019
39389
  switch (b.type) {
39020
39390
  case "text":
39021
39391
  return { type: "text", text: b.text };
@@ -39193,6 +39563,30 @@ function openaiErrorBody(code, message) {
39193
39563
  return { error: { message, type: e.type, code: e.code } };
39194
39564
  }
39195
39565
 
39566
+ // apps/gateway/src/ingress/model.ts
39567
+ var DISCOVERY_PREFIX = "claude/";
39568
+ var ONE_M_SUFFIX = "[1m]";
39569
+ function normalizeClientModel(raw, betas2 = []) {
39570
+ let model = raw.trim();
39571
+ let wantsOneM = false;
39572
+ if (model.toLowerCase().endsWith(ONE_M_SUFFIX)) {
39573
+ const stripped = model.slice(0, -ONE_M_SUFFIX.length).trim();
39574
+ if (stripped.length > 0) {
39575
+ model = stripped;
39576
+ wantsOneM = true;
39577
+ }
39578
+ }
39579
+ if (model.toLowerCase().startsWith(DISCOVERY_PREFIX)) {
39580
+ const stripped = model.slice(DISCOVERY_PREFIX.length);
39581
+ if (stripped.length > 0)
39582
+ model = stripped;
39583
+ }
39584
+ const merged = [...betas2];
39585
+ if (wantsOneM && !merged.includes(CONTEXT_1M_BETA))
39586
+ merged.push(CONTEXT_1M_BETA);
39587
+ return { model, betas: merged };
39588
+ }
39589
+
39196
39590
  // apps/gateway/src/ingress/anthropic.ts
39197
39591
  var textBlock = exports_external.object({
39198
39592
  type: exports_external.literal("text"),
@@ -39374,8 +39768,9 @@ function parseAnthropicRequest(body2, headers) {
39374
39768
  text: b.text,
39375
39769
  ...irCacheControl(b.cache_control)
39376
39770
  }));
39771
+ const named = normalizeClientModel(parsed.model, readBetas(headers));
39377
39772
  const request2 = {
39378
- model: parsed.model,
39773
+ model: named.model,
39379
39774
  messages,
39380
39775
  stream: parsed.stream ?? false
39381
39776
  };
@@ -39406,9 +39801,8 @@ function parseAnthropicRequest(body2, headers) {
39406
39801
  const extras = extraFields(body2, KNOWN);
39407
39802
  if (extras !== undefined)
39408
39803
  request2.vendor = { anthropic: extras };
39409
- const betas = readBetas(headers);
39410
- if (betas.length > 0)
39411
- request2.betas = betas;
39804
+ if (named.betas.length > 0)
39805
+ request2.betas = named.betas;
39412
39806
  return validateRequest(request2);
39413
39807
  }
39414
39808
 
@@ -39557,7 +39951,7 @@ function parseOpenAIRequest(body2) {
39557
39951
  throw new GatewayError("BAD_REQUEST", "messages: at least one non-system message is required");
39558
39952
  }
39559
39953
  const request2 = {
39560
- model: parsed.model,
39954
+ model: normalizeClientModel(parsed.model).model,
39561
39955
  messages,
39562
39956
  stream: parsed.stream ?? false
39563
39957
  };
@@ -39591,65 +39985,13 @@ function parseOpenAIRequest(body2) {
39591
39985
 
39592
39986
  // apps/gateway/src/routes/models.ts
39593
39987
  var CREATED_AT = new Date(0).toISOString();
39594
- function narrower(a, b) {
39595
- const context = [a.contextWindow, b.contextWindow].filter((n) => n !== undefined);
39596
- const output = [a.maxOutputTokens, b.maxOutputTokens].filter((n) => n !== undefined);
39597
- return {
39598
- ...context.length === 0 ? {} : { contextWindow: Math.min(...context) },
39599
- ...output.length === 0 ? {} : { maxOutputTokens: Math.min(...output) }
39600
- };
39601
- }
39602
- function targetLimits(target, auths) {
39603
- const ways = auths.size === 0 ? ["apiKey"] : [...auths];
39604
- let listed = {};
39605
- for (const auth of ways) {
39606
- const entry = catalogLimits(target.provider, target.model, auth);
39607
- if (entry === null)
39608
- continue;
39609
- listed = narrower(listed, {
39610
- contextWindow: entry.contextWindow,
39611
- maxOutputTokens: entry.maxOutputTokens
39612
- });
39613
- }
39614
- const contextWindow = target.contextWindow ?? listed.contextWindow;
39615
- const maxOutputTokens = target.maxOutputTokens ?? listed.maxOutputTokens;
39616
- return {
39617
- ...contextWindow === undefined ? {} : { contextWindow },
39618
- ...maxOutputTokens === undefined ? {} : { maxOutputTokens }
39619
- };
39620
- }
39621
- function limitsOf(model, authsByProvider) {
39622
- let limits = {};
39623
- for (const target of model.targets) {
39624
- limits = narrower(limits, targetLimits(target, authsByProvider.get(target.provider) ?? new Set));
39625
- }
39626
- return limits;
39627
- }
39628
- function displayName(model) {
39629
- const only = model.targets.length === 1 ? model.targets[0] : undefined;
39630
- if (only === undefined)
39631
- return model.id;
39632
- const labelled = PROVIDER_MODEL_CATALOG[only.provider]?.models.find((choice) => choice.id === only.model);
39633
- return labelled?.label ?? model.id;
39634
- }
39635
- function servingAuths(credentials) {
39636
- const byProvider = new Map;
39637
- for (const credential of credentials) {
39638
- if (!credential.enabled)
39639
- continue;
39640
- const ways = byProvider.get(credential.provider) ?? new Set;
39641
- ways.add(credential.authType);
39642
- byProvider.set(credential.provider, ways);
39643
- }
39644
- return byProvider;
39645
- }
39646
39988
  function describeModel(model, credentials) {
39647
- const limits = limitsOf(model, servingAuths(credentials));
39989
+ const limits = resolveModelLimits(model, credentials);
39648
39990
  return {
39649
39991
  id: model.id,
39650
39992
  object: "model",
39651
39993
  type: "model",
39652
- display_name: displayName(model),
39994
+ display_name: modelDisplayName(model),
39653
39995
  created: 0,
39654
39996
  created_at: CREATED_AT,
39655
39997
  owned_by: "omnigateway",
@@ -39657,8 +39999,29 @@ function describeModel(model, credentials) {
39657
39999
  ...limits.maxOutputTokens === undefined ? {} : { max_tokens: limits.maxOutputTokens }
39658
40000
  };
39659
40001
  }
39660
- function modelListBody(models, credentials) {
39661
- const data = models.map((model) => describeModel(model, credentials));
40002
+ var ALREADY_CLAUDE = /^(?:claude|anthropic)/i;
40003
+ var MIRROR_PREFIX = "claude/";
40004
+ function discoveryMirrors(data) {
40005
+ const taken = new Set(data.map((entry) => entry.id));
40006
+ const mirrors = [];
40007
+ for (const entry of data) {
40008
+ if (ALREADY_CLAUDE.test(entry.id))
40009
+ continue;
40010
+ const id = `${MIRROR_PREFIX}${entry.id}`;
40011
+ if (taken.has(id))
40012
+ continue;
40013
+ mirrors.push({
40014
+ ...entry,
40015
+ id,
40016
+ root: entry.id,
40017
+ display_name: `${entry.display_name} (OmniGateway)`
40018
+ });
40019
+ }
40020
+ return mirrors;
40021
+ }
40022
+ function modelListBody(models, credentials, options = {}) {
40023
+ const described = models.map((model) => describeModel(model, credentials));
40024
+ const data = options.discoveryMirrors === true ? [...described, ...discoveryMirrors(described)] : described;
39662
40025
  return {
39663
40026
  object: "list",
39664
40027
  data,
@@ -39808,32 +40171,6 @@ async function handle(deps, rateLimiter, surface, request2) {
39808
40171
  completed.durationMs = deps.now() - startedAt;
39809
40172
  }
39810
40173
  await finishLog(deps.store, completed, keyId, deps.logger);
39811
- const fields = {
39812
- requestId,
39813
- surface,
39814
- status: completed.status,
39815
- provider: completed.resolvedProvider ?? undefined,
39816
- model: completed.resolvedModel ?? undefined,
39817
- requestedModel: completed.requestedModel,
39818
- credentialId: completed.credentialId ?? undefined,
39819
- apiKeyId: keyId ?? undefined,
39820
- attempts: completed.attempts,
39821
- code: completed.errorCode ?? undefined,
39822
- stream: chatRequest.stream,
39823
- inputTokens: completed.inputTokens,
39824
- outputTokens: completed.outputTokens,
39825
- cacheReadTokens: completed.cacheReadTokens,
39826
- cacheWriteTokens: completed.cacheWriteTokens,
39827
- costUsd: completed.costUsd,
39828
- ttftMs: completed.ttftMs,
39829
- durationMs: completed.durationMs
39830
- };
39831
- if (wasCancelled)
39832
- deps.logger.debug("request cancelled", fields);
39833
- else if (completed.status >= 400)
39834
- deps.logger.error("request failed", fields);
39835
- else
39836
- deps.logger.info("request done", fields);
39837
40174
  };
39838
40175
  if (chatRequest.stream) {
39839
40176
  const frames = surface === "anthropic" ? anthropicStream(outcome.events, requestId) : openaiStream(outcome.events, requestId, Math.floor(deps.now() / 1000));
@@ -39861,15 +40198,11 @@ async function handle(deps, rateLimiter, surface, request2) {
39861
40198
  durationMs: deps.now() - startedAt
39862
40199
  });
39863
40200
  await finishLog(deps.store, completed, keyId, deps.logger);
39864
- deps.logger.error("request failed", {
40201
+ deps.logger.warn("request rejected", {
39865
40202
  requestId,
39866
40203
  surface,
39867
40204
  status: completed.status,
39868
- requestedModel,
39869
- apiKeyId: keyId ?? undefined,
39870
40205
  code: gatewayError.code,
39871
- attempts: completed.attempts,
39872
- durationMs: completed.durationMs,
39873
40206
  reason: gatewayError.message
39874
40207
  });
39875
40208
  return errorResponse(surface, gatewayError.code, gatewayError.message);
@@ -39890,7 +40223,9 @@ function proxyRoutes(deps) {
39890
40223
  const snapshot = await dispatchDeps.snapshots.get(deps.now());
39891
40224
  const models = [...snapshot.models.values()];
39892
40225
  const visibleModels = key.modelAllowlist === null ? models : models.filter((model) => key.modelAllowlist?.includes(model.id));
39893
- return Response.json(modelListBody(visibleModels, snapshot.credentials));
40226
+ return Response.json(modelListBody(visibleModels, snapshot.credentials, {
40227
+ discoveryMirrors: deps.discoveryMirrors === true
40228
+ }));
39894
40229
  } catch (error51) {
39895
40230
  const gatewayError = asGatewayError(error51);
39896
40231
  logger2.error("model listing failed", {
@@ -39900,6 +40235,25 @@ function proxyRoutes(deps) {
39900
40235
  });
39901
40236
  return errorResponse("anthropic", gatewayError.code, gatewayError.message);
39902
40237
  }
40238
+ }).post("/v1/messages/count_tokens", async ({ request: request2 }) => {
40239
+ try {
40240
+ const key = await authenticateApiKey(deps.store, apiKeyHeader(request2.headers));
40241
+ rateLimiter.consume(key.id, key.rateLimitPerMin);
40242
+ const body2 = await request2.json();
40243
+ const chatRequest = parseAnthropicRequest(body2, request2.headers);
40244
+ if (key.modelAllowlist !== null && !key.modelAllowlist.includes(chatRequest.model)) {
40245
+ throw new GatewayError("AUTH", `model "${chatRequest.model}" is not allowed for this API key`);
40246
+ }
40247
+ return Response.json({ input_tokens: estimateInputTokens(chatRequest) });
40248
+ } catch (error51) {
40249
+ const gatewayError = asGatewayError(error51);
40250
+ logger2.warn("token count failed", {
40251
+ status: HTTP_STATUS[gatewayError.code],
40252
+ code: gatewayError.code,
40253
+ reason: gatewayError.message
40254
+ });
40255
+ return errorResponse("anthropic", gatewayError.code, gatewayError.message);
40256
+ }
39903
40257
  }).onError(({ error: error51 }) => {
39904
40258
  const gatewayError = asGatewayError(error51);
39905
40259
  logger2.error("unhandled proxy route error", {
@@ -39947,13 +40301,17 @@ function createApp(deps) {
39947
40301
  refresh,
39948
40302
  requestId,
39949
40303
  rateLimiter,
39950
- logger: logger2
40304
+ logger: logger2,
40305
+ discoveryMirrors: deps.discoveryMirrors === true
39951
40306
  })).use(adminRoutes({
39952
40307
  store: deps.store,
39953
40308
  admin,
40309
+ baseUrl: deps.baseUrl,
40310
+ discoveryMirrors: deps.discoveryMirrors === true,
39954
40311
  now,
39955
40312
  sessionTtlMs: ADMIN_SESSION_TTL_MS,
39956
- logger: logger2
40313
+ logger: logger2,
40314
+ ...deps.console === undefined ? {} : { console: deps.console }
39957
40315
  })).use(connectRoutes({
39958
40316
  store: deps.store,
39959
40317
  admin,
@@ -40126,6 +40484,30 @@ function stdoutLogger(level) {
40126
40484
  });
40127
40485
  }
40128
40486
  var logger2 = stdoutLogger("info");
40487
+ function consoleSource(logFile) {
40488
+ const source = resolveConsoleSource({
40489
+ logFile,
40490
+ unitInstalled: process.env.JOURNAL_STREAM !== undefined,
40491
+ scope: process.env.MANAGERPID === undefined ? "system" : "user"
40492
+ });
40493
+ return {
40494
+ source,
40495
+ deps: {
40496
+ readFile: (path, lines) => tailFile(path, lines),
40497
+ run: async (argv) => {
40498
+ const [cmd, ...args] = argv;
40499
+ if (cmd === undefined)
40500
+ return { code: 1, stdout: "", stderr: "empty argv" };
40501
+ const proc = Bun.spawn([cmd, ...args], { stdout: "pipe", stderr: "pipe" });
40502
+ const [stdout, stderr] = await Promise.all([
40503
+ new Response(proc.stdout).text(),
40504
+ new Response(proc.stderr).text()
40505
+ ]);
40506
+ return { code: await proc.exited, stdout, stderr };
40507
+ }
40508
+ }
40509
+ };
40510
+ }
40129
40511
  async function main() {
40130
40512
  const config2 = loadConfig(process.env);
40131
40513
  logger2 = stdoutLogger(config2.logLevel);
@@ -40151,6 +40533,8 @@ async function main() {
40151
40533
  const swept = await store.usage.sweepPending();
40152
40534
  if (swept > 0)
40153
40535
  logger2.info("retired interrupted requests", { count: swept });
40536
+ const console2 = consoleSource(config2.logFile);
40537
+ logger2.info("console log source resolved", { reason: console2.source.kind });
40154
40538
  const now = () => Date.now();
40155
40539
  const http = nodeHttpClient({ logger: logger2, now });
40156
40540
  const refresh = createRefresher({ store, providers: OAUTH_PROVIDERS, http, now, logger: logger2 });
@@ -40165,7 +40549,9 @@ async function main() {
40165
40549
  now,
40166
40550
  refresh,
40167
40551
  staticDir,
40168
- logger: logger2
40552
+ logger: logger2,
40553
+ console: console2,
40554
+ discoveryMirrors: config2.exposeClaudeCodeAliases
40169
40555
  });
40170
40556
  const stopMaintenance = startMaintenance({ store, now, logger: logger2 });
40171
40557
  const stopRefreshScheduler = startRefreshScheduler({ store, refresh, now, logger: logger2 });