@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.
@@ -599,6 +599,7 @@ var TelemetryAdapter = class {
599
599
  break;
600
600
  case "onModelError":
601
601
  this.metrics.errors++;
602
+ this.metrics.inFlight = Math.max(0, this.metrics.inFlight - 1);
602
603
  break;
603
604
  case "onCostEntry":
604
605
  this.metrics.costUsd += event.ctx.entry?.cost?.total ?? 0;
@@ -3401,7 +3402,8 @@ var anthropic_default = {
3401
3402
  "anthropic/claude-opus-4.1": {
3402
3403
  providerModelName: "claude-opus-4-1-20250805",
3403
3404
  aliases: [
3404
- "claude-opus-4-1-20250805"
3405
+ "claude-opus-4-1-20250805",
3406
+ "claude-opus-4-1"
3405
3407
  ],
3406
3408
  pricing: {
3407
3409
  inputPerMTok: 15,
@@ -3465,7 +3467,8 @@ var anthropic_default = {
3465
3467
  "anthropic/claude-opus-4.5": {
3466
3468
  providerModelName: "claude-opus-4-5-20251101",
3467
3469
  aliases: [
3468
- "claude-opus-4-5-20251101"
3470
+ "claude-opus-4-5-20251101",
3471
+ "claude-opus-4-5"
3469
3472
  ],
3470
3473
  pricing: {
3471
3474
  inputPerMTok: 5,
@@ -3804,7 +3807,8 @@ var anthropic_default = {
3804
3807
  "anthropic/claude-sonnet-4.5": {
3805
3808
  providerModelName: "claude-sonnet-4-5-20250929",
3806
3809
  aliases: [
3807
- "claude-sonnet-4-5-20250929"
3810
+ "claude-sonnet-4-5-20250929",
3811
+ "claude-sonnet-4-5"
3808
3812
  ],
3809
3813
  pricing: {
3810
3814
  inputPerMTok: 3,
@@ -3938,7 +3942,8 @@ var anthropic_default = {
3938
3942
  "anthropic/claude-haiku-4.5": {
3939
3943
  providerModelName: "claude-haiku-4-5-20251001",
3940
3944
  aliases: [
3941
- "claude-haiku-4-5-20251001"
3945
+ "claude-haiku-4-5-20251001",
3946
+ "claude-haiku-4-5"
3942
3947
  ],
3943
3948
  pricing: {
3944
3949
  inputPerMTok: 1,
@@ -4297,7 +4302,7 @@ var google_default = {
4297
4302
  structuredOutput: true,
4298
4303
  vision: true,
4299
4304
  audio: true,
4300
- video: false,
4305
+ video: true,
4301
4306
  imageGeneration: false,
4302
4307
  audioGeneration: false,
4303
4308
  videoGeneration: false
@@ -4313,7 +4318,9 @@ var google_default = {
4313
4318
  inputModalities: [
4314
4319
  "text",
4315
4320
  "image",
4316
- "audio"
4321
+ "audio",
4322
+ "pdf",
4323
+ "video"
4317
4324
  ],
4318
4325
  outputModalities: [
4319
4326
  "text"
@@ -4369,7 +4376,7 @@ var google_default = {
4369
4376
  structuredOutput: true,
4370
4377
  vision: true,
4371
4378
  audio: true,
4372
- video: false,
4379
+ video: true,
4373
4380
  imageGeneration: false,
4374
4381
  audioGeneration: false,
4375
4382
  videoGeneration: false
@@ -4391,7 +4398,9 @@ var google_default = {
4391
4398
  inputModalities: [
4392
4399
  "text",
4393
4400
  "image",
4394
- "audio"
4401
+ "audio",
4402
+ "pdf",
4403
+ "video"
4395
4404
  ],
4396
4405
  outputModalities: [
4397
4406
  "text"
@@ -4429,7 +4438,7 @@ var google_default = {
4429
4438
  structuredOutput: true,
4430
4439
  vision: true,
4431
4440
  audio: true,
4432
- video: false,
4441
+ video: true,
4433
4442
  imageGeneration: false,
4434
4443
  audioGeneration: false,
4435
4444
  videoGeneration: false
@@ -4445,7 +4454,9 @@ var google_default = {
4445
4454
  inputModalities: [
4446
4455
  "text",
4447
4456
  "image",
4448
- "audio"
4457
+ "audio",
4458
+ "pdf",
4459
+ "video"
4449
4460
  ],
4450
4461
  outputModalities: [
4451
4462
  "text"
@@ -4504,7 +4515,7 @@ var google_default = {
4504
4515
  structuredOutput: true,
4505
4516
  vision: true,
4506
4517
  audio: true,
4507
- video: false,
4518
+ video: true,
4508
4519
  imageGeneration: false,
4509
4520
  audioGeneration: false,
4510
4521
  videoGeneration: false
@@ -4520,7 +4531,9 @@ var google_default = {
4520
4531
  inputModalities: [
4521
4532
  "text",
4522
4533
  "image",
4523
- "audio"
4534
+ "audio",
4535
+ "pdf",
4536
+ "video"
4524
4537
  ],
4525
4538
  outputModalities: [
4526
4539
  "text"
@@ -4579,7 +4592,7 @@ var google_default = {
4579
4592
  structuredOutput: true,
4580
4593
  vision: true,
4581
4594
  audio: true,
4582
- video: false,
4595
+ video: true,
4583
4596
  imageGeneration: false,
4584
4597
  audioGeneration: false,
4585
4598
  videoGeneration: false
@@ -4602,7 +4615,9 @@ var google_default = {
4602
4615
  inputModalities: [
4603
4616
  "text",
4604
4617
  "image",
4605
- "audio"
4618
+ "audio",
4619
+ "pdf",
4620
+ "video"
4606
4621
  ],
4607
4622
  outputModalities: [
4608
4623
  "text"
@@ -4657,7 +4672,7 @@ var google_default = {
4657
4672
  structuredOutput: true,
4658
4673
  vision: true,
4659
4674
  audio: true,
4660
- video: false,
4675
+ video: true,
4661
4676
  imageGeneration: false,
4662
4677
  audioGeneration: false,
4663
4678
  videoGeneration: false
@@ -4673,7 +4688,9 @@ var google_default = {
4673
4688
  inputModalities: [
4674
4689
  "text",
4675
4690
  "image",
4676
- "audio"
4691
+ "audio",
4692
+ "pdf",
4693
+ "video"
4677
4694
  ],
4678
4695
  outputModalities: [
4679
4696
  "text"
@@ -4732,7 +4749,7 @@ var google_default = {
4732
4749
  structuredOutput: true,
4733
4750
  vision: true,
4734
4751
  audio: true,
4735
- video: false,
4752
+ video: true,
4736
4753
  imageGeneration: false,
4737
4754
  audioGeneration: false,
4738
4755
  videoGeneration: false
@@ -4748,7 +4765,9 @@ var google_default = {
4748
4765
  inputModalities: [
4749
4766
  "text",
4750
4767
  "image",
4751
- "audio"
4768
+ "audio",
4769
+ "pdf",
4770
+ "video"
4752
4771
  ],
4753
4772
  outputModalities: [
4754
4773
  "text"
@@ -4808,7 +4827,7 @@ var google_default = {
4808
4827
  structuredOutput: true,
4809
4828
  vision: true,
4810
4829
  audio: true,
4811
- video: false,
4830
+ video: true,
4812
4831
  imageGeneration: false,
4813
4832
  audioGeneration: false,
4814
4833
  videoGeneration: false
@@ -4824,7 +4843,9 @@ var google_default = {
4824
4843
  inputModalities: [
4825
4844
  "text",
4826
4845
  "image",
4827
- "audio"
4846
+ "audio",
4847
+ "pdf",
4848
+ "video"
4828
4849
  ],
4829
4850
  outputModalities: [
4830
4851
  "text"
@@ -5807,7 +5828,8 @@ var google_default = {
5807
5828
  type: "chat",
5808
5829
  inputModalities: [
5809
5830
  "text",
5810
- "image"
5831
+ "image",
5832
+ "pdf"
5811
5833
  ],
5812
5834
  outputModalities: [
5813
5835
  "text"
@@ -5860,7 +5882,8 @@ var google_default = {
5860
5882
  type: "chat",
5861
5883
  inputModalities: [
5862
5884
  "text",
5863
- "image"
5885
+ "image",
5886
+ "pdf"
5864
5887
  ],
5865
5888
  outputModalities: [
5866
5889
  "text"
@@ -7029,7 +7052,7 @@ var google_default = {
7029
7052
  structuredOutput: true,
7030
7053
  vision: true,
7031
7054
  audio: true,
7032
- video: false,
7055
+ video: true,
7033
7056
  imageGeneration: false,
7034
7057
  audioGeneration: false,
7035
7058
  videoGeneration: false
@@ -7045,7 +7068,9 @@ var google_default = {
7045
7068
  inputModalities: [
7046
7069
  "text",
7047
7070
  "image",
7048
- "audio"
7071
+ "audio",
7072
+ "pdf",
7073
+ "video"
7049
7074
  ],
7050
7075
  outputModalities: [
7051
7076
  "text"
@@ -7101,7 +7126,7 @@ var google_default = {
7101
7126
  structuredOutput: true,
7102
7127
  vision: true,
7103
7128
  audio: true,
7104
- video: false,
7129
+ video: true,
7105
7130
  imageGeneration: false,
7106
7131
  audioGeneration: false,
7107
7132
  videoGeneration: false
@@ -7117,7 +7142,9 @@ var google_default = {
7117
7142
  inputModalities: [
7118
7143
  "text",
7119
7144
  "image",
7120
- "audio"
7145
+ "audio",
7146
+ "pdf",
7147
+ "video"
7121
7148
  ],
7122
7149
  outputModalities: [
7123
7150
  "text"
@@ -7172,7 +7199,7 @@ var google_default = {
7172
7199
  structuredOutput: true,
7173
7200
  vision: true,
7174
7201
  audio: true,
7175
- video: false,
7202
+ video: true,
7176
7203
  imageGeneration: false,
7177
7204
  audioGeneration: false,
7178
7205
  videoGeneration: false
@@ -7188,7 +7215,9 @@ var google_default = {
7188
7215
  inputModalities: [
7189
7216
  "text",
7190
7217
  "image",
7191
- "audio"
7218
+ "audio",
7219
+ "pdf",
7220
+ "video"
7192
7221
  ],
7193
7222
  outputModalities: [
7194
7223
  "text"
@@ -7478,7 +7507,8 @@ var openai_default = {
7478
7507
  type: "chat",
7479
7508
  inputModalities: [
7480
7509
  "text",
7481
- "image"
7510
+ "image",
7511
+ "pdf"
7482
7512
  ],
7483
7513
  outputModalities: [
7484
7514
  "text"
@@ -7538,7 +7568,8 @@ var openai_default = {
7538
7568
  type: "chat",
7539
7569
  inputModalities: [
7540
7570
  "text",
7541
- "image"
7571
+ "image",
7572
+ "pdf"
7542
7573
  ],
7543
7574
  outputModalities: [
7544
7575
  "text"
@@ -7594,7 +7625,8 @@ var openai_default = {
7594
7625
  type: "chat",
7595
7626
  inputModalities: [
7596
7627
  "text",
7597
- "image"
7628
+ "image",
7629
+ "pdf"
7598
7630
  ],
7599
7631
  outputModalities: [
7600
7632
  "text"
@@ -7705,7 +7737,8 @@ var openai_default = {
7705
7737
  type: "chat",
7706
7738
  inputModalities: [
7707
7739
  "text",
7708
- "image"
7740
+ "image",
7741
+ "pdf"
7709
7742
  ],
7710
7743
  outputModalities: [
7711
7744
  "text"
@@ -7761,7 +7794,8 @@ var openai_default = {
7761
7794
  type: "chat",
7762
7795
  inputModalities: [
7763
7796
  "text",
7764
- "image"
7797
+ "image",
7798
+ "pdf"
7765
7799
  ],
7766
7800
  outputModalities: [
7767
7801
  "text"
@@ -7841,7 +7875,8 @@ var openai_default = {
7841
7875
  type: "chat",
7842
7876
  inputModalities: [
7843
7877
  "text",
7844
- "image"
7878
+ "image",
7879
+ "pdf"
7845
7880
  ],
7846
7881
  outputModalities: [
7847
7882
  "text"
@@ -7914,7 +7949,8 @@ var openai_default = {
7914
7949
  type: "chat",
7915
7950
  inputModalities: [
7916
7951
  "text",
7917
- "image"
7952
+ "image",
7953
+ "pdf"
7918
7954
  ],
7919
7955
  outputModalities: [
7920
7956
  "text"
@@ -7969,7 +8005,8 @@ var openai_default = {
7969
8005
  type: "chat",
7970
8006
  inputModalities: [
7971
8007
  "text",
7972
- "image"
8008
+ "image",
8009
+ "pdf"
7973
8010
  ],
7974
8011
  outputModalities: [
7975
8012
  "text"
@@ -8028,7 +8065,8 @@ var openai_default = {
8028
8065
  type: "chat",
8029
8066
  inputModalities: [
8030
8067
  "text",
8031
- "image"
8068
+ "image",
8069
+ "pdf"
8032
8070
  ],
8033
8071
  outputModalities: [
8034
8072
  "text"
@@ -8087,7 +8125,8 @@ var openai_default = {
8087
8125
  type: "chat",
8088
8126
  inputModalities: [
8089
8127
  "text",
8090
- "image"
8128
+ "image",
8129
+ "pdf"
8091
8130
  ],
8092
8131
  outputModalities: [
8093
8132
  "text"
@@ -8146,7 +8185,8 @@ var openai_default = {
8146
8185
  type: "chat",
8147
8186
  inputModalities: [
8148
8187
  "text",
8149
- "image"
8188
+ "image",
8189
+ "pdf"
8150
8190
  ],
8151
8191
  outputModalities: [
8152
8192
  "text"
@@ -8563,7 +8603,8 @@ var openai_default = {
8563
8603
  type: "chat",
8564
8604
  inputModalities: [
8565
8605
  "text",
8566
- "image"
8606
+ "image",
8607
+ "pdf"
8567
8608
  ],
8568
8609
  outputModalities: [
8569
8610
  "text"
@@ -8626,7 +8667,8 @@ var openai_default = {
8626
8667
  type: "chat",
8627
8668
  inputModalities: [
8628
8669
  "text",
8629
- "image"
8670
+ "image",
8671
+ "pdf"
8630
8672
  ],
8631
8673
  outputModalities: [
8632
8674
  "text"
@@ -8706,7 +8748,8 @@ var openai_default = {
8706
8748
  type: "chat",
8707
8749
  inputModalities: [
8708
8750
  "text",
8709
- "image"
8751
+ "image",
8752
+ "pdf"
8710
8753
  ],
8711
8754
  outputModalities: [
8712
8755
  "text"
@@ -8769,7 +8812,8 @@ var openai_default = {
8769
8812
  type: "chat",
8770
8813
  inputModalities: [
8771
8814
  "text",
8772
- "image"
8815
+ "image",
8816
+ "pdf"
8773
8817
  ],
8774
8818
  outputModalities: [
8775
8819
  "text"
@@ -8836,7 +8880,8 @@ var openai_default = {
8836
8880
  type: "chat",
8837
8881
  inputModalities: [
8838
8882
  "text",
8839
- "image"
8883
+ "image",
8884
+ "pdf"
8840
8885
  ],
8841
8886
  outputModalities: [
8842
8887
  "text"
@@ -8911,7 +8956,8 @@ var openai_default = {
8911
8956
  type: "chat",
8912
8957
  inputModalities: [
8913
8958
  "text",
8914
- "image"
8959
+ "image",
8960
+ "pdf"
8915
8961
  ],
8916
8962
  outputModalities: [
8917
8963
  "text"
@@ -8965,7 +9011,8 @@ var openai_default = {
8965
9011
  type: "chat",
8966
9012
  inputModalities: [
8967
9013
  "text",
8968
- "image"
9014
+ "image",
9015
+ "pdf"
8969
9016
  ],
8970
9017
  outputModalities: [
8971
9018
  "text"
@@ -9019,7 +9066,8 @@ var openai_default = {
9019
9066
  type: "chat",
9020
9067
  inputModalities: [
9021
9068
  "text",
9022
- "image"
9069
+ "image",
9070
+ "pdf"
9023
9071
  ],
9024
9072
  outputModalities: [
9025
9073
  "text"
@@ -9089,7 +9137,8 @@ var openai_default = {
9089
9137
  type: "chat",
9090
9138
  inputModalities: [
9091
9139
  "text",
9092
- "image"
9140
+ "image",
9141
+ "pdf"
9093
9142
  ],
9094
9143
  outputModalities: [
9095
9144
  "text"
@@ -9153,7 +9202,8 @@ var openai_default = {
9153
9202
  type: "chat",
9154
9203
  inputModalities: [
9155
9204
  "text",
9156
- "image"
9205
+ "image",
9206
+ "pdf"
9157
9207
  ],
9158
9208
  outputModalities: [
9159
9209
  "text"
@@ -9211,7 +9261,8 @@ var openai_default = {
9211
9261
  type: "chat",
9212
9262
  inputModalities: [
9213
9263
  "text",
9214
- "image"
9264
+ "image",
9265
+ "pdf"
9215
9266
  ],
9216
9267
  outputModalities: [
9217
9268
  "text"
@@ -9268,7 +9319,8 @@ var openai_default = {
9268
9319
  type: "chat",
9269
9320
  inputModalities: [
9270
9321
  "text",
9271
- "image"
9322
+ "image",
9323
+ "pdf"
9272
9324
  ],
9273
9325
  outputModalities: [
9274
9326
  "text"
@@ -10348,7 +10400,8 @@ var openai_default = {
10348
10400
  type: "chat",
10349
10401
  inputModalities: [
10350
10402
  "text",
10351
- "image"
10403
+ "image",
10404
+ "pdf"
10352
10405
  ],
10353
10406
  outputModalities: [
10354
10407
  "text"
@@ -10405,7 +10458,8 @@ var openai_default = {
10405
10458
  type: "chat",
10406
10459
  inputModalities: [
10407
10460
  "text",
10408
- "image"
10461
+ "image",
10462
+ "pdf"
10409
10463
  ],
10410
10464
  outputModalities: [
10411
10465
  "text"
@@ -10470,7 +10524,8 @@ var openai_default = {
10470
10524
  type: "chat",
10471
10525
  inputModalities: [
10472
10526
  "text",
10473
- "image"
10527
+ "image",
10528
+ "pdf"
10474
10529
  ],
10475
10530
  outputModalities: [
10476
10531
  "text"
@@ -10587,7 +10642,8 @@ var openai_default = {
10587
10642
  type: "chat",
10588
10643
  inputModalities: [
10589
10644
  "text",
10590
- "image"
10645
+ "image",
10646
+ "pdf"
10591
10647
  ],
10592
10648
  outputModalities: [
10593
10649
  "text"
@@ -10655,7 +10711,8 @@ var openai_default = {
10655
10711
  type: "chat",
10656
10712
  inputModalities: [
10657
10713
  "text",
10658
- "image"
10714
+ "image",
10715
+ "pdf"
10659
10716
  ],
10660
10717
  outputModalities: [
10661
10718
  "text"
@@ -11695,7 +11752,8 @@ var openai_default = {
11695
11752
  type: "chat",
11696
11753
  inputModalities: [
11697
11754
  "text",
11698
- "image"
11755
+ "image",
11756
+ "pdf"
11699
11757
  ],
11700
11758
  outputModalities: [
11701
11759
  "text"
@@ -11773,7 +11831,8 @@ var openai_default = {
11773
11831
  type: "chat",
11774
11832
  inputModalities: [
11775
11833
  "text",
11776
- "image"
11834
+ "image",
11835
+ "pdf"
11777
11836
  ],
11778
11837
  outputModalities: [
11779
11838
  "text"
@@ -11851,7 +11910,8 @@ var openai_default = {
11851
11910
  type: "chat",
11852
11911
  inputModalities: [
11853
11912
  "text",
11854
- "image"
11913
+ "image",
11914
+ "pdf"
11855
11915
  ],
11856
11916
  outputModalities: [
11857
11917
  "text"
@@ -29558,14 +29618,23 @@ function withBuiltinTools(provider, caps) {
29558
29618
  if (!tools || tools.length === 0) return caps;
29559
29619
  return { ...caps, builtinTools: [...tools] };
29560
29620
  }
29621
+ function normalizeId(model) {
29622
+ return model.toLowerCase().replace(/(?<=\d)[-.](?=\d)/g, ".");
29623
+ }
29561
29624
  var ModelCatalog = class _ModelCatalog {
29562
29625
  models = /* @__PURE__ */ new Map();
29563
29626
  /** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
29564
29627
  * accept any callable id (providerModelName, dated snapshot) AND the slug. */
29565
29628
  aliasIndex = /* @__PURE__ */ new Map();
29629
+ /** `provider/normalized-id` → `provider/canonical-slug`. The last resort, so a
29630
+ * user's spelling of a version never decides whether the model is found. */
29631
+ normIndex = /* @__PURE__ */ new Map();
29566
29632
  key(provider, model) {
29567
29633
  return `${provider}/${model}`;
29568
29634
  }
29635
+ normKey(provider, model) {
29636
+ return `${provider}/${normalizeId(model)}`;
29637
+ }
29569
29638
  set(provider, model, info) {
29570
29639
  const canonical = this.key(provider, model);
29571
29640
  this.models.set(canonical, {
@@ -29603,19 +29672,39 @@ var ModelCatalog = class _ModelCatalog {
29603
29672
  this.aliasIndex.set(this.key(provider, alias), canonical);
29604
29673
  }
29605
29674
  }
29675
+ for (const name of [model, info.providerModelName, ...info.aliases ?? []]) {
29676
+ if (!name) continue;
29677
+ const nk = this.normKey(provider, name);
29678
+ if (!this.normIndex.has(nk)) this.normIndex.set(nk, canonical);
29679
+ }
29606
29680
  }
29607
29681
  get(provider, model) {
29608
29682
  const direct = this.models.get(this.key(provider, model));
29609
29683
  if (direct) return direct;
29610
- const canonical = this.aliasIndex.get(this.key(provider, model));
29684
+ const canonical = this.aliasIndex.get(this.key(provider, model)) ?? // Last resort: the same model under a different spelling of its version.
29685
+ this.normIndex.get(this.normKey(provider, model));
29611
29686
  return canonical ? this.models.get(canonical) ?? null : null;
29612
29687
  }
29613
- /** The exact id to SEND to the provider for a given model string. Translates
29614
- * our slug → providerModelName; passes an already-callable id (alias) through
29615
- * verbatim (respects an explicit choice); unknown model verbatim passthrough. */
29688
+ /** The exact id to SEND to the provider for a given model string.
29689
+ *
29690
+ * Three cases, and the difference between them is what the caller ASKED for:
29691
+ * - our slug → translate to providerModelName (the pinned snapshot)
29692
+ * - an id the provider itself accepts (a listed alias, e.g. a dated snapshot
29693
+ * or anthropic's undated name) → verbatim, because it is a deliberate choice
29694
+ * and rewriting it would pin a caller who asked to float
29695
+ * - a spelling variant that is NOT callable (`gemini-2-5-flash`) → the
29696
+ * canonical entry's providerModelName, since forwarding it verbatim only
29697
+ * produces a 404 with the user's typo in it
29698
+ * - unknown → verbatim, so a model we have never heard of still works */
29616
29699
  resolveModelId(provider, model) {
29617
29700
  const direct = this.models.get(this.key(provider, model));
29618
29701
  if (direct) return direct.providerModelName ?? model;
29702
+ if (this.aliasIndex.has(this.key(provider, model))) return model;
29703
+ const canonical = this.normIndex.get(this.normKey(provider, model));
29704
+ if (canonical) {
29705
+ const info = this.models.get(canonical);
29706
+ if (info) return info.providerModelName ?? info.model;
29707
+ }
29619
29708
  return model;
29620
29709
  }
29621
29710
  getPricing(provider, model) {
@@ -29764,14 +29853,31 @@ function evalCond(cond, ctx, reg) {
29764
29853
  const t = ctx.item?.value;
29765
29854
  return !reg.predicates.isFunctionTool(ctx) && t?.type === c.builtin;
29766
29855
  }
29856
+ if ("hasPartType" in c) {
29857
+ const messages = ctx.req.messages;
29858
+ return Boolean(
29859
+ messages?.some(
29860
+ (m) => Array.isArray(m?.content) ? m.content.some((part) => c.hasPartType.includes(part?.type)) : false
29861
+ )
29862
+ );
29863
+ }
29767
29864
  if ("hasTool" in c) {
29768
29865
  const tools = ctx.req.tools;
29769
- return Boolean(
29866
+ const present = Boolean(
29770
29867
  tools?.some((t) => {
29771
29868
  const itemCtx = { ...ctx, item: { value: t, index: 0, isLast: false } };
29772
29869
  return !reg.predicates.isFunctionTool(itemCtx) && t?.type === c.hasTool;
29773
29870
  })
29774
29871
  );
29872
+ if (!present) return false;
29873
+ const blocked = ctx.spec.toolConstraints?.find(
29874
+ (tc) => tc.tool === c.hasTool && evalCond(tc.conflictsWith, ctx, reg)
29875
+ );
29876
+ if (blocked) {
29877
+ if (ctx.notes && !ctx.notes.includes(blocked.why)) ctx.notes.push(blocked.why);
29878
+ return false;
29879
+ }
29880
+ return true;
29775
29881
  }
29776
29882
  if ("hasFunctionTool" in c) {
29777
29883
  const tools = ctx.req.tools;
@@ -29897,7 +30003,8 @@ function buildFromSpec(spec, req, reg, flavor = spec.provider, onUse, config = {
29897
30003
  flavor,
29898
30004
  config,
29899
30005
  variants: resolveVariants(spec, req.model ?? "", reg),
29900
- body: {}
30006
+ body: {},
30007
+ notes: []
29901
30008
  };
29902
30009
  for (const v of ctx.variants) onUse?.("variant", v);
29903
30010
  for (const f of spec.fields ?? []) {
@@ -29992,6 +30099,7 @@ function buildFromSpec(spec, req, reg, flavor = spec.provider, onUse, config = {
29992
30099
  }
29993
30100
  }
29994
30101
  const out = { body: ctx.body };
30102
+ if (ctx.notes?.length) out.notes = ctx.notes;
29995
30103
  if (ctx.multipart) out.multipart = ctx.multipart;
29996
30104
  if (spec.envelope?.bodyKind === "none") out.noBody = true;
29997
30105
  if (spec.envelope?.bodyKind === "raw") out.rawBody = true;
@@ -30861,7 +30969,7 @@ var xai_create_default = {
30861
30969
  var xai_addRequests_default = {
30862
30970
  id: "xai/batch.addRequests",
30863
30971
  extends: "xai/batch.base",
30864
- _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.",
30972
+ _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.",
30865
30973
  envelope: {
30866
30974
  method: "POST",
30867
30975
  url: { $join: [{ $config: "baseURL" }, "/v1/batches/", { $: "batchId" }, "/requests"] },
@@ -30877,7 +30985,7 @@ var xai_addRequests_default = {
30877
30985
  {
30878
30986
  value: {
30879
30987
  batch_request_id: { $: "@customId" },
30880
- batch_request: { endpoint: "responses", body: { $: "@body" } }
30988
+ batch_request: { responses: { $: "@body" } }
30881
30989
  }
30882
30990
  }
30883
30991
  ]
@@ -31229,6 +31337,42 @@ var xai_upload_default = {
31229
31337
  multipart: [{ name: "file", file: true }, { name: "purpose", value: "assistants" }]
31230
31338
  };
31231
31339
 
31340
+ // src/wire/specs/files/xai.delete.json
31341
+ var xai_delete_default = {
31342
+ id: "xai/files.delete",
31343
+ extends: "openai/files.base",
31344
+ _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.",
31345
+ envelope: {
31346
+ method: "DELETE",
31347
+ bodyKind: "none",
31348
+ url: { $join: [{ $config: "baseURL" }, "/v1/files/", { $: "remoteId" }] }
31349
+ }
31350
+ };
31351
+
31352
+ // src/wire/specs/files/xai.getInfo.json
31353
+ var xai_getInfo_default = {
31354
+ id: "xai/files.getInfo",
31355
+ extends: "openai/files.base",
31356
+ _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.",
31357
+ envelope: {
31358
+ method: "GET",
31359
+ bodyKind: "none",
31360
+ url: { $join: [{ $config: "baseURL" }, "/v1/files/", { $: "remoteId" }] }
31361
+ }
31362
+ };
31363
+
31364
+ // src/wire/specs/files/xai.list.json
31365
+ var xai_list_default = {
31366
+ id: "xai/files.list",
31367
+ extends: "openai/files.base",
31368
+ _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.",
31369
+ envelope: {
31370
+ method: "GET",
31371
+ bodyKind: "none",
31372
+ url: { $join: [{ $config: "baseURL" }, "/v1/files"] }
31373
+ }
31374
+ };
31375
+
31232
31376
  // src/wire/specs/files/google.finishUpload.json
31233
31377
  var google_finishUpload_default = {
31234
31378
  id: "google/files.finishUpload",
@@ -31469,6 +31613,9 @@ var DELTAS = new Map(
31469
31613
  google_getInfo_default,
31470
31614
  google_list_default,
31471
31615
  xai_upload_default,
31616
+ xai_delete_default,
31617
+ xai_getInfo_default,
31618
+ xai_list_default,
31472
31619
  google_finishUpload_default,
31473
31620
  videos_status_default,
31474
31621
  videos_cancel_default,
@@ -32279,6 +32426,14 @@ var anthropic_default3 = {
32279
32426
  value: {
32280
32427
  $config: "apiVersion"
32281
32428
  }
32429
+ },
32430
+ {
32431
+ name: "anthropic-dangerous-direct-browser-access",
32432
+ value: "true",
32433
+ _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.",
32434
+ when: {
32435
+ truthy: "browser"
32436
+ }
32282
32437
  }
32283
32438
  ]
32284
32439
  }
@@ -36289,6 +36444,18 @@ var LLMClient = class {
36289
36444
  return streamFile(file, this.retrieveContext());
36290
36445
  }
36291
36446
  /** Submit a request. Returns the parsed CompletionResponse. */
36447
+ /** Anything the spec left out on purpose reaches the caller as a warning.
36448
+ * Said once per request; the build already de-duplicates within one. */
36449
+ reportBuildNotes(req, ctx) {
36450
+ for (const note of req.notes ?? []) {
36451
+ this.hooks.emitSync("onWarning", {
36452
+ source: "llm",
36453
+ code: "request_adjusted",
36454
+ message: note,
36455
+ details: { provider: this.provider, model: this.model, ctx }
36456
+ });
36457
+ }
36458
+ }
36292
36459
  async complete(input, options = {}) {
36293
36460
  const rawMessages = normalizeInput(input);
36294
36461
  const { system: systemFromMessages, messages } = extractSystem(rawMessages);
@@ -36351,6 +36518,7 @@ var LLMClient = class {
36351
36518
  normalized.messages = decision.messages;
36352
36519
  }
36353
36520
  const providerReq = this.adapter.buildRequest(normalized);
36521
+ this.reportBuildNotes(providerReq, ctx);
36354
36522
  const url = this.adapter.baseURL() + (providerReq.path ?? this.adapter.completionPath());
36355
36523
  if (this.cacheKeyFn) {
36356
36524
  ctx.cacheKey = ctx.cacheKey ?? this.cacheKeyFn(normalized, ctx);
@@ -36512,6 +36680,7 @@ var LLMClient = class {
36512
36680
  normalized.messages = resolveCtx.messages;
36513
36681
  normalized.system = resolveCtx.system;
36514
36682
  const providerReq = this.adapter.buildRequest(normalized);
36683
+ this.reportBuildNotes(providerReq, ctx);
36515
36684
  this.adapter.enableStreaming?.(providerReq, normalized);
36516
36685
  const url = this.adapter.baseURL() + (providerReq.path ?? this.adapter.completionPath());
36517
36686
  const httpReq = {
@@ -36541,6 +36710,7 @@ var LLMClient = class {
36541
36710
  let moderationReport;
36542
36711
  const files = [];
36543
36712
  const builtinToolCalls = [];
36713
+ const citationsByUrl = /* @__PURE__ */ new Map();
36544
36714
  const fetchStream = this.fetchStreamFn;
36545
36715
  const queueName = this.queueName;
36546
36716
  const priority = this.priority;
@@ -36594,6 +36764,9 @@ var LLMClient = class {
36594
36764
  case "file":
36595
36765
  files.push(event.file);
36596
36766
  break;
36767
+ case "citation":
36768
+ citationsByUrl.set(event.citation.url, event.citation);
36769
+ break;
36597
36770
  case "builtin_tool_end":
36598
36771
  builtinToolCalls.push({
36599
36772
  tool: event.tool,
@@ -36628,6 +36801,7 @@ var LLMClient = class {
36628
36801
  media: [],
36629
36802
  ...files.length ? { files } : {},
36630
36803
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
36804
+ ...citationsByUrl.size ? { citations: [...citationsByUrl.values()] } : {},
36631
36805
  ...moderationReport ? { moderation: moderationReport } : {},
36632
36806
  latencyMs: performance.now() - start,
36633
36807
  raw: null
@@ -37290,6 +37464,14 @@ var generate_2_5_default = {
37290
37464
  id: "google/generate@2.5",
37291
37465
  provider: "google",
37292
37466
  api: "generate",
37467
+ _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.`,
37468
+ toolConstraints: [
37469
+ {
37470
+ tool: "code_interpreter",
37471
+ conflictsWith: { hasPartType: ["document", "video"] },
37472
+ why: "google: code execution cannot run alongside a PDF or video attachment, so the request was sent without it"
37473
+ }
37474
+ ],
37293
37475
  tables: {
37294
37476
  toolMode: {
37295
37477
  auto: "AUTO",
@@ -38430,6 +38612,87 @@ function pinFor(model, pins) {
38430
38612
  var ANTHROPIC_MESSAGE_PINS = anthropic_messages_default;
38431
38613
  var GOOGLE_GENERATE_PINS = google_generate_default;
38432
38614
 
38615
+ // src/llm/providers/_shared/citations.ts
38616
+ function fromAnthropic(raw) {
38617
+ const out = [];
38618
+ for (const block of raw.content ?? []) {
38619
+ for (const cite of block.citations ?? []) {
38620
+ const url = cite.url;
38621
+ if (url) {
38622
+ out.push({
38623
+ url,
38624
+ ...cite.title ? { title: cite.title } : {},
38625
+ // Anthropic is the only provider that reports the cited passage.
38626
+ ...cite.cited_text ? { text: cite.cited_text } : {}
38627
+ });
38628
+ }
38629
+ }
38630
+ }
38631
+ return out;
38632
+ }
38633
+ function fromGoogle(raw) {
38634
+ const out = [];
38635
+ for (const candidate of raw.candidates ?? []) {
38636
+ const grounding = candidate.groundingMetadata ?? {};
38637
+ for (const chunk of grounding.groundingChunks ?? []) {
38638
+ const web = chunk.web ?? {};
38639
+ const uri = web.uri;
38640
+ if (uri) out.push({ url: uri, ...web.title ? { title: web.title } : {} });
38641
+ }
38642
+ }
38643
+ return out;
38644
+ }
38645
+ function fromResponses(raw) {
38646
+ const out = [];
38647
+ for (const item of raw.output ?? []) {
38648
+ for (const part of item.content ?? []) {
38649
+ for (const note of part.annotations ?? []) {
38650
+ if (note.type === "url_citation" && note.url) {
38651
+ out.push({
38652
+ url: note.url,
38653
+ ...note.title ? { title: note.title } : {}
38654
+ });
38655
+ }
38656
+ }
38657
+ }
38658
+ }
38659
+ return out;
38660
+ }
38661
+ function fromCompletions(raw) {
38662
+ const out = [];
38663
+ const message = (raw.choices ?? [])[0]?.message ?? {};
38664
+ for (const note of message.annotations ?? []) {
38665
+ const detail = note.url_citation ?? note;
38666
+ if (note.type === "url_citation" && detail.url) {
38667
+ out.push({
38668
+ url: detail.url,
38669
+ ...detail.title ? { title: detail.title } : {}
38670
+ });
38671
+ }
38672
+ }
38673
+ for (const url of raw.citations ?? []) {
38674
+ if (typeof url === "string") out.push({ url });
38675
+ }
38676
+ return out;
38677
+ }
38678
+ var READERS = {
38679
+ messages: fromAnthropic,
38680
+ generate: fromGoogle,
38681
+ responses: fromResponses,
38682
+ completions: fromCompletions
38683
+ // `interactions` is DELIBERATELY absent. Google's Interactions API returns a
38684
+ // `steps[]` machine, not `candidates[]`, so `fromGoogle` would read a key that
38685
+ // is never there -- and no recorded Interactions response carries grounding, so
38686
+ // there is nothing to write a reader against. Guessing the shape would produce
38687
+ // a reader that is confidently wrong and passes a test built from the same
38688
+ // guess. The adapter is wired anyway: when the shape is measured, only this
38689
+ // table changes.
38690
+ };
38691
+ function extractCitations(api, raw) {
38692
+ if (raw == null || typeof raw !== "object") return [];
38693
+ return READERS[api]?.(raw) ?? [];
38694
+ }
38695
+
38433
38696
  // src/llm/providers/_shared/builtin-tools.ts
38434
38697
  var NATIVE_TO_UNIFIED = {
38435
38698
  // OpenAI / xAI Responses output-item types
@@ -38648,6 +38911,7 @@ var AnthropicAdapter = class {
38648
38911
  model_context_window_exceeded: "length",
38649
38912
  refusal: "content_filter"
38650
38913
  });
38914
+ const citations = extractCitations("messages", raw);
38651
38915
  return {
38652
38916
  id: r.id,
38653
38917
  model: r.model,
@@ -38657,6 +38921,7 @@ var AnthropicAdapter = class {
38657
38921
  text: content.filter((p) => p.type === "text").map((p) => p.text).join(""),
38658
38922
  toolCalls,
38659
38923
  media: [],
38924
+ ...citations.length ? { citations } : {},
38660
38925
  ...files.length ? { files } : {},
38661
38926
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
38662
38927
  thinking,
@@ -38684,6 +38949,20 @@ var AnthropicAdapter = class {
38684
38949
  if (delta.type === "text_delta") return [{ type: "text", text: delta.text }];
38685
38950
  if (delta.type === "thinking_delta")
38686
38951
  return [{ type: "thinking", text: delta.thinking }];
38952
+ if (delta.type === "citations_delta") {
38953
+ const cite = delta.citation ?? {};
38954
+ const url = cite.url;
38955
+ return url ? [
38956
+ {
38957
+ type: "citation",
38958
+ citation: {
38959
+ url,
38960
+ ...cite.title ? { title: cite.title } : {},
38961
+ ...cite.cited_text ? { text: cite.cited_text } : {}
38962
+ }
38963
+ }
38964
+ ] : [];
38965
+ }
38687
38966
  if (delta.type === "input_json_delta") {
38688
38967
  if (state.current) {
38689
38968
  state.current.json += delta.partial_json ?? "";
@@ -39234,6 +39513,7 @@ var GoogleAdapter = class {
39234
39513
  builtinToolCalls.push({ tool: "web_fetch", ...typeof url === "string" ? { url } : {} });
39235
39514
  }
39236
39515
  }
39516
+ const citations = extractCitations("generate", raw);
39237
39517
  return {
39238
39518
  // generateContent DOES return an id — `responseId`, at the top level. The
39239
39519
  // fallback stays for older payloads, but minting one unconditionally made
@@ -39249,6 +39529,7 @@ var GoogleAdapter = class {
39249
39529
  toolCalls,
39250
39530
  thinking,
39251
39531
  media,
39532
+ ...citations.length ? { citations } : {},
39252
39533
  ...files.length ? { files } : {},
39253
39534
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
39254
39535
  latencyMs,
@@ -39333,6 +39614,18 @@ var GoogleAdapter = class {
39333
39614
  events.push({ type: "tool_call_end", id: "" });
39334
39615
  }
39335
39616
  }
39617
+ for (const chunk of candidate.groundingMetadata?.groundingChunks ?? []) {
39618
+ const web = chunk.web ?? {};
39619
+ if (web.uri) {
39620
+ events.push({
39621
+ type: "citation",
39622
+ citation: {
39623
+ url: web.uri,
39624
+ ...web.title ? { title: web.title } : {}
39625
+ }
39626
+ });
39627
+ }
39628
+ }
39336
39629
  if (candidate.groundingMetadata && !state.webSearchEmitted) {
39337
39630
  state.webSearchEmitted = true;
39338
39631
  const q = candidate.groundingMetadata.webSearchQueries?.[0];
@@ -39555,6 +39848,7 @@ var GoogleInteractionsAdapter = class {
39555
39848
  queued: "pending",
39556
39849
  in_progress: "pending"
39557
39850
  });
39851
+ const citations = extractCitations("interactions", raw);
39558
39852
  return {
39559
39853
  id: r.id ?? crypto.randomUUID(),
39560
39854
  model: "",
@@ -39564,6 +39858,7 @@ var GoogleInteractionsAdapter = class {
39564
39858
  text,
39565
39859
  toolCalls,
39566
39860
  media,
39861
+ ...citations.length ? { citations } : {},
39567
39862
  thinking,
39568
39863
  latencyMs,
39569
39864
  raw
@@ -40905,13 +41200,14 @@ var OpenAIAdapter = class {
40905
41200
  content.push(parsed);
40906
41201
  toolCalls.push(parsed);
40907
41202
  }
40908
- const finishReason = extractFinishReason(
40909
- toolCalls.length > 0,
40910
- choice.finish_reason,
40911
- { tool_calls: "tool_use", length: "length", content_filter: "content_filter" }
40912
- );
41203
+ const finishReason = extractFinishReason(toolCalls.length > 0, choice.finish_reason, {
41204
+ tool_calls: "tool_use",
41205
+ length: "length",
41206
+ content_filter: "content_filter"
41207
+ });
40913
41208
  const reasoningContent = message.reasoning_content ?? null;
40914
41209
  const moderation = parseNativeModeration(r.moderation);
41210
+ const citations = extractCitations("completions", raw);
40915
41211
  return {
40916
41212
  id: r.id,
40917
41213
  model: r.model,
@@ -40921,6 +41217,7 @@ var OpenAIAdapter = class {
40921
41217
  text,
40922
41218
  toolCalls,
40923
41219
  media,
41220
+ ...citations.length ? { citations } : {},
40924
41221
  thinking: reasoningContent,
40925
41222
  ...moderation ? { moderation } : {},
40926
41223
  latencyMs,
@@ -40953,6 +41250,18 @@ var OpenAIAdapter = class {
40953
41250
  if (delta.content) {
40954
41251
  events.push({ type: "text", text: delta.content });
40955
41252
  }
41253
+ for (const note of delta.annotations ?? []) {
41254
+ const detail = note.url_citation ?? note;
41255
+ if (note.type === "url_citation" && detail.url) {
41256
+ events.push({
41257
+ type: "citation",
41258
+ citation: {
41259
+ url: detail.url,
41260
+ ...detail.title ? { title: detail.title } : {}
41261
+ }
41262
+ });
41263
+ }
41264
+ }
40956
41265
  const toolIdByIndex = state?.toolIdByIndex ?? /* @__PURE__ */ new Map();
40957
41266
  const toolCalls = delta.tool_calls ?? [];
40958
41267
  for (const tc of toolCalls) {
@@ -41863,6 +42172,7 @@ var OpenAIResponsesAdapter = class {
41863
42172
  if (text && content.length === 0) content.push({ type: "text", text });
41864
42173
  }
41865
42174
  const moderation = parseNativeModeration(r.moderation);
42175
+ const citations = extractCitations("responses", raw);
41866
42176
  return {
41867
42177
  id: r.id,
41868
42178
  model: r.model ?? "",
@@ -41873,6 +42183,7 @@ var OpenAIResponsesAdapter = class {
41873
42183
  toolCalls,
41874
42184
  thinking,
41875
42185
  media,
42186
+ ...citations.length ? { citations } : {},
41876
42187
  ...files.length ? { files } : {},
41877
42188
  ...builtinToolCalls.length ? { builtinToolCalls } : {},
41878
42189
  ...moderation ? { moderation } : {},
@@ -41885,6 +42196,19 @@ var OpenAIResponsesAdapter = class {
41885
42196
  const data = sseJson(event);
41886
42197
  const type = data.type;
41887
42198
  const events = [];
42199
+ if (type === "response.output_text.annotation.added") {
42200
+ const note = data.annotation ?? {};
42201
+ if (note.type === "url_citation" && note.url) {
42202
+ events.push({
42203
+ type: "citation",
42204
+ citation: {
42205
+ url: note.url,
42206
+ ...note.title ? { title: note.title } : {}
42207
+ }
42208
+ });
42209
+ }
42210
+ return events;
42211
+ }
41888
42212
  if (type === "response.output_text.delta") {
41889
42213
  const itemId = data.item_id;
41890
42214
  const phase = itemId ? phaseByItem?.get(itemId) : void 0;
@@ -42205,10 +42529,6 @@ var XAIBatchAdapter = class {
42205
42529
  throw new Error(`xAI batch create failed: ${JSON.stringify(createRes.body)}`);
42206
42530
  const batch2 = createRes.body ?? {};
42207
42531
  const batchId = batch2.batch_id ?? batch2.id;
42208
- const _batchRequests = requests.map((r) => ({
42209
- batch_request_id: r.customId,
42210
- batch_request: { endpoint: "responses", body: r.body }
42211
- }));
42212
42532
  const addRes = await fetch2(this.buildAddRequestsRequest(batchId, requests));
42213
42533
  if (addRes.status >= 400)
42214
42534
  throw new Error(`xAI batch add requests failed: ${JSON.stringify(addRes.body)}`);
@@ -43723,7 +44043,8 @@ function makeStepState() {
43723
44043
  stepToolCalls: [],
43724
44044
  toolCallAccum: /* @__PURE__ */ new Map(),
43725
44045
  stepUsage: emptyUsage(),
43726
- stepFinishReason: "stop"
44046
+ stepFinishReason: "stop",
44047
+ stepCitations: /* @__PURE__ */ new Map()
43727
44048
  };
43728
44049
  }
43729
44050
  function accumulateStreamEvent(event, state) {
@@ -43757,6 +44078,9 @@ function accumulateStreamEvent(event, state) {
43757
44078
  }
43758
44079
  return null;
43759
44080
  }
44081
+ case "citation":
44082
+ state.stepCitations.set(event.citation.url, event.citation);
44083
+ return null;
43760
44084
  case "usage":
43761
44085
  state.stepUsage = event.usage;
43762
44086
  return null;
@@ -43814,6 +44138,7 @@ function buildStepResponse(state, model, stepStart) {
43814
44138
  toolCalls: state.stepToolCalls,
43815
44139
  thinking: state.stepThinking || null,
43816
44140
  media: [],
44141
+ ...state.stepCitations.size ? { citations: [...state.stepCitations.values()] } : {},
43817
44142
  latencyMs: stepLatency,
43818
44143
  raw: null
43819
44144
  };
@@ -44155,6 +44480,8 @@ var AgentLoop = class _AgentLoop {
44155
44480
  // final LLM response (e.g. code-execution files produced during the run).
44156
44481
  ...last?.files ? { files: last.files } : {},
44157
44482
  ...last?.builtinToolCalls ? { builtinToolCalls: last.builtinToolCalls } : {},
44483
+ // Every step's sources, not just the final step's — see the accumulator.
44484
+ ...args.citations.length ? { citations: args.citations } : {},
44158
44485
  ...last?.moderation ? { moderation: last.moderation } : {},
44159
44486
  latencyMs: performance.now() - args.startPerf,
44160
44487
  raw: args.raw
@@ -44221,6 +44548,7 @@ var AgentLoop = class _AgentLoop {
44221
44548
  let stepCount = 0;
44222
44549
  let toolCallCount = 0;
44223
44550
  let lastResponse = null;
44551
+ const citationsByUrl = /* @__PURE__ */ new Map();
44224
44552
  let reason = "done";
44225
44553
  let errorMsg;
44226
44554
  let caughtError;
@@ -44262,6 +44590,7 @@ var AgentLoop = class _AgentLoop {
44262
44590
  const stepLatency = performance.now() - stepStart;
44263
44591
  totalLlmTimeMs += stepLatency;
44264
44592
  addUsage(totalUsage, lastResponse.usage);
44593
+ for (const cite of lastResponse.citations ?? []) citationsByUrl.set(cite.url, cite);
44265
44594
  if (this._reflectRetry?.handles(lastResponse.finishReason)) {
44266
44595
  const verdict = this._reflectRetry.recordFailure();
44267
44596
  await this.hooks.emit("onWarning", {
@@ -44369,6 +44698,7 @@ var AgentLoop = class _AgentLoop {
44369
44698
  totalUsage,
44370
44699
  lastResponse,
44371
44700
  media: lastResponse?.media ?? [],
44701
+ citations: [...citationsByUrl.values()],
44372
44702
  raw: lastResponse?.raw ?? null
44373
44703
  });
44374
44704
  await this.settleRun({
@@ -44414,6 +44744,7 @@ var AgentLoop = class _AgentLoop {
44414
44744
  let finalText = "";
44415
44745
  let finalContent = [];
44416
44746
  let lastResponse = null;
44747
+ const citationsByUrl = /* @__PURE__ */ new Map();
44417
44748
  let reason = "done";
44418
44749
  let errorMsg;
44419
44750
  let caughtError;
@@ -44468,6 +44799,7 @@ var AgentLoop = class _AgentLoop {
44468
44799
  finalContent = content;
44469
44800
  finalText = state.stepText;
44470
44801
  lastResponse = stepResponse;
44802
+ for (const cite of stepResponse.citations ?? []) citationsByUrl.set(cite.url, cite);
44471
44803
  this._history.append(
44472
44804
  {
44473
44805
  ...buildAssistantMessage(lastResponse, {
@@ -44551,6 +44883,7 @@ var AgentLoop = class _AgentLoop {
44551
44883
  finalContent,
44552
44884
  totalUsage,
44553
44885
  lastResponse,
44886
+ citations: [...citationsByUrl.values()],
44554
44887
  // The streaming path already emitted media as events, and never holds a
44555
44888
  // raw provider payload.
44556
44889
  media: [],
@@ -54544,37 +54877,26 @@ function listModels(opts = {}) {
54544
54877
  var LIVE = {
54545
54878
  openai: {
54546
54879
  url: "https://api.openai.com/v1/models",
54547
- headers: (k) => ({ authorization: `Bearer ${k}` }),
54548
54880
  items: (b) => b.data ?? [],
54549
54881
  id: (m) => m.id
54550
54882
  },
54551
54883
  openrouter: {
54552
54884
  url: "https://openrouter.ai/api/v1/models",
54553
- headers: (k) => ({ authorization: `Bearer ${k}` }),
54554
54885
  items: (b) => b.data ?? [],
54555
54886
  id: (m) => m.id
54556
54887
  },
54557
54888
  xai: {
54558
54889
  url: "https://api.x.ai/v1/models",
54559
- headers: (k) => ({ authorization: `Bearer ${k}` }),
54560
54890
  items: (b) => b.data ?? [],
54561
54891
  id: (m) => m.id
54562
54892
  },
54563
54893
  anthropic: {
54564
54894
  url: "https://api.anthropic.com/v1/models",
54565
- // Mirror the chat adapter: Anthropic rejects browser requests without the
54566
- // explicit opt-in header, so the /models call must send it too.
54567
- headers: (k) => ({
54568
- "x-api-key": k,
54569
- "anthropic-version": ANTHROPIC_API_VERSION,
54570
- ...isBrowser() ? { "anthropic-dangerous-direct-browser-access": "true" } : {}
54571
- }),
54572
54895
  items: (b) => b.data ?? [],
54573
54896
  id: (m) => m.id
54574
54897
  },
54575
54898
  google: {
54576
54899
  url: "https://generativelanguage.googleapis.com/v1beta/models",
54577
- headers: (k) => ({ "x-goog-api-key": k }),
54578
54900
  items: (b) => b.models ?? [],
54579
54901
  id: (m) => m.name.replace(/^models\//, "")
54580
54902
  }
@@ -54667,7 +54989,9 @@ async function fetchLiveBody(opts) {
54667
54989
  const p = (async () => {
54668
54990
  const built = buildFromSpec(
54669
54991
  utilitySpec(`${opts.provider}/models.list`),
54670
- {},
54992
+ // Anthropic refuses a browser request without an explicit opt-in header,
54993
+ // and the spec asks for `browser` to decide whether to send it.
54994
+ { browser: isBrowser() },
54671
54995
  makeRegistry({}),
54672
54996
  opts.provider,
54673
54997
  void 0,
@@ -54743,6 +55067,35 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
54743
55067
  ...Object.keys(CAP_KEYS),
54744
55068
  ...Object.keys(BUILTIN_TOOL_KEYS)
54745
55069
  ]);
55070
+ function filterFacets(catalog) {
55071
+ const models = catalog?.list() ?? [];
55072
+ const distinct = (pick) => [...new Set(models.map(pick).filter((v) => !!v))].sort();
55073
+ const facets = [
55074
+ { key: "type", category: "what it is", label: "Type", values: distinct((m) => m.type), numeric: false, bare: false },
55075
+ { key: "provider", category: "what it is", label: "Provider", values: distinct((m) => m.provider), numeric: false, bare: false },
55076
+ // The named cutoffs the parser compares against, plus `free` for exactly zero.
55077
+ { key: "price", category: "cost", label: "Price", values: ["free", "low", "mid", "high"], numeric: true, bare: false },
55078
+ // filled below from the models themselves — tiers are per model, not a fixed set
55079
+ { key: "tier", category: "cost", label: "Billing tier", values: [], numeric: false, bare: false },
55080
+ { key: "context", category: "inputs", label: "Context window", values: ["small", "large"], numeric: true, bare: false },
55081
+ { key: "reasoning", category: "thinking", label: "Reasoning", values: ["yes", "no"], numeric: false, bare: true },
55082
+ { key: "status", category: "availability", label: "Status", values: distinct((m) => m.status), numeric: false, bare: false },
55083
+ { key: "active", category: "availability", label: "Offered now", values: ["yes", "no"], numeric: false, bare: true }
55084
+ ];
55085
+ for (const key of Object.keys(CAP_KEYS)) {
55086
+ facets.push({ key, category: "inputs", label: key, values: ["yes", "no"], numeric: false, bare: true });
55087
+ }
55088
+ for (const key of Object.keys(BUILTIN_TOOL_KEYS)) {
55089
+ facets.push({ key, category: "hosted tools", label: key.replace(/_/g, " "), values: ["yes", "no"], numeric: false, bare: true });
55090
+ }
55091
+ const tiers = [...new Set(models.flatMap((m) => Object.keys(m.pricing?.tiers ?? {})))].sort();
55092
+ const tierFacet = facets.find((f) => f.key === "tier");
55093
+ if (tierFacet) tierFacet.values = tiers;
55094
+ return facets;
55095
+ }
55096
+ function filterAliases() {
55097
+ return { ...DEFAULT_TAGS };
55098
+ }
54746
55099
  function parseNum(v) {
54747
55100
  const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
54748
55101
  if (!m) return Number.NaN;
@@ -54864,6 +55217,7 @@ function select(query, opts = {}) {
54864
55217
  function createRealtime(opts) {
54865
55218
  const engine = opts.engine ?? coreRegistry.get();
54866
55219
  const { provider, model } = resolveModel(opts.model, opts.provider, "createRealtime");
55220
+ const sendModel = engine.catalog.resolveModelId(provider, model);
54867
55221
  const apiKey = opts.apiKey ?? engine.apiKeys[provider];
54868
55222
  if (!apiKey) {
54869
55223
  throw new Error(
@@ -54873,11 +55227,11 @@ function createRealtime(opts) {
54873
55227
  const adapter = resolveAdapter2(provider, apiKey);
54874
55228
  const voice = resolveVoice(provider, opts.audio?.voice ?? opts.voice);
54875
55229
  const session = adapter.connect(
54876
- { model, modalities: opts.modalities, voice, instructions: opts.instructions },
55230
+ { model: sendModel, modalities: opts.modalities, voice, instructions: opts.instructions },
54877
55231
  engine.connect
54878
55232
  );
54879
55233
  session.on("usage", (e) => {
54880
- void engine.hooks.emit("onCompletion", realtimeCompletionContext(provider, model, e.usage)).catch(() => {
55234
+ void engine.hooks.emit("onCompletion", realtimeCompletionContext(provider, sendModel, e.usage)).catch(() => {
54881
55235
  });
54882
55236
  });
54883
55237
  return session;
@@ -57248,6 +57602,8 @@ export {
57248
57602
  exchangeCode,
57249
57603
  extractLastUserText,
57250
57604
  extractSystemText,
57605
+ filterAliases,
57606
+ filterFacets,
57251
57607
  finalAnswerText,
57252
57608
  finishMcpAuth,
57253
57609
  formatBulletedList,