@auto-ai/agent 2.1.105 → 2.1.106

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.
@@ -127,6 +127,68 @@
127
127
  overflow: hidden;
128
128
  text-overflow: ellipsis;
129
129
  }
130
+ .agent-list-team {
131
+ font-size: 0.78rem;
132
+ color: var(--ds-accent);
133
+ white-space: nowrap;
134
+ overflow: hidden;
135
+ text-overflow: ellipsis;
136
+ }
137
+ .office-team-section {
138
+ margin-bottom: 1rem;
139
+ padding-top: 0.25rem;
140
+ border-top: 1px solid var(--ds-border);
141
+ }
142
+ .office-team-section h3 {
143
+ margin: 0 0 0.5rem;
144
+ font-size: 0.88rem;
145
+ font-weight: 600;
146
+ }
147
+ .office-team-chips {
148
+ display: flex;
149
+ flex-wrap: wrap;
150
+ gap: 6px;
151
+ margin-bottom: 0.65rem;
152
+ min-height: 28px;
153
+ }
154
+ .office-team-chip {
155
+ display: inline-flex;
156
+ align-items: center;
157
+ gap: 4px;
158
+ padding: 0.2rem 0.45rem 0.2rem 0.55rem;
159
+ border-radius: 999px;
160
+ background: rgba(10, 132, 255, 0.1);
161
+ border: 1px solid rgba(10, 132, 255, 0.25);
162
+ font-size: 0.78rem;
163
+ }
164
+ .office-team-chip button {
165
+ border: none;
166
+ background: transparent;
167
+ color: var(--ds-muted);
168
+ cursor: pointer;
169
+ font-size: 1rem;
170
+ line-height: 1;
171
+ padding: 0 2px;
172
+ }
173
+ .office-team-chip button:hover { color: var(--ds-danger); }
174
+ .office-team-empty {
175
+ font-size: 0.8rem;
176
+ color: var(--ds-muted);
177
+ margin: 0 0 0.5rem;
178
+ }
179
+ .office-team-add {
180
+ display: flex;
181
+ gap: 8px;
182
+ align-items: center;
183
+ }
184
+ .office-team-add select {
185
+ flex: 1;
186
+ min-width: 0;
187
+ border: 1px solid var(--ds-border);
188
+ border-radius: 10px;
189
+ padding: 0.45rem 0.55rem;
190
+ font: inherit;
191
+ }
130
192
  .agent-list-arrow {
131
193
  flex-shrink: 0;
132
194
  color: var(--ds-muted);
@@ -289,6 +351,17 @@
289
351
  <label for="officeAgentPrompt">agent.md</label>
290
352
  <textarea id="officeAgentPrompt" spellcheck="false" placeholder="Agent 提示词"></textarea>
291
353
  </div>
354
+ <section class="office-team-section" id="officeTeamSection" hidden>
355
+ <h3>绑定队员</h3>
356
+ <p id="officeTeamEmpty" class="office-team-empty" hidden>尚未绑定队员</p>
357
+ <div id="officeTeamChips" class="office-team-chips"></div>
358
+ <div class="office-team-add">
359
+ <select id="officeTeamPick" aria-label="选择队员">
360
+ <option value="">选择要添加的 Agent…</option>
361
+ </select>
362
+ <button type="button" class="office-btn office-btn-primary" id="btnOfficeAddTeamMember">添加</button>
363
+ </div>
364
+ </section>
292
365
  <div class="office-form-actions">
293
366
  <button type="button" class="office-btn office-btn-primary" id="btnOfficeSave">保存</button>
294
367
  <button type="button" class="office-btn office-btn-danger" id="btnOfficeDelete">删除</button>
@@ -332,6 +405,73 @@
332
405
  ).toString()
333
406
  }
334
407
 
