dsh-ide-git 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +334 -0
- package/src/index.js +29 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ide-git",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/client.js
CHANGED
|
@@ -302,6 +302,12 @@ window.__ModuleLoader__.load({
|
|
|
302
302
|
'composer.changesHint': '未提交变更数',
|
|
303
303
|
'composer.fileHint': '新增 {a} · 删除 {d} 个文件',
|
|
304
304
|
'composer.linesHint': '+{ins} −{del} 行',
|
|
305
|
+
'modelRoute.pick': '选择模型',
|
|
306
|
+
'modelRoute.trigger': 'AI 提交信息模型',
|
|
307
|
+
'modelRoute.modelRow': '模型',
|
|
308
|
+
'modelRoute.effortRow': '思考',
|
|
309
|
+
'modelRoute.effortDefault': '跟随默认',
|
|
310
|
+
'modelRoute.empty': '暂无可用模型',
|
|
305
311
|
'composer.behindHint': '远程新提交(可拉取)',
|
|
306
312
|
'composer.aheadHint': '本地待推送提交',
|
|
307
313
|
'composer.inWorktree': '已被其他工作树签出',
|
|
@@ -638,6 +644,12 @@ window.__ModuleLoader__.load({
|
|
|
638
644
|
'composer.changesHint': 'Uncommitted changes',
|
|
639
645
|
'composer.fileHint': '{a} added · {d} deleted files',
|
|
640
646
|
'composer.linesHint': '+{ins} −{del} lines',
|
|
647
|
+
'modelRoute.pick': 'Pick a model',
|
|
648
|
+
'modelRoute.trigger': 'AI commit message model',
|
|
649
|
+
'modelRoute.modelRow': 'Model',
|
|
650
|
+
'modelRoute.effortRow': 'Reasoning',
|
|
651
|
+
'modelRoute.effortDefault': 'Follow default',
|
|
652
|
+
'modelRoute.empty': 'No models available',
|
|
641
653
|
'composer.behindHint': 'New upstream commits (pullable)',
|
|
642
654
|
'composer.aheadHint': 'Local commits waiting to push',
|
|
643
655
|
'composer.inWorktree': 'Checked out in another worktree',
|
|
@@ -982,6 +994,12 @@ window.__ModuleLoader__.load({
|
|
|
982
994
|
'composer.changesHint': '未提交變更數',
|
|
983
995
|
'composer.fileHint': '新增 {a} · 刪除 {d} 個檔案',
|
|
984
996
|
'composer.linesHint': '+{ins} −{del} 行',
|
|
997
|
+
'modelRoute.pick': '選擇模型',
|
|
998
|
+
'modelRoute.trigger': 'AI 提交訊息模型',
|
|
999
|
+
'modelRoute.modelRow': '模型',
|
|
1000
|
+
'modelRoute.effortRow': '思考',
|
|
1001
|
+
'modelRoute.effortDefault': '跟隨預設',
|
|
1002
|
+
'modelRoute.empty': '暫無可用模型',
|
|
985
1003
|
'composer.behindHint': '遠端新提交(可以拉取)',
|
|
986
1004
|
'composer.aheadHint': '本地等待推送提交',
|
|
987
1005
|
'composer.inWorktree': '已經喺其他工作樹簽出',
|
|
@@ -1318,6 +1336,12 @@ window.__ModuleLoader__.load({
|
|
|
1318
1336
|
'composer.changesHint': '未提交變更數',
|
|
1319
1337
|
'composer.fileHint': '新增 {a} · 刪除 {d} 個檔案',
|
|
1320
1338
|
'composer.linesHint': '+{ins} −{del} 行',
|
|
1339
|
+
'modelRoute.pick': '選擇模型',
|
|
1340
|
+
'modelRoute.trigger': 'AI 提交訊息模型',
|
|
1341
|
+
'modelRoute.modelRow': '模型',
|
|
1342
|
+
'modelRoute.effortRow': '思考',
|
|
1343
|
+
'modelRoute.effortDefault': '跟隨預設',
|
|
1344
|
+
'modelRoute.empty': '暫無可用模型',
|
|
1321
1345
|
'composer.behindHint': '遠端新提交(可拉取)',
|
|
1322
1346
|
'composer.aheadHint': '本地待推送提交',
|
|
1323
1347
|
'composer.inWorktree': '已被其他工作樹簽出',
|
|
@@ -1654,6 +1678,12 @@ window.__ModuleLoader__.load({
|
|
|
1654
1678
|
'composer.changesHint': '未提交變更數',
|
|
1655
1679
|
'composer.fileHint': '新增 {a} · 刪除 {d} 個檔案',
|
|
1656
1680
|
'composer.linesHint': '+{ins} −{del} 行',
|
|
1681
|
+
'modelRoute.pick': '選擇模型',
|
|
1682
|
+
'modelRoute.trigger': 'AI 提交訊息模型',
|
|
1683
|
+
'modelRoute.modelRow': '模型',
|
|
1684
|
+
'modelRoute.effortRow': '思考',
|
|
1685
|
+
'modelRoute.effortDefault': '跟隨預設',
|
|
1686
|
+
'modelRoute.empty': '暫無可用模型',
|
|
1657
1687
|
'composer.behindHint': '遠端新提交(可以拉取)',
|
|
1658
1688
|
'composer.aheadHint': '本地等待推送提交',
|
|
1659
1689
|
'composer.inWorktree': '已經喺其他工作樹簽出',
|
|
@@ -1990,6 +2020,12 @@ window.__ModuleLoader__.load({
|
|
|
1990
2020
|
'composer.changesHint': '未コミットの変更数',
|
|
1991
2021
|
'composer.fileHint': '追加 {a} · 削除 {d} ファイル',
|
|
1992
2022
|
'composer.linesHint': '+{ins} −{del} 行',
|
|
2023
|
+
'modelRoute.pick': 'モデルを選択',
|
|
2024
|
+
'modelRoute.trigger': 'AI コミットメッセージのモデル',
|
|
2025
|
+
'modelRoute.modelRow': 'モデル',
|
|
2026
|
+
'modelRoute.effortRow': '思考',
|
|
2027
|
+
'modelRoute.effortDefault': 'デフォルトに従う',
|
|
2028
|
+
'modelRoute.empty': '利用可能なモデルがありません',
|
|
1993
2029
|
'composer.behindHint': 'リモートの新規コミット(取得可能)',
|
|
1994
2030
|
'composer.aheadHint': 'プッシュ待ちのローカルコミット',
|
|
1995
2031
|
'composer.inWorktree': '別のワークツリーでチェックアウト中',
|
|
@@ -2326,6 +2362,12 @@ window.__ModuleLoader__.load({
|
|
|
2326
2362
|
'composer.changesHint': '커밋되지 않은 변경 수',
|
|
2327
2363
|
'composer.fileHint': '추가 {a} · 삭제 {d}개 파일',
|
|
2328
2364
|
'composer.linesHint': '+{ins} −{del}줄',
|
|
2365
|
+
'modelRoute.pick': '모델 선택',
|
|
2366
|
+
'modelRoute.trigger': 'AI 커밋 메시지 모델',
|
|
2367
|
+
'modelRoute.modelRow': '모델',
|
|
2368
|
+
'modelRoute.effortRow': '추론',
|
|
2369
|
+
'modelRoute.effortDefault': '기본값 따르기',
|
|
2370
|
+
'modelRoute.empty': '사용 가능한 모델이 없습니다',
|
|
2329
2371
|
'composer.behindHint': '원격 새 커밋(가져오기 가능)',
|
|
2330
2372
|
'composer.aheadHint': '푸시 대기 중인 로컬 커밋',
|
|
2331
2373
|
'composer.inWorktree': '다른 작업 트리에서 체크아웃됨',
|
|
@@ -2662,6 +2704,12 @@ window.__ModuleLoader__.load({
|
|
|
2662
2704
|
'composer.changesHint': 'Uncommittete Änderungen',
|
|
2663
2705
|
'composer.fileHint': '{a} hinzugefügt · {d} gelöscht',
|
|
2664
2706
|
'composer.linesHint': '+{ins} −{del} Zeilen',
|
|
2707
|
+
'modelRoute.pick': 'Modell wählen',
|
|
2708
|
+
'modelRoute.trigger': 'Modell für KI-Commits',
|
|
2709
|
+
'modelRoute.modelRow': 'Modell',
|
|
2710
|
+
'modelRoute.effortRow': 'Denken',
|
|
2711
|
+
'modelRoute.effortDefault': 'Standard folgen',
|
|
2712
|
+
'modelRoute.empty': 'Keine Modelle verfügbar',
|
|
2665
2713
|
'composer.behindHint': 'Neue Upstream-Commits (abrufbar)',
|
|
2666
2714
|
'composer.aheadHint': 'Lokale Commits zum Pushen',
|
|
2667
2715
|
'composer.inWorktree': 'In anderem Worktree ausgecheckt',
|
|
@@ -2998,6 +3046,12 @@ window.__ModuleLoader__.load({
|
|
|
2998
3046
|
'composer.changesHint': 'Modifications non commitées',
|
|
2999
3047
|
'composer.fileHint': '{a} ajoutés · {d} supprimés',
|
|
3000
3048
|
'composer.linesHint': '+{ins} −{del} lignes',
|
|
3049
|
+
'modelRoute.pick': 'Choisir un modèle',
|
|
3050
|
+
'modelRoute.trigger': 'Modèle des messages de commit IA',
|
|
3051
|
+
'modelRoute.modelRow': 'Modèle',
|
|
3052
|
+
'modelRoute.effortRow': 'Raisonnement',
|
|
3053
|
+
'modelRoute.effortDefault': 'Suivre la valeur par défaut',
|
|
3054
|
+
'modelRoute.empty': 'Aucun modèle disponible',
|
|
3001
3055
|
'composer.behindHint': 'Nouveaux commits amont (récupérables)',
|
|
3002
3056
|
'composer.aheadHint': 'Commits locaux en attente d’envoi',
|
|
3003
3057
|
'composer.inWorktree': 'Extrait dans un autre arbre de travail',
|
|
@@ -3334,6 +3388,12 @@ window.__ModuleLoader__.load({
|
|
|
3334
3388
|
'composer.changesHint': 'Незакоммиченные изменения',
|
|
3335
3389
|
'composer.fileHint': 'добавлено: {a} · удалено: {d}',
|
|
3336
3390
|
'composer.linesHint': '+{ins} −{del} строк',
|
|
3391
|
+
'modelRoute.pick': 'Выбрать модель',
|
|
3392
|
+
'modelRoute.trigger': 'Модель сообщений коммитов ИИ',
|
|
3393
|
+
'modelRoute.modelRow': 'Модель',
|
|
3394
|
+
'modelRoute.effortRow': 'Рассуждение',
|
|
3395
|
+
'modelRoute.effortDefault': 'По умолчанию',
|
|
3396
|
+
'modelRoute.empty': 'Нет доступных моделей',
|
|
3337
3397
|
'composer.behindHint': 'Новые коммиты upstream (можно забрать)',
|
|
3338
3398
|
'composer.aheadHint': 'Локальные коммиты, ожидающие отправки',
|
|
3339
3399
|
'composer.inWorktree': 'Выверен в другом worktree',
|
|
@@ -3670,6 +3730,12 @@ window.__ModuleLoader__.load({
|
|
|
3670
3730
|
'composer.changesHint': 'Alterações não commitadas',
|
|
3671
3731
|
'composer.fileHint': '{a} adicionados · {d} removidos',
|
|
3672
3732
|
'composer.linesHint': '+{ins} −{del} linhas',
|
|
3733
|
+
'modelRoute.pick': 'Escolher modelo',
|
|
3734
|
+
'modelRoute.trigger': 'Modelo da mensagem de commit por IA',
|
|
3735
|
+
'modelRoute.modelRow': 'Modelo',
|
|
3736
|
+
'modelRoute.effortRow': 'Raciocínio',
|
|
3737
|
+
'modelRoute.effortDefault': 'Seguir o padrão',
|
|
3738
|
+
'modelRoute.empty': 'Nenhum modelo disponível',
|
|
3673
3739
|
'composer.behindHint': 'Novos commits remotos (para pull)',
|
|
3674
3740
|
'composer.aheadHint': 'Commits locais aguardando push',
|
|
3675
3741
|
'composer.inWorktree': 'Em checkout em outra worktree',
|
|
@@ -4006,6 +4072,12 @@ window.__ModuleLoader__.load({
|
|
|
4006
4072
|
'composer.changesHint': 'Modifiche non ancora committate',
|
|
4007
4073
|
'composer.fileHint': '{a} aggiunti · {d} eliminati',
|
|
4008
4074
|
'composer.linesHint': '+{ins} −{del} righe',
|
|
4075
|
+
'modelRoute.pick': 'Scegli modello',
|
|
4076
|
+
'modelRoute.trigger': 'Modello dei messaggi di commit IA',
|
|
4077
|
+
'modelRoute.modelRow': 'Modello',
|
|
4078
|
+
'modelRoute.effortRow': 'Ragionamento',
|
|
4079
|
+
'modelRoute.effortDefault': 'Segui il predefinito',
|
|
4080
|
+
'modelRoute.empty': 'Nessun modello disponibile',
|
|
4009
4081
|
'composer.behindHint': 'Nuovi commit upstream (scaricabili)',
|
|
4010
4082
|
'composer.aheadHint': 'Commit locali in attesa di push',
|
|
4011
4083
|
'composer.inWorktree': 'Estratto in un altro worktree',
|
|
@@ -4342,6 +4414,12 @@ window.__ModuleLoader__.load({
|
|
|
4342
4414
|
'composer.changesHint': 'Ongecommitte wijzigingen',
|
|
4343
4415
|
'composer.fileHint': '{a} toegevoegd · {d} verwijderd',
|
|
4344
4416
|
'composer.linesHint': '+{ins} −{del} regels',
|
|
4417
|
+
'modelRoute.pick': 'Kies een model',
|
|
4418
|
+
'modelRoute.trigger': 'Model voor AI-commitberichten',
|
|
4419
|
+
'modelRoute.modelRow': 'Model',
|
|
4420
|
+
'modelRoute.effortRow': 'Redeneren',
|
|
4421
|
+
'modelRoute.effortDefault': 'Standaard volgen',
|
|
4422
|
+
'modelRoute.empty': 'Geen modellen beschikbaar',
|
|
4345
4423
|
'composer.behindHint': 'Nieuwe upstream-commits (op te halen)',
|
|
4346
4424
|
'composer.aheadHint': 'Lokale commits die wachten op push',
|
|
4347
4425
|
'composer.inWorktree': 'Uitgecheckt in een andere worktree',
|
|
@@ -4678,6 +4756,12 @@ window.__ModuleLoader__.load({
|
|
|
4678
4756
|
'composer.changesHint': 'Niezatwierdzone zmiany',
|
|
4679
4757
|
'composer.fileHint': 'dodane: {a} · usunięte: {d}',
|
|
4680
4758
|
'composer.linesHint': '+{ins} −{del} linii',
|
|
4759
|
+
'modelRoute.pick': 'Wybierz model',
|
|
4760
|
+
'modelRoute.trigger': 'Model wiadomości commitów AI',
|
|
4761
|
+
'modelRoute.modelRow': 'Model',
|
|
4762
|
+
'modelRoute.effortRow': 'Rozumowanie',
|
|
4763
|
+
'modelRoute.effortDefault': 'Domyślne',
|
|
4764
|
+
'modelRoute.empty': 'Brak dostępnych modeli',
|
|
4681
4765
|
'composer.behindHint': 'Nowe commity upstream (do pobrania)',
|
|
4682
4766
|
'composer.aheadHint': 'Lokalne commity oczekujące na wysłanie',
|
|
4683
4767
|
'composer.inWorktree': 'Wybrane w innym drzewie roboczym',
|
|
@@ -5014,6 +5098,12 @@ window.__ModuleLoader__.load({
|
|
|
5014
5098
|
'composer.changesHint': 'Ocommittade ändringar',
|
|
5015
5099
|
'composer.fileHint': '{a} tillagda · {d} borttagna',
|
|
5016
5100
|
'composer.linesHint': '+{ins} −{del} rader',
|
|
5101
|
+
'modelRoute.pick': 'Välj modell',
|
|
5102
|
+
'modelRoute.trigger': 'Modell för AI-commit-meddelanden',
|
|
5103
|
+
'modelRoute.modelRow': 'Modell',
|
|
5104
|
+
'modelRoute.effortRow': 'Resonemang',
|
|
5105
|
+
'modelRoute.effortDefault': 'Följ standard',
|
|
5106
|
+
'modelRoute.empty': 'Inga modeller tillgängliga',
|
|
5017
5107
|
'composer.behindHint': 'Nya upstream-commits (hämtbara)',
|
|
5018
5108
|
'composer.aheadHint': 'Lokala commits som väntar på push',
|
|
5019
5109
|
'composer.inWorktree': 'Utcheckad i ett annat arbetsträd',
|
|
@@ -5350,6 +5440,12 @@ window.__ModuleLoader__.load({
|
|
|
5350
5440
|
'composer.changesHint': 'Commitlenmemiş değişiklikler',
|
|
5351
5441
|
'composer.fileHint': '{a} eklendi · {d} silindi',
|
|
5352
5442
|
'composer.linesHint': '+{ins} −{del} satır',
|
|
5443
|
+
'modelRoute.pick': 'Model seç',
|
|
5444
|
+
'modelRoute.trigger': 'Yapay zekâ commit mesajı modeli',
|
|
5445
|
+
'modelRoute.modelRow': 'Model',
|
|
5446
|
+
'modelRoute.effortRow': 'Akıl yürütme',
|
|
5447
|
+
'modelRoute.effortDefault': 'Varsayılanı izle',
|
|
5448
|
+
'modelRoute.empty': 'Kullanılabilir model yok',
|
|
5353
5449
|
'composer.behindHint': 'Yeni upstream commitleri (çekilebilir)',
|
|
5354
5450
|
'composer.aheadHint': 'Push bekleyen yerel commitler',
|
|
5355
5451
|
'composer.inWorktree': 'Başka bir worktree’da checkout yapılmış',
|
|
@@ -5686,6 +5782,12 @@ window.__ModuleLoader__.load({
|
|
|
5686
5782
|
'composer.changesHint': 'Perubahan yang belum di-commit',
|
|
5687
5783
|
'composer.fileHint': '{a} ditambahkan · {d} dihapus',
|
|
5688
5784
|
'composer.linesHint': '+{ins} −{del} baris',
|
|
5785
|
+
'modelRoute.pick': 'Pilih model',
|
|
5786
|
+
'modelRoute.trigger': 'Model pesan commit AI',
|
|
5787
|
+
'modelRoute.modelRow': 'Model',
|
|
5788
|
+
'modelRoute.effortRow': 'Penalaran',
|
|
5789
|
+
'modelRoute.effortDefault': 'Ikuti bawaan',
|
|
5790
|
+
'modelRoute.empty': 'Tidak ada model yang tersedia',
|
|
5689
5791
|
'composer.behindHint': 'Commit upstream baru (bisa ditarik)',
|
|
5690
5792
|
'composer.aheadHint': 'Commit lokal menunggu push',
|
|
5691
5793
|
'composer.inWorktree': 'Sudah di-checkout di worktree lain',
|
|
@@ -6022,6 +6124,12 @@ window.__ModuleLoader__.load({
|
|
|
6022
6124
|
'composer.changesHint': 'Thay đổi chưa commit',
|
|
6023
6125
|
'composer.fileHint': 'thêm {a} · xóa {d} tệp',
|
|
6024
6126
|
'composer.linesHint': '+{ins} −{del} dòng',
|
|
6127
|
+
'modelRoute.pick': 'Chọn mô hình',
|
|
6128
|
+
'modelRoute.trigger': 'Mô hình tin nhắn commit AI',
|
|
6129
|
+
'modelRoute.modelRow': 'Mô hình',
|
|
6130
|
+
'modelRoute.effortRow': 'Suy luận',
|
|
6131
|
+
'modelRoute.effortDefault': 'Theo mặc định',
|
|
6132
|
+
'modelRoute.empty': 'Không có mô hình nào',
|
|
6025
6133
|
'composer.behindHint': 'Commit mới từ xa (có thể kéo về)',
|
|
6026
6134
|
'composer.aheadHint': 'Commit cục bộ chờ đẩy lên',
|
|
6027
6135
|
'composer.inWorktree': 'Đã được checkout ở worktree khác',
|
|
@@ -6358,6 +6466,12 @@ window.__ModuleLoader__.load({
|
|
|
6358
6466
|
'composer.changesHint': 'تغييرات غير ملتزَم بها',
|
|
6359
6467
|
'composer.fileHint': 'مضاف {a} · محذوف {d}',
|
|
6360
6468
|
'composer.linesHint': '+{ins} −{del} أسطر',
|
|
6469
|
+
'modelRoute.pick': 'اختر نموذجًا',
|
|
6470
|
+
'modelRoute.trigger': 'نموذج رسائل الالتزام بالذكاء الاصطناعي',
|
|
6471
|
+
'modelRoute.modelRow': 'النموذج',
|
|
6472
|
+
'modelRoute.effortRow': 'الاستدلال',
|
|
6473
|
+
'modelRoute.effortDefault': 'اتبع الافتراضي',
|
|
6474
|
+
'modelRoute.empty': 'لا توجد نماذج متاحة',
|
|
6361
6475
|
'composer.behindHint': 'التزامات جديدة في المستودع البعيد (قابلة للسحب)',
|
|
6362
6476
|
'composer.aheadHint': 'التزامات محلية بانتظار الدفع',
|
|
6363
6477
|
'composer.inWorktree': 'مسحوب في شجرة عمل أخرى',
|
|
@@ -6694,6 +6808,12 @@ window.__ModuleLoader__.load({
|
|
|
6694
6808
|
'composer.changesHint': 'बिना कमिट बदलाव',
|
|
6695
6809
|
'composer.fileHint': '{a} जोड़ी गईं · {d} हटाई गईं',
|
|
6696
6810
|
'composer.linesHint': '+{ins} −{del} पंक्तियाँ',
|
|
6811
|
+
'modelRoute.pick': 'मॉडल चुनें',
|
|
6812
|
+
'modelRoute.trigger': 'AI कमिट संदेश मॉडल',
|
|
6813
|
+
'modelRoute.modelRow': 'मॉडल',
|
|
6814
|
+
'modelRoute.effortRow': 'तर्क',
|
|
6815
|
+
'modelRoute.effortDefault': 'डिफ़ॉल्ट का पालन करें',
|
|
6816
|
+
'modelRoute.empty': 'कोई मॉडल उपलब्ध नहीं',
|
|
6697
6817
|
'composer.behindHint': 'अपस्ट्रीम के नए कमिट (खींचने योग्य)',
|
|
6698
6818
|
'composer.aheadHint': 'पुश के लिए प्रतीक्षारत स्थानीय कमिट',
|
|
6699
6819
|
'composer.inWorktree': 'दूसरे वर्कट्री में चेकआउट',
|
|
@@ -7030,6 +7150,12 @@ window.__ModuleLoader__.load({
|
|
|
7030
7150
|
'composer.changesHint': 'การเปลี่ยนแปลงที่ยังไม่คอมมิต',
|
|
7031
7151
|
'composer.fileHint': 'เพิ่ม {a} · ลบ {d} ไฟล์',
|
|
7032
7152
|
'composer.linesHint': '+{ins} −{del} บรรทัด',
|
|
7153
|
+
'modelRoute.pick': 'เลือกโมเดล',
|
|
7154
|
+
'modelRoute.trigger': 'โมเดลข้อความคอมมิต AI',
|
|
7155
|
+
'modelRoute.modelRow': 'โมเดล',
|
|
7156
|
+
'modelRoute.effortRow': 'การใช้เหตุผล',
|
|
7157
|
+
'modelRoute.effortDefault': 'ตามค่าเริ่มต้น',
|
|
7158
|
+
'modelRoute.empty': 'ไม่มีโมเดลที่ใช้ได้',
|
|
7033
7159
|
'composer.behindHint': 'คอมมิตใหม่จากรีโมต (ดึงได้)',
|
|
7034
7160
|
'composer.aheadHint': 'คอมมิตในเครื่องรอพุช',
|
|
7035
7161
|
'composer.inWorktree': 'ถูกเช็คเอาต์ในเวิร์กทรีอื่น',
|
|
@@ -11060,6 +11186,9 @@ window.__ModuleLoader__.load({
|
|
|
11060
11186
|
function ChangesPanel(props) {
|
|
11061
11187
|
const t = props.t
|
|
11062
11188
|
const summary = props.summary
|
|
11189
|
+
/* The panel-side model picker (issue #19) rides the official model
|
|
11190
|
+
directories, soft-injected into a module ref at apply time — see
|
|
11191
|
+
ModelRoutePicker for why a bare ctx property read cannot work. */
|
|
11063
11192
|
const [message, setMessage] = useState('')
|
|
11064
11193
|
const [amend, setAmend] = useState(false)
|
|
11065
11194
|
const [collapsed, setCollapsed] = useState(() => {
|
|
@@ -11358,6 +11487,7 @@ window.__ModuleLoader__.load({
|
|
|
11358
11487
|
title: t('changes.aiQuota'),
|
|
11359
11488
|
onClick: () => { if (message.trim() === '') void aiRun(); else setAiAsk(true) },
|
|
11360
11489
|
}, aiBusy === true ? t('changes.aiBusy') : t('changes.aiWrite')),
|
|
11490
|
+
E(ModelRoutePicker, { sessionId: props.sessionId, t: t }),
|
|
11361
11491
|
E('button', {
|
|
11362
11492
|
type: 'button', className: 'dig-btn dig-btn-primary dig-btn-small',
|
|
11363
11493
|
disabled: message.trim() === '' || props.busy === true,
|
|
@@ -11379,6 +11509,7 @@ window.__ModuleLoader__.load({
|
|
|
11379
11509
|
disabled: aiBusy === true || props.busy === true,
|
|
11380
11510
|
onClick: () => { if (message.trim() === '') void aiRun(); else setAiAsk(true) },
|
|
11381
11511
|
}, aiBusy === true ? t('changes.aiBusy') : t('changes.aiWrite')),
|
|
11512
|
+
E(ModelRoutePicker, { sessionId: props.sessionId, t: t }),
|
|
11382
11513
|
E('span', { className: 'dig-commit-ai-hint' }, t('changes.aiQuota')),
|
|
11383
11514
|
aiUndo === null ? null : E('button', {
|
|
11384
11515
|
type: 'button', className: 'dig-link',
|
|
@@ -15200,6 +15331,22 @@ window.__ModuleLoader__.load({
|
|
|
15200
15331
|
'.dig-cs-item-note{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}',
|
|
15201
15332
|
'.dig-cs-item-counts{display:inline-flex;gap:5px;margin-left:auto;flex:none;font-variant-numeric:tabular-nums;font-size:11px}',
|
|
15202
15333
|
'.dig-cs-static{color:var(--dsw-alias-label-tertiary);cursor:default}',
|
|
15334
|
+
/* ---- ModelRoutePicker (issue #19) ---- */
|
|
15335
|
+
'.dig-mrp-root{position:relative;display:inline-flex;flex:none;min-width:0}',
|
|
15336
|
+
'.dig-mrp-trigger{display:inline-flex;align-items:center;gap:4px;min-width:0;max-width:220px;font-size:11px;line-height:16px}',
|
|
15337
|
+
'.dig-mrp-trigger[data-open=true]{color:var(--dsw-alias-brand-primary)}',
|
|
15338
|
+
'.dig-mrp-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
15339
|
+
'.dig-mrp-effort{flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:10px}',
|
|
15340
|
+
'.dig-mrp-menu{max-width:300px}',
|
|
15341
|
+
'.dig-mrp-row .dig-cs-item-name{display:none}',
|
|
15342
|
+
'.dig-mrp-row-label{flex:none}',
|
|
15343
|
+
'.dig-mrp-row-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-left:auto;color:var(--dsw-alias-label-tertiary)}',
|
|
15344
|
+
'.dig-mrp-row-arrow{flex:none;display:inline-flex;color:var(--dsw-alias-label-tertiary)}',
|
|
15345
|
+
'.dig-mrp-back{color:var(--dsw-alias-label-tertiary)}',
|
|
15346
|
+
'.dig-mrp-group{padding:6px 10px 2px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:14px}',
|
|
15347
|
+
'.dig-mrp-empty{padding:10px;color:var(--dsw-alias-label-tertiary);font-size:12px}',
|
|
15348
|
+
'.dig-mrp-error{padding:6px 10px;margin-bottom:2px;border-radius:var(--dsw-radius-sm,8px);background:rgba(220,38,38,.12);color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px;word-break:break-word}',
|
|
15349
|
+
'body[data-ds-dark-theme] .dig-mrp-error{background:rgba(248,113,113,.16)}',
|
|
15203
15350
|
].concat(LANE_TRACE_RULES).join('\n')
|
|
15204
15351
|
|
|
15205
15352
|
/* ============================== plugin ============================== */
|
|
@@ -15429,6 +15576,179 @@ window.__ModuleLoader__.load({
|
|
|
15429
15576
|
E('span', { className: 'dig-cs-chunk', 'aria-hidden': 'true' }, text)))
|
|
15430
15577
|
}
|
|
15431
15578
|
|
|
15579
|
+
/* ---- ModelRoutePicker (issue #19) ------------------------------------
|
|
15580
|
+
The panel-side model + reasoning picker, bound to the SAME session model
|
|
15581
|
+
route the composer's own selector writes: the host's ui-model-selection
|
|
15582
|
+
plugin exposes the modelDirectories resolver, and directoryFor(sessionId)
|
|
15583
|
+
hands out the session-scoped directory whose select() IS
|
|
15584
|
+
sessions.selectModel — so this picker, the composer pill and the /model
|
|
15585
|
+
command can never disagree. The VIEW is hand-drawn (ModelSelect is not
|
|
15586
|
+
exported from the official package); the DATA and the selection semantics
|
|
15587
|
+
are official end to end. The resolver arrives through a soft inject into
|
|
15588
|
+
a module-level ref (a bare `ctx.modelDirectories` read throws "without
|
|
15589
|
+
inject" on a real host — cordis gates EVERY property read behind an
|
|
15590
|
+
inject declaration); until it lands, and on any older host, the picker
|
|
15591
|
+
hides itself instead of throwing, and the AI-write button still works
|
|
15592
|
+
through the host's served-models fallback. */
|
|
15593
|
+
let modelDirectoriesRef = null
|
|
15594
|
+
const modelDirectorySubs = new Set()
|
|
15595
|
+
function ModelRoutePicker(props) {
|
|
15596
|
+
const t = props.t
|
|
15597
|
+
const sessionId = typeof props.sessionId === 'string' && props.sessionId !== '' ? props.sessionId : 'default'
|
|
15598
|
+
const [injectVersion, setInjectVersion] = useState(0)
|
|
15599
|
+
const [directory, setDirectory] = useState(null)
|
|
15600
|
+
const [state, setState] = useState(null)
|
|
15601
|
+
const [open, setOpen] = useState(false)
|
|
15602
|
+
const [pane, setPane] = useState('root')
|
|
15603
|
+
const rootRef = useRef(null)
|
|
15604
|
+
/* The soft inject may land AFTER first paint (apply runs once, this slot
|
|
15605
|
+
mounts per session view): re-render when the module ref refills. */
|
|
15606
|
+
useEffect(() => {
|
|
15607
|
+
const bump = () => setInjectVersion((v) => v + 1)
|
|
15608
|
+
modelDirectorySubs.add(bump)
|
|
15609
|
+
return () => modelDirectorySubs.delete(bump)
|
|
15610
|
+
}, [])
|
|
15611
|
+
const resolver = modelDirectoriesRef
|
|
15612
|
+
const ok = resolver !== undefined && resolver !== null && typeof resolver.directoryFor === 'function'
|
|
15613
|
+
/* Resolve the session-scoped directory once per session; subscribe AFTER
|
|
15614
|
+
taking the snapshot so a change that lands between the two is not lost. */
|
|
15615
|
+
useEffect(() => {
|
|
15616
|
+
if (ok !== true) return undefined
|
|
15617
|
+
let dir = null
|
|
15618
|
+
try { dir = resolver.directoryFor(sessionId) } catch (error) { void error }
|
|
15619
|
+
if (dir === null || dir === undefined) return undefined
|
|
15620
|
+
setState(dir.store.getSnapshot())
|
|
15621
|
+
setDirectory(dir)
|
|
15622
|
+
/* Snapshot and subscription live on dir.store (uSES-safe); load()/
|
|
15623
|
+
select() hang directly off the directory. */
|
|
15624
|
+
return dir.store.subscribe(() => setState(dir.store.getSnapshot()))
|
|
15625
|
+
}, [ok, resolver, sessionId, injectVersion])
|
|
15626
|
+
/* The catalog loads on mount; failures surface through the store and the
|
|
15627
|
+
menu shows them. A rejected load must not unmount or disable anything. */
|
|
15628
|
+
useEffect(() => {
|
|
15629
|
+
if (directory === null) return undefined
|
|
15630
|
+
directory.load().catch(() => {})
|
|
15631
|
+
return undefined
|
|
15632
|
+
}, [directory])
|
|
15633
|
+
/* Outside close. pointerdown + capture: the change list captures
|
|
15634
|
+
pointerdown for the rubber band, so a plain mousedown listener can
|
|
15635
|
+
miss the very click that opened another menu (v0.19.2 lesson). */
|
|
15636
|
+
useEffect(() => {
|
|
15637
|
+
if (open !== true) return undefined
|
|
15638
|
+
const close = (event) => {
|
|
15639
|
+
const root = rootRef.current
|
|
15640
|
+
if (root !== null && root.contains(event.target) === true) return
|
|
15641
|
+
setOpen(false)
|
|
15642
|
+
setPane('root')
|
|
15643
|
+
}
|
|
15644
|
+
document.addEventListener('pointerdown', close, true)
|
|
15645
|
+
return () => document.removeEventListener('pointerdown', close, true)
|
|
15646
|
+
}, [open])
|
|
15647
|
+
if (ok !== true || directory === null) return null
|
|
15648
|
+
const s = state === null ? directory.getSnapshot() : state
|
|
15649
|
+
const current = s === null || s.current === undefined ? null : s.current
|
|
15650
|
+
const busy = s !== null && s.pending !== null && s.pending !== undefined
|
|
15651
|
+
const groups = s === null || s.groups === undefined ? [] : s.groups
|
|
15652
|
+
/* The current route's reasoning table, looked up through the SAME
|
|
15653
|
+
directory groups the menu renders (official effectiveEffort rule). */
|
|
15654
|
+
let routeGroup = null
|
|
15655
|
+
let routeModel = null
|
|
15656
|
+
if (current !== null) {
|
|
15657
|
+
routeGroup = groups.filter((row) => row.id === current.provider)[0] || null
|
|
15658
|
+
routeModel = routeGroup === null ? null : routeGroup.models.filter((row) => row.id === current.model)[0] || null
|
|
15659
|
+
}
|
|
15660
|
+
const reasoning = routeModel === null || routeModel.reasoning === undefined ? undefined : routeModel.reasoning
|
|
15661
|
+
const effectiveEffort = current === null ? undefined : current.reasoningEffort !== undefined ? current.reasoningEffort : reasoning === undefined ? undefined : reasoning.defaultEffort
|
|
15662
|
+
const effortName = (effort) => {
|
|
15663
|
+
if (reasoning !== undefined) {
|
|
15664
|
+
const hit = reasoning.efforts.filter((row) => row.id === effort)[0]
|
|
15665
|
+
if (hit !== undefined) return hit.name
|
|
15666
|
+
}
|
|
15667
|
+
return effort === undefined ? t('modelRoute.effortDefault') : effort
|
|
15668
|
+
}
|
|
15669
|
+
const effortLabel = current === null ? '' : reasoning === undefined
|
|
15670
|
+
? (current.reasoningEffort === undefined ? '' : current.reasoningEffort)
|
|
15671
|
+
: (effectiveEffort === undefined ? t('modelRoute.effortDefault') : effortName(effectiveEffort))
|
|
15672
|
+
const modelName = current === null ? '' : routeModel === null ? current.model : routeModel.name
|
|
15673
|
+
const effortChoices = reasoning === undefined ? [] : ([
|
|
15674
|
+
...(reasoning.defaultEffort === undefined ? [{ key: 'provider-default', effort: undefined, label: t('modelRoute.effortDefault') }] : []),
|
|
15675
|
+
...reasoning.efforts.map((row) => ({ key: row.id, effort: row.id, label: row.name })),
|
|
15676
|
+
])
|
|
15677
|
+
const closeMenu = () => { setOpen(false); setPane('root') }
|
|
15678
|
+
const choose = (selection) => {
|
|
15679
|
+
void directory.select(selection).then((result) => {
|
|
15680
|
+
if (result !== undefined && result !== null && result.ok !== true) return
|
|
15681
|
+
closeMenu()
|
|
15682
|
+
}).catch(() => {})
|
|
15683
|
+
}
|
|
15684
|
+
const triggerTitle = t('modelRoute.trigger') + (current === null ? '' : ' · ' + current.provider + '/' + current.model + (effortLabel === '' ? '' : ' · ' + effortLabel))
|
|
15685
|
+
return E('div', { className: 'dig-mrp-root', ref: rootRef },
|
|
15686
|
+
E('button', {
|
|
15687
|
+
type: 'button',
|
|
15688
|
+
className: 'dig-btn dig-btn-small dig-mrp-trigger',
|
|
15689
|
+
'data-open': open === true ? 'true' : undefined,
|
|
15690
|
+
title: triggerTitle,
|
|
15691
|
+
onClick: () => { setOpen(!open); setPane('root') },
|
|
15692
|
+
},
|
|
15693
|
+
E(Icon, { name: 'settings', size: 11 }),
|
|
15694
|
+
E('span', { className: 'dig-mrp-label' }, current === null ? t('modelRoute.pick') : modelName),
|
|
15695
|
+
effortLabel === '' ? null : E('span', { className: 'dig-mrp-effort' }, effortLabel),
|
|
15696
|
+
E(Icon, { name: 'chevron', size: 9 })),
|
|
15697
|
+
open !== true ? null : E('div', { className: 'dig-cs-menu dig-mrp-menu', role: 'menu' },
|
|
15698
|
+
s !== null && typeof s.error === 'string' && s.error !== ''
|
|
15699
|
+
? E('div', { className: 'dig-mrp-error', role: 'status' }, s.error)
|
|
15700
|
+
: null,
|
|
15701
|
+
pane === 'root'
|
|
15702
|
+
? [
|
|
15703
|
+
E('button', {
|
|
15704
|
+
type: 'button', key: 'model', className: 'dig-cs-item dig-mrp-row', disabled: busy === true,
|
|
15705
|
+
onClick: () => setPane('model'),
|
|
15706
|
+
},
|
|
15707
|
+
E('span', { className: 'dig-mrp-row-label' }, t('modelRoute.modelRow')),
|
|
15708
|
+
E('span', { className: 'dig-mrp-row-value' }, current === null ? t('modelRoute.pick') : modelName),
|
|
15709
|
+
E('span', { className: 'dig-mrp-row-arrow' }, E(Icon, { name: 'chevron', size: 9 }))),
|
|
15710
|
+
E('button', {
|
|
15711
|
+
type: 'button', key: 'effort', className: 'dig-cs-item dig-mrp-row', disabled: busy === true || current === null || effortChoices.length === 0,
|
|
15712
|
+
onClick: () => setPane('effort'),
|
|
15713
|
+
},
|
|
15714
|
+
E('span', { className: 'dig-mrp-row-label' }, t('modelRoute.effortRow')),
|
|
15715
|
+
E('span', { className: 'dig-mrp-row-value' }, effortChoices.length === 0 ? '' : (effortLabel === '' ? t('modelRoute.effortDefault') : effortLabel)),
|
|
15716
|
+
E('span', { className: 'dig-mrp-row-arrow' }, E(Icon, { name: 'chevron', size: 9 }))),
|
|
15717
|
+
]
|
|
15718
|
+
: [
|
|
15719
|
+
E('button', {
|
|
15720
|
+
type: 'button', key: 'back', className: 'dig-cs-item dig-mrp-back', disabled: busy === true,
|
|
15721
|
+
onClick: () => setPane('root'),
|
|
15722
|
+
}, '‹ ' + (pane === 'model' ? t('modelRoute.modelRow') : t('modelRoute.effortRow'))),
|
|
15723
|
+
],
|
|
15724
|
+
pane === 'model' && groups.length === 0
|
|
15725
|
+
? E('div', { className: 'dig-mrp-empty' }, t('modelRoute.empty'))
|
|
15726
|
+
: null,
|
|
15727
|
+
pane === 'model' && groups.map((group) => E(Fragment, { key: group.id },
|
|
15728
|
+
E('div', { className: 'dig-mrp-group' }, group.name === undefined || group.name === null || group.name === '' ? group.id : group.name),
|
|
15729
|
+
group.models.map((model) => {
|
|
15730
|
+
const selected = current !== null && current.provider === group.id && current.model === model.id
|
|
15731
|
+
return E('button', {
|
|
15732
|
+
type: 'button', key: model.id, className: 'dig-cs-item', 'data-current': selected === true ? 'true' : undefined,
|
|
15733
|
+
disabled: busy === true,
|
|
15734
|
+
title: group.id + '/' + model.id,
|
|
15735
|
+
onClick: () => choose({ provider: group.id, model: model.id }),
|
|
15736
|
+
},
|
|
15737
|
+
E('span', { className: 'dig-cs-item-name' }, model.name === undefined || model.name === null ? model.id : model.name),
|
|
15738
|
+
E('span', { className: 'dig-cs-item-note' }, selected === true ? E(Icon, { name: 'check', size: 11 }) : null))
|
|
15739
|
+
}))),
|
|
15740
|
+
pane === 'effort' && current !== null && effortChoices.map((level) => {
|
|
15741
|
+
const selected = effectiveEffort === level.effort
|
|
15742
|
+
return E('button', {
|
|
15743
|
+
type: 'button', key: level.key, className: 'dig-cs-item', 'data-current': selected === true ? 'true' : undefined,
|
|
15744
|
+
disabled: busy === true,
|
|
15745
|
+
onClick: () => choose({ provider: current.provider, model: current.model, reasoningEffort: level.effort }),
|
|
15746
|
+
},
|
|
15747
|
+
E('span', { className: 'dig-cs-item-name' }, level.label),
|
|
15748
|
+
E('span', { className: 'dig-cs-item-note' }, selected === true ? E(Icon, { name: 'check', size: 11 }) : null))
|
|
15749
|
+
})))
|
|
15750
|
+
}
|
|
15751
|
+
|
|
15432
15752
|
function ComposerGitStatus(props) {
|
|
15433
15753
|
const ctx = props.ctx
|
|
15434
15754
|
const t = props.t
|
|
@@ -16330,6 +16650,20 @@ window.__ModuleLoader__.load({
|
|
|
16330
16650
|
}, 'dsh-ide-git: session-switch prewarm')
|
|
16331
16651
|
})
|
|
16332
16652
|
|
|
16653
|
+
/* ---- panel model picker: the official directories resolver ---- */
|
|
16654
|
+
/* ui-model-selection exposes the resolver as a CONTEXT PROPERTY, and
|
|
16655
|
+
cordis refuses bare property reads ("cannot get property … without
|
|
16656
|
+
inject") — the only legal consumer path is a declared inject. Soft:
|
|
16657
|
+
an older host without the service never fires this callback, the ref
|
|
16658
|
+
stays null and the picker hides itself (the AI-write button keeps its
|
|
16659
|
+
served-models fallback). Subscribers re-render the mounted pickers. */
|
|
16660
|
+
ctx.inject(['modelDirectories'], (mdCtx) => {
|
|
16661
|
+
const md = mdCtx.get('modelDirectories')
|
|
16662
|
+
if (md === undefined || md === null || typeof md.directoryFor !== 'function') return
|
|
16663
|
+
modelDirectoriesRef = md
|
|
16664
|
+
for (const notify of modelDirectorySubs) { try { notify() } catch (error) { void error } }
|
|
16665
|
+
})
|
|
16666
|
+
|
|
16333
16667
|
/* ---- settings: the row Config is the rail's source of truth ---- */
|
|
16334
16668
|
/* dsh >= 0.1.7 serves a plugin's row Config to the browser through
|
|
16335
16669
|
ctx.configForms; from then on the settings card and the rail read that one
|
package/src/index.js
CHANGED
|
@@ -1788,6 +1788,28 @@ function failureTextOf(failure) {
|
|
|
1788
1788
|
return String(failure.code === undefined ? failure : failure.code)
|
|
1789
1789
|
}
|
|
1790
1790
|
|
|
1791
|
+
/** The no-route fallback: the first provider this host serves that lists at
|
|
1792
|
+
least one model, and that provider's first model. Issue #19 — a fresh
|
|
1793
|
+
session (no model picked yet) used to fail the panel button with 'no-route'
|
|
1794
|
+
before the user ever saw a message; a commit message is too small a task to
|
|
1795
|
+
lecture anyone about routing. Errors per provider stay silent on purpose:
|
|
1796
|
+
a provider that fails its catalog simply is not a fallback candidate. */
|
|
1797
|
+
async function fallbackRouteOf() {
|
|
1798
|
+
const llm = commitServices.llm
|
|
1799
|
+
for (const row of llm.listProviders()) {
|
|
1800
|
+
try {
|
|
1801
|
+
const models = await llm.listModels(row.id)
|
|
1802
|
+
const model = models[0]
|
|
1803
|
+
if (model !== undefined && model !== null && typeof model.id === 'string' && model.id !== '') {
|
|
1804
|
+
return { provider: row.id, model: model.id }
|
|
1805
|
+
}
|
|
1806
|
+
} catch (error) {
|
|
1807
|
+
void error
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
return null
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1791
1813
|
/** Route the call exactly like the session does when it talks to the model. */
|
|
1792
1814
|
async function commitRouteOf(payload) {
|
|
1793
1815
|
const wanted = typeof payload.model === 'string' ? payload.model.trim() : ''
|
|
@@ -1801,7 +1823,13 @@ async function commitRouteOf(payload) {
|
|
|
1801
1823
|
try { header = session.requestHeader() } catch (error) { header = null; void error }
|
|
1802
1824
|
const config = header === null || header === undefined || header.config === undefined || header.config === null ? {} : header.config
|
|
1803
1825
|
if (typeof config.provider !== 'string' || typeof config.model !== 'string') {
|
|
1804
|
-
|
|
1826
|
+
/* Issue #19: 'no-route' is the failure every blank session hit. The
|
|
1827
|
+
served fallback below keeps the button useful on a brand-new session;
|
|
1828
|
+
the panel's model picker (this release) is the user's visible way to
|
|
1829
|
+
steer it, and it drives the SAME session route once a model is picked. */
|
|
1830
|
+
const fallback = await fallbackRouteOf()
|
|
1831
|
+
if (fallback !== null) return { provider: fallback.provider, model: fallback.model, source: 'fallback' }
|
|
1832
|
+
throw new PanelError('no-models', 'no model route and no served models to fall back to; pick a model in the session first', 502)
|
|
1805
1833
|
}
|
|
1806
1834
|
return { provider: config.provider, model: config.model, source: 'session' }
|
|
1807
1835
|
}
|