@combycode/llm-sdk 3.0.0 → 3.2.1

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.
package/dist/index.js CHANGED
@@ -548,6 +548,7 @@ var TelemetryAdapter = class {
548
548
  break;
549
549
  case "onModelError":
550
550
  this.metrics.errors++;
551
+ this.metrics.inFlight = Math.max(0, this.metrics.inFlight - 1);
551
552
  break;
552
553
  case "onCostEntry":
553
554
  this.metrics.costUsd += event.ctx.entry?.cost?.total ?? 0;
@@ -3328,7 +3329,8 @@ var anthropic_default = {
3328
3329
  "anthropic/claude-opus-4.1": {
3329
3330
  providerModelName: "claude-opus-4-1-20250805",
3330
3331
  aliases: [
3331
- "claude-opus-4-1-20250805"
3332
+ "claude-opus-4-1-20250805",
3333
+ "claude-opus-4-1"
3332
3334
  ],
3333
3335
  pricing: {
3334
3336
  inputPerMTok: 15,
@@ -3392,7 +3394,8 @@ var anthropic_default = {
3392
3394
  "anthropic/claude-opus-4.5": {
3393
3395
  providerModelName: "claude-opus-4-5-20251101",
3394
3396
  aliases: [
3395
- "claude-opus-4-5-20251101"
3397
+ "claude-opus-4-5-20251101",
3398
+ "claude-opus-4-5"
3396
3399
  ],
3397
3400
  pricing: {
3398
3401
  inputPerMTok: 5,
@@ -3731,7 +3734,8 @@ var anthropic_default = {
3731
3734
  "anthropic/claude-sonnet-4.5": {
3732
3735
  providerModelName: "claude-sonnet-4-5-20250929",
3733
3736
  aliases: [
3734
- "claude-sonnet-4-5-20250929"
3737
+ "claude-sonnet-4-5-20250929",
3738
+ "claude-sonnet-4-5"
3735
3739
  ],
3736
3740
  pricing: {
3737
3741
  inputPerMTok: 3,
@@ -3865,7 +3869,8 @@ var anthropic_default = {
3865
3869
  "anthropic/claude-haiku-4.5": {
3866
3870
  providerModelName: "claude-haiku-4-5-20251001",
3867
3871
  aliases: [
3868
- "claude-haiku-4-5-20251001"
3872
+ "claude-haiku-4-5-20251001",
3873
+ "claude-haiku-4-5"
3869
3874
  ],
3870
3875
  pricing: {
3871
3876
  inputPerMTok: 1,
@@ -4224,7 +4229,7 @@ var google_default = {
4224
4229
  structuredOutput: true,
4225
4230
  vision: true,
4226
4231
  audio: true,
4227
- video: false,
4232
+ video: true,
4228
4233
  imageGeneration: false,
4229
4234
  audioGeneration: false,
4230
4235
  videoGeneration: false
@@ -4240,7 +4245,9 @@ var google_default = {
4240
4245
  inputModalities: [
4241
4246
  "text",
4242
4247
  "image",
4243
- "audio"
4248
+ "audio",
4249
+ "pdf",
4250
+ "video"
4244
4251
  ],
4245
4252
  outputModalities: [
4246
4253
  "text"
@@ -4296,7 +4303,7 @@ var google_default = {
4296
4303
  structuredOutput: true,
4297
4304
  vision: true,
4298
4305
  audio: true,
4299
- video: false,
4306
+ video: true,
4300
4307
  imageGeneration: false,
4301
4308
  audioGeneration: false,
4302
4309
  videoGeneration: false
@@ -4318,7 +4325,9 @@ var google_default = {
4318
4325
  inputModalities: [
4319
4326
  "text",
4320
4327
  "image",
4321
- "audio"
4328
+ "audio",
4329
+ "pdf",
4330
+ "video"
4322
4331
  ],
4323
4332
  outputModalities: [
4324
4333
  "text"
@@ -4356,7 +4365,7 @@ var google_default = {
4356
4365
  structuredOutput: true,
4357
4366
  vision: true,
4358
4367
  audio: true,
4359
- video: false,
4368
+ video: true,
4360
4369
  imageGeneration: false,
4361
4370
  audioGeneration: false,
4362
4371
  videoGeneration: false
@@ -4372,7 +4381,9 @@ var google_default = {
4372
4381
  inputModalities: [
4373
4382
  "text",
4374
4383
  "image",
4375
- "audio"
4384
+ "audio",
4385
+ "pdf",
4386
+ "video"
4376
4387
  ],
4377
4388
  outputModalities: [
4378
4389
  "text"
@@ -4431,7 +4442,7 @@ var google_default = {
4431
4442
  structuredOutput: true,
4432
4443
  vision: true,
4433
4444
  audio: true,
4434
- video: false,
4445
+ video: true,
4435
4446
  imageGeneration: false,
4436
4447
  audioGeneration: false,
4437
4448
  videoGeneration: false
@@ -4447,7 +4458,9 @@ var google_default = {
4447
4458
  inputModalities: [
4448
4459
  "text",
4449
4460
  "image",
4450
- "audio"
4461
+ "audio",
4462
+ "pdf",
4463
+ "video"
4451
4464
  ],
4452
4465
  outputModalities: [
4453
4466
  "text"
@@ -4506,7 +4519,7 @@ var google_default = {
4506
4519
  structuredOutput: true,
4507
4520
  vision: true,
4508
4521
  audio: true,
4509
- video: false,
4522
+ video: true,
4510
4523
  imageGeneration: false,
4511
4524
  audioGeneration: false,
4512
4525
  videoGeneration: false
@@ -4529,7 +4542,9 @@ var google_default = {
4529
4542
  inputModalities: [
4530
4543
  "text",
4531
4544
  "image",
4532
- "audio"
4545
+ "audio",
4546
+ "pdf",
4547
+ "video"
4533
4548
  ],
4534
4549
  outputModalities: [
4535
4550
  "text"
@@ -4584,7 +4599,7 @@ var google_default = {
4584
4599
  structuredOutput: true,
4585
4600
  vision: true,
4586
4601
  audio: true,
4587
- video: false,
4602
+ video: true,
4588
4603
  imageGeneration: false,
4589
4604
  audioGeneration: false,
4590
4605
  videoGeneration: false
@@ -4600,7 +4615,9 @@ var google_default = {
4600
4615
  inputModalities: [
4601
4616
  "text",
4602
4617
  "image",
4603
- "audio"
4618
+ "audio",
4619
+ "pdf",
4620
+ "video"
4604
4621
  ],
4605
4622
  outputModalities: [
4606
4623
  "text"
@@ -4659,7 +4676,7 @@ var google_default = {
4659
4676
  structuredOutput: true,
4660
4677
  vision: true,
4661
4678
  audio: true,
4662
- video: false,
4679
+ video: true,
4663
4680
  imageGeneration: false,
4664
4681
  audioGeneration: false,
4665
4682
  videoGeneration: false
@@ -4675,7 +4692,9 @@ var google_default = {
4675
4692
  inputModalities: [
4676
4693
  "text",
4677
4694
  "image",
4678
- "audio"
4695
+ "audio",
4696
+ "pdf",
4697
+ "video"
4679
4698
  ],
4680
4699
  outputModalities: [
4681
4700
  "text"
@@ -4735,7 +4754,7 @@ var google_default = {
4735
4754
  structuredOutput: true,
4736
4755
  vision: true,
4737
4756
  audio: true,
4738
- video: false,
4757
+ video: true,
4739
4758
  imageGeneration: false,
4740
4759
  audioGeneration: false,
4741
4760
  videoGeneration: false
@@ -4751,7 +4770,9 @@ var google_default = {
4751
4770
  inputModalities: [
4752
4771
  "text",
4753
4772
  "image",
4754
- "audio"
4773
+ "audio",
4774
+ "pdf",
4775
+ "video"
4755
4776
  ],
4756
4777
  outputModalities: [
4757
4778
  "text"
@@ -5734,7 +5755,8 @@ var google_default = {
5734
5755
  type: "chat",
5735
5756
  inputModalities: [
5736
5757
  "text",
5737
- "image"
5758
+ "image",
5759
+ "pdf"
5738
5760
  ],
5739
5761
  outputModalities: [
5740
5762
  "text"
@@ -5787,7 +5809,8 @@ var google_default = {
5787
5809
  type: "chat",
5788
5810
  inputModalities: [
5789
5811
  "text",
5790
- "image"
5812
+ "image",
5813
+ "pdf"
5791
5814
  ],
5792
5815
  outputModalities: [
5793
5816
  "text"
@@ -6956,7 +6979,7 @@ var google_default = {
6956
6979
  structuredOutput: true,
6957
6980
  vision: true,
6958
6981
  audio: true,
6959
- video: false,
6982
+ video: true,
6960
6983
  imageGeneration: false,
6961
6984
  audioGeneration: false,
6962
6985
  videoGeneration: false
@@ -6972,7 +6995,9 @@ var google_default = {
6972
6995
  inputModalities: [
6973
6996
  "text",
6974
6997
  "image",
6975
- "audio"
6998
+ "audio",
6999
+ "pdf",
7000
+ "video"
6976
7001
  ],
6977
7002
  outputModalities: [
6978
7003
  "text"
@@ -7028,7 +7053,7 @@ var google_default = {
7028
7053
  structuredOutput: true,
7029
7054
  vision: true,
7030
7055
  audio: true,
7031
- video: false,
7056
+ video: true,
7032
7057
  imageGeneration: false,
7033
7058
  audioGeneration: false,
7034
7059
  videoGeneration: false
@@ -7044,7 +7069,9 @@ var google_default = {
7044
7069
  inputModalities: [
7045
7070
  "text",
7046
7071
  "image",
7047
- "audio"
7072
+ "audio",
7073
+ "pdf",
7074
+ "video"
7048
7075
  ],
7049
7076
  outputModalities: [
7050
7077
  "text"
@@ -7099,7 +7126,7 @@ var google_default = {
7099
7126
  structuredOutput: true,
7100
7127
  vision: true,
7101
7128
  audio: true,
7102
- video: false,
7129
+ video: true,
7103
7130
  imageGeneration: false,
7104
7131
  audioGeneration: false,
7105
7132
  videoGeneration: false
@@ -7115,7 +7142,9 @@ var google_default = {
7115
7142
  inputModalities: [
7116
7143
  "text",
7117
7144
  "image",
7118
- "audio"
7145
+ "audio",
7146
+ "pdf",
7147
+ "video"
7119
7148
  ],
7120
7149
  outputModalities: [
7121
7150
  "text"
@@ -7405,7 +7434,8 @@ var openai_default = {
7405
7434
  type: "chat",
7406
7435
  inputModalities: [
7407
7436
  "text",
7408
- "image"
7437
+ "image",
7438
+ "pdf"
7409
7439
  ],
7410
7440
  outputModalities: [
7411
7441
  "text"
@@ -7465,7 +7495,8 @@ var openai_default = {
7465
7495
  type: "chat",
7466
7496
  inputModalities: [
7467
7497
  "text",
7468
- "image"
7498
+ "image",
7499
+ "pdf"
7469
7500
  ],
7470
7501
  outputModalities: [
7471
7502
  "text"
@@ -7521,7 +7552,8 @@ var openai_default = {
7521
7552
  type: "chat",
7522
7553
  inputModalities: [
7523
7554
  "text",
7524
- "image"
7555
+ "image",
7556
+ "pdf"
7525
7557
  ],
7526
7558
  outputModalities: [
7527
7559
  "text"
@@ -7632,7 +7664,8 @@ var openai_default = {
7632
7664
  type: "chat",
7633
7665
  inputModalities: [
7634
7666
  "text",
7635
- "image"
7667
+ "image",
7668
+ "pdf"
7636
7669
  ],
7637
7670
  outputModalities: [
7638
7671
  "text"
@@ -7688,7 +7721,8 @@ var openai_default = {
7688
7721
  type: "chat",
7689
7722
  inputModalities: [
7690
7723
  "text",
7691
- "image"
7724
+ "image",
7725
+ "pdf"
7692
7726
  ],
7693
7727
  outputModalities: [
7694
7728
  "text"
@@ -7768,7 +7802,8 @@ var openai_default = {
7768
7802
  type: "chat",
7769
7803
  inputModalities: [
7770
7804
  "text",
7771
- "image"
7805
+ "image",
7806
+ "pdf"
7772
7807
  ],
7773
7808
  outputModalities: [
7774
7809
  "text"
@@ -7841,7 +7876,8 @@ var openai_default = {
7841
7876
  type: "chat",
7842
7877
  inputModalities: [
7843
7878
  "text",
7844
- "image"
7879
+ "image",
7880
+ "pdf"
7845
7881
  ],
7846
7882
  outputModalities: [
7847
7883
  "text"
@@ -7896,7 +7932,8 @@ var openai_default = {
7896
7932
  type: "chat",
7897
7933
  inputModalities: [
7898
7934
  "text",
7899
- "image"
7935
+ "image",
7936
+ "pdf"
7900
7937
  ],
7901
7938
  outputModalities: [
7902
7939
  "text"
@@ -7955,7 +7992,8 @@ var openai_default = {
7955
7992
  type: "chat",
7956
7993
  inputModalities: [
7957
7994
  "text",
7958
- "image"
7995
+ "image",
7996
+ "pdf"
7959
7997
  ],
7960
7998
  outputModalities: [
7961
7999
  "text"
@@ -8014,7 +8052,8 @@ var openai_default = {
8014
8052
  type: "chat",
8015
8053
  inputModalities: [
8016
8054
  "text",
8017
- "image"
8055
+ "image",
8056
+ "pdf"
8018
8057
  ],
8019
8058
  outputModalities: [
8020
8059
  "text"
@@ -8073,7 +8112,8 @@ var openai_default = {
8073
8112
  type: "chat",
8074
8113
  inputModalities: [
8075
8114
  "text",
8076
- "image"
8115
+ "image",
8116
+ "pdf"
8077
8117
  ],
8078
8118
  outputModalities: [
8079
8119
  "text"
@@ -8490,7 +8530,8 @@ var openai_default = {
8490
8530
  type: "chat",
8491
8531
  inputModalities: [
8492
8532
  "text",
8493
- "image"
8533
+ "image",
8534
+ "pdf"
8494
8535
  ],
8495
8536
  outputModalities: [
8496
8537
  "text"
@@ -8553,7 +8594,8 @@ var openai_default = {
8553
8594
  type: "chat",
8554
8595
  inputModalities: [
8555
8596
  "text",
8556
- "image"
8597
+ "image",
8598
+ "pdf"
8557
8599
  ],
8558
8600
  outputModalities: [
8559
8601
  "text"
@@ -8633,7 +8675,8 @@ var openai_default = {
8633
8675
  type: "chat",
8634
8676
  inputModalities: [
8635
8677
  "text",
8636
- "image"
8678
+ "image",
8679
+ "pdf"
8637
8680
  ],
8638
8681
  outputModalities: [
8639
8682
  "text"
@@ -8696,7 +8739,8 @@ var openai_default = {
8696
8739
  type: "chat",
8697
8740
  inputModalities: [
8698
8741
  "text",
8699
- "image"
8742
+ "image",
8743
+ "pdf"
8700
8744
  ],
8701
8745
  outputModalities: [
8702
8746
  "text"
@@ -8763,7 +8807,8 @@ var openai_default = {
8763
8807
  type: "chat",
8764
8808
  inputModalities: [
8765
8809
  "text",
8766
- "image"
8810
+ "image",
8811
+ "pdf"
8767
8812
  ],
8768
8813
  outputModalities: [
8769
8814
  "text"
@@ -8838,7 +8883,8 @@ var openai_default = {
8838
8883
  type: "chat",
8839
8884
  inputModalities: [
8840
8885
  "text",
8841
- "image"
8886
+ "image",
8887
+ "pdf"
8842
8888
  ],
8843
8889
  outputModalities: [
8844
8890
  "text"
@@ -8892,7 +8938,8 @@ var openai_default = {
8892
8938
  type: "chat",
8893
8939
  inputModalities: [
8894
8940
  "text",
8895
- "image"
8941
+ "image",
8942
+ "pdf"
8896
8943
  ],
8897
8944
  outputModalities: [
8898
8945
  "text"
@@ -8946,7 +8993,8 @@ var openai_default = {
8946
8993
  type: "chat",
8947
8994
  inputModalities: [
8948
8995
  "text",
8949
- "image"
8996
+ "image",
8997
+ "pdf"
8950
8998
  ],
8951
8999
  outputModalities: [
8952
9000
  "text"
@@ -9016,7 +9064,8 @@ var openai_default = {
9016
9064
  type: "chat",
9017
9065
  inputModalities: [
9018
9066
  "text",
9019
- "image"
9067
+ "image",
9068
+ "pdf"
9020
9069
  ],
9021
9070
  outputModalities: [
9022
9071
  "text"
@@ -9080,7 +9129,8 @@ var openai_default = {
9080
9129
  type: "chat",
9081
9130
  inputModalities: [
9082
9131
  "text",
9083
- "image"
9132
+ "image",
9133
+ "pdf"
9084
9134
  ],
9085
9135
  outputModalities: [
9086
9136
  "text"
@@ -9138,7 +9188,8 @@ var openai_default = {
9138
9188
  type: "chat",
9139
9189
  inputModalities: [
9140
9190
  "text",
9141
- "image"
9191
+ "image",
9192
+ "pdf"
9142
9193
  ],
9143
9194
  outputModalities: [
9144
9195
  "text"
@@ -9195,7 +9246,8 @@ var openai_default = {
9195
9246
  type: "chat",
9196
9247
  inputModalities: [
9197
9248
  "text",
9198
- "image"
9249
+ "image",
9250
+ "pdf"
9199
9251
  ],
9200
9252
  outputModalities: [
9201
9253
  "text"
@@ -10275,7 +10327,8 @@ var openai_default = {
10275
10327
  type: "chat",
10276
10328
  inputModalities: [
10277
10329
  "text",
10278
- "image"
10330
+ "image",
10331
+ "pdf"
10279
10332
  ],
10280
10333
  outputModalities: [
10281
10334
  "text"
@@ -10332,7 +10385,8 @@ var openai_default = {
10332
10385
  type: "chat",
10333
10386
  inputModalities: [
10334
10387
  "text",
10335
- "image"
10388
+ "image",
10389
+ "pdf"
10336
10390
  ],
10337
10391
  outputModalities: [
10338
10392
  "text"
@@ -10397,7 +10451,8 @@ var openai_default = {
10397
10451
  type: "chat",
10398
10452
  inputModalities: [
10399
10453
  "text",
10400
- "image"
10454
+ "image",
10455
+ "pdf"
10401
10456
  ],
10402
10457
  outputModalities: [
10403
10458
  "text"
@@ -10514,7 +10569,8 @@ var openai_default = {
10514
10569
  type: "chat",
10515
10570
  inputModalities: [
10516
10571
  "text",
10517
- "image"
10572
+ "image",
10573
+ "pdf"
10518
10574
  ],
10519
10575
  outputModalities: [
10520
10576
  "text"
@@ -10582,7 +10638,8 @@ var openai_default = {
10582
10638
  type: "chat",
10583
10639
  inputModalities: [
10584
10640
  "text",
10585
- "image"
10641
+ "image",
10642
+ "pdf"
10586
10643
  ],
10587
10644
  outputModalities: [
10588
10645
  "text"
@@ -11622,7 +11679,8 @@ var openai_default = {
11622
11679
  type: "chat",
11623
11680
  inputModalities: [
11624
11681
  "text",
11625
- "image"
11682
+ "image",
11683
+ "pdf"
11626
11684
  ],
11627
11685
  outputModalities: [
11628
11686
  "text"
@@ -11700,7 +11758,8 @@ var openai_default = {
11700
11758
  type: "chat",
11701
11759
  inputModalities: [
11702
11760
  "text",
11703
- "image"
11761
+ "image",
11762
+ "pdf"
11704
11763
  ],
11705
11764
  outputModalities: [
11706
11765
  "text"
@@ -11778,7 +11837,8 @@ var openai_default = {
11778
11837
  type: "chat",
11779
11838
  inputModalities: [
11780
11839
  "text",
11781
- "image"
11840
+ "image",
11841
+ "pdf"
11782
11842
  ],
11783
11843
  outputModalities: [
11784
11844
  "text"
@@ -29485,14 +29545,23 @@ function withBuiltinTools(provider, caps) {
29485
29545
  if (!tools || tools.length === 0) return caps;
29486
29546
  return { ...caps, builtinTools: [...tools] };
29487
29547
  }
29548
+ function normalizeId(model) {
29549
+ return model.toLowerCase().replace(/(?<=\d)[-.](?=\d)/g, ".");
29550
+ }
29488
29551
  var ModelCatalog = class _ModelCatalog {
29489
29552
  models = /* @__PURE__ */ new Map();
29490
29553
  /** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
29491
29554
  * accept any callable id (providerModelName, dated snapshot) AND the slug. */
29492
29555
  aliasIndex = /* @__PURE__ */ new Map();
29556
+ /** `provider/normalized-id` → `provider/canonical-slug`. The last resort, so a
29557
+ * user's spelling of a version never decides whether the model is found. */
29558
+ normIndex = /* @__PURE__ */ new Map();
29493
29559
  key(provider, model) {
29494
29560
  return `${provider}/${model}`;
29495
29561
  }
29562
+ normKey(provider, model) {
29563
+ return `${provider}/${normalizeId(model)}`;
29564
+ }
29496
29565
  set(provider, model, info) {
29497
29566
  const canonical = this.key(provider, model);
29498
29567
  this.models.set(canonical, {
@@ -29530,19 +29599,39 @@ var ModelCatalog = class _ModelCatalog {
29530
29599
  this.aliasIndex.set(this.key(provider, alias), canonical);
29531
29600
  }
29532
29601
  }
29602
+ for (const name of [model, info.providerModelName, ...info.aliases ?? []]) {
29603
+ if (!name) continue;
29604
+ const nk = this.normKey(provider, name);
29605
+ if (!this.normIndex.has(nk)) this.normIndex.set(nk, canonical);
29606
+ }
29533
29607
  }
29534
29608
  get(provider, model) {
29535
29609
  const direct = this.models.get(this.key(provider, model));
29536
29610
  if (direct) return direct;
29537
- const canonical = this.aliasIndex.get(this.key(provider, model));
29611
+ const canonical = this.aliasIndex.get(this.key(provider, model)) ?? // Last resort: the same model under a different spelling of its version.
29612
+ this.normIndex.get(this.normKey(provider, model));
29538
29613
  return canonical ? this.models.get(canonical) ?? null : null;
29539
29614
  }
29540
- /** The exact id to SEND to the provider for a given model string. Translates
29541
- * our slug → providerModelName; passes an already-callable id (alias) through
29542
- * verbatim (respects an explicit choice); unknown model verbatim passthrough. */
29615
+ /** The exact id to SEND to the provider for a given model string.
29616
+ *
29617
+ * Three cases, and the difference between them is what the caller ASKED for:
29618
+ * - our slug → translate to providerModelName (the pinned snapshot)
29619
+ * - an id the provider itself accepts (a listed alias, e.g. a dated snapshot
29620
+ * or anthropic's undated name) → verbatim, because it is a deliberate choice
29621
+ * and rewriting it would pin a caller who asked to float
29622
+ * - a spelling variant that is NOT callable (`gemini-2-5-flash`) → the
29623
+ * canonical entry's providerModelName, since forwarding it verbatim only
29624
+ * produces a 404 with the user's typo in it
29625
+ * - unknown → verbatim, so a model we have never heard of still works */
29543
29626
  resolveModelId(provider, model) {
29544
29627
  const direct = this.models.get(this.key(provider, model));
29545
29628
  if (direct) return direct.providerModelName ?? model;
29629
+ if (this.aliasIndex.has(this.key(provider, model))) return model;
29630
+ const canonical = this.normIndex.get(this.normKey(provider, model));
29631
+ if (canonical) {
29632
+ const info = this.models.get(canonical);
29633
+ if (info) return info.providerModelName ?? info.model;
29634
+ }
29546
29635
  return model;
29547
29636
  }
29548
29637
  getPricing(provider, model) {
@@ -29691,14 +29780,31 @@ function evalCond(cond, ctx, reg) {
29691
29780
  const t = ctx.item?.value;
29692
29781
  return !reg.predicates.isFunctionTool(ctx) && t?.type === c.builtin;
29693
29782
  }
29783
+ if ("hasPartType" in c) {
29784
+ const messages = ctx.req.messages;
29785
+ return Boolean(
29786
+ messages?.some(
29787
+ (m) => Array.isArray(m?.content) ? m.content.some((part) => c.hasPartType.includes(part?.type)) : false
29788
+ )
29789
+ );
29790
+ }
29694
29791
  if ("hasTool" in c) {
29695
29792
  const tools = ctx.req.tools;
29696
- return Boolean(
29793
+ const present = Boolean(
29697
29794
  tools?.some((t) => {
29698
29795
  const itemCtx = { ...ctx, item: { value: t, index: 0, isLast: false } };
29699
29796
  return !reg.predicates.isFunctionTool(itemCtx) && t?.type === c.hasTool;
29700
29797
  })
29701
29798
  );
29799
+ if (!present) return false;
29800
+ const blocked = ctx.spec.toolConstraints?.find(
29801
+ (tc) => tc.tool === c.hasTool && evalCond(tc.conflictsWith, ctx, reg)
29802
+ );
29803
+ if (blocked) {
29804
+ if (ctx.notes && !ctx.notes.includes(blocked.why)) ctx.notes.push(blocked.why);
29805
+ return false;
29806
+ }
29807
+ return true;
29702
29808
  }
29703
29809
  if ("hasFunctionTool" in c) {
29704
29810
  const tools = ctx.req.tools;
@@ -29824,7 +29930,8 @@ function buildFromSpec(spec, req, reg, flavor = spec.provider, onUse, config = {
29824
29930
  flavor,
29825
29931
  config,
29826
29932
  variants: resolveVariants(spec, req.model ?? "", reg),
29827
- body: {}
29933
+ body: {},
29934
+ notes: []
29828
29935
  };
29829
29936
  for (const v of ctx.variants) onUse?.("variant", v);
29830
29937
  for (const f of spec.fields ?? []) {
@@ -29919,6 +30026,7 @@ function buildFromSpec(spec, req, reg, flavor = spec.provider, onUse, config = {
29919
30026
  }
29920
30027
  }
29921
30028
  const out = { body: ctx.body };
30029
+ if (ctx.notes?.length) out.notes = ctx.notes;
29922
30030
  if (ctx.multipart) out.multipart = ctx.multipart;
29923
30031
  if (spec.envelope?.bodyKind === "none") out.noBody = true;
29924
30032
  if (spec.envelope?.bodyKind === "raw") out.rawBody = true;
@@ -30788,7 +30896,7 @@ var xai_create_default = {
30788
30896
  var xai_addRequests_default = {
30789
30897
  id: "xai/batch.addRequests",
30790
30898
  extends: "xai/batch.base",
30791
- _note: "Step 2 of submit. Step 1 creates the batch and is NOT modelled: its body embeds Date.now(), so the request is not reproducible. See report 039 A3.",
30899
+ _note: "Step 2 of submit: the batch opened in step 1 is filled here. batch_request is a TAGGED UNION whose variant name is the KEY -- {responses: <body>}, never {endpoint: 'responses', body: <body>}. Measured live 2026-08-28: the endpoint-field form is rejected 422 with 'unknown variant endpoint, expected one of chat_get_completion, responses, image_generation, image_edit, video_generation, video_extension'. That form had never been sent to xAI, here or in the TypeScript adapter this spec came from.",
30792
30900
  envelope: {
30793
30901
  method: "POST",
30794
30902
  url: { $join: [{ $config: "baseURL" }, "/v1/batches/", { $: "batchId" }, "/requests"] },
@@ -30804,7 +30912,7 @@ var xai_addRequests_default = {
30804
30912
  {
30805
30913
  value: {
30806
30914
  batch_request_id: { $: "@customId" },
30807
- batch_request: { endpoint: "responses", body: { $: "@body" } }
30915
+ batch_request: { responses: { $: "@body" } }
30808
30916
  }
30809
30917
  }
30810
30918
  ]
@@ -31156,6 +31264,42 @@ var xai_upload_default = {
31156
31264
  multipart: [{ name: "file", file: true }, { name: "purpose", value: "assistants" }]
31157
31265
  };
31158
31266
 
31267
+ // src/wire/specs/files/xai.delete.json
31268
+ var xai_delete_default = {
31269
+ id: "xai/files.delete",
31270
+ extends: "openai/files.base",
31271
+ _note: "Same shape as OpenAI's, per xai.upload's note that the surface differs only in the upload purpose. baseURL is supplied by the adapter, so the path is all that is declared here.",
31272
+ envelope: {
31273
+ method: "DELETE",
31274
+ bodyKind: "none",
31275
+ url: { $join: [{ $config: "baseURL" }, "/v1/files/", { $: "remoteId" }] }
31276
+ }
31277
+ };
31278
+
31279
+ // src/wire/specs/files/xai.getInfo.json
31280
+ var xai_getInfo_default = {
31281
+ id: "xai/files.getInfo",
31282
+ extends: "openai/files.base",
31283
+ _note: "Same shape as OpenAI's, per xai.upload's note that the surface differs only in the upload purpose. baseURL is supplied by the adapter, so the path is all that is declared here.",
31284
+ envelope: {
31285
+ method: "GET",
31286
+ bodyKind: "none",
31287
+ url: { $join: [{ $config: "baseURL" }, "/v1/files/", { $: "remoteId" }] }
31288
+ }
31289
+ };
31290
+
31291
+ // src/wire/specs/files/xai.list.json
31292
+ var xai_list_default = {
31293
+ id: "xai/files.list",
31294
+ extends: "openai/files.base",
31295
+ _note: "Same shape as OpenAI's, per xai.upload's note that the surface differs only in the upload purpose. baseURL is supplied by the adapter, so the path is all that is declared here.",
31296
+ envelope: {
31297
+ method: "GET",
31298
+ bodyKind: "none",
31299
+ url: { $join: [{ $config: "baseURL" }, "/v1/files"] }
31300
+ }
31301
+ };
31302
+
31159
31303
  // src/wire/specs/files/google.finishUpload.json
31160
31304
  var google_finishUpload_default = {
31161
31305
  id: "google/files.finishUpload",
@@ -31396,6 +31540,9 @@ var DELTAS = new Map(
31396
31540
  google_getInfo_default,
31397
31541
  google_list_default,
31398
31542
  xai_upload_default,
31543
+ xai_delete_default,
31544
+ xai_getInfo_default,
31545
+ xai_list_default,
31399
31546
  google_finishUpload_default,
31400
31547
  videos_status_default,
31401
31548
  videos_cancel_default,
@@ -32206,6 +32353,14 @@ var anthropic_default3 = {
32206
32353
  value: {
32207
32354
  $config: "apiVersion"
32208
32355
  }
32356
+ },
32357
+ {
32358
+ name: "anthropic-dangerous-direct-browser-access",
32359
+ value: "true",
32360
+ _note: "Only in a browser, where the request is otherwise blocked by CORS. The chat path sends it (messages.ts) and so do the files specs; models.list lost it when this request moved to a spec, because the header lived in the helper's own table.",
32361
+ when: {
32362
+ truthy: "browser"
32363
+ }
32209
32364
  }
32210
32365
  ]
32211
32366
  }
@@ -36216,6 +36371,18 @@ var LLMClient = class {
36216
36371
  return streamFile(file, this.retrieveContext());
36217
36372
  }
36218
36373
  /** Submit a request. Returns the parsed CompletionResponse. */
36374
+ /** Anything the spec left out on purpose reaches the caller as a warning.
36375
+ * Said once per request; the build already de-duplicates within one. */
36376
+ reportBuildNotes(req, ctx) {
36377
+ for (const note of req.notes ?? []) {
36378
+ this.hooks.emitSync("onWarning", {
36379
+ source: "llm",
36380
+ code: "request_adjusted",
36381
+ message: note,
36382
+ details: { provider: this.provider, model: this.model, ctx }
36383
+ });
36384
+ }
36385
+ }
36219
36386
  async complete(input, options = {}) {
36220
36387
  const rawMessages = normalizeInput(input);
36221
36388
  const { system: systemFromMessages, messages } = extractSystem(rawMessages);
@@ -36278,6 +36445,7 @@ var LLMClient = class {
36278
36445
  normalized.messages = decision.messages;
36279
36446
  }
36280
36447
  const providerReq = this.adapter.buildRequest(normalized);
36448
+ this.reportBuildNotes(providerReq, ctx);
36281
36449
  const url = this.adapter.baseURL() + (providerReq.path ?? this.adapter.completionPath());
36282
36450
  if (this.cacheKeyFn) {
36283
36451
  ctx.cacheKey = ctx.cacheKey ?? this.cacheKeyFn(normalized, ctx);
@@ -36439,6 +36607,7 @@ var LLMClient = class {
36439
36607
  normalized.messages = resolveCtx.messages;
36440
36608
  normalized.system = resolveCtx.system;
36441
36609
  const providerReq = this.adapter.buildRequest(normalized);
36610
+ this.reportBuildNotes(providerReq, ctx);
36442
36611
  this.adapter.enableStreaming?.(providerReq, normalized);
36443
36612
  const url = this.adapter.baseURL() + (providerReq.path ?? this.adapter.completionPath());
36444
36613
  const httpReq = {
@@ -36468,6 +36637,7 @@ var LLMClient = class {
36468
36637
  let moderationReport;
36469
36638
  const files = [];
36470
36639
  const builtinToolCalls = [];
36640
+ const citationsByUrl = /* @__PURE__ */ new Map();
36471
36641
  const fetchStream = this.fetchStreamFn;
36472
36642
  const queueName = this.queueName;
36473
36643
  const priority = this.priority;
@@ -36521,6 +36691,9 @@ var LLMClient = class {
36521
36691
  case "file":
36522
36692
  files.push(event.file);
36523
36693
  break;
36694
+ case "citation":
36695
+ citationsByUrl.set(event.citation.url, event.citation);
36696
+ break;
36524
36697
  case "builtin_tool_end":
36525
36698
  builtinToolCalls.push({
36526
36699
  tool: event.tool,
@@ -36555,6 +36728,7 @@ var LLMClient = class {
36555
36728
  media: [],
36556
36729
  ...files.length ? { files } : {},
36557
36730
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
36731
+ ...citationsByUrl.size ? { citations: [...citationsByUrl.values()] } : {},
36558
36732
  ...moderationReport ? { moderation: moderationReport } : {},
36559
36733
  latencyMs: performance.now() - start,
36560
36734
  raw: null
@@ -37217,6 +37391,14 @@ var generate_2_5_default = {
37217
37391
  id: "google/generate@2.5",
37218
37392
  provider: "google",
37219
37393
  api: "generate",
37394
+ _toolConstraints: `Measured 2026-08-25: Google answers 400 "The mime type: X is not supported for code execution" when codeExecution is sent with a PDF or a video part. Images are fine, googleSearch is fine, and OpenAI accepts every combination -- so this is one provider's rule, kept with that provider's spec.`,
37395
+ toolConstraints: [
37396
+ {
37397
+ tool: "code_interpreter",
37398
+ conflictsWith: { hasPartType: ["document", "video"] },
37399
+ why: "google: code execution cannot run alongside a PDF or video attachment, so the request was sent without it"
37400
+ }
37401
+ ],
37220
37402
  tables: {
37221
37403
  toolMode: {
37222
37404
  auto: "AUTO",
@@ -38357,6 +38539,87 @@ function pinFor(model, pins) {
38357
38539
  var ANTHROPIC_MESSAGE_PINS = anthropic_messages_default;
38358
38540
  var GOOGLE_GENERATE_PINS = google_generate_default;
38359
38541
 
38542
+ // src/llm/providers/_shared/citations.ts
38543
+ function fromAnthropic(raw) {
38544
+ const out = [];
38545
+ for (const block of raw.content ?? []) {
38546
+ for (const cite of block.citations ?? []) {
38547
+ const url = cite.url;
38548
+ if (url) {
38549
+ out.push({
38550
+ url,
38551
+ ...cite.title ? { title: cite.title } : {},
38552
+ // Anthropic is the only provider that reports the cited passage.
38553
+ ...cite.cited_text ? { text: cite.cited_text } : {}
38554
+ });
38555
+ }
38556
+ }
38557
+ }
38558
+ return out;
38559
+ }
38560
+ function fromGoogle(raw) {
38561
+ const out = [];
38562
+ for (const candidate of raw.candidates ?? []) {
38563
+ const grounding = candidate.groundingMetadata ?? {};
38564
+ for (const chunk of grounding.groundingChunks ?? []) {
38565
+ const web = chunk.web ?? {};
38566
+ const uri = web.uri;
38567
+ if (uri) out.push({ url: uri, ...web.title ? { title: web.title } : {} });
38568
+ }
38569
+ }
38570
+ return out;
38571
+ }
38572
+ function fromResponses(raw) {
38573
+ const out = [];
38574
+ for (const item of raw.output ?? []) {
38575
+ for (const part of item.content ?? []) {
38576
+ for (const note of part.annotations ?? []) {
38577
+ if (note.type === "url_citation" && note.url) {
38578
+ out.push({
38579
+ url: note.url,
38580
+ ...note.title ? { title: note.title } : {}
38581
+ });
38582
+ }
38583
+ }
38584
+ }
38585
+ }
38586
+ return out;
38587
+ }
38588
+ function fromCompletions(raw) {
38589
+ const out = [];
38590
+ const message = (raw.choices ?? [])[0]?.message ?? {};
38591
+ for (const note of message.annotations ?? []) {
38592
+ const detail = note.url_citation ?? note;
38593
+ if (note.type === "url_citation" && detail.url) {
38594
+ out.push({
38595
+ url: detail.url,
38596
+ ...detail.title ? { title: detail.title } : {}
38597
+ });
38598
+ }
38599
+ }
38600
+ for (const url of raw.citations ?? []) {
38601
+ if (typeof url === "string") out.push({ url });
38602
+ }
38603
+ return out;
38604
+ }
38605
+ var READERS = {
38606
+ messages: fromAnthropic,
38607
+ generate: fromGoogle,
38608
+ responses: fromResponses,
38609
+ completions: fromCompletions
38610
+ // `interactions` is DELIBERATELY absent. Google's Interactions API returns a
38611
+ // `steps[]` machine, not `candidates[]`, so `fromGoogle` would read a key that
38612
+ // is never there -- and no recorded Interactions response carries grounding, so
38613
+ // there is nothing to write a reader against. Guessing the shape would produce
38614
+ // a reader that is confidently wrong and passes a test built from the same
38615
+ // guess. The adapter is wired anyway: when the shape is measured, only this
38616
+ // table changes.
38617
+ };
38618
+ function extractCitations(api, raw) {
38619
+ if (raw == null || typeof raw !== "object") return [];
38620
+ return READERS[api]?.(raw) ?? [];
38621
+ }
38622
+
38360
38623
  // src/llm/providers/_shared/builtin-tools.ts
38361
38624
  var NATIVE_TO_UNIFIED = {
38362
38625
  // OpenAI / xAI Responses output-item types
@@ -38575,6 +38838,7 @@ var AnthropicAdapter = class {
38575
38838
  model_context_window_exceeded: "length",
38576
38839
  refusal: "content_filter"
38577
38840
  });
38841
+ const citations = extractCitations("messages", raw);
38578
38842
  return {
38579
38843
  id: r.id,
38580
38844
  model: r.model,
@@ -38584,6 +38848,7 @@ var AnthropicAdapter = class {
38584
38848
  text: content.filter((p) => p.type === "text").map((p) => p.text).join(""),
38585
38849
  toolCalls,
38586
38850
  media: [],
38851
+ ...citations.length ? { citations } : {},
38587
38852
  ...files.length ? { files } : {},
38588
38853
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
38589
38854
  thinking,
@@ -38611,6 +38876,20 @@ var AnthropicAdapter = class {
38611
38876
  if (delta.type === "text_delta") return [{ type: "text", text: delta.text }];
38612
38877
  if (delta.type === "thinking_delta")
38613
38878
  return [{ type: "thinking", text: delta.thinking }];
38879
+ if (delta.type === "citations_delta") {
38880
+ const cite = delta.citation ?? {};
38881
+ const url = cite.url;
38882
+ return url ? [
38883
+ {
38884
+ type: "citation",
38885
+ citation: {
38886
+ url,
38887
+ ...cite.title ? { title: cite.title } : {},
38888
+ ...cite.cited_text ? { text: cite.cited_text } : {}
38889
+ }
38890
+ }
38891
+ ] : [];
38892
+ }
38614
38893
  if (delta.type === "input_json_delta") {
38615
38894
  if (state.current) {
38616
38895
  state.current.json += delta.partial_json ?? "";
@@ -39161,6 +39440,7 @@ var GoogleAdapter = class {
39161
39440
  builtinToolCalls.push({ tool: "web_fetch", ...typeof url === "string" ? { url } : {} });
39162
39441
  }
39163
39442
  }
39443
+ const citations = extractCitations("generate", raw);
39164
39444
  return {
39165
39445
  // generateContent DOES return an id — `responseId`, at the top level. The
39166
39446
  // fallback stays for older payloads, but minting one unconditionally made
@@ -39176,6 +39456,7 @@ var GoogleAdapter = class {
39176
39456
  toolCalls,
39177
39457
  thinking,
39178
39458
  media,
39459
+ ...citations.length ? { citations } : {},
39179
39460
  ...files.length ? { files } : {},
39180
39461
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
39181
39462
  latencyMs,
@@ -39260,6 +39541,18 @@ var GoogleAdapter = class {
39260
39541
  events.push({ type: "tool_call_end", id: "" });
39261
39542
  }
39262
39543
  }
39544
+ for (const chunk of candidate.groundingMetadata?.groundingChunks ?? []) {
39545
+ const web = chunk.web ?? {};
39546
+ if (web.uri) {
39547
+ events.push({
39548
+ type: "citation",
39549
+ citation: {
39550
+ url: web.uri,
39551
+ ...web.title ? { title: web.title } : {}
39552
+ }
39553
+ });
39554
+ }
39555
+ }
39263
39556
  if (candidate.groundingMetadata && !state.webSearchEmitted) {
39264
39557
  state.webSearchEmitted = true;
39265
39558
  const q = candidate.groundingMetadata.webSearchQueries?.[0];
@@ -39482,6 +39775,7 @@ var GoogleInteractionsAdapter = class {
39482
39775
  queued: "pending",
39483
39776
  in_progress: "pending"
39484
39777
  });
39778
+ const citations = extractCitations("interactions", raw);
39485
39779
  return {
39486
39780
  id: r.id ?? crypto.randomUUID(),
39487
39781
  model: "",
@@ -39491,6 +39785,7 @@ var GoogleInteractionsAdapter = class {
39491
39785
  text,
39492
39786
  toolCalls,
39493
39787
  media,
39788
+ ...citations.length ? { citations } : {},
39494
39789
  thinking,
39495
39790
  latencyMs,
39496
39791
  raw
@@ -40832,13 +41127,14 @@ var OpenAIAdapter = class {
40832
41127
  content.push(parsed);
40833
41128
  toolCalls.push(parsed);
40834
41129
  }
40835
- const finishReason = extractFinishReason(
40836
- toolCalls.length > 0,
40837
- choice.finish_reason,
40838
- { tool_calls: "tool_use", length: "length", content_filter: "content_filter" }
40839
- );
41130
+ const finishReason = extractFinishReason(toolCalls.length > 0, choice.finish_reason, {
41131
+ tool_calls: "tool_use",
41132
+ length: "length",
41133
+ content_filter: "content_filter"
41134
+ });
40840
41135
  const reasoningContent = message.reasoning_content ?? null;
40841
41136
  const moderation = parseNativeModeration(r.moderation);
41137
+ const citations = extractCitations("completions", raw);
40842
41138
  return {
40843
41139
  id: r.id,
40844
41140
  model: r.model,
@@ -40848,6 +41144,7 @@ var OpenAIAdapter = class {
40848
41144
  text,
40849
41145
  toolCalls,
40850
41146
  media,
41147
+ ...citations.length ? { citations } : {},
40851
41148
  thinking: reasoningContent,
40852
41149
  ...moderation ? { moderation } : {},
40853
41150
  latencyMs,
@@ -40880,6 +41177,18 @@ var OpenAIAdapter = class {
40880
41177
  if (delta.content) {
40881
41178
  events.push({ type: "text", text: delta.content });
40882
41179
  }
41180
+ for (const note of delta.annotations ?? []) {
41181
+ const detail = note.url_citation ?? note;
41182
+ if (note.type === "url_citation" && detail.url) {
41183
+ events.push({
41184
+ type: "citation",
41185
+ citation: {
41186
+ url: detail.url,
41187
+ ...detail.title ? { title: detail.title } : {}
41188
+ }
41189
+ });
41190
+ }
41191
+ }
40883
41192
  const toolIdByIndex = state?.toolIdByIndex ?? /* @__PURE__ */ new Map();
40884
41193
  const toolCalls = delta.tool_calls ?? [];
40885
41194
  for (const tc of toolCalls) {
@@ -41790,6 +42099,7 @@ var OpenAIResponsesAdapter = class {
41790
42099
  if (text && content.length === 0) content.push({ type: "text", text });
41791
42100
  }
41792
42101
  const moderation = parseNativeModeration(r.moderation);
42102
+ const citations = extractCitations("responses", raw);
41793
42103
  return {
41794
42104
  id: r.id,
41795
42105
  model: r.model ?? "",
@@ -41800,6 +42110,7 @@ var OpenAIResponsesAdapter = class {
41800
42110
  toolCalls,
41801
42111
  thinking,
41802
42112
  media,
42113
+ ...citations.length ? { citations } : {},
41803
42114
  ...files.length ? { files } : {},
41804
42115
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
41805
42116
  ...moderation ? { moderation } : {},
@@ -41812,6 +42123,19 @@ var OpenAIResponsesAdapter = class {
41812
42123
  const data = sseJson(event);
41813
42124
  const type = data.type;
41814
42125
  const events = [];
42126
+ if (type === "response.output_text.annotation.added") {
42127
+ const note = data.annotation ?? {};
42128
+ if (note.type === "url_citation" && note.url) {
42129
+ events.push({
42130
+ type: "citation",
42131
+ citation: {
42132
+ url: note.url,
42133
+ ...note.title ? { title: note.title } : {}
42134
+ }
42135
+ });
42136
+ }
42137
+ return events;
42138
+ }
41815
42139
  if (type === "response.output_text.delta") {
41816
42140
  const itemId = data.item_id;
41817
42141
  const phase = itemId ? phaseByItem?.get(itemId) : void 0;
@@ -42132,10 +42456,6 @@ var XAIBatchAdapter = class {
42132
42456
  throw new Error(`xAI batch create failed: ${JSON.stringify(createRes.body)}`);
42133
42457
  const batch2 = createRes.body ?? {};
42134
42458
  const batchId = batch2.batch_id ?? batch2.id;
42135
- const _batchRequests = requests.map((r) => ({
42136
- batch_request_id: r.customId,
42137
- batch_request: { endpoint: "responses", body: r.body }
42138
- }));
42139
42459
  const addRes = await fetch2(this.buildAddRequestsRequest(batchId, requests));
42140
42460
  if (addRes.status >= 400)
42141
42461
  throw new Error(`xAI batch add requests failed: ${JSON.stringify(addRes.body)}`);
@@ -43650,7 +43970,8 @@ function makeStepState() {
43650
43970
  stepToolCalls: [],
43651
43971
  toolCallAccum: /* @__PURE__ */ new Map(),
43652
43972
  stepUsage: emptyUsage(),
43653
- stepFinishReason: "stop"
43973
+ stepFinishReason: "stop",
43974
+ stepCitations: /* @__PURE__ */ new Map()
43654
43975
  };
43655
43976
  }
43656
43977
  function accumulateStreamEvent(event, state) {
@@ -43684,6 +44005,9 @@ function accumulateStreamEvent(event, state) {
43684
44005
  }
43685
44006
  return null;
43686
44007
  }
44008
+ case "citation":
44009
+ state.stepCitations.set(event.citation.url, event.citation);
44010
+ return null;
43687
44011
  case "usage":
43688
44012
  state.stepUsage = event.usage;
43689
44013
  return null;
@@ -43741,6 +44065,7 @@ function buildStepResponse(state, model, stepStart) {
43741
44065
  toolCalls: state.stepToolCalls,
43742
44066
  thinking: state.stepThinking || null,
43743
44067
  media: [],
44068
+ ...state.stepCitations.size ? { citations: [...state.stepCitations.values()] } : {},
43744
44069
  latencyMs: stepLatency,
43745
44070
  raw: null
43746
44071
  };
@@ -44082,6 +44407,8 @@ var AgentLoop = class _AgentLoop {
44082
44407
  // final LLM response (e.g. code-execution files produced during the run).
44083
44408
  ...last?.files ? { files: last.files } : {},
44084
44409
  ...last?.builtinToolCalls ? { builtinToolCalls: last.builtinToolCalls } : {},
44410
+ // Every step's sources, not just the final step's — see the accumulator.
44411
+ ...args.citations.length ? { citations: args.citations } : {},
44085
44412
  ...last?.moderation ? { moderation: last.moderation } : {},
44086
44413
  latencyMs: performance.now() - args.startPerf,
44087
44414
  raw: args.raw
@@ -44148,6 +44475,7 @@ var AgentLoop = class _AgentLoop {
44148
44475
  let stepCount = 0;
44149
44476
  let toolCallCount = 0;
44150
44477
  let lastResponse = null;
44478
+ const citationsByUrl = /* @__PURE__ */ new Map();
44151
44479
  let reason = "done";
44152
44480
  let errorMsg;
44153
44481
  let caughtError;
@@ -44189,6 +44517,7 @@ var AgentLoop = class _AgentLoop {
44189
44517
  const stepLatency = performance.now() - stepStart;
44190
44518
  totalLlmTimeMs += stepLatency;
44191
44519
  addUsage(totalUsage, lastResponse.usage);
44520
+ for (const cite of lastResponse.citations ?? []) citationsByUrl.set(cite.url, cite);
44192
44521
  if (this._reflectRetry?.handles(lastResponse.finishReason)) {
44193
44522
  const verdict = this._reflectRetry.recordFailure();
44194
44523
  await this.hooks.emit("onWarning", {
@@ -44296,6 +44625,7 @@ var AgentLoop = class _AgentLoop {
44296
44625
  totalUsage,
44297
44626
  lastResponse,
44298
44627
  media: lastResponse?.media ?? [],
44628
+ citations: [...citationsByUrl.values()],
44299
44629
  raw: lastResponse?.raw ?? null
44300
44630
  });
44301
44631
  await this.settleRun({
@@ -44341,6 +44671,7 @@ var AgentLoop = class _AgentLoop {
44341
44671
  let finalText = "";
44342
44672
  let finalContent = [];
44343
44673
  let lastResponse = null;
44674
+ const citationsByUrl = /* @__PURE__ */ new Map();
44344
44675
  let reason = "done";
44345
44676
  let errorMsg;
44346
44677
  let caughtError;
@@ -44395,6 +44726,7 @@ var AgentLoop = class _AgentLoop {
44395
44726
  finalContent = content;
44396
44727
  finalText = state.stepText;
44397
44728
  lastResponse = stepResponse;
44729
+ for (const cite of stepResponse.citations ?? []) citationsByUrl.set(cite.url, cite);
44398
44730
  this._history.append(
44399
44731
  {
44400
44732
  ...buildAssistantMessage(lastResponse, {
@@ -44478,6 +44810,7 @@ var AgentLoop = class _AgentLoop {
44478
44810
  finalContent,
44479
44811
  totalUsage,
44480
44812
  lastResponse,
44813
+ citations: [...citationsByUrl.values()],
44481
44814
  // The streaming path already emitted media as events, and never holds a
44482
44815
  // raw provider payload.
44483
44816
  media: [],
@@ -54471,37 +54804,26 @@ function listModels(opts = {}) {
54471
54804
  var LIVE = {
54472
54805
  openai: {
54473
54806
  url: "https://api.openai.com/v1/models",
54474
- headers: (k) => ({ authorization: `Bearer ${k}` }),
54475
54807
  items: (b) => b.data ?? [],
54476
54808
  id: (m) => m.id
54477
54809
  },
54478
54810
  openrouter: {
54479
54811
  url: "https://openrouter.ai/api/v1/models",
54480
- headers: (k) => ({ authorization: `Bearer ${k}` }),
54481
54812
  items: (b) => b.data ?? [],
54482
54813
  id: (m) => m.id
54483
54814
  },
54484
54815
  xai: {
54485
54816
  url: "https://api.x.ai/v1/models",
54486
- headers: (k) => ({ authorization: `Bearer ${k}` }),
54487
54817
  items: (b) => b.data ?? [],
54488
54818
  id: (m) => m.id
54489
54819
  },
54490
54820
  anthropic: {
54491
54821
  url: "https://api.anthropic.com/v1/models",
54492
- // Mirror the chat adapter: Anthropic rejects browser requests without the
54493
- // explicit opt-in header, so the /models call must send it too.
54494
- headers: (k) => ({
54495
- "x-api-key": k,
54496
- "anthropic-version": ANTHROPIC_API_VERSION,
54497
- ...isBrowser() ? { "anthropic-dangerous-direct-browser-access": "true" } : {}
54498
- }),
54499
54822
  items: (b) => b.data ?? [],
54500
54823
  id: (m) => m.id
54501
54824
  },
54502
54825
  google: {
54503
54826
  url: "https://generativelanguage.googleapis.com/v1beta/models",
54504
- headers: (k) => ({ "x-goog-api-key": k }),
54505
54827
  items: (b) => b.models ?? [],
54506
54828
  id: (m) => m.name.replace(/^models\//, "")
54507
54829
  }
@@ -54594,7 +54916,9 @@ async function fetchLiveBody(opts) {
54594
54916
  const p = (async () => {
54595
54917
  const built = buildFromSpec(
54596
54918
  utilitySpec(`${opts.provider}/models.list`),
54597
- {},
54919
+ // Anthropic refuses a browser request without an explicit opt-in header,
54920
+ // and the spec asks for `browser` to decide whether to send it.
54921
+ { browser: isBrowser() },
54598
54922
  makeRegistry({}),
54599
54923
  opts.provider,
54600
54924
  void 0,
@@ -54670,6 +54994,35 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
54670
54994
  ...Object.keys(CAP_KEYS),
54671
54995
  ...Object.keys(BUILTIN_TOOL_KEYS)
54672
54996
  ]);
54997
+ function filterFacets(catalog) {
54998
+ const models = catalog?.list() ?? [];
54999
+ const distinct = (pick) => [...new Set(models.map(pick).filter((v) => !!v))].sort();
55000
+ const facets = [
55001
+ { key: "type", category: "what it is", label: "Type", values: distinct((m) => m.type), numeric: false, bare: false },
55002
+ { key: "provider", category: "what it is", label: "Provider", values: distinct((m) => m.provider), numeric: false, bare: false },
55003
+ // The named cutoffs the parser compares against, plus `free` for exactly zero.
55004
+ { key: "price", category: "cost", label: "Price", values: ["free", "low", "mid", "high"], numeric: true, bare: false },
55005
+ // filled below from the models themselves — tiers are per model, not a fixed set
55006
+ { key: "tier", category: "cost", label: "Billing tier", values: [], numeric: false, bare: false },
55007
+ { key: "context", category: "inputs", label: "Context window", values: ["small", "large"], numeric: true, bare: false },
55008
+ { key: "reasoning", category: "thinking", label: "Reasoning", values: ["yes", "no"], numeric: false, bare: true },
55009
+ { key: "status", category: "availability", label: "Status", values: distinct((m) => m.status), numeric: false, bare: false },
55010
+ { key: "active", category: "availability", label: "Offered now", values: ["yes", "no"], numeric: false, bare: true }
55011
+ ];
55012
+ for (const key of Object.keys(CAP_KEYS)) {
55013
+ facets.push({ key, category: "inputs", label: key, values: ["yes", "no"], numeric: false, bare: true });
55014
+ }
55015
+ for (const key of Object.keys(BUILTIN_TOOL_KEYS)) {
55016
+ facets.push({ key, category: "hosted tools", label: key.replace(/_/g, " "), values: ["yes", "no"], numeric: false, bare: true });
55017
+ }
55018
+ const tiers = [...new Set(models.flatMap((m) => Object.keys(m.pricing?.tiers ?? {})))].sort();
55019
+ const tierFacet = facets.find((f) => f.key === "tier");
55020
+ if (tierFacet) tierFacet.values = tiers;
55021
+ return facets;
55022
+ }
55023
+ function filterAliases() {
55024
+ return { ...DEFAULT_TAGS };
55025
+ }
54673
55026
  function parseNum(v) {
54674
55027
  const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
54675
55028
  if (!m) return Number.NaN;
@@ -54791,6 +55144,7 @@ function select(query, opts = {}) {
54791
55144
  function createRealtime(opts) {
54792
55145
  const engine = opts.engine ?? coreRegistry.get();
54793
55146
  const { provider, model } = resolveModel(opts.model, opts.provider, "createRealtime");
55147
+ const sendModel = engine.catalog.resolveModelId(provider, model);
54794
55148
  const apiKey = opts.apiKey ?? engine.apiKeys[provider];
54795
55149
  if (!apiKey) {
54796
55150
  throw new Error(
@@ -54800,11 +55154,11 @@ function createRealtime(opts) {
54800
55154
  const adapter = resolveAdapter2(provider, apiKey);
54801
55155
  const voice = resolveVoice(provider, opts.audio?.voice ?? opts.voice);
54802
55156
  const session = adapter.connect(
54803
- { model, modalities: opts.modalities, voice, instructions: opts.instructions },
55157
+ { model: sendModel, modalities: opts.modalities, voice, instructions: opts.instructions },
54804
55158
  engine.connect
54805
55159
  );
54806
55160
  session.on("usage", (e) => {
54807
- void engine.hooks.emit("onCompletion", realtimeCompletionContext(provider, model, e.usage)).catch(() => {
55161
+ void engine.hooks.emit("onCompletion", realtimeCompletionContext(provider, sendModel, e.usage)).catch(() => {
54808
55162
  });
54809
55163
  });
54810
55164
  return session;
@@ -57175,6 +57529,8 @@ export {
57175
57529
  exchangeCode,
57176
57530
  extractLastUserText,
57177
57531
  extractSystemText,
57532
+ filterAliases,
57533
+ filterFacets,
57178
57534
  finalAnswerText,
57179
57535
  finishMcpAuth,
57180
57536
  formatBulletedList,