@auto-ai/agent 2.1.189 → 2.1.192

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 (55) 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/_next/static/chunks/5f0286b194e88c68.js +1 -0
  4. package/dist/safe-a/_next/static/chunks/65982c1925fca4ab.js +1 -0
  5. package/dist/safe-a/_next/static/chunks/69aecc2a7893ba05.js +1 -0
  6. package/dist/safe-a/_next/static/chunks/743bc613bbda46b4.css +1 -0
  7. package/dist/safe-a/_next/static/chunks/{5022180fa579a73a.js → 760380631ca561e0.js} +1 -1
  8. package/dist/safe-a/_next/static/chunks/{2bac8f0a4e762326.js → 9dcb662588dbf520.js} +1 -1
  9. package/dist/safe-a/_next/static/chunks/cb85ccd601c65174.js +1 -0
  10. package/dist/safe-a/_next/static/chunks/e784a1fa0acaaaa0.css +7 -0
  11. package/dist/safe-a/_next/static/chunks/{2d7f6aa6628bccd0.js → eca5690d7fb3d712.js} +1 -1
  12. package/dist/safe-a/index.html +2 -2
  13. package/dist/safe-a/index.txt +10 -10
  14. package/dist/safe-a/manage/about/index.html +2 -2
  15. package/dist/safe-a/manage/about/index.txt +11 -11
  16. package/dist/safe-a/manage/env/index.html +2 -2
  17. package/dist/safe-a/manage/env/index.txt +13 -13
  18. package/dist/safe-a/manage/geelib/index.html +2 -2
  19. package/dist/safe-a/manage/geelib/index.txt +13 -13
  20. package/dist/safe-a/manage/general/index.html +2 -2
  21. package/dist/safe-a/manage/general/index.txt +11 -11
  22. package/dist/safe-a/manage/git/index.html +2 -2
  23. package/dist/safe-a/manage/git/index.txt +13 -13
  24. package/dist/safe-a/manage/im/index.html +2 -2
  25. package/dist/safe-a/manage/im/index.txt +13 -13
  26. package/dist/safe-a/manage/index.html +2 -2
  27. package/dist/safe-a/manage/index.txt +8 -8
  28. package/dist/safe-a/manage/library/index.html +2 -2
  29. package/dist/safe-a/manage/library/index.txt +11 -11
  30. package/dist/safe-a/manage/mcp/index.html +2 -2
  31. package/dist/safe-a/manage/mcp/index.txt +11 -11
  32. package/dist/safe-a/manage/permissions/index.html +2 -2
  33. package/dist/safe-a/manage/permissions/index.txt +11 -11
  34. package/dist/safe-a/manage/skills/index.html +2 -2
  35. package/dist/safe-a/manage/skills/index.txt +11 -11
  36. package/dist/safe-a/manage/system/index.html +2 -2
  37. package/dist/safe-a/manage/system/index.txt +13 -13
  38. package/dist/safe-a/manage/task/index.html +2 -2
  39. package/dist/safe-a/manage/task/index.txt +11 -11
  40. package/dist/safe-a/manage/teams/index.html +2 -2
  41. package/dist/safe-a/manage/teams/index.txt +11 -11
  42. package/dist/safe-a/manage/tools/index.html +2 -2
  43. package/dist/safe-a/manage/tools/index.txt +11 -11
  44. package/dist/ws-test/ws-test.js +218 -4
  45. package/mcps-runtime/claude-ws-channel/server/gateway.bundle.mjs +87 -74
  46. package/package.json +6 -6
  47. package/dist/safe-a/_next/static/chunks/26f5809c84b263c0.css +0 -7
  48. package/dist/safe-a/_next/static/chunks/a5b851b271052e89.js +0 -1
  49. package/dist/safe-a/_next/static/chunks/a6aafbc087d75717.js +0 -1
  50. package/dist/safe-a/_next/static/chunks/d16ae4f1eb111da5.js +0 -1
  51. package/dist/safe-a/_next/static/chunks/d47487bdc4a277fd.css +0 -1
  52. package/dist/safe-a/_next/static/chunks/e00679a7df05e016.js +0 -1
  53. /package/dist/safe-a/_next/static/{XxDw4q1JeD7OUmzpG1zOw → jccK1lHzHeCZKJ8IiMFik}/_buildManifest.js +0 -0
  54. /package/dist/safe-a/_next/static/{XxDw4q1JeD7OUmzpG1zOw → jccK1lHzHeCZKJ8IiMFik}/_clientMiddlewareManifest.json +0 -0
  55. /package/dist/safe-a/_next/static/{XxDw4q1JeD7OUmzpG1zOw → jccK1lHzHeCZKJ8IiMFik}/_ssgManifest.js +0 -0
@@ -1534,6 +1534,7 @@
1534
1534
  const item = ev.payload
1535
1535
  const m = item && item.message ? item.message : null
1536
1536
  if (!m) return ''
