@auto-ai/agent 2.1.220 → 2.1.222

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.
Files changed (45) hide show
  1. package/dist/safe-a/404/index.html +1 -1
  2. package/dist/safe-a/404.html +1 -1
  3. package/dist/safe-a/index.html +2 -2
  4. package/dist/safe-a/index.txt +1 -1
  5. package/dist/safe-a/manage/about/index.html +2 -2
  6. package/dist/safe-a/manage/about/index.txt +1 -1
  7. package/dist/safe-a/manage/env/index.html +2 -2
  8. package/dist/safe-a/manage/env/index.txt +1 -1
  9. package/dist/safe-a/manage/geelib/index.html +2 -2
  10. package/dist/safe-a/manage/geelib/index.txt +1 -1
  11. package/dist/safe-a/manage/general/index.html +2 -2
  12. package/dist/safe-a/manage/general/index.txt +1 -1
  13. package/dist/safe-a/manage/git/index.html +2 -2
  14. package/dist/safe-a/manage/git/index.txt +1 -1
  15. package/dist/safe-a/manage/im/index.html +2 -2
  16. package/dist/safe-a/manage/im/index.txt +1 -1
  17. package/dist/safe-a/manage/index.html +2 -2
  18. package/dist/safe-a/manage/index.txt +1 -1
  19. package/dist/safe-a/manage/library/index.html +2 -2
  20. package/dist/safe-a/manage/library/index.txt +1 -1
  21. package/dist/safe-a/manage/mcp/index.html +2 -2
  22. package/dist/safe-a/manage/mcp/index.txt +1 -1
  23. package/dist/safe-a/manage/permissions/index.html +2 -2
  24. package/dist/safe-a/manage/permissions/index.txt +1 -1
  25. package/dist/safe-a/manage/skills/index.html +2 -2
  26. package/dist/safe-a/manage/skills/index.txt +1 -1
  27. package/dist/safe-a/manage/task/index.html +2 -2
  28. package/dist/safe-a/manage/task/index.txt +1 -1
  29. package/dist/safe-a/manage/teams/index.html +2 -2
  30. package/dist/safe-a/manage/teams/index.txt +1 -1
  31. package/dist/safe-a/manage/tools/index.html +2 -2
  32. package/dist/safe-a/manage/tools/index.txt +1 -1
  33. package/dist/ws-test/agent-teams.html +1400 -0
  34. package/dist/ws-test/team-chat.js +760 -0
  35. package/dist/ws-test/ws-test.css +613 -6
  36. package/dist/ws-test/ws-test.html +23 -7
  37. package/dist/ws-test/ws-test.js +303 -86
  38. package/mcps-runtime/claude-tuitui-channel/server/index.mjs +5 -0
  39. package/package.json +6 -6
  40. package/tools-runtime/git-tool/index.cjs +32 -31
  41. package/tools-runtime/git-tool/src/workspace.ts +42 -5
  42. package/tools-runtime/git-tool/src/wsInit.ts +13 -2
  43. /package/dist/safe-a/_next/static/{tLBo5EqCFXrKR3ERjvMIi → O9azldpveh8Nr32O4nBb7}/_buildManifest.js +0 -0
  44. /package/dist/safe-a/_next/static/{tLBo5EqCFXrKR3ERjvMIi → O9azldpveh8Nr32O4nBb7}/_clientMiddlewareManifest.json +0 -0
  45. /package/dist/safe-a/_next/static/{tLBo5EqCFXrKR3ERjvMIi → O9azldpveh8Nr32O4nBb7}/_ssgManifest.js +0 -0
@@ -167,6 +167,63 @@
167
167
  const mcpPackageImportInput = $('mcpPackageImportInput')
168
168
  const sessionListEl = $('sessionList')
169
169
  const sessionListMetaEl = $('sessionListMeta')