408
+ /** 构建队员管理子资源地址 */
409
+ function buildAgentTeamMembersUrl(leaderId) {
410
+ return new URL(
411
+ '/api/agents/' +
412
+ encodeURIComponent(String(leaderId || '').trim()) +
413
+ '/team-members',
414
+ httpOriginForApi() + '/',
415
+ ).toString()
416
+ }
417
+
418
+ function buildAgentTeamMemberUrl(leaderId, agentType) {
419
+ return new URL(
420
+ '/api/agents/' +
421
+ encodeURIComponent(String(leaderId || '').trim()) +
422
+ '/team-members/' +
423
+ encodeURIComponent(String(agentType || '').trim()),
424
+ httpOriginForApi() + '/',
425
+ ).toString()
426
+ }
427
+
428
+ /**
429
+ * 拉取可绑定队员候选:仅 agent-runtime 下已创建的 Agent(与 agent.json.agentTeams 同源)。
430
+ * 不使用 /api/agent-teams(那是仓库内置 subagent 类型,不能写入 agentTeams)。
431
+ */
432
+ async function fetchTeamMemberCatalog(leaderId) {
433
+ const leader = String(leaderId || '').trim()
434
+ const agents = await fetchAgentsList()
435
+ const out = []
436
+ for (let i = 0; i < agents.length; i++) {
437
+ const row = agents[i]
438
+ const id = String(row.id || '').trim()
439
+ if (!id || id === leader) continue
440
+ out.push({
441
+ agentType: id,
442
+ whenToUse: typeof row.whentouse === 'string' ? row.whentouse : '',
443
+ })
444
+ }
445
+ out.sort((a, b) => a.agentType.localeCompare(b.agentType))
446
+ return out
447
+ }
448
+
449
+ /** 向 leader 追加一名绑定队员 */
450
+ async function addTeamMember(leaderId, agentType) {
451
+ const r = await fetch(buildAgentTeamMembersUrl(leaderId), {
452
+ method: 'POST',
453
+ headers: { 'content-type': 'application/json; charset=utf-8' },
454
+ body: JSON.stringify({ agentType: String(agentType || '').trim() }),
455
+ })
456
+ const body = await r.json().catch(() => ({}))
457
+ if (!r.ok) {
458
+ throw new Error(body.message || body.error || r.statusText)
459
+ }
460
+ return body.agent || null
461
+ }
462
+
463
+ /** 从 leader 移除一名绑定队员 */
464
+ async function removeTeamMember(leaderId, agentType) {
465
+ const r = await fetch(buildAgentTeamMemberUrl(leaderId, agentType), {
466
+ method: 'DELETE',
467
+ })
468
+ const body = await r.json().catch(() => ({}))
469
+ if (!r.ok) {
470
+ throw new Error(body.message || body.error || r.statusText)
471
+ }
472
+ return body.agent || null
473
+ }
474
+
335
475
  /** 校验新建 agent id:字母数字下划线中划线,1~64 */
336
476
  function isValidAgentIdInput(id) {
337
477
  return /^[a-zA-Z0-9_-]{1,64}$/.test(String(id || '').trim())
@@ -505,6 +645,19 @@
505
645
  return s.length <= max ? s : s.slice(0, max - 1) + '…'
506
646
  }
507
647
 
648
+ /** 列表副标题:绑定队员摘要 */
649
+ function formatAgentTeamLine(row) {
650
+ const members = Array.isArray(row.agentTeamMembers)
651
+ ? row.agentTeamMembers
652
+ : []
653
+ if (!members.length) return ''
654
+ const names = members.map((m) => String(m.agentType || '').trim()).filter(Boolean)
655
+ const n = names.length
656
+ const preview = names.slice(0, 3).join('、')
657
+ const suffix = n > 3 ? '…' : ''
658
+ return n + ' 名队员 · ' + preview + suffix
659
+ }
660
+
508
661
  /** Agent 列表 DOM 渲染 */
509
662
  class AgentList {
510
663
  constructor(container, onPick) {
@@ -558,6 +711,14 @@
558
711
  body.appendChild(title)
559
712
  body.appendChild(desc)
560
713
 
714
+ const teamLine = formatAgentTeamLine(row)
715
+ if (teamLine) {
716
+ const team = document.createElement('span')
717
+ team.className = 'agent-list-team'
718
+ team.textContent = teamLine
719
+ body.appendChild(team)
720
+ }
721
+
561
722
  const arrow = document.createElement('span')
562
723
  arrow.className = 'agent-list-arrow'
563
724
  arrow.textContent = '›'
@@ -604,6 +765,12 @@
604
765
  this.btnWorkbench = root.querySelector('#btnOfficeWorkbench')
605
766
  this.btnClose = root.querySelector('#btnOfficePanelClose')
606
767
  this.toastEl = root.querySelector('#officeToast')
768
+ this.teamSection = root.querySelector('#officeTeamSection')
769
+ this.teamChips = root.querySelector('#officeTeamChips')
770
+ this.teamEmpty = root.querySelector('#officeTeamEmpty')
771
+ this.teamPick = root.querySelector('#officeTeamPick')
772
+ this.btnAddTeamMember = root.querySelector('#btnOfficeAddTeamMember')
773
+ this.boundTeamMembers = []
607
774
 
608
775
  this.bindEvents()
609
776
  this.hidePanel()
@@ -635,6 +802,150 @@
635
802
  '/ws-test.html?agent=' + encodeURIComponent(id)
636
803
  })
637
804
  }