1537
+ if (m.type === 'user' && !shouldShowWsUserMessage(m)) return ''
1537
1538
  if (m.type === 'user' || m.type === 'assistant') {
1538
1539
  const blocks = getMessageContentBlocks(m)
1539
1540
  const nonTool = blocks.filter(function (b) {
@@ -2035,6 +2036,7 @@
2035
2036
  shouldShowWsUserMessage(msg)
2036
2037
  ) {
2037
2038
  clearPendingUserEcho()
2039
+ refreshSessionTitleFromChat(sessionIdInput.value.trim())
2038
2040
  }
2039
2041
  requestRenderChat()
2040
2042
  return true
@@ -2482,6 +2484,7 @@
2482
2484
  sessionReplayInProgress = false
2483
2485
  flushPendingConversationDeltas()
2484
2486
  requestRenderChat()
2487
+ refreshSessionTitleFromChat(sid)
2485
2488
  }
2486
2489
  }
2487
2490
 
@@ -4633,8 +4636,217 @@
4633
4636
  }
4634
4637
  }
4635
4638
 
4639
+ /** 曾从 transcript / 对话中提取过的会话标题,避免列表刷新后丢失 */
4640
+ const sessionTitleById = Object.create(null)
4641
+ /** 正在拉 replay 补标题的 session,避免重复请求 */
4642
+ const sessionTitleHydrateInflight = new Set()
4643
+
4644
+ function rememberSessionTitle(sessionId, title) {
4645
+ const sid = typeof sessionId === 'string' ? sessionId.trim() : ''
4646
+ const t = typeof title === 'string' ? title.trim() : ''
4647
+ if (!sid || !t) return
4648
+ sessionTitleById[sid] = t
4649
+ }
4650
+
4651
+ /** 旧版 API 从 tool input 误抓的 CVE 摘要,不得作为标题 */
4652
+ function isPoisonedSessionSummary(summary) {
4653
+ const s = typeof summary === 'string' ? summary.trim() : ''
4654
+ return /^CVE-\d{4}-\d+$/i.test(s)
4655
+ }
4656
+
4657
+ /** 无首条用户输入时服务端回退的 sessionId 短标签 */
4658
+ function isSessionIdFallbackTitle(title, sessionId) {
4659
+ const t = typeof title === 'string' ? title.trim() : ''
4660
+ const sid = typeof sessionId === 'string' ? sessionId.trim() : ''
4661
+ if (!t || !sid) return false
4662
+ if (t === sid) return true
4663
+ if (t.endsWith('…') && sid.startsWith(t.slice(0, -1))) return true
4664
+ if (t.endsWith('...') && sid.startsWith(t.slice(0, -3))) return true
4665
+ return false
4666
+ }
4667
+
4668
+ /**
4669
+ * 从 /api/sessions 单条记录解析标题:优先 firstPrompt,否则用同口径 summary。
4670
+ * 排除 CVE 误抓;sessionId 短标签视为无效标题。
4671
+ */
4672
+ function sessionTitleFromApiItem(s) {
4673
+ if (!s || typeof s !== 'object') return ''
4674
+ const fp = typeof s.firstPrompt === 'string' ? s.firstPrompt.trim() : ''
4675
+ if (fp && !isSessionIdFallbackTitle(fp, s.sessionId)) return fp
4676
+ const sum = typeof s.summary === 'string' ? s.summary.trim() : ''
4677
+ if (!sum || sum === '新对话' || isPoisonedSessionSummary(sum)) return ''
4678
+ if (isSessionIdFallbackTitle(sum, s.sessionId)) return ''
4679
+ return sum
4680
+ }
4681
+
4682
+ function needsSessionTitleHydration(s) {
4683
+ if (!s || !s.sessionId) return false
4684
+ const title = sessionTitleFromApiItem(s)
4685
+ const remembered = sessionTitleById[s.sessionId.trim()]
4686
+ if (remembered && remembered.trim()) return false
4687
+ return !title
4688
+ }
4689
+
4690
+ /**
4691
+ * 解析侧栏/顶栏展示标题:仅首条用户输入。
4692
+ */
4693
+ function pickSessionFirstPrompt(s) {
4694
+ if (!s || typeof s !== 'object') return ''
4695
+ const fp = typeof s.firstPrompt === 'string' ? s.firstPrompt.trim() : ''
4696
+ if (fp && !isSessionIdFallbackTitle(fp, s.sessionId)) return fp
4697
+ const fromApi = sessionTitleFromApiItem(s)
4698
+ if (fromApi) return fromApi
4699
+ const remembered =
4700
+ typeof s.sessionId === 'string' ? sessionTitleById[s.sessionId.trim()] : ''
4701
+ return typeof remembered === 'string' ? remembered.trim() : ''
4702
+ }
4703
+
4704
+ /** 从 replay 信封提取首条用户输入(与侧栏 hydrate 同源) */
4705
+ function extractFirstUserPromptFromReplayEnvelopes(envelopes) {
4706
+ if (!Array.isArray(envelopes)) return ''
4707
+ for (let i = 0; i < envelopes.length; i++) {
4708
+ const env = envelopes[i]
4709
+ if (!env || env.protocolVersion !== 3 || env.type !== 'conversation.delta') continue
4710
+ const payload = env.data && typeof env.data === 'object' ? env.data : {}
4711
+ if (payload.action !== 'message_added') continue
4712
+ const msg = payload.message
4713
+ if (!msg || msg.type !== 'user' || !shouldShowWsUserMessage(msg)) continue
4714
+ const blocks = getMessageContentBlocks(msg)
4715
+ const nonToolBlocks = blocks.filter(function (b) {
4716
+ return b && b.type !== 'tool_result'
4717
+ })
4718
+ if (!nonToolBlocks.length) continue
4719
+ const body = unwrapChannelWrappedText(
4720
+ contentToPlainText(nonToolBlocks).trim(),
4721
+ ).body.trim()
4722
+ if (body) return body.length > 120 ? body.slice(0, 120) : body
4723
+ }
4724
+ return ''
4725
+ }
4726
+
4727
+ /**
4728
+ * 列表接口常缺 firstPrompt(仅带 summary 或 sessionId 短标签)。
4729
+ * 首次进页时对缺标题会话并行拉 replay 补首条用户输入。
4730
+ */
4731
+ async function hydrateMissingSessionTitles(sessions) {
4732
+ const list = Array.isArray(sessions) ? sessions : []
4733
+ const targets = list.filter(function (s) {
4734
+ if (!s || !s.sessionId) return false
4735
+ if (sessionTitleHydrateInflight.has(s.sessionId)) return false
4736
+ return needsSessionTitleHydration(s)
4737
+ })
4738
+ if (!targets.length) return
4739
+ await Promise.all(
4740
+ targets.map(async function (s) {
4741
+ const sid = s.sessionId
4742
+ sessionTitleHydrateInflight.add(sid)
4743
+ try {
4744
+ const r = await fetch(buildSessionReplayUrl(sid))
4745
+ const data = await r.json()
4746
+ if (!r.ok) return
4747
+ const title = extractFirstUserPromptFromReplayEnvelopes(data.envelopes)
4748
+ if (!title) return
4749
+ patchSessionListFirstPrompt(sid, title)
4750
+ } catch {
4751
+ /* 单条失败不影响其它 session */
4752
+ } finally {
4753
+ sessionTitleHydrateInflight.delete(sid)
4754
+ }
4755
+ }),
4756
+ )
4757
+ }
4758
+
4759
+ /**
4760
+ * 从已加载的对话状态提取首条用户输入,供标题展示。
4761
+ * 与 shouldShowWsUserMessage / unwrapChannelWrappedText 规则一致。
4762
+ */
4763
+ function extractFirstUserPromptFromChatState() {
4764
+ const mainRun = chatState.messagesByRun.get('main')
4765
+ if (!Array.isArray(mainRun)) return ''
4766
+ for (let i = 0; i < mainRun.length; i++) {
4767
+ const item = mainRun[i]
4768
+ const m = item && item.message ? item.message : null
4769
+ if (!m || m.type !== 'user' || !shouldShowWsUserMessage(m)) continue
4770
+ const blocks = getMessageContentBlocks(m)
4771
+ const nonToolBlocks = blocks.filter(function (b) {
4772
+ return b && b.type !== 'tool_result'
4773
+ })
4774
+ if (!nonToolBlocks.length) continue
4775
+ const body = unwrapChannelWrappedText(contentToPlainText(nonToolBlocks).trim()).body.trim()
4776
+ if (body) return body.length > 120 ? body.slice(0, 120) : body
4777
+ }
4778
+ return ''
4779
+ }
4780
+
4781
+ /** 将首条用户输入写回侧栏会话缓存,保证标题与 transcript 一致 */
4782
+ function patchSessionListFirstPrompt(sessionId, firstPrompt) {
4783
+ const sid = typeof sessionId === 'string' ? sessionId.trim() : ''
4784
+ const fp = typeof firstPrompt === 'string' ? firstPrompt.trim() : ''
4785
+ if (!sid || !fp || !Array.isArray(window.__lastSessions)) return
4786
+ rememberSessionTitle(sid, fp)
4787
+ let changed = false
4788
+ for (let i = 0; i < window.__lastSessions.length; i++) {
4789
+ const s = window.__lastSessions[i]
4790
+ if (!s || s.sessionId !== sid) continue
4791
+ if (s.firstPrompt !== fp) {
4792
+ s.firstPrompt = fp
4793
+ s.summary = fp
4794
+ changed = true
4795
+ }
4796
+ break
4797
+ }
4798
+ if (changed) renderSessionList(window.__lastSessions)
4799
+ }
4800
+
4801
+ /**
4802
+ * 合并 /api/sessions 响应与本地已解析标题,避免轮询刷新把侧栏标题冲成(无标题)。
4803
+ */
4804
+ function mergeSessionsWithRememberedTitles(incoming) {
4805
+ const list = Array.isArray(incoming) ? incoming.slice() : []
4806
+ const prevById = new Map()
4807
+ if (Array.isArray(window.__lastSessions)) {
4808
+ for (let i = 0; i < window.__lastSessions.length; i++) {
4809
+ const s = window.__lastSessions[i]
4810
+ if (s && s.sessionId) prevById.set(s.sessionId, s)
4811
+ }
4812
+ }
4813
+ return list.map(function (s) {
4814
+ if (!s || !s.sessionId) return s
4815
+ const fromApi = sessionTitleFromApiItem(s)
4816
+ if (fromApi) {
4817
+ rememberSessionTitle(s.sessionId, fromApi)
4818
+ return { ...s, firstPrompt: fromApi, summary: fromApi }
4819
+ }
4820
+ const prev = prevById.get(s.sessionId)
4821
+ const prevTitle = prev ? sessionTitleFromApiItem(prev) : ''
4822
+ const remembered = sessionTitleById[s.sessionId] || prevTitle
4823
+ if (remembered) {
4824
+ rememberSessionTitle(s.sessionId, remembered)
4825
+ return { ...s, firstPrompt: remembered, summary: remembered }
4826
+ }
4827
+ return s
4828
+ })
4829
+ }
4830
+
4831
+ /** 会话标题仅取自首条用户输入 */
4636
4832
  function sessionTitle(s) {
4637
- return (s.summary || s.firstPrompt || s.customTitle || '(无标题)').slice(0, 120)
4833
+ const fp = pickSessionFirstPrompt(s)
4834
+ if (fp) return fp.slice(0, 120)
4835
+ if (s && s.sessionId === sessionIdInput.value.trim()) {
4836
+ const fromChat = extractFirstUserPromptFromChatState()
4837
+ if (fromChat) return fromChat
4838
+ }
4839
+ return '(无标题)'
4840
+ }
4841
+
4842
+ /** 历史回放或实时消息落盘后,用 transcript 首条用户输入刷新标题 */
4843
+ function refreshSessionTitleFromChat(sessionId) {
4844
+ const sid = typeof sessionId === 'string' ? sessionId.trim() : ''
4845
+ if (!sid || sessionIdInput.value.trim() !== sid) return
4846
+ const title = extractFirstUserPromptFromChatState()
4847
+ if (!title) return
4848
+ patchSessionListFirstPrompt(sid, title)
4849
+ updateMainHeader(title)
4638
4850
  }