170
+ /** 侧栏会话来源:ws / tuitui / geelib / team / legacy */
171
+ var SESSION_KIND_LABELS = {
172
+ ws: '网页',
173
+ tuitui: '推推',
174
+ geelib: 'GeeLib',
175
+ team: '群聊',
176
+ legacy: '会话',
177
+ }
178
+ function resolveSessionKindForItem(s) {
179
+ if (!s || typeof s !== 'object') return 'ws'
180
+ var kind = s.sessionKind
181
+ if (kind && SESSION_KIND_LABELS[kind]) return kind
182
+ if (kind === 'user') return 'ws'
183
+ var sid = typeof s.sessionId === 'string' ? s.sessionId : ''
184
+ if (sid.indexOf('team-') === 0) return 'team'
185
+ if (sid.indexOf('tuitui-') === 0) return 'tuitui'
186
+ if (sid.indexOf('geelib-') === 0) return 'geelib'
187
+ if (sid.indexOf('ws-') === 0) return 'ws'
188
+ return 'legacy'
189
+ }
190
+ var SESSION_KIND_ICON_SVG = {
191
+ ws:
192
+ '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
193
+ '<rect x="3" y="4" width="18" height="14" rx="2"/>' +
194
+ '<path d="M8 20h8"/>' +
195
+ '</svg>',
196
+ tuitui:
197
+ '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
198
+ '<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>' +
199
+ '<path d="M13.73 21a2 2 0 0 1-3.46 0"/>' +
200
+ '</svg>',
201
+ geelib:
202
+ '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
203
+ '<path d="M4 4h16v16H4z"/>' +
204
+ '<path d="M4 9h16M9 4v16"/>' +
205
+ '</svg>',
206
+ team:
207
+ '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
208
+ '<circle cx="8" cy="8" r="2.8"/>' +
209
+ '<circle cx="16.2" cy="9.2" r="2.4"/>' +
210
+ '<path d="M3.2 18c.8-2.7 2.8-4.2 4.8-4.2s4 1.5 4.8 4.2"/>' +
211
+ '<path d="M13.2 18c.6-2 2.2-3.2 4-3.2 1.6 0 3.1 1 3.8 2.7"/>' +
212
+ '</svg>',
213
+ legacy:
214
+ '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
215
+ '<path d="M21 11.5a8.4 8.4 0 0 1-9 8.4 8.4 8.4 0 0 1-3.8-.9L3 20l1.9-5.2A8.4 8.4 0 1 1 21 11.5z"/>' +
216
+ '</svg>',
217
+ }
218
+ function createSessionKindIcon(kind) {
219
+ var resolved = SESSION_KIND_LABELS[kind] ? kind : 'ws'
220
+ var icon = document.createElement('span')
221
+ icon.className = 'sidebar-session-kind-icon sidebar-session-kind-icon--' + resolved
222
+ icon.setAttribute('aria-hidden', 'true')
223
+ icon.title = SESSION_KIND_LABELS[resolved]
224
+ icon.innerHTML = SESSION_KIND_ICON_SVG[resolved]
225
+ return icon
226
+ }
170
227
  /** 侧栏会话列表运行时状态轮询定时器 */
171
228
  let sessionRuntimePollTimer = null
172
229
  /** 当前附着 session 的 turn 是否进行中(由 query.progress 驱动) */
@@ -280,18 +337,21 @@
280
337
  const composerToolAskSelect = $('composerToolAskSelect')
281
338
  const btnSaveAgentConfig = $('btnSaveAgentConfig')
282
339
  const pickerManageView = $('pickerManageView')
283
- const pickerNavPanel = $('pickerNavPanel')
284
- const pickerNavList = $('pickerNavList')
285
340
  const pickerSearch = $('pickerSearch')
286
341
  const btnPickerMcpJson = $('btnPickerMcpJson')
287
342
  const btnPickerImport = $('btnPickerImport')
343
+ const btnPickerSkillStore = $('btnPickerSkillStore')
288
344
  const btnPickerCancel = $('btnPickerCancel')
289
345
  const btnPickerSave = $('btnPickerSave')
290
346
  const pickerEntriesTable = $('pickerEntriesTable')
291
347
  const pickerTableHeadRow = $('pickerTableHeadRow')
292
348
  const pickerEntriesBody = $('pickerEntriesBody')
293
- const pickerCloudPanel = $('pickerCloudPanel')
294
349
  const pickerContentState = $('pickerContentState')
350
+ const skillStoreModal = $('skillStoreModal')
351
+ const skillStoreModalBackdrop = $('skillStoreModalBackdrop')
352
+ const skillStoreModalBody = $('skillStoreModalBody')
353
+ const btnSkillStoreModalClose = $('btnSkillStoreModalClose')
354
+ const btnSkillStoreClose = $('btnSkillStoreClose')
295
355
  const pickerImportInput = $('pickerImportInput')
296
356
  const mcpJsonInputModal = $('mcpJsonInputModal')
297
357
  const mcpJsonInputModalBackdrop = $('mcpJsonInputModalBackdrop')
@@ -362,6 +422,7 @@
362
422
  let refreshToolPickerRows = null
363
423
  let refreshMcpPickerRows = null
364
424
  let refreshSkillPickerRows = null
425
+ let openSkillStoreModalFn = null
365
426
  let pickerRenderContent = null
366
427
  let remoteMcpServers = []
367
428
  const WS_PERMISSION_MODE_KEY = 'WS_PERMISSION_MODE'
@@ -720,6 +781,8 @@
720
781
  let renderScheduled = false
721
782
  /** HTTP 历史回放进行中:暂存实时 conversation.delta,避免与回放竞态写乱 transcript 下标 */
722
783
  let sessionReplayInProgress = false
784
+ /** 递增以作废进行中的 HTTP 历史回放,避免快速切换 session 时旧内容回填 */
785
+ let hydrateSessionGeneration = 0
723
786
  const pendingConversationDeltaEnvelopes = []