805
+ if (this.btnAddTeamMember) {
806
+ this.btnAddTeamMember.addEventListener('click', () =>
807
+ void this.handleAddTeamMember(),
808
+ )
809
+ }
810
+ }
811
+
812
+ /** 同步侧栏队员 chips 与下拉候选 */
813
+ renderTeamMembers(leaderId, members, catalog) {
814
+ if (!this.teamSection) return
815
+ const isEdit = this.mode === 'edit' && !!leaderId
816
+ this.teamSection.hidden = !isEdit
817
+ if (!isEdit) return
818
+
819
+ const list = Array.isArray(members) ? members : []
820
+ this.boundTeamMembers = list
821
+
822
+ if (this.teamEmpty) {
823
+ this.teamEmpty.hidden = list.length > 0
824
+ }
825
+ if (this.teamChips) {
826
+ this.teamChips.innerHTML = ''
827
+ const boundSet = new Set(
828
+ list.map((m) => String(m.agentType || '').trim()).filter(Boolean),
829
+ )
830
+ for (let i = 0; i < list.length; i++) {
831
+ const m = list[i]
832
+ const agentType = String(m.agentType || '').trim()
833
+ if (!agentType) continue
834
+ const chip = document.createElement('span')
835
+ chip.className = 'office-team-chip'
836
+ chip.title =
837
+ typeof m.whentouse === 'string' && m.whentouse.trim()
838
+ ? m.whentouse.trim()
839
+ : agentType
840
+ const label = document.createElement('span')
841
+ label.textContent = agentType
842
+ const rm = document.createElement('button')
843
+ rm.type = 'button'
844
+ rm.setAttribute('aria-label', '移除 ' + agentType)
845
+ rm.textContent = '×'
846
+ rm.addEventListener('click', () =>
847
+ void this.handleRemoveTeamMember(agentType),
848
+ )
849
+ chip.appendChild(label)
850
+ chip.appendChild(rm)
851
+ this.teamChips.appendChild(chip)
852
+ }
853
+
854
+ if (this.teamPick) {
855
+ this.teamPick.innerHTML =
856
+ '<option value="">选择要添加的 Agent…</option>'
857
+ const types = Array.isArray(catalog) ? catalog : []
858
+ for (let j = 0; j < types.length; j++) {
859
+ const t = types[j]
860
+ const id = String(t.agentType || '').trim()
861
+ if (!id || id === leaderId || boundSet.has(id)) continue
862
+ const opt = document.createElement('option')
863
+ opt.value = id
864
+ const whenRaw =
865
+ typeof t.whenToUse === 'string'
866
+ ? t.whenToUse
867
+ : typeof t.whentouse === 'string'
868
+ ? t.whentouse
869
+ : ''
870
+ const when = whenRaw.trim()
871
+ opt.textContent = when ? id + ' — ' + when.slice(0, 40) : id
872
+ this.teamPick.appendChild(opt)
873
+ }
874
+ }
875
+ }
876
+ }
877
+
878
+ async handleAddTeamMember() {
879
+ const leaderId = this.editingId
880
+ if (!leaderId || !this.teamPick) return
881
+ const agentType = this.teamPick.value.trim()
882
+ if (!agentType) {
883
+ this.showToast('请先选择要添加的队员', true)
884
+ return
885
+ }
886
+ try {
887
+ const agent = await addTeamMember(leaderId, agentType)
888
+ await this.refreshAgents()
889
+ const members =
890
+ agent && Array.isArray(agent.agentTeamMembers)
891
+ ? agent.agentTeamMembers
892
+ : this.boundTeamMembers
893
+ this.syncBaseConfigFromTeamAgent(agent)
894
+ const catalog = await fetchTeamMemberCatalog(leaderId)
895
+ this.renderTeamMembers(leaderId, members, catalog)
896
+ this.teamPick.value = ''
897
+ this.showToast('已添加队员 ' + agentType)
898
+ } catch (e) {
899
+ this.showToast('添加队员失败: ' + String(e), true)
900
+ }
901
+ }
902
+
903
+ async handleRemoveTeamMember(agentType) {
904
+ const leaderId = this.editingId
905
+ if (!leaderId) return
906
+ if (
907
+ !window.confirm('确定移除队员「' + agentType + '」?')
908
+ ) {
909
+ return
910
+ }
911
+ try {
912
+ const agent = await removeTeamMember(leaderId, agentType)
913
+ await this.refreshAgents()
914
+ const members =
915
+ agent && Array.isArray(agent.agentTeamMembers)
916
+ ? agent.agentTeamMembers
917
+ : []
918
+ this.syncBaseConfigFromTeamAgent(agent)
919
+ const catalog = await fetchTeamMemberCatalog(leaderId)
920
+ this.renderTeamMembers(leaderId, members, catalog)
921
+ this.showToast('已移除队员')
922
+ } catch (e) {
923
+ this.showToast('移除队员失败: ' + String(e), true)
924
+ }
925
+ }
926
+
927
+ /** 队员增删后同步 baseConfig,避免点「保存」把 agentTeams 写回旧值 */
928
+ syncBaseConfigFromTeamAgent(agentRow) {
929
+ if (!agentRow || typeof agentRow !== 'object') return
930
+ const teams = Array.isArray(agentRow.agentTeams)
931
+ ? agentRow.agentTeams.map(String)
932
+ : []
933
+ if (this.baseConfig && typeof this.baseConfig === 'object') {
934
+ this.baseConfig = { ...this.baseConfig, agentTeams: teams }
935
+ return
936
+ }
937
+ this.baseConfig = {
938
+ tools: [],
939
+ disallowedTools: [],
940
+ mcpServers: [],
941
+ skills: [],
942
+ agentTeams: teams,
943
+ model: '',
944
+ background: false,
945
+ isolation: '',
946
+ whentouse: this.whenInput ? this.whenInput.value : '',
947
+ env: {},
948
+ }
638
949
  }
