@auto-ai/agent 2.1.172 → 2.1.175
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/safe-a/404/index.html +1 -1
- package/dist/safe-a/404.html +1 -1
- package/dist/safe-a/index.html +2 -2
- package/dist/safe-a/index.txt +1 -1
- package/dist/safe-a/manage/about/index.html +2 -2
- package/dist/safe-a/manage/about/index.txt +1 -1
- package/dist/safe-a/manage/env/index.html +2 -2
- package/dist/safe-a/manage/env/index.txt +1 -1
- package/dist/safe-a/manage/general/index.html +2 -2
- package/dist/safe-a/manage/general/index.txt +1 -1
- package/dist/safe-a/manage/index.html +2 -2
- package/dist/safe-a/manage/index.txt +1 -1
- package/dist/safe-a/manage/mcp/index.html +2 -2
- package/dist/safe-a/manage/mcp/index.txt +1 -1
- package/dist/safe-a/manage/permissions/index.html +2 -2
- package/dist/safe-a/manage/permissions/index.txt +1 -1
- package/dist/safe-a/manage/skills/index.html +2 -2
- package/dist/safe-a/manage/skills/index.txt +1 -1
- package/dist/safe-a/manage/task/index.html +2 -2
- package/dist/safe-a/manage/task/index.txt +1 -1
- package/dist/safe-a/manage/teams/index.html +2 -2
- package/dist/safe-a/manage/teams/index.txt +1 -1
- package/dist/safe-a/manage/tools/index.html +2 -2
- package/dist/safe-a/manage/tools/index.txt +1 -1
- package/dist/ws-test/ws-test.css +259 -68
- package/dist/ws-test/ws-test.html +90 -137
- package/dist/ws-test/ws-test.js +555 -519
- package/package.json +6 -6
- /package/dist/safe-a/_next/static/{-ohtVxolUnzQcSsncJVgK → u2RBPyRRGWwqO4bEEh88j}/_buildManifest.js +0 -0
- /package/dist/safe-a/_next/static/{-ohtVxolUnzQcSsncJVgK → u2RBPyRRGWwqO4bEEh88j}/_clientMiddlewareManifest.json +0 -0
- /package/dist/safe-a/_next/static/{-ohtVxolUnzQcSsncJVgK → u2RBPyRRGWwqO4bEEh88j}/_ssgManifest.js +0 -0
package/dist/ws-test/ws-test.js
CHANGED
|
@@ -50,28 +50,27 @@
|
|
|
50
50
|
const btnSkillMdCancel = $('btnSkillMdCancel')
|
|
51
51
|
const btnSkillMdSave = $('btnSkillMdSave')
|
|
52
52
|
let skillMdEditingName = ''
|
|
53
|
-
const
|
|
54
|
-
const agentEnvModalCard = $('agentEnvModalCard')
|
|
55
|
-
const agentEnvModalBackdrop = $('agentEnvModalBackdrop')
|
|
56
|
-
const agentEnvSessionTabStrip = $('agentEnvSessionTabStrip')
|
|
53
|
+
const agentSettingsView = $('agentSettingsView')
|
|
57
54
|
const agentEnvSessionTabList = $('agentEnvSessionTabList')
|
|
58
|
-
const
|
|
55
|
+
const agentEnvPanelUnified = $('agentEnvPanelUnified')
|
|
59
56
|
const agentEnvPanelAgentMd = $('agentEnvPanelAgentMd')
|
|
60
|
-
const agentEnvPanelSettings = $('agentEnvPanelSettings')
|
|
61
57
|
const agentEnvAgentMdEditor = $('agentEnvAgentMdEditor')
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
58
|
+
const llmProvidersSettingsBlock = $('llmProvidersSettingsBlock')
|
|
59
|
+
const llmFormId = $('llmFormId')
|
|
60
|
+
const llmFormName = $('llmFormName')
|
|
61
|
+
const llmFormProvider = $('llmFormProvider')
|
|
62
|
+
const llmFormBaseUrl = $('llmFormBaseUrl')
|
|
63
|
+
const llmFormApiKey = $('llmFormApiKey')
|
|
64
|
+
const llmFormAuthToken = $('llmFormAuthToken')
|
|
65
|
+
const llmFormModelId = $('llmFormModelId')
|
|
66
|
+
const llmFormModelLabel = $('llmFormModelLabel')
|
|
67
|
+
const llmProvidersEmptyHint = $('llmProvidersEmptyHint')
|
|
68
|
+
const llmProvidersList = $('llmProvidersList')
|
|
69
|
+
const btnLlmProvidersAdd = $('btnLlmProvidersAdd')
|
|
70
|
+
const LLM_MASKED_CREDENTIAL = '***'
|
|
71
|
+
/** 设置页 LLM 服务商草稿,保存时写入 agent.json.llmProviders */
|
|
72
|
+
let llmProvidersDraft = []
|
|
73
73
|
const agentEnvList = $('agentEnvList')
|
|
74
|
-
const btnAgentEnvModalClose = $('btnAgentEnvModalClose')
|
|
75
74
|
const btnAgentEnvCancel = $('btnAgentEnvCancel')
|
|
76
75
|
const btnAgentEnvSave = $('btnAgentEnvSave')
|
|
77
76
|
const toolFilesModal = $('toolFilesModal')
|
|
@@ -318,14 +317,14 @@
|
|
|
318
317
|
isolation: '',
|
|
319
318
|
whentouse: '',
|
|
320
319
|
env: {},
|
|
320
|
+
llmProviders: [],
|
|
321
321
|
}
|
|
322
322
|
// session 级 overrides:只影响当前 sessionId 子进程;不写 agent.json
|
|
323
|
-
let sessionOverrideState = { model: '', env: {} }
|
|
323
|
+
let sessionOverrideState = { providerId: '', model: '', env: {} }
|
|
324
324
|
// 未分配 sessionId 时暂存,收到 {type:'session', sessionId} 后补写
|
|
325
325
|
let pendingSessionOverrides = null
|
|
326
|
-
const AGENT_ENV_GROUP_ORDER = ['
|
|
326
|
+
const AGENT_ENV_GROUP_ORDER = ['git', 'geelib', 'tutui', 'system']
|
|
327
327
|
const AGENT_ENV_GROUP_LABELS = {
|
|
328
|
-
llm: 'llm设置',
|
|
329
328
|
git: 'git设置',
|
|
330
329
|
geelib: 'geelib设置',
|
|
331
330
|
tutui: 'tutui设置',
|
|
@@ -333,7 +332,7 @@
|
|
|
333
332
|
}
|
|
334
333
|
let agentEnvSchema = []
|
|
335
334
|
let agentEnvFieldValues = {}
|
|
336
|
-
let agentEnvActiveTab = '
|
|
335
|
+
let agentEnvActiveTab = 'settings'
|
|
337
336
|
let agentEnvAgentMdLoadOk = false
|
|
338
337
|
let pickerKind = 'tools'
|
|
339
338
|
let pickerSelected = new Set()
|
|
@@ -343,6 +342,7 @@
|
|
|
343
342
|
let remoteMcpServers = []
|
|
344
343
|
const WS_PERMISSION_MODE_KEY = 'WS_PERMISSION_MODE'
|
|
345
344
|
const LLM_MODEL_KEY = 'LLM_MODEL'
|
|
345
|
+
const LLM_PROVIDER_ID_KEY = 'LLM_PROVIDER_ID'
|
|
346
346
|
const WS_ALLOW_TOOL_ASK_KEY = 'WS_ALLOW_TOOL_ASK'
|
|
347
347
|
const TUITUI_CHANNEL_SESSION_KEY = 'TUITUI_CHANNEL_SESSION'
|
|
348
348
|
const TUITUI_CHANNEL_SESSION_ALLOWED = new Set(['0', '1'])
|
|
@@ -364,10 +364,13 @@
|
|
|
364
364
|
|
|
365
365
|
/** 项目根 .env 中的默认值(agent.json 未配置时用于新 session 展示) */
|
|
366
366
|
let projectEnvDefaults = {
|
|
367
|
-
LLM_MODEL: '',
|
|
368
367
|
WS_PERMISSION_MODE: '',
|
|
369
368
|
WS_ALLOW_TOOL_ASK: '',
|
|
370
369
|
}
|
|
370
|
+
|
|
371
|
+
/** LLM providers merged 默认 provider/model(由刷新函数计算) */
|
|
372
|
+
let llmDefaultProviderId = ''
|
|
373
|
+
let llmDefaultModelId = ''
|
|
371
374
|
/** 固定表单字段在项目 .env 的默认值(用于环境变量面板回显) */
|
|
372
375
|
let projectEnvFormDefaults = {}
|
|
373
376
|
|
|
@@ -456,7 +459,7 @@
|
|
|
456
459
|
/**
|
|
457
460
|
* 业务逻辑:
|
|
458
461
|
* 1) 环境变量固定表单必须由后端 schema 驱动,避免前后端字段漂移;
|
|
459
|
-
* 2) 仅接收
|
|
462
|
+
* 2) 仅接收 git/geelib/tutui/system 分组字段,其他分组直接忽略;
|
|
460
463
|
* 3) 字段顺序保持后端下发顺序,确保展示和保存稳定可预期。
|
|
461
464
|
*/
|
|
462
465
|
function normalizeAgentEnvSchema(rawSchema) {
|
|
@@ -482,22 +485,49 @@
|
|
|
482
485
|
return out
|
|
483
486
|
}
|
|
484
487
|
|
|
488
|
+
/** 读取设置页 whentouse 输入框(由 renderAgentEnvList 动态挂载在「系统设置」分组) */
|
|
489
|
+
function queryAgentEnvWhenToUseInput() {
|
|
490
|
+
return $('agentEnvWhenToUseInput')
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* 在「系统设置」分组渲染 agent.json.whentouse 输入框。
|
|
495
|
+
*/
|
|
496
|
+
function appendAgentEnvWhentouseRow(section, whentouseValue) {
|
|
497
|
+
const row = document.createElement('div')
|
|
498
|
+
row.className = 'agent-env-fixed-row agent-env-fixed-row--whentouse'
|
|
499
|
+
const label = document.createElement('label')
|
|
500
|
+
label.className = 'agent-env-fixed-label'
|
|
501
|
+
label.setAttribute('for', 'agentEnvWhenToUseInput')
|
|
502
|
+
label.textContent = '使用时机(whentouse)'
|
|
503
|
+
const textarea = document.createElement('textarea')
|
|
504
|
+
textarea.id = 'agentEnvWhenToUseInput'
|
|
505
|
+
textarea.className = 'agent-md-editor settings-textarea--whentouse agent-env-kv-input'
|
|
506
|
+
textarea.rows = 3
|
|
507
|
+
textarea.spellcheck = false
|
|
508
|
+
textarea.placeholder = '输入该 agent 的使用时机描述'
|
|
509
|
+
textarea.setAttribute('aria-label', 'agent.json whentouse')
|
|
510
|
+
textarea.value = typeof whentouseValue === 'string' ? whentouseValue : ''
|
|
511
|
+
row.appendChild(label)
|
|
512
|
+
row.appendChild(textarea)
|
|
513
|
+
section.appendChild(row)
|
|
514
|
+
}
|
|
515
|
+
|
|
485
516
|
/**
|
|
486
517
|
* 业务逻辑:
|
|
487
518
|
* 1) 固定表单只允许 schema 内字段,所有输入值写入内存 map;
|
|
488
519
|
* 2) 保存前由 collectAndValidateAgentEnvDraft 统一做 trim 与空值过滤;
|
|
489
|
-
* 3)
|
|
520
|
+
* 3) 渲染时按分组插入标题;「系统设置」仅含 whentouse,不含全局 .env 变量。
|
|
490
521
|
*/
|
|
491
522
|
function renderAgentEnvList() {
|
|
492
523
|
if (!agentEnvList) return
|
|
524
|
+
const priorWhenInput = queryAgentEnvWhenToUseInput()
|
|
525
|
+
const priorWhentouse = priorWhenInput
|
|
526
|
+
? String(priorWhenInput.value || '')
|
|
527
|
+
: typeof agentConfigState.whentouse === 'string'
|
|
528
|
+
? agentConfigState.whentouse
|
|
529
|
+
: ''
|
|
493
530
|
agentEnvList.innerHTML = ''
|
|
494
|
-
if (!agentEnvSchema.length) {
|
|
495
|
-
const empty = document.createElement('div')
|
|
496
|
-
empty.className = 'tool-files-empty'
|
|
497
|
-
empty.textContent = '未加载到环境变量表单配置'
|
|
498
|
-
agentEnvList.appendChild(empty)
|
|
499
|
-
return
|
|
500
|
-
}
|
|
501
531
|
const schemaByGroup = {}
|
|
502
532
|
for (let i = 0; i < AGENT_ENV_GROUP_ORDER.length; i++) {
|
|
503
533
|
schemaByGroup[AGENT_ENV_GROUP_ORDER[i]] = []
|
|
@@ -507,9 +537,22 @@
|
|
|
507
537
|
schemaByGroup[field.group].push(field)
|
|
508
538
|
}
|
|
509
539
|
const frag = document.createDocumentFragment()
|
|
540
|
+
let renderedAny = false
|
|
510
541
|
for (let i = 0; i < AGENT_ENV_GROUP_ORDER.length; i++) {
|
|
511
542
|
const group = AGENT_ENV_GROUP_ORDER[i]
|
|
512
543
|
const fields = schemaByGroup[group]
|
|
544
|
+
if (group === 'system') {
|
|
545
|
+
const section = document.createElement('section')
|
|
546
|
+
section.className = 'agent-env-fixed-section'
|
|
547
|
+
const title = document.createElement('h3')
|
|
548
|
+
title.className = 'agent-env-fixed-section-title'
|
|
549
|
+
title.textContent = AGENT_ENV_GROUP_LABELS.system
|
|
550
|
+
section.appendChild(title)
|
|
551
|
+
appendAgentEnvWhentouseRow(section, priorWhentouse)
|
|
552
|
+
frag.appendChild(section)
|
|
553
|
+
renderedAny = true
|
|
554
|
+
continue
|
|
555
|
+
}
|
|
513
556
|
if (!fields || !fields.length) continue
|
|
514
557
|
const section = document.createElement('section')
|
|
515
558
|
section.className = 'agent-env-fixed-section'
|
|
@@ -555,36 +598,6 @@
|
|
|
555
598
|
agentEnvFieldValues[field.key] = select.value
|
|
556
599
|
})
|
|
557
600
|
input = select
|
|
558
|
-
} else if (field.key === 'LLM_PROVIDER') {
|
|
559
|
-
/**
|
|
560
|
-
* 业务逻辑:模型类型是受控枚举,使用下拉框约束可选值,
|
|
561
|
-
* 避免手输导致 provider 非法。
|
|
562
|
-
*/
|
|
563
|
-
const select = document.createElement('select')
|
|
564
|
-
select.id = inputId
|
|
565
|
-
select.className = 'agent-env-kv-input'
|
|
566
|
-
const providerOptions = [
|
|
567
|
-
{ value: '', label: '请选择' },
|
|
568
|
-
{ value: 'anthropic', label: 'anthropic' },
|
|
569
|
-
{ value: 'openai', label: 'openai' },
|
|
570
|
-
]
|
|
571
|
-
for (let k = 0; k < providerOptions.length; k++) {
|
|
572
|
-
const option = document.createElement('option')
|
|
573
|
-
option.value = providerOptions[k].value
|
|
574
|
-
option.textContent = providerOptions[k].label
|
|
575
|
-
select.appendChild(option)
|
|
576
|
-
}
|
|
577
|
-
const currentValue =
|
|
578
|
-
typeof agentEnvFieldValues[field.key] === 'string'
|
|
579
|
-
? agentEnvFieldValues[field.key]
|
|
580
|
-
: ''
|
|
581
|
-
select.value = currentValue === 'anthropic' || currentValue === 'openai'
|
|
582
|
-
? currentValue
|
|
583
|
-
: ''
|
|
584
|
-
select.addEventListener('change', function () {
|
|
585
|
-
agentEnvFieldValues[field.key] = select.value
|
|
586
|
-
})
|
|
587
|
-
input = select
|
|
588
601
|
} else {
|
|
589
602
|
const textInput = document.createElement('input')
|
|
590
603
|
textInput.id = inputId
|
|
@@ -605,18 +618,29 @@
|
|
|
605
618
|
section.appendChild(row)
|
|
606
619
|
}
|
|
607
620
|
frag.appendChild(section)
|
|
621
|
+
renderedAny = true
|
|
622
|
+
}
|
|
623
|
+
if (!renderedAny) {
|
|
624
|
+
const empty = document.createElement('div')
|
|
625
|
+
empty.className = 'tool-files-empty'
|
|
626
|
+
empty.textContent = '未加载到环境变量表单配置'
|
|
627
|
+
agentEnvList.appendChild(empty)
|
|
628
|
+
return
|
|
608
629
|
}
|
|
609
630
|
agentEnvList.appendChild(frag)
|
|
610
631
|
}
|
|
611
632
|
|
|
612
|
-
/**
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
633
|
+
/**
|
|
634
|
+
* 新 session 的默认 provider + model:
|
|
635
|
+
* 由后端 /api/llm-providers 返回的 merged.defaultProviderId 与 provider.defaultModel 推导。
|
|
636
|
+
*
|
|
637
|
+
* 业务语义:若默认值尚未准备好,直接抛错暴露配置/加载时序问题,
|
|
638
|
+
* 不做静默兜底(避免错误模型运行)。
|
|
639
|
+
*/
|
|
640
|
+
function readDefaultLlmProviderAndModel() {
|
|
641
|
+
const providerId = String(llmDefaultProviderId || '').trim()
|
|
642
|
+
const model = String(llmDefaultModelId || '').trim()
|
|
643
|
+
return { providerId, model }
|
|
620
644
|
}
|
|
621
645
|
|
|
622
646
|
/**
|
|
@@ -678,8 +702,10 @@
|
|
|
678
702
|
* 3) 将结果投影为 sessionOverrideState,保证 UI 与后端 PATCH 入参一致。
|
|
679
703
|
*/
|
|
680
704
|
function buildDefaultSessionOverrideState() {
|
|
705
|
+
const { providerId, model } = readDefaultLlmProviderAndModel()
|
|
681
706
|
return {
|
|
682
|
-
|
|
707
|
+
providerId,
|
|
708
|
+
model,
|
|
683
709
|
env: {
|
|
684
710
|
[WS_PERMISSION_MODE_KEY]: readDefaultWsPermissionMode(),
|
|
685
711
|
[WS_ALLOW_TOOL_ASK_KEY]: readDefaultWsAllowToolAsk(),
|
|
@@ -2503,11 +2529,21 @@
|
|
|
2503
2529
|
treeCache: new Map(),
|
|
2504
2530
|
}
|
|
2505
2531
|
|
|
2532
|
+
/** Agent 设置主视图是否激活(与聊天区、文件管理互斥) */
|
|
2533
|
+
const agentSettingsState = {
|
|
2534
|
+
viewActive: false,
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2506
2537
|
/** 当前是否在文件管理视图 */
|
|
2507
2538
|
function isRepoManageViewActive() {
|
|
2508
2539
|
return repoManageState.viewActive
|
|
2509
2540
|
}
|
|
2510
2541
|
|
|
2542
|
+
/** 当前是否在 Agent 设置主视图 */
|
|
2543
|
+
function isAgentSettingsViewActive() {
|
|
2544
|
+
return agentSettingsState.viewActive
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2511
2547
|
/** 判断是否为可在页面内展示的文本类文件 */
|
|
2512
2548
|
function isRepoTextFile(fileName) {
|
|
2513
2549
|
const base = String(fileName || '').split('/').pop() || ''
|
|
@@ -2648,6 +2684,9 @@
|
|
|
2648
2684
|
|
|
2649
2685
|
/** 显示/隐藏文件浏览主区域、聊天区;顶栏在文件视图下展示「文件」而非会话标题 */
|
|
2650
2686
|
function setRepoManageVisible(visible) {
|
|
2687
|
+
if (visible) {
|
|
2688
|
+
closeAgentSettingsView()
|
|
2689
|
+
}
|
|
2651
2690
|
repoManageState.viewActive = visible
|
|
2652
2691
|
if (repoManageView) repoManageView.hidden = !visible
|
|
2653
2692
|
if (chatWorkspace) chatWorkspace.hidden = visible
|
|
@@ -2670,6 +2709,41 @@
|
|
|
2670
2709
|
if (btnManageRepo) btnManageRepo.classList.remove('is-active')
|
|
2671
2710
|
}
|
|
2672
2711
|
|
|
2712
|
+
/**
|
|
2713
|
+
* 切换 Agent 设置主视图与聊天区可见性。
|
|
2714
|
+
* 业务逻辑:设置页与文件管理、聊天区互斥,顶栏展示「设置」与当前 agent 名。
|
|
2715
|
+
*/
|
|
2716
|
+
function setAgentSettingsVisible(visible) {
|
|
2717
|
+
if (visible) {
|
|
2718
|
+
repoManageState.viewActive = false
|
|
2719
|
+
if (repoManageView) repoManageView.hidden = true
|
|
2720
|
+
if (btnManageRepo) btnManageRepo.classList.remove('is-active')
|
|
2721
|
+
}
|
|
2722
|
+
agentSettingsState.viewActive = visible
|
|
2723
|
+
if (agentSettingsView) agentSettingsView.hidden = !visible
|
|
2724
|
+
if (chatWorkspace) chatWorkspace.hidden = visible
|
|
2725
|
+
if (chatComposer) chatComposer.hidden = visible
|
|
2726
|
+
if (mainHeaderEl) mainHeaderEl.hidden = false
|
|
2727
|
+
if (visible) {
|
|
2728
|
+
if (mainTitleEl) mainTitleEl.textContent = '设置'
|
|
2729
|
+
if (mainSubEl) {
|
|
2730
|
+
mainSubEl.textContent = currentAgentParam()
|
|
2731
|
+
mainSubEl.hidden = false
|
|
2732
|
+
}
|
|
2733
|
+
if (btnEditAgentEnv) btnEditAgentEnv.classList.add('is-active')
|
|
2734
|
+
} else {
|
|
2735
|
+
if (btnEditAgentEnv) btnEditAgentEnv.classList.remove('is-active')
|
|
2736
|
+
updateMainHeader()
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
/** 关闭设置主视图,回到聊天 */
|
|
2741
|
+
function closeAgentSettingsView() {
|
|
2742
|
+
if (!agentSettingsState.viewActive) return
|
|
2743
|
+
setAgentSettingsVisible(false)
|
|
2744
|
+
if (agentEnvSessionTabList) agentEnvSessionTabList.innerHTML = ''
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2673
2747
|
/** 设置 entries/history 面板加载或错误态 */
|
|
2674
2748
|
function setRepoPanelState(el, message, isError) {
|
|
2675
2749
|
if (!el) return
|
|
@@ -4469,6 +4543,14 @@
|
|
|
4469
4543
|
}
|
|
4470
4544
|
return
|
|
4471
4545
|
}
|
|
4546
|
+
if (isAgentSettingsViewActive()) {
|
|
4547
|
+
mainTitleEl.textContent = '设置'
|
|
4548
|
+
if (mainSubEl) {
|
|
4549
|
+
mainSubEl.textContent = currentAgentParam()
|
|
4550
|
+
mainSubEl.hidden = false
|
|
4551
|
+
}
|
|
4552
|
+
return
|
|
4553
|
+
}
|
|
4472
4554
|
if (typeof titleText === 'string' && titleText.trim()) {
|
|
4473
4555
|
mainTitleEl.textContent = titleText.trim()
|
|
4474
4556
|
return
|
|
@@ -4600,181 +4682,6 @@
|
|
|
4600
4682
|
return u.toString()
|
|
4601
4683
|
}
|
|
4602
4684
|
|
|
4603
|
-
function buildAgentTeamMembersPostUrl(leaderId) {
|
|
4604
|
-
return new URL(
|
|
4605
|
-
'/api/agents/' + encodeURIComponent(String(leaderId || '').trim()) + '/team-members',
|
|
4606
|
-
httpOriginForApi() + '/',
|
|
4607
|
-
).toString()
|
|
4608
|
-
}
|
|
4609
|
-
|
|
4610
|
-
function buildAgentTeamMemberDeleteUrl(leaderId, agentType) {
|
|
4611
|
-
return new URL(
|
|
4612
|
-
'/api/agents/' +
|
|
4613
|
-
encodeURIComponent(String(leaderId || '').trim()) +
|
|
4614
|
-
'/team-members/' +
|
|
4615
|
-
encodeURIComponent(String(agentType || '').trim()),
|
|
4616
|
-
httpOriginForApi() + '/',
|
|
4617
|
-
).toString()
|
|
4618
|
-
}
|
|
4619
|
-
|
|
4620
|
-
function buildAgentsListUrlForOffice() {
|
|
4621
|
-
return new URL('/api/agents', httpOriginForApi() + '/').toString()
|
|
4622
|
-
}
|
|
4623
|
-
|
|
4624
|
-
/** 设置弹窗:可绑定队员仅来自 agent-runtime(GET /api/agents) */
|
|
4625
|
-
async function fetchTeamMemberCatalogForSettings(leaderId) {
|
|
4626
|
-
const leader = String(leaderId || '').trim()
|
|
4627
|
-
const r = await fetch(buildAgentsListUrlForOffice())
|
|
4628
|
-
const body = await r.json().catch(function () {
|
|
4629
|
-
return {}
|
|
4630
|
-
})
|
|
4631
|
-
if (!r.ok) {
|
|
4632
|
-
throw new Error(body.message || body.error || r.statusText)
|
|
4633
|
-
}
|
|
4634
|
-
const agents = Array.isArray(body.agents) ? body.agents : []
|
|
4635
|
-
const out = []
|
|
4636
|
-
for (let i = 0; i < agents.length; i++) {
|
|
4637
|
-
const row = agents[i]
|
|
4638
|
-
const id = String(row.id || '').trim()
|
|
4639
|
-
if (!id || id === leader) continue
|
|
4640
|
-
out.push({
|
|
4641
|
-
agentType: id,
|
|
4642
|
-
whenToUse: typeof row.whentouse === 'string' ? row.whentouse : '',
|
|
4643
|
-
})
|
|
4644
|
-
}
|
|
4645
|
-
out.sort(function (a, b) {
|
|
4646
|
-
return a.agentType.localeCompare(b.agentType)
|
|
4647
|
-
})
|
|
4648
|
-
return out
|
|
4649
|
-
}
|
|
4650
|
-
|
|
4651
|
-
/** 设置弹窗:渲染已绑定队员与添加下拉 */
|
|
4652
|
-
async function renderAgentEnvTeamMembers() {
|
|
4653
|
-
if (!agentEnvTeamChips) return
|
|
4654
|
-
const leaderId = currentAgentParam()
|
|
4655
|
-
const members = Array.isArray(agentConfigState.agentTeamMembers)
|
|
4656
|
-
? agentConfigState.agentTeamMembers
|
|
4657
|
-
: []
|
|
4658
|
-
if (agentEnvTeamEmpty) {
|
|
4659
|
-
agentEnvTeamEmpty.hidden = members.length > 0
|
|
4660
|
-
}
|
|
4661
|
-
agentEnvTeamChips.innerHTML = ''
|
|
4662
|
-
const boundSet = new Set()
|
|
4663
|
-
for (let i = 0; i < members.length; i++) {
|
|
4664
|
-
const m = members[i]
|
|
4665
|
-
const agentType = String(m.agentType || '').trim()
|
|
4666
|
-
if (!agentType) continue
|
|
4667
|
-
boundSet.add(agentType)
|
|
4668
|
-
const chip = document.createElement('span')
|
|
4669
|
-
chip.className = 'agent-env-team-chip'
|
|
4670
|
-
chip.title =
|
|
4671
|
-
typeof m.whentouse === 'string' && m.whentouse.trim()
|
|
4672
|
-
? m.whentouse.trim()
|
|
4673
|
-
: agentType
|
|
4674
|
-
const label = document.createElement('span')
|
|
4675
|
-
label.textContent = agentType
|
|
4676
|
-
const rm = document.createElement('button')
|
|
4677
|
-
rm.type = 'button'
|
|
4678
|
-
rm.setAttribute('aria-label', '移除 ' + agentType)
|
|
4679
|
-
rm.textContent = '×'
|
|
4680
|
-
rm.addEventListener('click', function () {
|
|
4681
|
-
void removeAgentEnvTeamMember(agentType)
|
|
4682
|
-
})
|
|
4683
|
-
chip.appendChild(label)
|
|
4684
|
-
chip.appendChild(rm)
|
|
4685
|
-
agentEnvTeamChips.appendChild(chip)
|
|
4686
|
-
}
|
|
4687
|
-
if (agentEnvTeamPick) {
|
|
4688
|
-
agentEnvTeamPick.innerHTML = '<option value="">选择要添加的 Agent…</option>'
|
|
4689
|
-
let catalog = []
|
|
4690
|
-
try {
|
|
4691
|
-
catalog = await fetchTeamMemberCatalogForSettings(leaderId)
|
|
4692
|
-
} catch {
|
|
4693
|
-
catalog = []
|
|
4694
|
-
}
|
|
4695
|
-
for (let j = 0; j < catalog.length; j++) {
|
|
4696
|
-
const t = catalog[j]
|
|
4697
|
-
const id = String(t.agentType || '').trim()
|
|
4698
|
-
if (!id || id === leaderId || boundSet.has(id)) continue
|
|
4699
|
-
const opt = document.createElement('option')
|
|
4700
|
-
opt.value = id
|
|
4701
|
-
const when =
|
|
4702
|
-
typeof t.whenToUse === 'string' && t.whenToUse.trim() ? t.whenToUse.trim() : ''
|
|
4703
|
-
opt.textContent = when ? id + ' — ' + when.slice(0, 36) : id
|
|
4704
|
-
agentEnvTeamPick.appendChild(opt)
|
|
4705
|
-
}
|
|
4706
|
-
}
|
|
4707
|
-
}
|
|
4708
|
-
|
|
4709
|
-
function applyAgentTeamMembersFromApi(agentRow) {
|
|
4710
|
-
if (!agentRow || typeof agentRow !== 'object') return
|
|
4711
|
-
if (Array.isArray(agentRow.agentTeamMembers)) {
|
|
4712
|
-
agentConfigState.agentTeamMembers = agentRow.agentTeamMembers.map(function (m) {
|
|
4713
|
-
return {
|
|
4714
|
-
agentType: String(m.agentType || ''),
|
|
4715
|
-
whentouse: typeof m.whentouse === 'string' ? m.whentouse : '',
|
|
4716
|
-
}
|
|
4717
|
-
})
|
|
4718
|
-
}
|
|
4719
|
-
if (Array.isArray(agentRow.agentTeams)) {
|
|
4720
|
-
agentConfigState.agentTeams = agentRow.agentTeams.map(String)
|
|
4721
|
-
} else if (Array.isArray(agentRow.agentTeamMembers)) {
|
|
4722
|
-
agentConfigState.agentTeams = agentRow.agentTeamMembers
|
|
4723
|
-
.map(function (m) {
|
|
4724
|
-
return String(m.agentType || '')
|
|
4725
|
-
})
|
|
4726
|
-
.filter(Boolean)
|
|
4727
|
-
}
|
|
4728
|
-
renderBindChips()
|
|
4729
|
-
}
|
|
4730
|
-
|
|
4731
|
-
async function addAgentEnvTeamMember() {
|
|
4732
|
-
const leaderId = currentAgentParam()
|
|
4733
|
-
if (!agentEnvTeamPick) return
|
|
4734
|
-
const agentType = agentEnvTeamPick.value.trim()
|
|
4735
|
-
if (!agentType) {
|
|
4736
|
-
window.alert('请先选择要添加的队员')
|
|
4737
|
-
return
|
|
4738
|
-
}
|
|
4739
|
-
const r = await fetch(buildAgentTeamMembersPostUrl(leaderId), {
|
|
4740
|
-
method: 'POST',
|
|
4741
|
-
headers: { 'content-type': 'application/json; charset=utf-8' },
|
|
4742
|
-
body: JSON.stringify({ agentType: agentType }),
|
|
4743
|
-
})
|
|
4744
|
-
const body = await r.json().catch(function () {
|
|
4745
|
-
return {}
|
|
4746
|
-
})
|
|
4747
|
-
if (!r.ok) {
|
|
4748
|
-
window.alert('添加队员失败:' + (body.message || body.error || r.statusText))
|
|
4749
|
-
return
|
|
4750
|
-
}
|
|
4751
|
-
applyAgentTeamMembersFromApi(body.agent)
|
|
4752
|
-
agentEnvTeamPick.value = ''
|
|
4753
|
-
await renderAgentEnvTeamMembers()
|
|
4754
|
-
void persistAgentConfigAndRestartSession({ silent: true })
|
|
4755
|
-
setStatus('已添加队员 ' + agentType, serverReady ? 'ready' : '')
|
|
4756
|
-
}
|
|
4757
|
-
|
|
4758
|
-
async function removeAgentEnvTeamMember(agentType) {
|
|
4759
|
-
const leaderId = currentAgentParam()
|
|
4760
|
-
if (!window.confirm('确定移除队员「' + agentType + '」?')) {
|
|
4761
|
-
return
|
|
4762
|
-
}
|
|
4763
|
-
const r = await fetch(buildAgentTeamMemberDeleteUrl(leaderId, agentType), {
|
|
4764
|
-
method: 'DELETE',
|
|
4765
|
-
})
|
|
4766
|
-
const body = await r.json().catch(function () {
|
|
4767
|
-
return {}
|
|
4768
|
-
})
|
|
4769
|
-
if (!r.ok) {
|
|
4770
|
-
window.alert('移除队员失败:' + (body.message || body.error || r.statusText))
|
|
4771
|
-
return
|
|
4772
|
-
}
|
|
4773
|
-
applyAgentTeamMembersFromApi(body.agent)
|
|
4774
|
-
await renderAgentEnvTeamMembers()
|
|
4775
|
-
void persistAgentConfigAndRestartSession({ silent: true })
|
|
4776
|
-
setStatus('已移除队员 ' + agentType, serverReady ? 'ready' : '')
|
|
4777
|
-
}
|
|
4778
4685
|
async function loadSessions(options) {
|
|
4779
4686
|
sessionListMetaEl.textContent = '加载中…'
|
|
4780
4687
|
let data
|
|
@@ -4825,6 +4732,7 @@
|
|
|
4825
4732
|
|
|
4826
4733
|
function switchToSession(id) {
|
|
4827
4734
|
closeRepoManageView()
|
|
4735
|
+
closeAgentSettingsView()
|
|
4828
4736
|
const nextSid = typeof id === 'string' ? id : ''
|
|
4829
4737
|
sessionIdInput.value = nextSid
|
|
4830
4738
|
currentSessionContextPercent = null
|
|
@@ -5644,12 +5552,16 @@
|
|
|
5644
5552
|
|
|
5645
5553
|
function buildModelsApiUrl() {
|
|
5646
5554
|
const id = currentAgentParam()
|
|
5647
|
-
const u = new URL('/api/
|
|
5555
|
+
const u = new URL('/api/llm-providers', httpOriginForApi() + '/')
|
|
5648
5556
|
u.searchParams.set('agent', id)
|
|
5649
5557
|
return u.toString()
|
|
5650
5558
|
}
|
|
5651
5559
|
|
|
5652
|
-
/**
|
|
5560
|
+
/**
|
|
5561
|
+
* 会话模型候选:来自 /api/llm-providers.merged 的本地配置(不走云端探测)。
|
|
5562
|
+
*
|
|
5563
|
+
* 注意:同一个 model.id 在不同 provider 下可能重复,因此候选需要 providerId 一起参与。
|
|
5564
|
+
*/
|
|
5653
5565
|
let apiModelRows = []
|
|
5654
5566
|
let lastModelsFetchTs = 0
|
|
5655
5567
|
|
|
@@ -5668,102 +5580,79 @@
|
|
|
5668
5580
|
if (!r.ok) {
|
|
5669
5581
|
throw new Error(b.message || b.error || r.statusText)
|
|
5670
5582
|
}
|
|
5671
|
-
const
|
|
5672
|
-
apiModelRows = rows
|
|
5673
|
-
.filter(function (x) {
|
|
5674
|
-
return x && typeof x.id === 'string' && x.id.trim()
|
|
5675
|
-
})
|
|
5676
|
-
.map(function (x) {
|
|
5677
|
-
return {
|
|
5678
|
-
id: String(x.id).trim(),
|
|
5679
|
-
label:
|
|
5680
|
-
typeof x.label === 'string' && x.label.trim()
|
|
5681
|
-
? String(x.label).trim()
|
|
5682
|
-
: undefined,
|
|
5683
|
-
}
|
|
5684
|
-
})
|
|
5685
|
-
logLine('models-api', { source: b.source, count: apiModelRows.length })
|
|
5686
|
-
} catch (e) {
|
|
5687
|
-
apiModelRows = []
|
|
5688
|
-
logLine('models-api', 'load failed: ' + e)
|
|
5689
|
-
}
|
|
5690
|
-
}
|
|
5583
|
+
const providers = Array.isArray(b.providers) ? b.providers : []
|
|
5691
5584
|
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
if (!m || !String(m).trim()) return
|
|
5717
|
-
const v = String(m).trim()
|
|
5718
|
-
let arr = readRecentModels().filter(function (x) {
|
|
5719
|
-
return x !== v
|
|
5720
|
-
})
|
|
5721
|
-
arr.unshift(v)
|
|
5722
|
-
arr = arr.slice(0, 12)
|
|
5723
|
-
try {
|
|
5724
|
-
localStorage.setItem(RECENT_MODELS_KEY, JSON.stringify(arr))
|
|
5725
|
-
} catch {
|
|
5726
|
-
/* ignore */
|
|
5727
|
-
}
|
|
5728
|
-
}
|
|
5585
|
+
apiModelRows = []
|
|
5586
|
+
for (let i = 0; i < providers.length; i++) {
|
|
5587
|
+
const p = providers[i]
|
|
5588
|
+
const providerId = typeof p.id === 'string' ? p.id.trim() : ''
|
|
5589
|
+
const providerName = typeof p.name === 'string' ? p.name.trim() : ''
|
|
5590
|
+
const models = Array.isArray(p.models) ? p.models : []
|
|
5591
|
+
for (let j = 0; j < models.length; j++) {
|
|
5592
|
+
const m = models[j]
|
|
5593
|
+
const modelId = typeof m.id === 'string' ? m.id.trim() : ''
|
|
5594
|
+
if (!modelId) continue
|
|
5595
|
+
const label =
|
|
5596
|
+
typeof m.label === 'string' && m.label.trim() ? m.label.trim() : undefined
|
|
5597
|
+
const display = providerName
|
|
5598
|
+
? providerName + ' · ' + (label ? label : modelId)
|
|
5599
|
+
: (label ? label : modelId)
|
|
5600
|
+
apiModelRows.push({
|
|
5601
|
+
providerId,
|
|
5602
|
+
providerName,
|
|
5603
|
+
id: modelId,
|
|
5604
|
+
label,
|
|
5605
|
+
display,
|
|
5606
|
+
})
|
|
5607
|
+
}
|
|
5608
|
+
}
|
|
5729
5609
|
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
const
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5610
|
+
// 计算 merged 默认 provider/model(给新 session 使用)
|
|
5611
|
+
const defaultProviderIdRaw =
|
|
5612
|
+
typeof b.defaultProviderId === 'string' ? b.defaultProviderId.trim() : ''
|
|
5613
|
+
const resolvedDefaultProviderId =
|
|
5614
|
+
defaultProviderIdRaw ||
|
|
5615
|
+
(apiModelRows.length ? apiModelRows[0].providerId : '')
|
|
5616
|
+
llmDefaultProviderId = resolvedDefaultProviderId
|
|
5617
|
+
const defaultProvider = providers.find(p => String(p.id) === resolvedDefaultProviderId) || providers[0]
|
|
5618
|
+
const defaultModelRaw =
|
|
5619
|
+
defaultProvider && typeof defaultProvider.defaultModel === 'string'
|
|
5620
|
+
? defaultProvider.defaultModel.trim()
|
|
5621
|
+
: ''
|
|
5622
|
+
const defaultModelId =
|
|
5623
|
+
defaultModelRaw ||
|
|
5624
|
+
(defaultProvider && Array.isArray(defaultProvider.models) && defaultProvider.models[0]
|
|
5625
|
+
? String(defaultProvider.models[0].id).trim()
|
|
5626
|
+
: '')
|
|
5627
|
+
llmDefaultModelId = defaultModelId
|
|
5628
|
+
|
|
5629
|
+
logLine('llm-providers-api', { count: apiModelRows.length, defaultProviderId: llmDefaultProviderId, defaultModelId: llmDefaultModelId })
|
|
5630
|
+
} catch (e) {
|
|
5631
|
+
apiModelRows = []
|
|
5632
|
+
llmDefaultProviderId = ''
|
|
5633
|
+
llmDefaultModelId = ''
|
|
5634
|
+
logLine('llm-providers-api', 'load failed: ' + e)
|
|
5749
5635
|
}
|
|
5750
|
-
return out
|
|
5751
5636
|
}
|
|
5752
5637
|
|
|
5753
5638
|
function filterModelCandidates(query) {
|
|
5754
|
-
const all = mergeModelCandidates()
|
|
5755
5639
|
const q = String(query || '')
|
|
5756
5640
|
.trim()
|
|
5757
5641
|
.toLowerCase()
|
|
5642
|
+
const all = Array.isArray(apiModelRows) ? apiModelRows : []
|
|
5758
5643
|
if (!q) return all.slice(0, 50)
|
|
5759
5644
|
const hit = []
|
|
5760
5645
|
for (let i = 0; i < all.length; i++) {
|
|
5761
|
-
const
|
|
5762
|
-
const
|
|
5763
|
-
const
|
|
5764
|
-
const
|
|
5765
|
-
|
|
5766
|
-
|
|
5646
|
+
const x = all[i]
|
|
5647
|
+
const idHit = x.id.toLowerCase().indexOf(q) >= 0
|
|
5648
|
+
const labelHit = x.label && x.label.toLowerCase().indexOf(q) >= 0
|
|
5649
|
+
const providerHit =
|
|
5650
|
+
x.providerName && x.providerName.toLowerCase().indexOf(q) >= 0
|
|
5651
|
+
const providerIdHit = x.providerId && x.providerId.toLowerCase().indexOf(q) >= 0
|
|
5652
|
+
if (idHit || labelHit || providerHit || providerIdHit) {
|
|
5653
|
+
hit.push(x)
|
|
5654
|
+
if (hit.length >= 50) break
|
|
5655
|
+
}
|
|
5767
5656
|
}
|
|
5768
5657
|
return hit
|
|
5769
5658
|
}
|
|
@@ -5796,17 +5685,23 @@
|
|
|
5796
5685
|
return
|
|
5797
5686
|
}
|
|
5798
5687
|
for (let i = 0; i < items.length; i++) {
|
|
5799
|
-
const
|
|
5800
|
-
const
|
|
5688
|
+
const entry = items[i]
|
|
5689
|
+
const id = entry && typeof entry.id === 'string' ? entry.id : ''
|
|
5801
5690
|
const li = document.createElement('li')
|
|
5802
5691
|
li.setAttribute('role', 'option')
|
|
5803
5692
|
li.setAttribute('data-model', id)
|
|
5804
|
-
|
|
5693
|
+
if (entry && typeof entry.providerId === 'string') {
|
|
5694
|
+
li.setAttribute('data-provider-id', entry.providerId)
|
|
5695
|
+
}
|
|
5696
|
+
li.textContent = entry && entry.display ? entry.display : id
|
|
5805
5697
|
if (i === composerModelHighlight) li.classList.add('is-active')
|
|
5806
5698
|
li.addEventListener('mousedown', function (ev) {
|
|
5807
5699
|
ev.preventDefault()
|
|
5808
5700
|
composerModelSuppressBlurCommit = true
|
|
5809
5701
|
if (composerModelInput) composerModelInput.value = id
|
|
5702
|
+
if (entry && typeof entry.providerId === 'string') {
|
|
5703
|
+
sessionOverrideState.providerId = entry.providerId
|
|
5704
|
+
}
|
|
5810
5705
|
syncComposerModelTitle()
|
|
5811
5706
|
hideComposerModelDropdown()
|
|
5812
5707
|
void commitComposerModelFromInput()
|
|
@@ -5854,8 +5749,10 @@
|
|
|
5854
5749
|
let idx = composerModelHighlight
|
|
5855
5750
|
if (idx < 0 || idx >= opts.length) idx = 0
|
|
5856
5751
|
const id = opts[idx].getAttribute('data-model')
|
|
5752
|
+
const pid = opts[idx].getAttribute('data-provider-id') || ''
|
|
5857
5753
|
if (id) {
|
|
5858
5754
|
composerModelInput.value = id
|
|
5755
|
+
if (pid) sessionOverrideState.providerId = pid
|
|
5859
5756
|
syncComposerModelTitle()
|
|
5860
5757
|
hideComposerModelDropdown()
|
|
5861
5758
|
void commitComposerModelFromInput()
|
|
@@ -5865,25 +5762,83 @@
|
|
|
5865
5762
|
async function commitComposerModelFromInput() {
|
|
5866
5763
|
if (!composerModelInput) return
|
|
5867
5764
|
const v = composerModelInput.value.trim()
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5765
|
+
const defaults = buildDefaultSessionOverrideState()
|
|
5766
|
+
|
|
5767
|
+
// 留空语义:清除 session 级 overrides,让 worker 依据项目/agent 默认配置自动选择 provider/model。
|
|
5768
|
+
if (!v) {
|
|
5769
|
+
const shouldUpdate =
|
|
5770
|
+
(sessionOverrideState.model || '') !== '' ||
|
|
5771
|
+
(sessionOverrideState.providerId || '') !== ''
|
|
5772
|
+
if (!shouldUpdate) return
|
|
5773
|
+
sessionOverrideState.model = ''
|
|
5774
|
+
sessionOverrideState.providerId = ''
|
|
5775
|
+
await persistSessionOverridesAndRestart({ silent: true })
|
|
5776
|
+
return
|
|
5777
|
+
}
|
|
5778
|
+
|
|
5779
|
+
const modelId = v
|
|
5780
|
+
|
|
5781
|
+
const matches = Array.isArray(apiModelRows)
|
|
5782
|
+
? apiModelRows.filter(function (x) {
|
|
5783
|
+
return x && x.id === modelId
|
|
5784
|
+
})
|
|
5785
|
+
: []
|
|
5786
|
+
|
|
5787
|
+
let providerId = sessionOverrideState.providerId || ''
|
|
5788
|
+
if (matches.length === 1) {
|
|
5789
|
+
providerId = matches[0].providerId
|
|
5790
|
+
} else if (matches.length > 1) {
|
|
5791
|
+
// 相同 model.id 可能出现在多个 provider 下:
|
|
5792
|
+
// 若当前 provider 已匹配其中之一则沿用,否则阻止保存要求用户明确选择。
|
|
5793
|
+
const found = providerId
|
|
5794
|
+
? matches.some(function (x) {
|
|
5795
|
+
return x.providerId === providerId
|
|
5796
|
+
})
|
|
5797
|
+
: false
|
|
5798
|
+
if (!found) {
|
|
5799
|
+
window.alert('该模型 ID 在多个服务商中重复,请从下拉列表选择完整项(服务商 · 模型)。')
|
|
5800
|
+
return
|
|
5801
|
+
}
|
|
5802
|
+
} else {
|
|
5803
|
+
// 没匹配到 model.id:不允许用不受控的 provider/model 组合运行。
|
|
5804
|
+
window.alert('模型 ID 未配置:' + modelId)
|
|
5805
|
+
return
|
|
5806
|
+
}
|
|
5807
|
+
|
|
5808
|
+
const shouldUpdate =
|
|
5809
|
+
modelId !== (sessionOverrideState.model || '') || providerId !== (sessionOverrideState.providerId || '')
|
|
5810
|
+
if (!shouldUpdate) return
|
|
5811
|
+
|
|
5812
|
+
sessionOverrideState.model = modelId
|
|
5813
|
+
sessionOverrideState.providerId = providerId
|
|
5871
5814
|
await persistSessionOverridesAndRestart({ silent: true })
|
|
5872
5815
|
}
|
|
5873
5816
|
|
|
5874
5817
|
function syncComposerModelTitle() {
|
|
5875
5818
|
if (!composerModelInput) return
|
|
5876
5819
|
const v = composerModelInput.value.trim()
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5820
|
+
if (!v) {
|
|
5821
|
+
composerModelInput.title = '留空=跟随默认服务商与模型'
|
|
5822
|
+
return
|
|
5823
|
+
}
|
|
5824
|
+
const entry = Array.isArray(apiModelRows)
|
|
5825
|
+
? apiModelRows.find(function (x) {
|
|
5826
|
+
return x && x.id === v && (!sessionOverrideState.providerId || x.providerId === sessionOverrideState.providerId)
|
|
5827
|
+
})
|
|
5828
|
+
: null
|
|
5829
|
+
composerModelInput.title = entry
|
|
5830
|
+
? 'session 级模型覆盖(服务商 · 模型):' + entry.display
|
|
5831
|
+
: 'session 级模型覆盖(模型 ID,需与配置一致):' + v
|
|
5880
5832
|
}
|
|
5881
5833
|
|
|
5882
5834
|
function syncModelStateToEnv() {
|
|
5883
5835
|
const env = getSessionOverrideEnv()
|
|
5884
|
-
const model = typeof sessionOverrideState.model === 'string'
|
|
5885
|
-
|
|
5886
|
-
|
|
5836
|
+
const model = typeof sessionOverrideState.model === 'string' ? sessionOverrideState.model.trim() : ''
|
|
5837
|
+
const providerId =
|
|
5838
|
+
typeof sessionOverrideState.providerId === 'string'
|
|
5839
|
+
? sessionOverrideState.providerId.trim()
|
|
5840
|
+
: ''
|
|
5841
|
+
env[LLM_PROVIDER_ID_KEY] = providerId
|
|
5887
5842
|
env[LLM_MODEL_KEY] = model
|
|
5888
5843
|
}
|
|
5889
5844
|
|
|
@@ -6044,6 +5999,7 @@
|
|
|
6044
5999
|
function resetSessionOverrideStateToDefault() {
|
|
6045
6000
|
const defaults = buildDefaultSessionOverrideState()
|
|
6046
6001
|
sessionOverrideState = {
|
|
6002
|
+
providerId: defaults.providerId,
|
|
6047
6003
|
model: defaults.model,
|
|
6048
6004
|
env: Object.assign({}, defaults.env),
|
|
6049
6005
|
}
|
|
@@ -6075,6 +6031,10 @@
|
|
|
6075
6031
|
)
|
|
6076
6032
|
: {}
|
|
6077
6033
|
sessionOverrideState = {
|
|
6034
|
+
providerId:
|
|
6035
|
+
typeof ovEnv[LLM_PROVIDER_ID_KEY] === 'string'
|
|
6036
|
+
? String(ovEnv[LLM_PROVIDER_ID_KEY]).trim()
|
|
6037
|
+
: defaults.providerId,
|
|
6078
6038
|
model:
|
|
6079
6039
|
typeof ovEnv[LLM_MODEL_KEY] === 'string'
|
|
6080
6040
|
? String(ovEnv[LLM_MODEL_KEY]).trim()
|
|
@@ -6102,6 +6062,10 @@
|
|
|
6102
6062
|
const env = getSessionOverrideEnv()
|
|
6103
6063
|
const patchEnv = {}
|
|
6104
6064
|
const defaults = buildDefaultSessionOverrideState()
|
|
6065
|
+
patchEnv[LLM_PROVIDER_ID_KEY] =
|
|
6066
|
+
typeof sessionOverrideState.providerId === 'string'
|
|
6067
|
+
? sessionOverrideState.providerId.trim()
|
|
6068
|
+
: defaults.providerId
|
|
6105
6069
|
patchEnv[LLM_MODEL_KEY] =
|
|
6106
6070
|
typeof sessionOverrideState.model === 'string'
|
|
6107
6071
|
? sessionOverrideState.model.trim()
|
|
@@ -6317,6 +6281,7 @@
|
|
|
6317
6281
|
isolation: '',
|
|
6318
6282
|
whentouse: '',
|
|
6319
6283
|
env: {},
|
|
6284
|
+
llmProviders: [],
|
|
6320
6285
|
}
|
|
6321
6286
|
renderBindChips()
|
|
6322
6287
|
syncModelInputFromState()
|
|
@@ -6353,6 +6318,9 @@
|
|
|
6353
6318
|
: '',
|
|
6354
6319
|
whentouse: typeof c.whentouse === 'string' ? c.whentouse : '',
|
|
6355
6320
|
env: c.env && typeof c.env === 'object' ? Object.fromEntries(Object.entries(c.env).map(function (kv) { return [String(kv[0]), String(kv[1])] })) : {},
|
|
6321
|
+
llmProviders: Array.isArray(c.llmProviders)
|
|
6322
|
+
? cloneLlmProvidersDraft(c.llmProviders)
|
|
6323
|
+
: [],
|
|
6356
6324
|
}
|
|
6357
6325
|
} else {
|
|
6358
6326
|
agentConfigState = {
|
|
@@ -6368,6 +6336,7 @@
|
|
|
6368
6336
|
isolation: '',
|
|
6369
6337
|
whentouse: '',
|
|
6370
6338
|
env: {},
|
|
6339
|
+
llmProviders: [],
|
|
6371
6340
|
}
|
|
6372
6341
|
}
|
|
6373
6342
|
if (body.agent && Array.isArray(body.agent.agentTeamMembers)) {
|
|
@@ -7623,6 +7592,7 @@
|
|
|
7623
7592
|
ev.preventDefault()
|
|
7624
7593
|
ev.stopPropagation()
|
|
7625
7594
|
closeRepoManageView()
|
|
7595
|
+
closeAgentSettingsView()
|
|
7626
7596
|
const items = sidebarNavEl.querySelectorAll('.sidebar-nav-row')
|
|
7627
7597
|
for (let i = 0; i < items.length; i++) {
|
|
7628
7598
|
items[i].classList.toggle('is-active', items[i] === btn)
|
|
@@ -7644,6 +7614,7 @@
|
|
|
7644
7614
|
ev.preventDefault()
|
|
7645
7615
|
ev.stopPropagation()
|
|
7646
7616
|
closeRepoManageView()
|
|
7617
|
+
closeAgentSettingsView()
|
|
7647
7618
|
const items = sidebarNavEl.querySelectorAll('.sidebar-nav-row')
|
|
7648
7619
|
for (let i = 0; i < items.length; i++) {
|
|
7649
7620
|
items[i].classList.toggle('is-active', items[i] === btn)
|
|
@@ -7872,6 +7843,7 @@
|
|
|
7872
7843
|
ev.preventDefault()
|
|
7873
7844
|
ev.stopPropagation()
|
|
7874
7845
|
closeRepoManageView()
|
|
7846
|
+
closeAgentSettingsView()
|
|
7875
7847
|
if (sidebarNavEl) {
|
|
7876
7848
|
const items = sidebarNavEl.querySelectorAll('.sidebar-nav-row')
|
|
7877
7849
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -7960,40 +7932,40 @@
|
|
|
7960
7932
|
}
|
|
7961
7933
|
}
|
|
7962
7934
|
|
|
7963
|
-
function closeAgentEnvModal() {
|
|
7964
|
-
if (!agentEnvModal) return
|
|
7965
|
-
agentEnvModal.classList.remove('is-open')
|
|
7966
|
-
agentEnvModal.setAttribute('aria-hidden', 'true')
|
|
7967
|
-
document.body.style.overflow = ''
|
|
7968
|
-
if (agentEnvModalCard) {
|
|
7969
|
-
agentEnvModalCard.removeAttribute('aria-label')
|
|
7970
|
-
agentEnvModalCard.setAttribute('aria-labelledby', 'agentEnvModalTitle')
|
|
7971
|
-
}
|
|
7972
|
-
if (agentEnvSessionTabStrip) agentEnvSessionTabStrip.setAttribute('hidden', '')
|
|
7973
|
-
if (agentEnvSessionTabList) agentEnvSessionTabList.innerHTML = ''
|
|
7974
|
-
}
|
|
7975
|
-
|
|
7976
7935
|
function syncAgentEnvTabPanels() {
|
|
7977
|
-
if (!
|
|
7978
|
-
|
|
7936
|
+
if (!agentEnvPanelUnified || !agentEnvPanelAgentMd) return
|
|
7937
|
+
agentEnvPanelUnified.hidden = agentEnvActiveTab !== 'settings'
|
|
7979
7938
|
agentEnvPanelAgentMd.hidden = agentEnvActiveTab !== 'agentMd'
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7939
|
+
}
|
|
7940
|
+
|
|
7941
|
+
/** 渲染左侧配置导航(样式对齐文件管理目录树) */
|
|
7942
|
+
function renderAgentSettingsNav() {
|
|
7943
|
+
if (!agentEnvSessionTabList) return
|
|
7944
|
+
agentEnvSessionTabList.innerHTML = ''
|
|
7945
|
+
const tabs = [
|
|
7946
|
+
{ id: 'settings', label: '设置' },
|
|
7947
|
+
{ id: 'agentMd', label: '编辑 agent.md' },
|
|
7948
|
+
]
|
|
7949
|
+
for (let i = 0; i < tabs.length; i++) {
|
|
7950
|
+
const tab = tabs[i]
|
|
7951
|
+
const btn = document.createElement('button')
|
|
7952
|
+
btn.type = 'button'
|
|
7953
|
+
btn.className =
|
|
7954
|
+
'agent-settings-nav-item' + (tab.id === agentEnvActiveTab ? ' is-selected' : '')
|
|
7955
|
+
btn.setAttribute('role', 'tab')
|
|
7956
|
+
btn.setAttribute('aria-selected', tab.id === agentEnvActiveTab ? 'true' : 'false')
|
|
7957
|
+
btn.textContent = tab.label
|
|
7958
|
+
btn.addEventListener('click', function () {
|
|
7959
|
+
if (agentEnvActiveTab === tab.id) return
|
|
7960
|
+
agentEnvActiveTab = tab.id
|
|
7961
|
+
renderAgentSettingsNav()
|
|
7994
7962
|
syncAgentEnvTabPanels()
|
|
7995
|
-
|
|
7996
|
-
|
|
7963
|
+
if (tab.id === 'agentMd') {
|
|
7964
|
+
void loadAgentEnvAgentMdFromApi()
|
|
7965
|
+
}
|
|
7966
|
+
})
|
|
7967
|
+
agentEnvSessionTabList.appendChild(btn)
|
|
7968
|
+
}
|
|
7997
7969
|
}
|
|
7998
7970
|
|
|
7999
7971
|
function loadAgentEnvAgentMdFromApi() {
|
|
@@ -8044,27 +8016,21 @@
|
|
|
8044
8016
|
})
|
|
8045
8017
|
}
|
|
8046
8018
|
|
|
8047
|
-
function
|
|
8048
|
-
if (!
|
|
8049
|
-
|
|
8019
|
+
function openAgentSettingsView(initialTab) {
|
|
8020
|
+
if (!agentSettingsView) return
|
|
8021
|
+
closeRepoManageView()
|
|
8022
|
+
if (initialTab === 'agentMd' || initialTab === 'settings') {
|
|
8050
8023
|
agentEnvActiveTab = initialTab
|
|
8051
8024
|
} else {
|
|
8052
|
-
agentEnvActiveTab = '
|
|
8025
|
+
agentEnvActiveTab = 'settings'
|
|
8053
8026
|
}
|
|
8054
8027
|
const envObj =
|
|
8055
8028
|
agentConfigState.env && typeof agentConfigState.env === 'object'
|
|
8056
8029
|
? agentConfigState.env
|
|
8057
8030
|
: {}
|
|
8058
8031
|
agentEnvFieldValues = buildAgentEnvFieldValues(envObj)
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
agentEnvModalCard.removeAttribute('aria-labelledby')
|
|
8062
|
-
}
|
|
8063
|
-
if (agentEnvSessionTabStrip) {
|
|
8064
|
-
agentEnvSessionTabStrip.setAttribute('aria-label', '环境变量、编辑 agent.md、设置')
|
|
8065
|
-
agentEnvSessionTabStrip.removeAttribute('hidden')
|
|
8066
|
-
}
|
|
8067
|
-
/** 每次打开设置弹窗都拉最新 schema,避免后端新增字段后页面仍用旧缓存。 */
|
|
8032
|
+
setAgentSettingsVisible(true)
|
|
8033
|
+
/** 每次进入设置页都拉最新 schema,避免后端新增字段后页面仍用旧缓存。 */
|
|
8068
8034
|
void loadProjectEnvDefaults().then(function () {
|
|
8069
8035
|
const latestEnvObj =
|
|
8070
8036
|
agentConfigState.env && typeof agentConfigState.env === 'object'
|
|
@@ -8073,104 +8039,188 @@
|
|
|
8073
8039
|
agentEnvFieldValues = buildAgentEnvFieldValues(latestEnvObj)
|
|
8074
8040
|
renderAgentEnvList()
|
|
8075
8041
|
})
|
|
8076
|
-
|
|
8042
|
+
renderAgentSettingsNav()
|
|
8077
8043
|
syncAgentEnvTabPanels()
|
|
8078
|
-
|
|
8079
|
-
agentEnvModal.setAttribute('aria-hidden', 'false')
|
|
8080
|
-
document.body.style.overflow = 'hidden'
|
|
8044
|
+
initLlmProvidersSettingsUi()
|
|
8081
8045
|
renderAgentEnvList()
|
|
8082
|
-
|
|
8083
|
-
|
|
8046
|
+
const whenInput = queryAgentEnvWhenToUseInput()
|
|
8047
|
+
if (whenInput) {
|
|
8048
|
+
whenInput.value = typeof agentConfigState.whentouse === 'string'
|
|
8084
8049
|
? agentConfigState.whentouse
|
|
8085
8050
|
: ''
|
|
8086
8051
|
}
|
|
8087
|
-
if (agentEnvAgentTypeInput) {
|
|
8088
|
-
agentEnvAgentTypeInput.value = currentAgentParam()
|
|
8089
|
-
}
|
|
8090
|
-
if (agentEnvToolsInput) {
|
|
8091
|
-
agentEnvToolsInput.value = Array.isArray(agentConfigState.tools)
|
|
8092
|
-
? agentConfigState.tools.join('\n')
|
|
8093
|
-
: ''
|
|
8094
|
-
}
|
|
8095
|
-
if (agentEnvModelInput) {
|
|
8096
|
-
agentEnvModelInput.value = typeof agentConfigState.model === 'string'
|
|
8097
|
-
? agentConfigState.model
|
|
8098
|
-
: ''
|
|
8099
|
-
}
|
|
8100
|
-
syncAgentEffortSelectValue(agentConfigState.effort)
|
|
8101
|
-
if (agentEnvBackgroundInput) {
|
|
8102
|
-
agentEnvBackgroundInput.value = agentConfigState.background === true ? 'true' : 'false'
|
|
8103
|
-
}
|
|
8104
|
-
if (agentEnvIsolationInput) {
|
|
8105
|
-
const nextIsolation =
|
|
8106
|
-
agentConfigState.isolation === 'worktree' || agentConfigState.isolation === 'remote'
|
|
8107
|
-
? agentConfigState.isolation
|
|
8108
|
-
: ''
|
|
8109
|
-
agentEnvIsolationInput.value = nextIsolation
|
|
8110
|
-
}
|
|
8111
|
-
void renderAgentEnvTeamMembers()
|
|
8112
8052
|
void loadAgentEnvAgentMdFromApi()
|
|
8113
8053
|
}
|
|
8114
8054
|
|
|
8115
8055
|
/**
|
|
8116
|
-
*
|
|
8117
|
-
* 业务逻辑:
|
|
8118
|
-
* 1) 空值表示不设置 effort,统一写回 null;
|
|
8119
|
-
* 2) low/medium/high/max 直接透传;
|
|
8120
|
-
* 3) 兼容历史整数值(旧版本允许手输整数),继续转成 number 写回;
|
|
8121
|
-
* 4) 其他值视为非法,阻止保存并提示。
|
|
8056
|
+
* 深拷贝 agent.json.llmProviders 到设置页草稿。
|
|
8122
8057
|
*/
|
|
8123
|
-
function
|
|
8124
|
-
const
|
|
8125
|
-
|
|
8126
|
-
|
|
8058
|
+
function cloneLlmProvidersDraft(raw) {
|
|
8059
|
+
const list = Array.isArray(raw) ? raw : []
|
|
8060
|
+
return list.map(function (p) {
|
|
8061
|
+
const models = Array.isArray(p.models) ? p.models : []
|
|
8062
|
+
return {
|
|
8063
|
+
id: typeof p.id === 'string' ? p.id.trim() : '',
|
|
8064
|
+
name: typeof p.name === 'string' ? p.name.trim() : '',
|
|
8065
|
+
baseUrl: typeof p.baseUrl === 'string' ? p.baseUrl.trim() : '',
|
|
8066
|
+
apiKey: typeof p.apiKey === 'string' ? p.apiKey : '',
|
|
8067
|
+
authToken: typeof p.authToken === 'string' ? p.authToken : '',
|
|
8068
|
+
provider: p.provider === 'anthropic' ? 'anthropic' : 'openai',
|
|
8069
|
+
models: models.map(function (m) {
|
|
8070
|
+
return {
|
|
8071
|
+
id: typeof m.id === 'string' ? m.id.trim() : '',
|
|
8072
|
+
label: typeof m.label === 'string' ? m.label.trim() : '',
|
|
8073
|
+
}
|
|
8074
|
+
}),
|
|
8075
|
+
defaultModel: typeof p.defaultModel === 'string' ? p.defaultModel.trim() : '',
|
|
8076
|
+
}
|
|
8077
|
+
})
|
|
8078
|
+
}
|
|
8079
|
+
|
|
8080
|
+
function clearLlmProviderForm() {
|
|
8081
|
+
if (llmFormId) llmFormId.value = ''
|
|
8082
|
+
if (llmFormName) llmFormName.value = ''
|
|
8083
|
+
if (llmFormProvider) llmFormProvider.value = 'openai'
|
|
8084
|
+
if (llmFormBaseUrl) llmFormBaseUrl.value = ''
|
|
8085
|
+
if (llmFormApiKey) llmFormApiKey.value = ''
|
|
8086
|
+
if (llmFormAuthToken) llmFormAuthToken.value = ''
|
|
8087
|
+
if (llmFormModelId) llmFormModelId.value = ''
|
|
8088
|
+
if (llmFormModelLabel) llmFormModelLabel.value = ''
|
|
8089
|
+
}
|
|
8090
|
+
|
|
8091
|
+
/**
|
|
8092
|
+
* 从添加表单读取一条服务商配置;校验失败返回错误文案。
|
|
8093
|
+
*/
|
|
8094
|
+
function readLlmProviderFromForm() {
|
|
8095
|
+
const id = llmFormId ? String(llmFormId.value || '').trim() : ''
|
|
8096
|
+
const name = llmFormName ? String(llmFormName.value || '').trim() : ''
|
|
8097
|
+
const provider =
|
|
8098
|
+
llmFormProvider && llmFormProvider.value === 'anthropic' ? 'anthropic' : 'openai'
|
|
8099
|
+
const baseUrl = llmFormBaseUrl ? String(llmFormBaseUrl.value || '').trim() : ''
|
|
8100
|
+
const apiKey = llmFormApiKey ? String(llmFormApiKey.value || '').trim() : ''
|
|
8101
|
+
const authToken = llmFormAuthToken ? String(llmFormAuthToken.value || '').trim() : ''
|
|
8102
|
+
const modelId = llmFormModelId ? String(llmFormModelId.value || '').trim() : ''
|
|
8103
|
+
const modelLabel = llmFormModelLabel ? String(llmFormModelLabel.value || '').trim() : ''
|
|
8104
|
+
if (!id) return { ok: false, message: '请填写服务商 ID' }
|
|
8105
|
+
if (!/^[A-Za-z0-9_-]+$/.test(id)) {
|
|
8106
|
+
return { ok: false, message: '服务商 id 仅允许字母数字下划线中划线' }
|
|
8107
|
+
}
|
|
8108
|
+
if (llmProvidersDraft.some(function (p) {
|
|
8109
|
+
return p.id === id
|
|
8110
|
+
})) {
|
|
8111
|
+
return { ok: false, message: '服务商 id 已存在: ' + id }
|
|
8112
|
+
}
|
|
8113
|
+
if (!name) return { ok: false, message: '请填写展示名称' }
|
|
8114
|
+
if (!baseUrl) return { ok: false, message: '请填写 Base URL' }
|
|
8115
|
+
if (!apiKey && !authToken) {
|
|
8116
|
+
return { ok: false, message: 'API Key 与 Auth Token 至少填写一项' }
|
|
8117
|
+
}
|
|
8118
|
+
if (!modelId) return { ok: false, message: '请填写模型 ID' }
|
|
8119
|
+
return {
|
|
8120
|
+
ok: true,
|
|
8121
|
+
entry: {
|
|
8122
|
+
id: id,
|
|
8123
|
+
name: name,
|
|
8124
|
+
baseUrl: baseUrl,
|
|
8125
|
+
apiKey: apiKey,
|
|
8126
|
+
authToken: authToken,
|
|
8127
|
+
provider: provider,
|
|
8128
|
+
models: [{ id: modelId, label: modelLabel }],
|
|
8129
|
+
defaultModel: modelId,
|
|
8130
|
+
},
|
|
8127
8131
|
}
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8132
|
+
}
|
|
8133
|
+
|
|
8134
|
+
function renderLlmProvidersList() {
|
|
8135
|
+
if (!llmProvidersList) return
|
|
8136
|
+
llmProvidersList.innerHTML = ''
|
|
8137
|
+
if (llmProvidersEmptyHint) {
|
|
8138
|
+
llmProvidersEmptyHint.hidden = llmProvidersDraft.length > 0
|
|
8131
8139
|
}
|
|
8132
|
-
|
|
8133
|
-
|
|
8140
|
+
for (let i = 0; i < llmProvidersDraft.length; i++) {
|
|
8141
|
+
const p = llmProvidersDraft[i]
|
|
8142
|
+
const li = document.createElement('li')
|
|
8143
|
+
li.className = 'llm-provider-list-item'
|
|
8144
|
+
|
|
8145
|
+
const nameWrap = document.createElement('span')
|
|
8146
|
+
nameWrap.className = 'llm-provider-list-name'
|
|
8147
|
+
nameWrap.textContent = p.name || p.id
|
|
8148
|
+
if (i === 0) {
|
|
8149
|
+
const badge = document.createElement('span')
|
|
8150
|
+
badge.className = 'llm-provider-list-default'
|
|
8151
|
+
badge.textContent = '默认'
|
|
8152
|
+
nameWrap.appendChild(badge)
|
|
8153
|
+
}
|
|
8154
|
+
|
|
8155
|
+
const removeBtn = document.createElement('button')
|
|
8156
|
+
removeBtn.type = 'button'
|
|
8157
|
+
removeBtn.className = 'llm-provider-list-remove'
|
|
8158
|
+
removeBtn.textContent = '删除'
|
|
8159
|
+
removeBtn.addEventListener('click', function () {
|
|
8160
|
+
llmProvidersDraft.splice(i, 1)
|
|
8161
|
+
renderLlmProvidersList()
|
|
8162
|
+
})
|
|
8163
|
+
|
|
8164
|
+
li.appendChild(nameWrap)
|
|
8165
|
+
li.appendChild(removeBtn)
|
|
8166
|
+
llmProvidersList.appendChild(li)
|
|
8134
8167
|
}
|
|
8135
|
-
|
|
8168
|
+
}
|
|
8169
|
+
|
|
8170
|
+
function initLlmProvidersSettingsUi() {
|
|
8171
|
+
llmProvidersDraft = cloneLlmProvidersDraft(agentConfigState.llmProviders)
|
|
8172
|
+
clearLlmProviderForm()
|
|
8173
|
+
renderLlmProvidersList()
|
|
8136
8174
|
}
|
|
8137
8175
|
|
|
8138
8176
|
/**
|
|
8139
|
-
*
|
|
8140
|
-
* 业务逻辑:
|
|
8141
|
-
* 1) 常规场景只展示固定档位(low/medium/high/max);
|
|
8142
|
-
* 2) 若 agent.json 中已有历史整数值,为避免用户打开弹窗后被意外清空,
|
|
8143
|
-
* 动态追加一个“当前值”选项用于承载该值;
|
|
8144
|
-
* 3) 每次打开弹窗先清理旧的动态选项,防止重复追加。
|
|
8177
|
+
* 保存前校验 llmProviders 草稿;首项为默认厂商,允许空数组。
|
|
8145
8178
|
*/
|
|
8146
|
-
function
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8179
|
+
function validateLlmProvidersDraftForSave(providers) {
|
|
8180
|
+
const list = Array.isArray(providers) ? providers : []
|
|
8181
|
+
if (!list.length) return ''
|
|
8182
|
+
const seenIds = new Set()
|
|
8183
|
+
for (let i = 0; i < list.length; i++) {
|
|
8184
|
+
const p = list[i]
|
|
8185
|
+
const id = String(p.id || '').trim()
|
|
8186
|
+
if (!id) return '第 ' + (i + 1) + ' 个服务商缺少 id'
|
|
8187
|
+
if (!/^[A-Za-z0-9_-]+$/.test(id)) {
|
|
8188
|
+
return '服务商 id "' + id + '" 仅允许字母数字下划线中划线'
|
|
8189
|
+
}
|
|
8190
|
+
if (seenIds.has(id)) return '服务商 id 重复: ' + id
|
|
8191
|
+
seenIds.add(id)
|
|
8192
|
+
if (!String(p.name || '').trim()) return '服务商 "' + id + '" 缺少展示名称'
|
|
8193
|
+
if (!String(p.baseUrl || '').trim()) return '服务商 "' + id + '" 缺少 baseUrl'
|
|
8194
|
+
if (p.provider !== 'anthropic' && p.provider !== 'openai') {
|
|
8195
|
+
return '服务商 "' + id + '" 协议类型非法'
|
|
8196
|
+
}
|
|
8197
|
+
const apiKeyOk =
|
|
8198
|
+
p.apiKey === LLM_MASKED_CREDENTIAL || String(p.apiKey || '').trim().length > 0
|
|
8199
|
+
const authTokenOk =
|
|
8200
|
+
p.authToken === LLM_MASKED_CREDENTIAL ||
|
|
8201
|
+
String(p.authToken || '').trim().length > 0
|
|
8202
|
+
if (!apiKeyOk && !authTokenOk) {
|
|
8203
|
+
return '服务商 "' + id + '" 需配置 API Key 或 Auth Token 至少一项'
|
|
8204
|
+
}
|
|
8205
|
+
if (!Array.isArray(p.models) || p.models.length === 0) {
|
|
8206
|
+
return '服务商 "' + id + '" 至少配置 1 个模型'
|
|
8207
|
+
}
|
|
8208
|
+
for (let j = 0; j < p.models.length; j++) {
|
|
8209
|
+
if (!String(p.models[j].id || '').trim()) {
|
|
8210
|
+
return '服务商 "' + id + '" 第 ' + (j + 1) + ' 个模型缺少 id'
|
|
8211
|
+
}
|
|
8152
8212
|
}
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
return
|
|
8163
|
-
}
|
|
8164
|
-
if (/^-?\d+$/.test(raw)) {
|
|
8165
|
-
const opt = document.createElement('option')
|
|
8166
|
-
opt.value = raw
|
|
8167
|
-
opt.textContent = '当前值:' + raw + '(历史整数)'
|
|
8168
|
-
opt.setAttribute('data-dynamic-effort', 'true')
|
|
8169
|
-
agentEnvEffortInput.appendChild(opt)
|
|
8170
|
-
agentEnvEffortInput.value = raw
|
|
8171
|
-
return
|
|
8213
|
+
const defaultModel = String(p.defaultModel || '').trim() || p.models[0].id
|
|
8214
|
+
if (
|
|
8215
|
+
!p.models.some(function (m) {
|
|
8216
|
+
return m.id === defaultModel
|
|
8217
|
+
})
|
|
8218
|
+
) {
|
|
8219
|
+
return '服务商 "' + id + '" 默认模型不在模型列表中'
|
|
8220
|
+
}
|
|
8221
|
+
p.defaultModel = defaultModel
|
|
8172
8222
|
}
|
|
8173
|
-
|
|
8223
|
+
return ''
|
|
8174
8224
|
}
|
|
8175
8225
|
|
|
8176
8226
|
function openAgentMdModal() {
|
|
@@ -8244,30 +8294,11 @@
|
|
|
8244
8294
|
btnAgentEnvSave.disabled = true
|
|
8245
8295
|
}
|
|
8246
8296
|
try {
|
|
8247
|
-
const
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
: ''
|
|
8253
|
-
const effortParsed = parseAgentEffortFromInput(
|
|
8254
|
-
agentEnvEffortInput ? agentEnvEffortInput.value : '',
|
|
8255
|
-
)
|
|
8256
|
-
if (!effortParsed.ok) {
|
|
8257
|
-
window.alert(effortParsed.message)
|
|
8258
|
-
return
|
|
8259
|
-
}
|
|
8260
|
-
const nextBackground =
|
|
8261
|
-
agentEnvBackgroundInput && agentEnvBackgroundInput.value === 'true'
|
|
8262
|
-
const nextIsolationRaw = agentEnvIsolationInput
|
|
8263
|
-
? String(agentEnvIsolationInput.value || '').trim()
|
|
8264
|
-
: ''
|
|
8265
|
-
if (
|
|
8266
|
-
nextIsolationRaw !== '' &&
|
|
8267
|
-
nextIsolationRaw !== 'worktree' &&
|
|
8268
|
-
nextIsolationRaw !== 'remote'
|
|
8269
|
-
) {
|
|
8270
|
-
window.alert('isolation 仅支持 worktree/remote 或留空')
|
|
8297
|
+
const whenInput = queryAgentEnvWhenToUseInput()
|
|
8298
|
+
const whentouseValue = whenInput ? String(whenInput.value || '') : ''
|
|
8299
|
+
const llmErr = validateLlmProvidersDraftForSave(llmProvidersDraft)
|
|
8300
|
+
if (llmErr) {
|
|
8301
|
+
window.alert('LLM:' + llmErr)
|
|
8271
8302
|
return
|
|
8272
8303
|
}
|
|
8273
8304
|
const patchBody = {
|
|
@@ -8277,12 +8308,17 @@
|
|
|
8277
8308
|
mcpServers: agentConfigState.mcpServers.slice(),
|
|
8278
8309
|
skills: agentConfigState.skills.slice(),
|
|
8279
8310
|
agentTeams: agentConfigState.agentTeams.slice(),
|
|
8280
|
-
model:
|
|
8281
|
-
effort:
|
|
8282
|
-
background:
|
|
8283
|
-
isolation:
|
|
8311
|
+
model: typeof agentConfigState.model === 'string' ? agentConfigState.model : '',
|
|
8312
|
+
effort: agentConfigState.effort,
|
|
8313
|
+
background: agentConfigState.background === true,
|
|
8314
|
+
isolation:
|
|
8315
|
+
agentConfigState.isolation === 'worktree' ||
|
|
8316
|
+
agentConfigState.isolation === 'remote'
|
|
8317
|
+
? agentConfigState.isolation
|
|
8318
|
+
: '',
|
|
8284
8319
|
whentouse: whentouseValue,
|
|
8285
8320
|
env: checked.env,
|
|
8321
|
+
llmProviders: cloneLlmProvidersDraft(llmProvidersDraft),
|
|
8286
8322
|
},
|
|
8287
8323
|
}
|
|
8288
8324
|
const includeAgentMd =
|
|
@@ -8299,14 +8335,14 @@
|
|
|
8299
8335
|
return {}
|
|
8300
8336
|
})
|
|
8301
8337
|
if (!r.ok) {
|
|
8302
|
-
window.alert(
|
|
8338
|
+
window.alert(
|
|
8339
|
+
'Agent 配置保存失败:' + (body.message || body.error || r.statusText),
|
|
8340
|
+
)
|
|
8303
8341
|
return
|
|
8304
8342
|
}
|
|
8343
|
+
void refreshModelCatalogFromApi(true)
|
|
8305
8344
|
agentConfigState.env = Object.assign({}, checked.env)
|
|
8306
|
-
agentConfigState.
|
|
8307
|
-
agentConfigState.effort = effortParsed.value
|
|
8308
|
-
agentConfigState.background = nextBackground
|
|
8309
|
-
agentConfigState.isolation = nextIsolationRaw
|
|
8345
|
+
agentConfigState.llmProviders = cloneLlmProvidersDraft(llmProvidersDraft)
|
|
8310
8346
|
agentConfigState.whentouse = whentouseValue
|
|
8311
8347
|
syncPermissionModeSelectFromState()
|
|
8312
8348
|
if (includeAgentMd) {
|
|
@@ -8318,15 +8354,15 @@
|
|
|
8318
8354
|
window.alert('已保存到磁盘,但同步提示词到当前连接失败: ' + e)
|
|
8319
8355
|
}
|
|
8320
8356
|
}
|
|
8321
|
-
restartCurrentSessionSubprocessWithReason('save-agent-
|
|
8357
|
+
restartCurrentSessionSubprocessWithReason('save-agent-settings-prompt')
|
|
8322
8358
|
} else {
|
|
8323
|
-
restartCurrentSessionSubprocessWithReason('save-agent-
|
|
8359
|
+
restartCurrentSessionSubprocessWithReason('save-agent-settings')
|
|
8324
8360
|
}
|
|
8325
|
-
|
|
8361
|
+
closeAgentSettingsView()
|
|
8326
8362
|
setStatus('已保存', serverReady ? 'ready' : '')
|
|
8327
8363
|
window.alert('已保存到磁盘,并已重连 / 尝试同步当前 session。')
|
|
8328
8364
|
} catch (e) {
|
|
8329
|
-
window.alert(
|
|
8365
|
+
window.alert(String(e && e.message ? e.message : e))
|
|
8330
8366
|
} finally {
|
|
8331
8367
|
if (btnAgentEnvSave) {
|
|
8332
8368
|
btnAgentEnvSave.disabled = false
|
|
@@ -8372,7 +8408,7 @@
|
|
|
8372
8408
|
}
|
|
8373
8409
|
if (btnEditAgentEnv) {
|
|
8374
8410
|
btnEditAgentEnv.addEventListener('click', function () {
|
|
8375
|
-
|
|
8411
|
+
openAgentSettingsView('settings')
|
|
8376
8412
|
})
|
|
8377
8413
|
}
|
|
8378
8414
|
if (btnSkillPackageManager) {
|
|
@@ -8621,23 +8657,24 @@
|
|
|
8621
8657
|
openToolFilesModal()
|
|
8622
8658
|
})
|
|
8623
8659
|
}
|
|
8624
|
-
if (btnAgentEnvModalClose) {
|
|
8625
|
-
btnAgentEnvModalClose.addEventListener('click', closeAgentEnvModal)
|
|
8626
|
-
}
|
|
8627
8660
|
if (btnAgentEnvCancel) {
|
|
8628
|
-
btnAgentEnvCancel.addEventListener('click',
|
|
8629
|
-
}
|
|
8630
|
-
if (agentEnvModalBackdrop) {
|
|
8631
|
-
agentEnvModalBackdrop.addEventListener('click', closeAgentEnvModal)
|
|
8661
|
+
btnAgentEnvCancel.addEventListener('click', closeAgentSettingsView)
|
|
8632
8662
|
}
|
|
8633
8663
|
if (btnAgentEnvSave) {
|
|
8634
8664
|
btnAgentEnvSave.addEventListener('click', function () {
|
|
8635
8665
|
void saveAgentEnvAndReconnect()
|
|
8636
8666
|
})
|
|
8637
8667
|
}
|
|
8638
|
-
if (
|
|
8639
|
-
|
|
8640
|
-
|
|
8668
|
+
if (btnLlmProvidersAdd) {
|
|
8669
|
+
btnLlmProvidersAdd.addEventListener('click', function () {
|
|
8670
|
+
const parsed = readLlmProviderFromForm()
|
|
8671
|
+
if (!parsed.ok) {
|
|
8672
|
+
window.alert(parsed.message)
|
|
8673
|
+
return
|
|
8674
|
+
}
|
|
8675
|
+
llmProvidersDraft.push(parsed.entry)
|
|
8676
|
+
clearLlmProviderForm()
|
|
8677
|
+
renderLlmProvidersList()
|
|
8641
8678
|
})
|
|
8642
8679
|
}
|
|
8643
8680
|
if (btnAgentMdModalClose) {
|
|
@@ -8705,10 +8742,6 @@
|
|
|
8705
8742
|
closeSkillMdModal()
|
|
8706
8743
|
return
|
|
8707
8744
|
}
|
|
8708
|
-
if (agentEnvModal && agentEnvModal.classList.contains('is-open')) {
|
|
8709
|
-
closeAgentEnvModal()
|
|
8710
|
-
return
|
|
8711
|
-
}
|
|
8712
8745
|
if (scheduleRunsModal && scheduleRunsModal.classList.contains('is-open')) {
|
|
8713
8746
|
closeScheduleRunsModal()
|
|
8714
8747
|
return
|
|
@@ -8758,6 +8791,9 @@
|
|
|
8758
8791
|
void bootstrapSessionSelection()
|
|
8759
8792
|
void refreshModelCatalogFromApi(true)
|
|
8760
8793
|
void loadAgentConfig()
|
|
8794
|
+
if (isAgentSettingsViewActive()) {
|
|
8795
|
+
openAgentSettingsView(agentEnvActiveTab)
|
|
8796
|
+
}
|
|
8761
8797
|
}
|
|
8762
8798
|
if (agentIdInput) {
|
|
8763
8799
|
agentIdInput.addEventListener('change', onAgentChanged)
|