724
787
  /**
725
788
  * 用户点击发送后、服务端 conversation.delta 到达前的本地回显。
@@ -767,6 +830,7 @@
767
830
  source: source,
768
831
  runId: runId,
769
832
  correlationId: correlationId,
833
+ turnId: typeof item.turnId === 'string' ? item.turnId : '',
770
834
  text: body,
771
835
  status: status,
772
836
  scheduledTrigger: item.scheduledTrigger === true,
@@ -1500,6 +1564,84 @@
1500
1564
  bodyEl.insertAdjacentElement('afterend', btn)
1501
1565
  }
1502
1566
 
1567
+ /**
1568
+ * 业务语义:HTTP 回放/初始化 turnId 为 replay|init,需按 user 消息再切分;
1569
+ * 实时 turn 每轮 prompt 有独立 UUID turnId,直接按 turnId 分组。
1570
+ */
1571
+ function shouldSplitTurnOnUserMessage(turnId) {
1572
+ const tid = typeof turnId === 'string' ? turnId.trim() : ''
1573
+ return !tid || tid === 'replay' || tid === 'init'
1574
+ }
1575
+
1576
+ function isRealUserPromptMessage(m) {
1577
+ if (!m || m.type !== 'user') return false
1578
+ if (!shouldShowWsUserMessage(m)) return false
1579
+ const blocks = getMessageContentBlocks(m)
1580
+ const nonToolBlocks = blocks.filter(function (b) {
1581
+ return b && b.type !== 'tool_result'
1582
+ })
1583
+ if (nonToolBlocks.length === 0) return false
1584
+ const rawBody = contentToPlainText(nonToolBlocks).trim()
1585
+ return !!unwrapChannelWrappedText(rawBody).body.trim()
1586
+ }
1587
+
1588
+ function isTimelineUserTurnStart(ev) {
1589
+ if (!ev || ev.kind !== 'conversation') return false
1590
+ const item = ev.payload
1591
+ const m = item && item.message
1592
+ return isRealUserPromptMessage(m)
1593
+ }
1594
+
1595
+ function isToolResultOnlyEvent(ev) {
1596
+ if (!ev || ev.kind !== 'conversation') return false
1597
+ const m = ev.payload && ev.payload.message
1598
+ if (!m || m.type !== 'user') return false
1599
+ const blocks = getMessageContentBlocks(m)
1600
+ if (!blocks.length) return false
1601
+ return blocks.every(function (b) {
1602
+ return b && b.type === 'tool_result'
1603
+ })
1604
+ }
1605
+
1606
+ function coalesceToolResultTurns(turns) {
1607
+ for (let i = 1; i < turns.length; i++) {
1608
+ const turn = turns[i]
1609
+ if (turn.items.length > 0 && turn.items.every(isToolResultOnlyEvent)) {
1610
+ turns[i - 1].items.push.apply(turns[i - 1].items, turn.items)
1611
+ turns.splice(i, 1)
1612
+ i -= 1
1613
+ }
1614
+ }
1615
+ return turns
1616
+ }
1617
+
1618
+ function groupItemsByTurn(blockItems) {
1619
+ const turns = []
1620
+ for (let i = 0; i < blockItems.length; i++) {
1621
+ const ev = blockItems[i]
1622
+ const turnId = typeof ev.turnId === 'string' ? ev.turnId.trim() : ''
1623
+ const splitOnUser = shouldSplitTurnOnUserMessage(turnId)
1624
+ const isUserStart = splitOnUser && isTimelineUserTurnStart(ev)
1625
+ let targetTurn = turns.length > 0 ? turns[turns.length - 1] : null
1626
+
1627
+ if (splitOnUser) {
1628
+ if (isUserStart || !targetTurn) {
1629
+ targetTurn = { turnId: turnId || 'unknown', items: [] }
1630
+ turns.push(targetTurn)
1631
+ }
1632
+ targetTurn.items.push(ev)
1633
+ continue
1634
+ }
1635
+
1636
+ if (!targetTurn || targetTurn.turnId !== turnId) {
1637
+ targetTurn = { turnId: turnId, items: [] }
1638
+ turns.push(targetTurn)
1639
+ }
1640
+ targetTurn.items.push(ev)
1641
+ }
1642
+ return coalesceToolResultTurns(turns)
1643
+ }
1644
+
1503
1645
  function renderChat() {
1504
1646
  /**
1505
1647
  * 业务语义:run 折叠头需要展示“最后一条消息前三行”预览。
@@ -1714,6 +1856,7 @@
1714
1856
  Number.isInteger(item.arrivalSeq) ? item.arrivalSeq : Number.MAX_SAFE_INTEGER,
1715
1857
  fallbackOrder: timelineFallbackOrder++,
1716
1858
  runId: runId,
1859
+ turnId: typeof item.turnId === 'string' ? item.turnId : '',
1717
1860
  sourceAgent: resolveRunDisplayAgent(runId, item.sourceAgent),
1718
1861
  scheduledTrigger: item.scheduledTrigger === true,
1719
1862
  kind: 'conversation',
@@ -1734,6 +1877,7 @@
1734
1877
  Number.isInteger(item.arrivalSeq) ? item.arrivalSeq : Number.MAX_SAFE_INTEGER,
1735
1878
  fallbackOrder: timelineFallbackOrder++,
1736
1879
  runId: rid,
1880
+ turnId: typeof item.turnId === 'string' ? item.turnId : '',
1737
1881
  sourceAgent: resolveRunDisplayAgent(rid, item.source),
1738
1882
  scheduledTrigger: item.scheduledTrigger === true,
1739
1883
  kind: 'source',
@@ -1822,16 +1966,24 @@
1822
1966
  const groupContainer = document.createElement('div')
1823
1967
  groupContainer.className = 'chat-run-group' + (blockAllScheduled ? ' chat-run-group--scheduled' : '')
1824
1968
  if (runId === 'main') {
1825
- const fixedHeader = document.createElement('div')
1826
- fixedHeader.className = 'chat-msg-label'
1827
- fixedHeader.textContent = blockAllScheduled
1828
- ? '⏱ 定时任务 · ' + summaryText
1829
- : summaryText
1830
- groupContainer.appendChild(fixedHeader)
1831
- const groupBody = document.createElement('div')
1832
- groupBody.className = 'chat-run-group-body'
1833
- appendRunItemsToContainer(groupBody, blockItems, runId, defaultSourceAgent)
1834
- groupContainer.appendChild(groupBody)
1969
+ groupContainer.classList.add('chat-run-group--main')
1970
+ const turnGroups = groupItemsByTurn(blockItems)
1971
+ for (let t = 0; t < turnGroups.length; t++) {
1972
+ const turn = turnGroups[t]
1973
+ if (!turn.items.length) continue
1974
+ const isLastTurn = t === turnGroups.length - 1
1975
+ const isActiveTurn = isLastTurn && sessionTurnActive
1976
+ const turnSection = document.createElement('section')
1977
+ turnSection.className =
1978
+ 'chat-turn' +
1979
+ (isActiveTurn ? ' chat-turn--active' : '') +
1980
+ (blockAllScheduled ? ' chat-turn--scheduled' : '')
1981
+ const turnBody = document.createElement('div')
1982
+ turnBody.className = 'chat-turn-body'
1983
+ appendRunItemsToContainer(turnBody, turn.items, runId, defaultSourceAgent)
1984
+ turnSection.appendChild(turnBody)
1985
+ groupContainer.appendChild(turnSection)
1986
+ }
1835
1987
  } else {
1836
1988
  const summaryCard = document.createElement('div')
1837
1989
  summaryCard.className =
@@ -1865,9 +2017,12 @@
1865
2017
  chatThreadEl.appendChild(groupContainer)
1866
2018
  }
1867
2019
  if (pendingUserEcho) {
1868
- const echoWrap = document.createElement('div')
1869
- echoWrap.className = 'chat-run-group chat-run-group--pending-echo'
1870
- appendPendingUserEchoToContainer(echoWrap)
2020
+ const echoWrap = document.createElement('section')
2021
+ echoWrap.className = 'chat-turn chat-turn--active chat-turn--pending-echo'
2022
+ const echoBody = document.createElement('div')
2023
+ echoBody.className = 'chat-turn-body'
2024
+ appendPendingUserEchoToContainer(echoBody)
2025
+ echoWrap.appendChild(echoBody)
1871
2026
  chatThreadEl.appendChild(echoWrap)
1872
2027
  }
1873
2028
  if (streamEntries.length > 0) {
@@ -2163,6 +2318,7 @@
2163
2318
  const runMessage = {
2164
2319
  message: msg,
2165
2320
  sourceAgent: sourceAgent,
2321
+ turnId: typeof data.turnId === 'string' ? data.turnId : '',
2166
2322
  scheduledTrigger: payload.scheduledTrigger === true,
2167
2323
  ts:
2168
2324
  typeof data.ts === 'string' && data.ts
@@ -2205,6 +2361,7 @@
2205
2361
  source: source,
2206
2362
  runId: runId,
2207
2363
  correlationId: corr,
2364
+ turnId: typeof data.turnId === 'string' ? data.turnId : '',
2208
2365
  status: payload.status === 'resolved' ? 'resolved' : 'failed',
2209
2366
  text: text,
2210
2367
  scheduledTrigger: payload.scheduledTrigger === true,
@@ -2286,6 +2443,7 @@
2286
2443
  source: source,
2287
2444
  runId: runId,
2288
2445
  correlationId: '',
2446
+ turnId: typeof data.turnId === 'string' ? data.turnId : '',
2289
2447
  status: type === 'session.error' ? 'failed' : 'resolved',
2290
2448
  text: payloadText,
2291
2449
  key: typeof data.eventId === 'string' ? data.eventId : '',
@@ -2307,6 +2465,17 @@
2307
2465
  if (payload.agent) updateBrandAgent(payload.agent)
2308
2466
  setStatus('已就绪', 'ready')
2309
2467
  void loadPendingPrompts()
2468
+ const mc =
2469
+ typeof payload.messageCount === 'number' ? payload.messageCount : 0
2470
+ const sid =
2471
+ typeof payload.sessionId === 'string' && payload.sessionId.trim()
2472
+ ? payload.sessionId.trim()
2473
+ : sessionIdInput.value.trim()
2474
+ if (mc === 0) {
2475
+ resetChatUi()
2476
+ } else if (sid) {
2477
+ void hydrateSessionHistoryFromHttp(sid)
2478
+ }
2310
2479
  return true
2311
2480
  }
2312
2481
  if (type === 'session.meta') {
@@ -2356,6 +2525,7 @@
2356
2525
  }
2357
2526
  void loadSessions({ silent: true })
2358
2527
  }
2528
+ requestRenderChat()
2359
2529
  return true
2360
2530
  }
2361
2531
  if (type === 'session.error') {
@@ -2626,6 +2796,60 @@
2626
2796
  return u.toString()
2627
2797
  }
2628
2798
 
2799
+ /**
2800
+ * HTTP replay 信封中需跳过的类型:replay.start 会把 serverReady 置 false 并重新打开全屏 loading。
2801
+ */
2802
+ function shouldSkipHttpReplayEnvelope(ev) {
2803
+ if (!ev || typeof ev !== 'object') return true
2804
+ const replayType = typeof ev.type === 'string' ? ev.type : ''
2805
+ if (replayType === 'history.replay') return true
2806
+ if (replayType !== 'run.lifecycle') return false
2807
+ const payload = ev.data && typeof ev.data === 'object' ? ev.data : {}
2808
+ const state = typeof payload.state === 'string' ? payload.state : ''
2809
+ return state === 'replay.start' || state === 'replay.end'
2810
+ }
2811
+
2812
+ /**
2813
+ * meta-only snapshot 后异步拉 HTTP replay 补全聊天区,不阻塞 session.ready 与 prompt 发送。
2814
+ */
2815
+ async function hydrateSessionHistoryFromHttp(sessionId) {
2816
+ const sid = typeof sessionId === 'string' ? sessionId.trim() : ''
2817
+ if (!sid) return
2818
+ const generation = hydrateSessionGeneration
2819
+ const wasReady = serverReady
2820
+ try {
2821
+ resetChatUi()
2822
+ sessionReplayInProgress = true
2823
+ pendingConversationDeltaEnvelopes.length = 0
2824
+ const r = await fetch(buildSessionReplayUrl(sid))
2825
+ if (generation !== hydrateSessionGeneration) return
2826
+ if (!r.ok) {
2827
+ logLine('error', 'hydrateSessionHistoryFromHttp: HTTP ' + r.status)
2828
+ return
2829
+ }
2830
+ const data = await r.json()
2831
+ const envelopes = Array.isArray(data.envelopes) ? data.envelopes : []
2832
+ for (let i = 0; i < envelopes.length; i++) {
2833
+ if (generation !== hydrateSessionGeneration) return
2834
+ const ev = envelopes[i]
2835
+ if (shouldSkipHttpReplayEnvelope(ev)) continue
2836
+ applyWsEnvelopeV3(ev)
2837
+ }
2838
+ } catch (e) {
2839
+ logLine('error', 'hydrateSessionHistoryFromHttp: ' + String(e))
2840
+ } finally {
2841
+ if (generation !== hydrateSessionGeneration) return
2842
+ sessionReplayInProgress = false
2843
+ flushPendingConversationDeltas()
2844
+ requestRenderChat()
2845
+ if (wasReady || serverReady) {
2846
+ serverReady = true
2847
+ setStatus('已就绪', 'ready')
2848
+ updateButtons()
2849
+ }
2850
+ }
2851
+ }
2852
+
2629
2853
  /** UI 不展示 resume 合成 assistant 占位(与 messages.SYNTHETIC_MESSAGES 对齐) */
2630
2854
  const WS_SYNTHETIC_ASSISTANT_TEXTS = new Set(['No response requested.'])
2631
2855
 
@@ -3277,12 +3501,10 @@
3277
3501
  return (pickerSearch && pickerSearch.value.trim().toLowerCase()) || ''
3278
3502
  }