639
950
 
640
951
  showToast(msg, isError) {
@@ -653,6 +964,7 @@
653
964
  this.editingId = null
654
965
  this.baseConfig = null
655
966
  this.mode = 'idle'
967
+ this.renderTeamMembers('', [], [])
656
968
  this.list.render(this.agents, null)
657
969
  }
658
970
 
@@ -681,6 +993,7 @@
681
993
  if (this.promptInput) this.promptInput.value = ''
682
994
  if (this.btnDelete) this.btnDelete.disabled = true
683
995
  if (this.btnWorkbench) this.btnWorkbench.disabled = true
996
+ this.renderTeamMembers('', [], [])
684
997
  this.list.render(this.agents, null)
685
998
  }
686
999
 
@@ -714,21 +1027,36 @@
714
1027
  this.promptInput.value =
715
1028
  typeof agent.prompt === 'string' ? agent.prompt : ''
716
1029
  }
1030
+ const members = Array.isArray(agent.agentTeamMembers)
1031
+ ? agent.agentTeamMembers
1032
+ : []
1033
+ const catalog = await fetchTeamMemberCatalog(agentId)
1034
+ this.renderTeamMembers(agentId, members, catalog)
1035
+ if (this.baseConfig && typeof this.baseConfig === 'object') {
1036
+ this.baseConfig.agentTeams = Array.isArray(agent.agentTeams)
1037
+ ? agent.agentTeams.map(String)
1038
+ : members.map((m) => String(m.agentType || '')).filter(Boolean)
1039
+ }
717
1040
  if (this.panelMetaEl) {
1041
+ const teamHint =
1042
+ members.length > 0
1043
+ ? ' · ' + members.length + ' 名队员'
1044
+ : ''
718
1045
  this.panelMetaEl.textContent =
719
1046
  '共 ' +
720
1047
  String(this.agents.length) +
721
- ' 个 Agent · 上下文:' +
722
- this.contextAgent
1048
+ ' 个 Agent' +
1049
+ teamHint
723
1050
  }
724
1051
  } catch (e) {
725
1052
  if (this.panelMetaEl) this.panelMetaEl.textContent = '加载失败'
1053
+ this.renderTeamMembers('', [], [])
726
1054
  this.showToast('加载 Agent 失败: ' + String(e), true)
727
1055
  }
728
1056
  }
729
1057
 
730
1058
  async refreshAgents() {
731
- this.agents = await fetchAgentsList(this.contextAgent)
1059
+ this.agents = await fetchAgentsList()
732
1060
  this.list.render(this.agents, this.controller.selectedId)
733
1061
  if (this.hooks.onAgentsLoaded) this.hooks.onAgentsLoaded(this.agents)
734
1062
  return this.agents
@@ -773,6 +1101,11 @@
773
1101
  if (this.panelTitleEl) this.panelTitleEl.textContent = 'Agent · ' + id
774
1102
  this.controller.selectAgent(id)
775
1103
  this.syncUrlAgentParam(id)
1104
+ if (isEdit) {
1105
+ await this.loadAgentIntoForm(id)
1106
+ } else {
1107
+ this.showPanelForAgent(id)
1108
+ }
776
1109
  this.showToast(isEdit ? '已保存' : '已创建')
777
1110
  } catch (e) {
778
1111
  this.showToast((isEdit ? '保存失败: ' : '创建失败: ') + String(e), true)
package/dist/ws-test.html CHANGED
@@ -910,6 +910,65 @@
910
910
  #agentEnvModalCard .agent-env-panel--md .agent-md-editor {
911
911
  min-height: min(42vh, 360px);
912
912
  }