4639
4851
 
4640
4852
  function monthKeyFromSession(s) {
@@ -4769,7 +4981,7 @@
4769
4981
  const list = hasCurrent
4770
4982
  ? rawList
4771
4983
  : current
4772
- ? [{ sessionId: current, summary: '新对话' }].concat(rawList)
4984
+ ? [{ sessionId: current, firstPrompt: '新对话', summary: '新对话' }].concat(rawList)
4773
4985
  : rawList
4774
4986
  sessionListEl.innerHTML = ''
4775
4987
  updateMainHeader()
@@ -4980,8 +5192,10 @@
4980
5192
  '个会话 · agent: ' +
4981
5193
  ag +
4982
5194
  (dir ? ' · ' + dir : '')
4983
- window.__lastSessions = sessions
4984
- renderSessionList(sessions)
5195
+ const merged = mergeSessionsWithRememberedTitles(sessions)
5196
+ window.__lastSessions = merged
5197
+ renderSessionList(merged)
5198
+ void hydrateMissingSessionTitles(merged)
4985
5199
  const shouldActivateFirst = !!(options && options.activateFirstIfEmpty)
4986
5200
  if (shouldActivateFirst && sessionIdInput && !sessionIdInput.value.trim() && sessions.length) {
4987
5201
  const firstId = sessions[0] && typeof sessions[0].sessionId === 'string'
@@ -51002,6 +51002,14 @@ function extractJsonStringField(text, key) {
51002
51002
  }
51003
51003
  return;
51004
51004
  }
51005
+ function extractLastMetadataStringField(tail, entryType, fieldKey) {
51006
+ const prefix = `{"type":"${entryType}"`;
51007
+ const line = tail.split(`
51008
+ `).findLast((l) => l.startsWith(prefix));
51009
+ if (!line)
51010
+ return;
51011
+ return extractLastJsonStringField(line, fieldKey);
51012
+ }
51005
51013
  function extractLastJsonStringField(text, key) {
51006
51014
  const patterns = [`"${key}":"`, `"${key}": "`];
51007
51015
  let lastValue;
@@ -51029,6 +51037,12 @@ function extractLastJsonStringField(text, key) {
51029
51037
  }
51030
51038
  return lastValue;
51031
51039
  }
51040
+ function unwrapChannelWrappedPrompt(text) {
51041
+ const matched = text.match(/^<channel\s+[^>]+>\s*([\s\S]*?)\s*<\/channel>\s*$/i);
51042
+ if (!matched)
51043
+ return text;
51044
+ return (matched[1] ?? "").trim();
51045
+ }
51032
51046
  function extractFirstPromptFromHead(head) {
51033
51047
  let start = 0;
51034
51048
  let commandFallback = "";
@@ -51078,7 +51092,7 @@ function extractFirstPromptFromHead(head) {
51078
51092
  }
51079
51093
  }
51080
51094
  for (const raw of texts) {
51081
- let result = raw.replace(/\n/g, " ").trim();
51095
+ let result = unwrapChannelWrappedPrompt(raw).replace(/\n/g, " ").trim();
51082
51096
  if (!result)
51083
51097
  continue;
51084
51098
  const cmdMatch = COMMAND_NAME_RE.exec(result);
@@ -296049,7 +296063,7 @@ function migrateLegacyAttachmentTypes(message) {
296049
296063
  }
296050
296064
  return message;
296051
296065
  }
296052
- function deserializeMessagesWithInterruptDetection(serializedMessages) {
296066
+ function deserializeMessagesWithInterruptDetection(serializedMessages, options) {
296053
296067
  try {
296054
296068
  const migratedMessages = serializedMessages.map(migrateLegacyAttachmentTypes);
296055
296069
  const validModes = new Set(PERMISSION_MODES);
@@ -296060,10 +296074,10 @@ function deserializeMessagesWithInterruptDetection(serializedMessages) {
296060
296074
  }
296061
296075
  const filteredToolUses = filterUnresolvedToolUses(migratedMessages);
296062
296076
  const filteredThinking = filterOrphanedThinkingOnlyMessages(filteredToolUses);
296063
- const filteredMessages = filterWhitespaceOnlyAssistantMessages(filteredThinking);
296077
+ const filteredMessages = filteredThinking;
296064
296078
  const internalState = detectTurnInterruption(filteredMessages);
296065
296079
  let turnInterruptionState;
296066
- if (internalState.kind === "interrupted_turn") {
296080
+ if (internalState.kind === "interrupted_turn" && !options?.skipInterruptedPlaceholders) {
296067
296081
  const [continuationMessage] = normalizeMessages([
296068
296082
  createUserMessage({
296069
296083
  content: "Continue from where you left off.",
@@ -296078,11 +296092,13 @@ function deserializeMessagesWithInterruptDetection(serializedMessages) {
296078
296092
  } else {
296079
296093
  turnInterruptionState = internalState;
296080
296094
  }
296081
- const lastRelevantIdx = filteredMessages.findLastIndex((m4) => m4.type !== "system" && m4.type !== "progress");
296082
- if (lastRelevantIdx !== -1 && filteredMessages[lastRelevantIdx].type === "user") {
296083
- filteredMessages.splice(lastRelevantIdx + 1, 0, createAssistantMessage({
296084
- content: NO_RESPONSE_REQUESTED
296085
- }));
296095
+ if (!options?.skipInterruptedPlaceholders) {
296096
+ const lastRelevantIdx = filteredMessages.findLastIndex((m4) => m4.type !== "system" && m4.type !== "progress");
296097
+ if (lastRelevantIdx !== -1 && filteredMessages[lastRelevantIdx].type === "user") {
296098
+ filteredMessages.splice(lastRelevantIdx + 1, 0, createAssistantMessage({
296099
+ content: NO_RESPONSE_REQUESTED
296100
+ }));
296101
+ }
296086
296102
  }
296087
296103
  return { messages: filteredMessages, turnInterruptionState };
296088
296104
  } catch (error49) {
@@ -296221,7 +296237,9 @@ async function loadConversationForResume(source, sourceJsonlFile, options) {
296221
296237
  checkResumeConsistency(messages);
296222
296238
  }
296223
296239
  restoreSkillStateFromMessages(messages);
296224
- const deserialized = deserializeMessagesWithInterruptDetection(messages);
296240
+ const deserialized = deserializeMessagesWithInterruptDetection(messages, {
296241
+ skipInterruptedPlaceholders: options?.skipInterruptedPlaceholders
296242
+ });
296225
296243
  messages = deserialized.messages;
296226
296244
  const hookMessages = options?.headlessWsResume ? [] : await processSessionStartHooks("resume", { sessionId });
296227
296245
  messages.push(...hookMessages);
@@ -325630,7 +325648,7 @@ function parseSessionInfoFromLite(sessionId, lite, projectPath) {
325630
325648
  if (!Number.isNaN(parsed))
325631
325649
  createdAt = parsed;
325632
325650
  }
325633
- const summary = customTitle || extractLastJsonStringField(tail, "lastPrompt") || extractLastJsonStringField(tail, "summary") || firstPrompt || fallbackSessionSummary(sessionId);
325651
+ const summary = firstPrompt || fallbackSessionSummary(sessionId);
325634
325652
  const gitBranch = extractLastJsonStringField(tail, "gitBranch") || extractJsonStringField(head, "gitBranch") || undefined;
325635
325653
  const sessionCwd = extractJsonStringField(head, "cwd") || projectPath || undefined;
325636
325654
  const tagLine = tail.split(`
@@ -388005,7 +388023,7 @@ function normalizeMessagesForAPI(messages, tools = []) {
388005
388023
  timestamp: message.timestamp
388006
388024
  });
388007
388025
  const lastMessage = last_default(result);
388008
- if (lastMessage?.type === "user") {
388026
+ if (lastMessage?.type === "user" && canMergeAdjacentUserMessages(lastMessage, userMsg)) {
388009
388027
  result[result.length - 1] = mergeUserMessages(lastMessage, userMsg);
388010
388028
  return;
388011
388029
  }
@@ -388054,7 +388072,7 @@ function normalizeMessagesForAPI(messages, tools = []) {
388054
388072
  }
388055
388073
  }
388056
388074
  const lastMessage = last_default(result);
388057
- if (lastMessage?.type === "user") {
388075
+ if (lastMessage?.type === "user" && canMergeAdjacentUserMessages(lastMessage, normalizedMessage)) {
388058
388076
  result[result.length - 1] = mergeUserMessages(lastMessage, normalizedMessage);
388059
388077
  return;
388060
388078
  }
@@ -388111,7 +388129,21 @@ function normalizeMessagesForAPI(messages, tools = []) {
388111
388129
  const attachmentMessage = checkStatsigFeatureGate_CACHED_MAY_BE_STALE("tengu_chair_sermon") ? rawAttachmentMessage.map(ensureSystemReminderWrap) : rawAttachmentMessage;
388112
388130
  const lastMessage = last_default(result);
388113
388131
  if (lastMessage?.type === "user") {
388114
- result[result.length - 1] = attachmentMessage.reduce((p4, c8) => mergeUserMessagesAndToolResults(p4, c8), lastMessage);
388132
+ const mergedUsers = [];
388133
+ let acc = lastMessage;
388134
+ for (const candidate of attachmentMessage) {
388135
+ if (canMergeAdjacentUserMessages(acc, candidate)) {
388136
+ acc = mergeUserMessagesAndToolResults(acc, candidate);
388137
+ } else {
388138
+ mergedUsers.push(acc);
388139
+ acc = candidate;
388140
+ }
388141
+ }
388142
+ mergedUsers.push(acc);
388143
+ result[result.length - 1] = mergedUsers[0];
388144
+ for (let i6 = 1;i6 < mergedUsers.length; i6 += 1) {
388145
+ result.push(mergedUsers[i6]);
388146
+ }
388115
388147
  return;
388116
388148
  }
388117
388149
  result.push(...attachmentMessage);
@@ -388122,8 +388154,7 @@ function normalizeMessagesForAPI(messages, tools = []) {
388122
388154
  const relocated = checkStatsigFeatureGate_CACHED_MAY_BE_STALE("tengu_toolref_defer_j8m") ? relocateToolReferenceSiblings(result) : result;
388123
388155
  const withFilteredOrphans = filterOrphanedThinkingOnlyMessages(relocated);
388124
388156
  const withFilteredThinking = filterTrailingThinkingFromLastAssistant(withFilteredOrphans);
388125
- const withFilteredWhitespace = filterWhitespaceOnlyAssistantMessages(withFilteredThinking);
388126
- const withNonEmpty = ensureNonEmptyAssistantContent(withFilteredWhitespace);
388157
+ const withNonEmpty = ensureNonEmptyAssistantContent(withFilteredThinking);
388127
388158
  const smooshed = checkStatsigFeatureGate_CACHED_MAY_BE_STALE("tengu_chair_sermon") ? smooshSystemReminderSiblings(mergeAdjacentUserMessages(withNonEmpty)) : withNonEmpty;
388128
388159
  const sanitized = sanitizeErrorToolResultContent(smooshed);
388129
388160
  if (false) {}
@@ -388131,10 +388162,14 @@ function normalizeMessagesForAPI(messages, tools = []) {
388131
388162
  return sanitized;
388132
388163
  }
388133
388164
  function mergeUserMessagesAndToolResults(a5, b5) {
388165
+ if (!canMergeAdjacentUserMessages(a5, b5)) {
388166
+ return b5;
388167
+ }
388134
388168
  const lastContent = normalizeUserTextContent(a5.message.content);
388135
388169
  const currentContent = normalizeUserTextContent(b5.message.content);
388136
388170
  return {
388137
388171
  ...a5,
388172
+ isMeta: a5.isMeta && b5.isMeta ? true : undefined,
388138
388173
  message: {
388139
388174
  ...a5.message,
388140
388175
  content: hoistToolResults(mergeUserContentBlocks(lastContent, currentContent))
@@ -388159,12 +388194,16 @@ function isToolResultMessage(msg) {
388159
388194
  return false;
388160
388195
  return content.some((block) => block.type === "tool_result");
388161
388196
  }
388197
+ function canMergeAdjacentUserMessages(a5, b5) {
388198
+ return Boolean(a5.isMeta) === Boolean(b5.isMeta);
388199
+ }
388162
388200
  function mergeUserMessages(a5, b5) {
388163
388201
  const lastContent = normalizeUserTextContent(a5.message.content);
388164
388202
  const currentContent = normalizeUserTextContent(b5.message.content);
388165
388203
  if (false) {}
388166
388204
  return {
388167
388205
  ...a5,
388206
+ isMeta: a5.isMeta && b5.isMeta ? true : undefined,
388168
388207
  uuid: a5.isMeta ? b5.uuid : a5.uuid,
388169
388208
  message: {
388170
388209
  ...a5.message,
@@ -388176,7 +388215,7 @@ function mergeAdjacentUserMessages(msgs) {
388176
388215
  const out = [];
388177
388216
  for (const m4 of msgs) {
388178
388217
  const prev = out.at(-1);
388179
- if (m4.type === "user" && prev?.type === "user") {
388218
+ if (m4.type === "user" && prev?.type === "user" && canMergeAdjacentUserMessages(prev, m4)) {
388180
388219
  out[out.length - 1] = mergeUserMessages(prev, m4);
388181
388220
  } else {
388182
388221
  out.push(m4);
@@ -389608,53 +389647,6 @@ function filterTrailingThinkingFromLastAssistant(messages) {
389608
389647
  };
389609
389648
  return result;
389610
389649
  }
389611
- function hasOnlyWhitespaceTextContent(content) {
389612
- if (content.length === 0) {
389613
- return false;
389614
- }
389615
- for (const block of content) {
389616
- if (block.type !== "text") {
389617
- return false;
389618
- }
389619
- if (block.text !== undefined && block.text.trim() !== "") {
389620
- return false;
389621
- }
389622
- }
389623
- return true;
389624
- }
389625
- function filterWhitespaceOnlyAssistantMessages(messages) {
389626
- let hasChanges = false;
389627
- const filtered = messages.filter((message) => {
389628
- if (message.type !== "assistant") {
389629
- return true;
389630
- }
389631
- const content = message.message.content;
389632
- if (!Array.isArray(content) || content.length === 0) {
389633
- return true;
389634
- }
389635
- if (hasOnlyWhitespaceTextContent(content)) {
389636
- hasChanges = true;
389637
- logEvent("tengu_filtered_whitespace_only_assistant", {
389638
- messageUUID: message.uuid
389639
- });
389640
- return false;
389641
- }
389642
- return true;
389643
- });
389644
- if (!hasChanges) {
389645
- return messages;
389646
- }
389647
- const merged = [];
389648
- for (const message of filtered) {
389649
- const prev = merged.at(-1);
389650
- if (message.type === "user" && prev?.type === "user") {
389651
- merged[merged.length - 1] = mergeUserMessages(prev, message);
389652
- } else {
389653
- merged.push(message);
389654
- }
389655
- }
389656
- return merged;
389657
- }
389658
389650
  function ensureNonEmptyAssistantContent(messages) {
389659
389651
  if (messages.length === 0) {
389660
389652
  return messages;
@@ -398792,27 +398784,28 @@ function getFirstMeaningfulUserMessageTextContent(transcript) {
398792
398784
  for (const textContent of texts) {
398793
398785
  if (!textContent)
398794
398786
  continue;
398795
- const commandNameTag = extractTag(textContent, COMMAND_NAME_TAG);
398787
+ const normalized = unwrapChannelWrappedPrompt(textContent);
398788
+ const commandNameTag = extractTag(normalized, COMMAND_NAME_TAG);
398796
398789
  if (commandNameTag) {
398797
398790
  const commandName = commandNameTag.replace(/^\//, "");
398798
398791
  if (builtInCommandNames().has(commandName)) {
398799
398792
  continue;
398800
398793
  } else {
398801
- const commandArgs = extractTag(textContent, "command-args")?.trim();
398794
+ const commandArgs = extractTag(normalized, "command-args")?.trim();
398802
398795
  if (!commandArgs) {
398803
398796
  continue;
398804
398797
  }
398805
398798
  return `${commandNameTag} ${commandArgs}`;
398806
398799
  }
398807
398800
  }
398808
- const bashInput = extractTag(textContent, "bash-input");
398801
+ const bashInput = extractTag(normalized, "bash-input");
398809
398802
  if (bashInput) {
398810
398803
  return `! ${bashInput}`;
398811
398804
  }
398812
- if (SKIP_FIRST_PROMPT_PATTERN2.test(textContent)) {
398805
+ if (SKIP_FIRST_PROMPT_PATTERN2.test(normalized)) {
398813
398806
  continue;
398814
398807
  }
398815
- return textContent;
398808
+ return normalized;
398816
398809
  }
398817
398810
  }
398818
398811
  return;
@@ -400394,10 +400387,12 @@ async function readLiteMetadata(filePath, fileSize, buf) {
400394
400387
  const projectPath = extractJsonStringField(head, "cwd");
400395
400388
  const teamName = extractJsonStringField(head, "teamName");
400396
400389
  const agentSetting = extractJsonStringField(head, "agentSetting");
400397
- const firstPrompt = extractLastJsonStringField(tail, "lastPrompt") || extractFirstPromptFromChunk(head) || extractJsonStringFieldPrefix(head, "content", 200) || extractJsonStringFieldPrefix(head, "text", 200) || "";
400390
+ const firstPrompt = extractLastMetadataStringField(tail, "last-prompt", "lastPrompt") || extractFirstPromptFromChunk(head) || extractJsonStringFieldPrefix(head, "content", 200) || extractJsonStringFieldPrefix(head, "text", 200) || "";
400398
400391
  const customTitle = extractLastJsonStringField(tail, "customTitle") ?? extractLastJsonStringField(head, "customTitle") ?? extractLastJsonStringField(tail, "aiTitle") ?? extractLastJsonStringField(head, "aiTitle");
400399
- const summary = extractLastJsonStringField(tail, "summary");
400400
- const tag2 = extractLastJsonStringField(tail, "tag");
400392
+ const summary = extractLastMetadataStringField(tail, "summary", "summary");
400393
+ const tagLine = tail.split(`
400394
+ `).findLast((l4) => l4.startsWith('{"type":"tag"'));
400395
+ const tag2 = tagLine ? extractLastJsonStringField(tagLine, "tag") || undefined : undefined;
400401
400396
  const gitBranch = extractLastJsonStringField(tail, "gitBranch") ?? extractJsonStringField(head, "gitBranch");
400402
400397
  const prUrl = extractLastJsonStringField(tail, "prUrl");
400403
400398
  const prRepository = extractLastJsonStringField(tail, "prRepository");
@@ -400468,7 +400463,9 @@ function extractFirstPromptFromChunk(chunk) {
400468
400463
  for (const text2 of texts) {
400469
400464
  if (!text2)
400470
400465
  continue;
400471
- let result = text2.replace(/\n/g, " ").trim();
400466
+ let result = unwrapChannelWrappedPrompt(text2).replace(/\n/g, " ").trim();
400467
+ if (!result)
400468
+ continue;
400472
400469
  const commandNameTag = extractTag(result, COMMAND_NAME_TAG);
400473
400470
  if (commandNameTag) {
400474
400471
  const name3 = commandNameTag.replace(/^\//, "");
@@ -405853,6 +405850,20 @@ function messagesToWsV3Envelopes(args) {
405853
405850
  }
405854
405851
 
405855
405852
  // src/server/api/sessionReplayController.ts
405853
+ async function isSessionChildRunning(agent, storageId) {
405854
+ const socket = process.env.WS_MAIN_RPC_SOCKET?.trim();
405855
+ if (!socket) {
405856
+ return false;
405857
+ }
405858
+ const statuses = await new MainRpcClient(socket).listSessionRuntimeStatus(agent);
405859
+ if (!statuses?.length) {
405860
+ return false;
405861
+ }
405862
+ return statuses.some((st) => {
405863
+ const key2 = resolveSessionTranscriptStorageId(st.sessionId) ?? st.sessionId;
405864
+ return key2 === storageId && st.running === true;
405865
+ });
405866
+ }
405856
405867
  async function handleSessionReplayApi(req, url3) {
405857
405868
  if (url3.pathname !== "/api/sessions/replay") {
405858
405869
  return;
@@ -405903,8 +405914,10 @@ async function handleSessionReplayApi(req, url3) {
405903
405914
  let messages = [];
405904
405915
  try {
405905
405916
  await access3(jsonlPath);
405917
+ const childRunning = await isSessionChildRunning(agent, storageId);
405906
405918
  const loaded = await loadConversationForResume(storageId, jsonlPath, {
405907
- headlessWsResume: true
405919
+ headlessWsResume: true,
405920
+ skipInterruptedPlaceholders: childRunning
405908
405921
  });
405909
405922
  messages = loaded?.messages ?? [];
405910
405923
  } catch (e5) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auto-ai/agent",
3
- "version": "2.1.189",
3
+ "version": "2.1.192",
4
4
  "description": "Auto AI Agent 网关 CLI(WebSocket,独立二进制,无需 Bun)",
5
5
  "type": "module",
6
6
  "maintainers": [
@@ -32,11 +32,11 @@
32
32
  "zod": "^4.3.6"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@auto-ai/agent-linux-x64": "2.1.189",
36
- "@auto-ai/agent-linux-arm64": "2.1.189",
37
- "@auto-ai/agent-darwin-x64": "2.1.189",
38
- "@auto-ai/agent-darwin-arm64": "2.1.189",
39
- "@auto-ai/agent-win-x64": "2.1.189"
35
+ "@auto-ai/agent-linux-x64": "2.1.192",
36
+ "@auto-ai/agent-linux-arm64": "2.1.192",
37
+ "@auto-ai/agent-darwin-x64": "2.1.192",
38
+ "@auto-ai/agent-darwin-arm64": "2.1.192",
39
+ "@auto-ai/agent-win-x64": "2.1.192"
40
40
  },
41
41
  "scripts": {
42
42
  "prepare": "node ../../scripts/sync-launcher-env.js",