3279
3503
 
3280
- /** 切换表格与云端面板显示 */
3281
- function setPickerContentMode(mode) {
3282
- const isCloud = mode === 'cloud'
3283
- if (pickerEntriesTable) pickerEntriesTable.hidden = isCloud
3284
- if (pickerCloudPanel) pickerCloudPanel.hidden = !isCloud
3285
- if (!isCloud && pickerContentState) {
3504
+ /** 切换 Picker 内容为表格模式 */
3505
+ function setPickerContentMode() {
3506
+ if (pickerEntriesTable) pickerEntriesTable.hidden = false
3507
+ if (pickerContentState) {
3286
3508
  pickerContentState.hidden = true
3287
3509
  pickerContentState.textContent = ''
3288
3510
  }
@@ -3329,34 +3551,6 @@
3329
3551
  return td
3330
3552
  }
3331
3553
 
3332
- /** 控制 Picker 左侧分类栏显隐(仅 Skill 页保留分类,其余合并为单表) */
3333
- function setPickerNavPanelVisible(visible) {
3334
- if (!pickerNavPanel) return
3335
- pickerNavPanel.hidden = !visible
3336
- if (!visible && pickerNavList) pickerNavList.innerHTML = ''
3337
- }
3338
-
3339
- /** 渲染 Picker 左侧分类导航 */
3340
- function renderPickerNavTabs(tabDefs, activeId, onChange) {
3341
- if (!pickerNavList) return
3342
- pickerNavList.innerHTML = ''
3343
- pickerManageState.activeTab = activeId
3344
- for (let i = 0; i < tabDefs.length; i++) {
3345
- const tab = tabDefs[i]
3346
- const btn = document.createElement('button')
3347
- btn.type = 'button'
3348
- btn.className = 'picker-nav-item' + (tab.id === activeId ? ' is-selected' : '')
3349
- btn.setAttribute('role', 'tab')
3350
- btn.setAttribute('aria-selected', tab.id === activeId ? 'true' : 'false')
3351
- btn.textContent = tab.label
3352
- btn.addEventListener('click', function () {
3353
- if (tab.id === activeId) return
3354
- onChange(tab.id)
3355
- })
3356
- pickerNavList.appendChild(btn)
3357
- }
3358
- }
3359
-
3360
3554
  /** 设置 entries/history 面板加载或错误态 */
3361
3555
  function setRepoPanelState(el, message, isError) {
3362
3556
  if (!el) return
@@ -4914,6 +5108,13 @@
4914
5108
  skillPackageModal.setAttribute('aria-hidden', 'true')
4915
5109
  document.body.style.overflow = ''
4916
5110
  }
5111
+
5112
+ function closeSkillStoreModal() {
5113
+ if (!skillStoreModal) return
5114
+ skillStoreModal.classList.remove('is-open')
5115
+ skillStoreModal.setAttribute('aria-hidden', 'true')
5116
+ document.body.style.overflow = ''
5117
+ }
4917
5118
  function closeRunGroupModal() {
4918
5119
  if (!runGroupModal) return
4919
5120
  runGroupModal.classList.remove('is-open')
@@ -5458,7 +5659,7 @@
5458
5659
  if (!list.length) {
5459
5660
  const empty = document.createElement('span')
5460
5661
  empty.className = 'session-tabs-empty'
5461
- empty.textContent = '暂无历史会话'
5662
+ empty.textContent = '暂无会话'
5462
5663
  sessionListEl.appendChild(empty)
5463
5664
  return
5464
5665
  }
@@ -5484,6 +5685,7 @@
5484
5685
  const mainBtn = document.createElement('button')
5485
5686
  mainBtn.type = 'button'
5486
5687
  mainBtn.className = 'sidebar-session-main'
5688
+ mainBtn.appendChild(createSessionKindIcon(resolveSessionKindForItem(s)))
5487
5689
  if (isRunning) {
5488
5690
  const runBadge = document.createElement('span')
5489
5691
  runBadge.className = 'sidebar-session-running-badge'
@@ -5572,7 +5774,7 @@
5572
5774
  return u.toString()
5573
5775
  }
5574
5776
 
5575
- /** 列表里是否存在运行中 session(子进程存活或当前页 turn 进行中) */
5777
+ /** 列表里是否存在运行中 session(主 run query 进行中或当前页 turn 进行中) */
5576
5778
  function sessionListHasRunning(sessions) {
5577
5779
  if (!Array.isArray(sessions)) return false
5578
5780
  const current = sessionIdInput ? sessionIdInput.value.trim() : ''
@@ -5713,6 +5915,9 @@
5713
5915
  }
5714
5916
  sessionIdInput.value = nextSid
5715
5917
  currentSessionContextPercent = null
5918
+ sessionTurnActive = false
5919
+ hydrateSessionGeneration += 1
5920
+ resetChatUi()
5716
5921
  void loadPendingPrompts()
5717
5922
  if (nextSid) {
5718
5923
  void loadSessionOverridesIntoComposer()
@@ -7420,15 +7625,18 @@
7420
7625
  kind === 'mcp' ? '导入 MCP' : kind === 'skills' ? '导入 Skill' : '导入 zip'
7421
7626
  btnPickerImport.disabled = false
7422
7627
  }
7628
+ if (btnPickerSkillStore) {
7629
+ btnPickerSkillStore.hidden = kind !== 'skills'
7630
+ btnPickerSkillStore.disabled = false
7631
+ }
7423
7632
  if (btnPickerMcpJson) {
7424
7633
  btnPickerMcpJson.hidden = kind !== 'mcp'
7425
7634
  btnPickerMcpJson.disabled = false
7426
7635
  }
7427
7636
  if (btnPickerCancel) btnPickerCancel.hidden = kind === 'skills' || kind === 'mcp'
7428
7637
  if (btnPickerSave) btnPickerSave.hidden = kind === 'skills' || kind === 'mcp'
7429
- setPickerNavPanelVisible(kind === 'skills')
7430
7638
  setPickerContentState('加载中…', false)
7431
- setPickerContentMode('table')
7639
+ setPickerContentMode()
7432
7640
  clearPickerTableBody()
7433
7641
 
7434
7642
  if (kind === 'tools') {
@@ -7553,7 +7761,7 @@
7553
7761
  return tr
7554
7762
  }
7555
7763
  const renderToolsRows = function () {
7556
- setPickerContentMode('table')
7764
+ setPickerContentMode()
7557
7765
  setPickerTableHead(['名称', '类型', '详细信息', '操作'])
7558
7766
  clearPickerTableBody()
7559
7767
  setPickerContentState('', false)
@@ -7727,7 +7935,7 @@
7727
7935
  return tr
7728
7936
  }
7729
7937
  const renderMcpRows = function () {
7730
- setPickerContentMode('table')
7938
+ setPickerContentMode()
7731
7939
  setPickerTableHead(['名称', '来源', '操作'])
7732
7940
  clearPickerTableBody()
7733
7941
  setPickerContentState('', false)
@@ -7828,7 +8036,7 @@
7828
8036
  return (tags.length ? tags.join(' ') : '—') + (summary ? ' — ' + summary : '')
7829
8037
  }
7830
8038
  const renderAgentTeamsRows = function () {
7831
- setPickerContentMode('table')
8039
+ setPickerContentMode()
7832
8040
  setPickerTableHead(['成员', '配置', '操作'])
7833
8041
  clearPickerTableBody()
7834
8042
  setPickerContentState('', false)
@@ -7881,7 +8089,6 @@
7881
8089
  })
7882
8090
  } else {
7883
8091
  const skillPickerState = {
7884
- activeTab: 'list',
7885
8092
  baseRows: [],
7886
8093
  installedRows: [],
7887
8094
  cloudRows: [],
@@ -7995,8 +8202,8 @@
7995
8202
  return tr
7996
8203
  }
7997
8204
  const renderCloudSkillPanel = function () {
7998
- if (!pickerCloudPanel) return
7999
- pickerCloudPanel.innerHTML = ''
8205
+ if (!skillStoreModalBody) return
8206
+ skillStoreModalBody.innerHTML = ''
8000
8207
  const cloudSearch = document.createElement('input')
8001
8208
  cloudSearch.type = 'search'
8002
8209
  cloudSearch.className = 'agent-picker-cloud-search'
@@ -8007,13 +8214,13 @@
8007
8214
  skillPickerState.cloudPage = 1
8008
8215
  void loadCloudSkillRows()
8009
8216
  }
8010
- pickerCloudPanel.appendChild(cloudSearch)
8217
+ skillStoreModalBody.appendChild(cloudSearch)
8011
8218
  const meta = document.createElement('div')
8012
8219
  meta.className = 'agent-picker-cloud-meta'
8013
8220
  if (skillPickerState.cloudLoading) meta.textContent = '加载中…'
8014
8221
  else if (skillPickerState.cloudError) meta.textContent = '加载失败:' + skillPickerState.cloudError
8015
8222
  else meta.textContent = '展示前 ' + skillPickerState.cloudRows.length + ' 项'
8016
- pickerCloudPanel.appendChild(meta)
8223
+ skillStoreModalBody.appendChild(meta)
8017
8224
  if (!skillPickerState.cloudError && skillPickerState.cloudRows.length) {
8018
8225
  for (let i = 0; i < skillPickerState.cloudRows.length; i++) {
8019
8226
  const item = skillPickerState.cloudRows[i]
@@ -8031,7 +8238,7 @@
8031
8238
  installing ? '处理中…' : installed ? '卸载' : '安装', '', function () {
8032
8239
  if (installBtn.disabled) return
8033
8240
  skillPickerState.installingMap[item.id] = true
8034
- renderSkillRows()
8241
+ renderCloudSkillPanel()
8035
8242
  const request = installed
8036
8243
  ? fetch(buildRuntimeSkillDeleteUrl(item.packageId), { method: 'DELETE' })
8037
8244
  : fetch(buildCloudSkillInstallUrl(), {
@@ -8049,21 +8256,23 @@
8049
8256
  else pickerSelected.add(item.skillName || item.id)
8050
8257
  setStatus('云端技能已' + (installed ? '卸载' : '安装') + ':' + item.title, serverReady ? 'ready' : '')
8051
8258
  return persistSkillSelectionNow().then(function () {
8052
- return refreshSkillPickerRows()
8259
+ return refreshSkillPickerRows().then(function () {
8260
+ return loadCloudSkillRows()
8261
+ })
8053
8262
  })
8054
8263
  })
8055
8264
  .catch(function (e) { window.alert((installed ? '卸载' : '安装') + '失败: ' + e) })
8056
8265
  .finally(function () {
8057
8266
  delete skillPickerState.installingMap[item.id]
8058
- renderSkillRows()
8267
+ renderCloudSkillPanel()
8059
8268
  })
8060
8269
  }, { disabled: installing },
8061
8270
  )
8062
8271
  actions.appendChild(installBtn)
8063
8272
  row.appendChild(actions)
8064
- pickerCloudPanel.appendChild(row)
8273
+ skillStoreModalBody.appendChild(row)
8065
8274
  }
8066
- pickerCloudPanel.appendChild(createCloudPager({
8275
+ skillStoreModalBody.appendChild(createCloudPager({
8067
8276
  page: skillPickerState.cloudPage,
8068
8277
  totalPages: skillPickerState.cloudTotalPages,
8069
8278
  loading: skillPickerState.cloudLoading,
@@ -8075,24 +8284,11 @@
8075
8284
  const empty = document.createElement('div')
8076
8285
  empty.className = 'agent-picker-empty'
8077
8286
  empty.textContent = skillPickerState.cloudLoading ? '(技能商店加载中)' : '(无匹配项)'
8078
- pickerCloudPanel.appendChild(empty)
8287
+ skillStoreModalBody.appendChild(empty)
8079
8288
  }
8080
8289
  }
8081
8290
  const renderSkillRows = function () {
8082
- renderPickerNavTabs(
8083
- [
8084
- { id: 'list', label: 'Skill' },
8085
- { id: 'cloud', label: 'skill商店' },
8086
- ],
8087
- skillPickerState.activeTab,
8088
- function (id) { skillPickerState.activeTab = id; renderSkillRows() },
8089
- )
8090
- if (skillPickerState.activeTab === 'cloud') {
8091
- setPickerContentMode('cloud')
8092
- renderCloudSkillPanel()
8093
- return
8094
- }
8095
- setPickerContentMode('table')
8291
+ setPickerContentMode()
8096
8292
  setPickerTableHead(['名称', '描述', '操作'])
8097
8293
  clearPickerTableBody()
8098
8294
  setPickerContentState('', false)
@@ -8118,7 +8314,7 @@
8118
8314
  skillPickerState.cloudSeq = seq
8119
8315
  skillPickerState.cloudLoading = true
8120
8316
  skillPickerState.cloudError = ''
8121
- renderSkillRows()
8317
+ renderCloudSkillPanel()
8122
8318
  return fetch(buildCloudSkillCatalogUrl(skillPickerState.cloudQuery, skillPickerState.cloudPage, skillPickerState.cloudPageSize))
8123
8319
  .then(function (r) {
8124
8320
  return r.json().then(function (b) {
@@ -8131,21 +8327,28 @@
8131
8327
  skillPickerState.cloudRows = Array.isArray(x.b.items) ? x.b.items : []
8132
8328
  skillPickerState.cloudTotalPages = Number(x.b.totalPages || 0)
8133
8329
  skillPickerState.cloudLoading = false
8134
- renderSkillRows()
8330
+ renderCloudSkillPanel()
8135
8331
  })
8136
8332
  .catch(function (e) {
8137
8333
  if (seq !== skillPickerState.cloudSeq) return
8138
8334
  skillPickerState.cloudRows = []
8139
8335
  skillPickerState.cloudLoading = false
8140
8336
  skillPickerState.cloudError = String(e)
8141
- renderSkillRows()
8337
+ renderCloudSkillPanel()
8142
8338
  })
8143
8339
  }
8340
+ openSkillStoreModalFn = function () {
8341
+ if (!skillStoreModal) return
8342
+ skillStoreModal.classList.add('is-open')
8343
+ skillStoreModal.setAttribute('aria-hidden', 'false')
8344
+ document.body.style.overflow = 'hidden'
8345
+ skillPickerState.cloudPage = 1
8346
+ void loadCloudSkillRows()
8347
+ }
8144
8348
  refreshSkillPickerRows = function () {
8145
8349
  return Promise.all([
8146
8350
  fetchSkillRows(buildSkillBaseListUrl),
8147
8351
  fetchSkillRows(buildSkillInstalledListUrl),
8148
- loadCloudSkillRows(),
8149
8352
  ])
8150
8353
  .then(function (allRows) {
8151
8354
  skillPickerState.baseRows = allRows[0]
@@ -9100,6 +9303,20 @@
9100
9303
  openSkillPackageModal()
9101
9304
  })
9102
9305
  }
9306
+ if (btnPickerSkillStore) {
9307
+ btnPickerSkillStore.addEventListener('click', function () {
9308
+ if (typeof openSkillStoreModalFn === 'function') openSkillStoreModalFn()
9309
+ })
9310
+ }
9311
+ if (btnSkillStoreModalClose) {
9312
+ btnSkillStoreModalClose.addEventListener('click', closeSkillStoreModal)
9313
+ }
9314
+ if (btnSkillStoreClose) {
9315
+ btnSkillStoreClose.addEventListener('click', closeSkillStoreModal)
9316
+ }
9317
+ if (skillStoreModalBackdrop) {
9318
+ skillStoreModalBackdrop.addEventListener('click', closeSkillStoreModal)
9319
+ }
9103
9320
  if (btnSkillPackageModalClose) {
9104
9321
  btnSkillPackageModalClose.addEventListener('click', closeSkillPackageModal)
9105
9322
  }