913
+ .agent-env-team-block {
914
+ margin-top: 0.35rem;
915
+ padding-top: 0.65rem;
916
+ border-top: 1px solid var(--ds-border, #e5e5ea);
917
+ }
918
+ .agent-env-team-block > label {
919
+ font-size: 0.78rem;
920
+ color: var(--ds-text-soft);
921
+ display: block;
922
+ margin-bottom: 0.35rem;
923
+ }
924
+ .agent-env-team-empty {
925
+ margin: 0 0 0.45rem;
926
+ font-size: 0.78rem;
927
+ color: var(--ds-text-soft);
928
+ }
929
+ .agent-env-team-chips {
930
+ display: flex;
931
+ flex-wrap: wrap;
932
+ gap: 6px;
933
+ margin-bottom: 0.55rem;
934
+ min-height: 24px;
935
+ }
936
+ .agent-env-team-chip {
937
+ display: inline-flex;
938
+ align-items: center;
939
+ gap: 4px;
940
+ padding: 0.15rem 0.4rem 0.15rem 0.5rem;
941
+ border-radius: 999px;
942
+ background: rgba(10, 132, 255, 0.12);
943
+ border: 1px solid rgba(10, 132, 255, 0.28);
944
+ font-size: 0.76rem;
945
+ }
946
+ .agent-env-team-chip button {
947
+ border: none;
948
+ background: transparent;
949
+ color: var(--ds-text-soft);
950
+ cursor: pointer;
951
+ font-size: 1rem;
952
+ line-height: 1;
953
+ padding: 0 2px;
954
+ }
955
+ .agent-env-team-chip button:hover {
956
+ color: var(--ds-danger, #ff3b30);
957
+ }
958
+ .agent-env-team-add {
959
+ display: flex;
960
+ gap: 8px;
961
+ align-items: center;
962
+ }
963
+ .agent-env-team-add select {
964
+ flex: 1;
965
+ min-width: 0;
966
+ border: 1px solid var(--ds-border, #e5e5ea);
967
+ border-radius: 8px;
968
+ padding: 0.4rem 0.5rem;
969
+ font: inherit;
970
+ font-size: 0.82rem;
971
+ }
913
972
  .agent-md-modal-close {
914
973
  border: none;
915
974
  background: transparent;
@@ -2986,6 +3045,17 @@
2986
3045
  aria-label="agent.json whentouse"
2987
3046
  style="min-height: 110px"
2988
3047
  ></textarea>
3048
+ <div class="agent-env-team-block">
3049
+ <label>绑定队员(agentTeams)</label>
3050
+ <p id="agentEnvTeamEmpty" class="agent-env-team-empty" hidden>尚未绑定队员</p>
3051
+ <div id="agentEnvTeamChips" class="agent-env-team-chips"></div>
3052
+ <div class="agent-env-team-add">
3053
+ <select id="agentEnvTeamPick" aria-label="选择队员">
3054
+ <option value="">选择要添加的 Agent…</option>
3055
+ </select>
3056
+ <button type="button" class="btn-primary" id="btnAgentEnvAddTeamMember">添加</button>
3057
+ </div>
3058
+ </div>
2989
3059
  <label for="agentEnvToolsInput" style="font-size: 0.78rem; color: var(--ds-text-soft)">
2990
3060
  <code>agent.json.tools</code>(逗号或换行分隔)
2991
3061
  </label>
@@ -3280,6 +3350,10 @@
3280
3350
  const agentEnvAgentMdEditor = $('agentEnvAgentMdEditor')
3281
3351
  const agentEnvAgentTypeInput = $('agentEnvAgentTypeInput')
3282
3352
  const agentEnvWhenToUseInput = $('agentEnvWhenToUseInput')
3353
+ const agentEnvTeamEmpty = $('agentEnvTeamEmpty')
3354
+ const agentEnvTeamChips = $('agentEnvTeamChips')
3355
+ const agentEnvTeamPick = $('agentEnvTeamPick')
3356
+ const btnAgentEnvAddTeamMember = $('btnAgentEnvAddTeamMember')
3283
3357
  const agentEnvToolsInput = $('agentEnvToolsInput')
3284
3358
  const agentEnvModelInput = $('agentEnvModelInput')
3285
3359
  const agentEnvEffortInput = $('agentEnvEffortInput')
@@ -3500,6 +3574,7 @@
3500
3574
  mcpServers: [],
3501
3575
  skills: [],
3502
3576
  agentTeams: [],
3577
+ agentTeamMembers: [],
3503
3578
  model: '',
3504
3579
  effort: 'low',
3505
3580
  background: false,
@@ -3532,6 +3607,8 @@
3532
3607
  const WS_PERMISSION_MODE_KEY = 'WS_PERMISSION_MODE'
3533
3608
  const LLM_MODEL_KEY = 'LLM_MODEL'
3534
3609
  const WS_ALLOW_TOOL_ASK_KEY = 'WS_ALLOW_TOOL_ASK'
3610
+ const TUITUI_CHANNEL_SESSION_KEY = 'TUITUI_CHANNEL_SESSION'
3611
+ const TUITUI_CHANNEL_SESSION_ALLOWED = new Set(['0', '1'])
3535
3612
  const WS_PERMISSION_MODE_ALLOWED = new Set([
3536
3613
  'acceptEdits',
3537
3614
  'plan',
@@ -3713,7 +3790,35 @@
3713
3790
  label.textContent = field.label
3714
3791
  const inputId = 'agentEnvField_' + field.key
3715
3792
  let input
3716
- if (field.key === 'LLM_PROVIDER') {
3793
+ if (field.key === TUITUI_CHANNEL_SESSION_KEY) {
3794
+ /**
3795
+ * 业务逻辑:推推常驻会话开关仅允许 0/1,与主进程 reconcileChannelPairSessions 读取一致。
3796
+ */
3797
+ const select = document.createElement('select')
3798
+ select.id = inputId
3799
+ select.className = 'agent-env-kv-input'
3800
+ const channelSessionOptions = [
3801
+ { value: '0', label: '禁用' },
3802
+ { value: '1', label: '启用' },
3803
+ ]
3804
+ for (let k = 0; k < channelSessionOptions.length; k++) {
3805
+ const option = document.createElement('option')
3806
+ option.value = channelSessionOptions[k].value
3807
+ option.textContent = channelSessionOptions[k].label
3808
+ select.appendChild(option)
3809
+ }
3810
+ const currentChannelSession =
3811
+ typeof agentEnvFieldValues[field.key] === 'string'
3812
+ ? agentEnvFieldValues[field.key].trim()
3813
+ : ''
3814
+ select.value = TUITUI_CHANNEL_SESSION_ALLOWED.has(currentChannelSession)
3815
+ ? currentChannelSession
3816
+ : '0'
3817
+ select.addEventListener('change', function () {
3818
+ agentEnvFieldValues[field.key] = select.value
3819
+ })
3820
+ input = select
3821
+ } else if (field.key === 'LLM_PROVIDER') {
3717
3822
  /**
3718
3823
  * 业务逻辑:模型类型是受控枚举,使用下拉框约束可选值,
3719
3824
  * 避免手输导致 provider 非法。
@@ -6311,6 +6416,182 @@
6311
6416
  u.searchParams.set('agent', currentAgentParam())
6312
6417
  return u.toString()
6313
6418
  }
6419
+
6420
+ function buildAgentTeamMembersPostUrl(leaderId) {
6421
+ return new URL(
6422
+ '/api/agents/' + encodeURIComponent(String(leaderId || '').trim()) + '/team-members',
6423
+ httpOriginForApi() + '/',
6424
+ ).toString()
6425
+ }
6426
+
6427
+ function buildAgentTeamMemberDeleteUrl(leaderId, agentType) {
6428
+ return new URL(
6429
+ '/api/agents/' +
6430
+ encodeURIComponent(String(leaderId || '').trim()) +
6431
+ '/team-members/' +
6432
+ encodeURIComponent(String(agentType || '').trim()),
6433
+ httpOriginForApi() + '/',
6434
+ ).toString()
6435
+ }
6436
+
6437
+ function buildAgentsListUrlForOffice() {
6438
+ return new URL('/api/agents', httpOriginForApi() + '/').toString()
6439
+ }
6440
+
6441
+ /** 设置弹窗:可绑定队员仅来自 agent-runtime(GET /api/agents) */
6442
+ async function fetchTeamMemberCatalogForSettings(leaderId) {
6443
+ const leader = String(leaderId || '').trim()
6444
+ const r = await fetch(buildAgentsListUrlForOffice())
6445
+ const body = await r.json().catch(function () {
6446
+ return {}
6447
+ })
6448
+ if (!r.ok) {
6449
+ throw new Error(body.message || body.error || r.statusText)
6450
+ }
6451
+ const agents = Array.isArray(body.agents) ? body.agents : []
6452
+ const out = []
6453
+ for (let i = 0; i < agents.length; i++) {
6454
+ const row = agents[i]
6455
+ const id = String(row.id || '').trim()
6456
+ if (!id || id === leader) continue
6457
+ out.push({
6458
+ agentType: id,
6459
+ whenToUse: typeof row.whentouse === 'string' ? row.whentouse : '',
6460
+ })
6461
+ }
6462
+ out.sort(function (a, b) {
6463
+ return a.agentType.localeCompare(b.agentType)
6464
+ })
6465
+ return out
6466
+ }
6467
+
6468
+ /** 设置弹窗:渲染已绑定队员与添加下拉 */
6469
+ async function renderAgentEnvTeamMembers() {
6470
+ if (!agentEnvTeamChips) return
6471
+ const leaderId = currentAgentParam()
6472
+ const members = Array.isArray(agentConfigState.agentTeamMembers)
6473
+ ? agentConfigState.agentTeamMembers
6474
+ : []
6475
+ if (agentEnvTeamEmpty) {
6476
+ agentEnvTeamEmpty.hidden = members.length > 0
6477
+ }
6478
+ agentEnvTeamChips.innerHTML = ''
6479
+ const boundSet = new Set()
6480
+ for (let i = 0; i < members.length; i++) {
6481
+ const m = members[i]
6482
+ const agentType = String(m.agentType || '').trim()
6483
+ if (!agentType) continue
6484
+ boundSet.add(agentType)
6485
+ const chip = document.createElement('span')
6486
+ chip.className = 'agent-env-team-chip'
6487
+ chip.title =
6488
+ typeof m.whentouse === 'string' && m.whentouse.trim()
6489
+ ? m.whentouse.trim()
6490
+ : agentType
6491
+ const label = document.createElement('span')
6492
+ label.textContent = agentType
6493
+ const rm = document.createElement('button')
6494
+ rm.type = 'button'
6495
+ rm.setAttribute('aria-label', '移除 ' + agentType)
6496
+ rm.textContent = '×'
6497
+ rm.addEventListener('click', function () {
6498
+ void removeAgentEnvTeamMember(agentType)
6499
+ })
6500
+ chip.appendChild(label)
6501
+ chip.appendChild(rm)
6502
+ agentEnvTeamChips.appendChild(chip)
6503
+ }
6504
+ if (agentEnvTeamPick) {
6505
+ agentEnvTeamPick.innerHTML = '<option value="">选择要添加的 Agent…</option>'
6506
+ let catalog = []
6507
+ try {
6508
+ catalog = await fetchTeamMemberCatalogForSettings(leaderId)
6509
+ } catch {
6510
+ catalog = []
6511
+ }
6512
+ for (let j = 0; j < catalog.length; j++) {
6513
+ const t = catalog[j]
6514
+ const id = String(t.agentType || '').trim()
6515
+ if (!id || id === leaderId || boundSet.has(id)) continue
6516
+ const opt = document.createElement('option')
6517
+ opt.value = id
6518
+ const when =
6519
+ typeof t.whenToUse === 'string' && t.whenToUse.trim() ? t.whenToUse.trim() : ''
6520
+ opt.textContent = when ? id + ' — ' + when.slice(0, 36) : id
6521
+ agentEnvTeamPick.appendChild(opt)
6522
+ }
6523
+ }
6524
+ }
6525
+
6526
+ function applyAgentTeamMembersFromApi(agentRow) {
6527
+ if (!agentRow || typeof agentRow !== 'object') return
6528
+ if (Array.isArray(agentRow.agentTeamMembers)) {
6529
+ agentConfigState.agentTeamMembers = agentRow.agentTeamMembers.map(function (m) {
6530
+ return {
6531
+ agentType: String(m.agentType || ''),
6532
+ whentouse: typeof m.whentouse === 'string' ? m.whentouse : '',
6533
+ }
6534
+ })
6535
+ }
6536
+ if (Array.isArray(agentRow.agentTeams)) {
6537
+ agentConfigState.agentTeams = agentRow.agentTeams.map(String)
6538
+ } else if (Array.isArray(agentRow.agentTeamMembers)) {
6539
+ agentConfigState.agentTeams = agentRow.agentTeamMembers
6540
+ .map(function (m) {
6541
+ return String(m.agentType || '')
6542
+ })
6543
+ .filter(Boolean)
6544
+ }
6545
+ renderBindChips()
6546
+ }
6547
+
6548
+ async function addAgentEnvTeamMember() {
6549
+ const leaderId = currentAgentParam()
6550
+ if (!agentEnvTeamPick) return
6551
+ const agentType = agentEnvTeamPick.value.trim()
6552
+ if (!agentType) {
6553
+ window.alert('请先选择要添加的队员')
6554
+ return
6555
+ }
6556
+ const r = await fetch(buildAgentTeamMembersPostUrl(leaderId), {
6557
+ method: 'POST',
6558
+ headers: { 'content-type': 'application/json; charset=utf-8' },
6559
+ body: JSON.stringify({ agentType: agentType }),
6560
+ })
6561
+ const body = await r.json().catch(function () {
6562
+ return {}
6563
+ })
6564
+ if (!r.ok) {
6565
+ window.alert('添加队员失败:' + (body.message || body.error || r.statusText))
6566
+ return
6567
+ }
6568
+ applyAgentTeamMembersFromApi(body.agent)
6569
+ agentEnvTeamPick.value = ''
6570
+ await renderAgentEnvTeamMembers()
6571
+ void persistAgentConfigAndRestartSession({ silent: true })
6572
+ setStatus('已添加队员 ' + agentType, serverReady ? 'ready' : '')
6573
+ }
6574
+
6575
+ async function removeAgentEnvTeamMember(agentType) {
6576
+ const leaderId = currentAgentParam()
6577
+ if (!window.confirm('确定移除队员「' + agentType + '」?')) {
6578
+ return
6579
+ }
6580
+ const r = await fetch(buildAgentTeamMemberDeleteUrl(leaderId, agentType), {
6581
+ method: 'DELETE',
6582
+ })
6583
+ const body = await r.json().catch(function () {
6584
+ return {}
6585
+ })
6586
+ if (!r.ok) {
6587
+ window.alert('移除队员失败:' + (body.message || body.error || r.statusText))
6588
+ return
6589
+ }
6590
+ applyAgentTeamMembersFromApi(body.agent)
6591
+ await renderAgentEnvTeamMembers()
6592
+ void persistAgentConfigAndRestartSession({ silent: true })
6593
+ setStatus('已移除队员 ' + agentType, serverReady ? 'ready' : '')
6594
+ }
6314
6595
  async function loadSessions(options) {
6315
6596
  sessionListMetaEl.textContent = '加载中…'
6316
6597
  let data
@@ -7244,6 +7525,7 @@
7244
7525
  mcpServers: [],
7245
7526
  skills: [],
7246
7527
  agentTeams: [],
7528
+ agentTeamMembers: [],
7247
7529
  model: '',
7248
7530
  effort: 'low',
7249
7531
  background: false,
@@ -7273,6 +7555,7 @@
7273
7555
  mcpServers: Array.isArray(c.mcpServers) ? c.mcpServers.map(String) : [],
7274
7556
  skills: Array.isArray(c.skills) ? c.skills.map(String) : [],
7275
7557
  agentTeams: Array.isArray(c.agentTeams) ? c.agentTeams.map(String) : [],
7558
+ agentTeamMembers: [],
7276
7559
  model: typeof c.model === 'string' ? c.model : '',
7277
7560
  effort:
7278
7561
  typeof c.effort === 'number' || typeof c.effort === 'string'
@@ -7293,6 +7576,7 @@
7293
7576
  mcpServers: [],
7294
7577
  skills: [],
7295
7578
  agentTeams: [],
7579
+ agentTeamMembers: [],
7296
7580
  model: '',
7297
7581
  effort: 'low',
7298
7582
  background: false,
@@ -7301,6 +7585,21 @@
7301
7585
  env: {},
7302
7586
  }
7303
7587
  }
7588
+ if (body.agent && Array.isArray(body.agent.agentTeamMembers)) {
7589
+ agentConfigState.agentTeamMembers = body.agent.agentTeamMembers.map(function (m) {
7590
+ return {
7591
+ agentType: String(m.agentType || ''),
7592
+ whentouse: typeof m.whentouse === 'string' ? m.whentouse : '',
7593
+ }
7594
+ })
7595
+ } else {
7596
+ agentConfigState.agentTeamMembers = agentConfigState.agentTeams.map(function (id) {
7597
+ return { agentType: id, whentouse: '' }
7598
+ })
7599
+ }
7600
+ if (body.agent && Array.isArray(body.agent.agentTeams)) {
7601
+ agentConfigState.agentTeams = body.agent.agentTeams.map(String)
7602
+ }
7304
7603
  renderBindChips()
7305
7604
  syncModelInputFromState()
7306
7605
  void loadProjectEnvDefaults().then(function () {
@@ -9077,6 +9376,7 @@
9077
9376
  : ''
9078
9377
  agentEnvIsolationInput.value = nextIsolation
9079
9378
  }
9379
+ void renderAgentEnvTeamMembers()
9080
9380
  void loadAgentEnvAgentMdFromApi()
9081
9381
  }
9082
9382
 
@@ -9189,6 +9489,14 @@
9189
9489
  : ''
9190
9490
  const value = String(raw ?? '').trim()
9191
9491
  if (!value) continue
9492
+ if (field.key === TUITUI_CHANNEL_SESSION_KEY) {
9493
+ if (!TUITUI_CHANNEL_SESSION_ALLOWED.has(value)) {
9494
+ return {
9495
+ ok: false,
9496
+ message: 'TUITUI_CHANNEL_SESSION 只允许 0(禁用)或 1(启用)',
9497
+ }
9498
+ }
9499
+ }
9192
9500
  out[field.key] = value
9193
9501
  }
9194
9502
  return { ok: true, env: out }
@@ -9595,6 +9903,11 @@
9595
9903
  void saveAgentEnvAndReconnect()
9596
9904
  })
9597
9905
  }
9906
+ if (btnAgentEnvAddTeamMember) {
9907
+ btnAgentEnvAddTeamMember.addEventListener('click', function () {
9908
+ void addAgentEnvTeamMember()
9909
+ })
9910
+ }
9598
9911
  if (btnAgentMdModalClose) {
9599
9912
  btnAgentMdModalClose.addEventListener('click', closeAgentMdModal)
9600
9913
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auto-ai/agent",
3
- "version": "2.1.105",
3
+ "version": "2.1.106",
4
4
  "description": "Auto AI Agent 网关 CLI(WebSocket,独立二进制,无需 Bun)",
5
5
  "type": "module",
6
6
  "maintainers": [
@@ -27,11 +27,11 @@
27
27
  "node": ">=18"
28
28
  },
29
29
  "optionalDependencies": {
30
- "@auto-ai/agent-linux-x64": "2.1.105",
31
- "@auto-ai/agent-linux-arm64": "2.1.105",
32
- "@auto-ai/agent-darwin-x64": "2.1.105",
33
- "@auto-ai/agent-darwin-arm64": "2.1.105",
34
- "@auto-ai/agent-win-x64": "2.1.105"
30
+ "@auto-ai/agent-linux-x64": "2.1.106",
31
+ "@auto-ai/agent-linux-arm64": "2.1.106",
32
+ "@auto-ai/agent-darwin-x64": "2.1.106",
33
+ "@auto-ai/agent-darwin-arm64": "2.1.106",
34
+ "@auto-ai/agent-win-x64": "2.1.106"
35
35
  },
36
36
  "scripts": {
37
37
  "prepare": "node ../../scripts/sync-launcher-env.js",